##### Coverage Report - org.webmacro.directive.BeanDirective

Classes in this File | Line Coverage | Branch Coverage | Complexity
BeanDirective |
9% | 11/115
0% | 0/40
| 3.7;3.7

1 |  |
     /*
2 |  |
      * Copyright (C) 1998-2001 Semiotek Inc.  All Rights Reserved.
3 |  |
      *
4 |  |
      * Redistribution and use in source and binary forms, with or without
5 |  |
      * modification, are permitted under the terms of either of the following
6 |  |
      * Open Source licenses:
7 |  |
      *
8 |  |
      * The GNU General Public License, version 2, or any later version, as
9 |  |
      * published by the Free Software Foundation
10 |  |
      * (http://www.fsf.org/copyleft/gpl.html);
11 |  |
      *
12 |  |
      *  or
13 |  |
      *
14 |  |
      * The Semiotek Public License (http://webmacro.org/LICENSE.)
15 |  |
      *
16 |  |
      * This software is provided "as is", with NO WARRANTY, not even the
17 |  |
      * implied warranties of fitness to purpose, or merchantability. You
18 |  |
      * assume all risks and liabilities associated with its use.
19 |  |
      *
20 |  |
      * See www.webmacro.org for more information on the WebMacro project.
21 |  |
      */
22 |  |
23 |  |
     package org.webmacro.directive;
24 |  |
25 |  |
     /**
26 |  |
      * This directive allows the instantiation of objects in WMScript.
27 |  |
      */
28 |  |
29 |  |
     import java.io.IOException;
30 |  |
     import java.util.HashMap;
31 |  |
     import java.util.Map;
32 |  |
33 |  |
     import org.slf4j.Logger;
34 |  |
     import org.slf4j.LoggerFactory;
35 |  |
     import org.webmacro.Broker;
36 |  |
     import org.webmacro.Context;
37 |  |
     import org.webmacro.FastWriter;
38 |  |
     import org.webmacro.Macro;
39 |  |
     import org.webmacro.PropertyException;
40 |  |
     import org.webmacro.TemplateVisitor;
41 |  |
     import org.webmacro.WebMacroException;
42 |  |
     import org.webmacro.engine.Block;
43 |  |
     import org.webmacro.engine.BuildContext;
44 |  |
     import org.webmacro.engine.BuildException;
45 |  |
     import org.webmacro.engine.Builder;
46 |  |
     import org.webmacro.engine.UndefinedMacro;
47 |  |
     import org.webmacro.engine.Variable;
48 |  |
     import org.webmacro.util.Instantiator;
49 |  |
50 |  |
     /**
51 |  |
      * Implements a directive allowing a script writer
52 |  |
      * to instantiate an arbitrary
53 |  |
      * java object in a WebMacro template subject to
54 |  |
      * security restrictions.
55 |  |
      */
56 |  |
57 |  |
     public class BeanDirective extends Directive
58 |  |
     {
59 |  |
60 | 2 |
                static Logger _log =  LoggerFactory.getLogger(BeanDirective.class);
61 |  |
62 |  |
        private static final String APP_BEANS_KEY = "org.webmacro.directive.BeanDirective.appBeans";
63 |  |
64 |  |
        private static final int BEAN_TARGET = 1;
65 |  |
66 |  |
        private static final int BEAN_CLASS_NAME = 2;
67 |  |
68 |  |
        private static final int BEAN_SCOPE = 3;
69 |  |
70 |  |
        private static final int BEAN_SCOPE_GLOBAL = 4;
71 |  |
72 |  |
        private static final int BEAN_SCOPE_APPLICATION = 5;
73 |  |
74 |  |
        private static final int BEAN_SCOPE_SESSION = 6;
75 |  |
76 |  |
        private static final int BEAN_SCOPE_PAGE = 7;
77 |  |
78 |  |
        private static final int BEAN_INITARGS = 8;
79 |  |
80 |  |
        private static final int BEAN_INITARGS_VAL = 9;
81 |  |
82 |  |
        private static final int BEAN_TYPE_STATIC = 10;
83 |  |
84 |  |
        private static final int BEAN_ON_NEW = 11;
85 |  |
86 |  |
        private static final int BEAN_ON_NEW_BLOCK = 12;
87 |  |
88 | 2 |
        private static final UndefinedMacro UNDEF = UndefinedMacro.getInstance();
89 |  |
90 | 2 |
        private static int[] BEAN_SCOPES =
91 |  |
        { BEAN_SCOPE_GLOBAL, BEAN_SCOPE_APPLICATION, BEAN_SCOPE_SESSION,
92 |  |
                 BEAN_SCOPE_PAGE };
93 |  |
94 | 2 |
        static Map globalBeans = new HashMap(20);
95 |  |
96 |  |
        private Variable target;
97 |  |
98 |  |
        private String targetName;
99 |  |
100 |  |
        private String _className;
101 |  |
102 |  |
        private int scope;
103 |  |
104 |  |
        private boolean isStaticClass;
105 |  |
106 |  |
        private Object initArgObj;
107 |  |
108 |  |
        private Object[] initArgs;
109 |  |
110 |  |
        private Block onNewBlock;
111 |  |
112 |  |
        private Broker _broker;
113 |  |
114 | 2 |
        private static final ArgDescriptor[] myArgs = new ArgDescriptor[]
115 |  |
        { new LValueArg(BEAN_TARGET), new AssignmentArg(),
116 |  |
                 new QuotedStringArg(BEAN_CLASS_NAME), new OptionalGroup(3),
117 |  |
                 new KeywordArg(BEAN_SCOPE, "scope"), new AssignmentArg(),
118 |  |
                 new SingleOptionChoice(5), new OptionalGroup(1),
119 |  |
                 new KeywordArg(BEAN_SCOPE_GLOBAL, "global"), new OptionalGroup(1),
120 |  |
                 new KeywordArg(BEAN_SCOPE_APPLICATION, "application"),
121 |  |
                 new OptionalGroup(1),
122 |  |
                 new KeywordArg(BEAN_SCOPE_SESSION, "session"),
123 |  |
                 new OptionalGroup(1), new KeywordArg(BEAN_SCOPE_PAGE, "page"),
124 |  |
                 new OptionalGroup(1), new KeywordArg(BEAN_TYPE_STATIC, "static"),
125 |  |
                 new OptionalGroup(2), new KeywordArg(BEAN_INITARGS, "initArgs"),
126 |  |
                 new RValueArg(BEAN_INITARGS_VAL), new OptionalGroup(2),
127 |  |
                 new KeywordArg(BEAN_ON_NEW, "onNew"),
128 |  |
                 new BlockArg(BEAN_ON_NEW_BLOCK) };
129 |  |
130 | 2 |
        private static final DirectiveDescriptor myDescr = new DirectiveDescriptor(
131 |  |
                 "bean", null, myArgs, null);
132 |  |
133 |  |
        public static DirectiveDescriptor getDescriptor()
134 |  |
        {
135 | 6 |
           return myDescr;
136 |  |
        }
137 |  |
138 |  |
        public BeanDirective()
139 | 0 |
        {
140 | 0 |
        }
141 |  |
142 |  |
        public Object build(DirectiveBuilder builder, BuildContext bc)
143 |  |
                 throws BuildException
144 |  |
        {
145 | 0 |
           _broker = bc.getBroker();
146 |  |
           // appBeans map is created by the init method when this directive
147 |  |
           // is registered by the DirectiveProvider
148 |  |
           try
149 |  |
           {
150 | 0 |
              target = (Variable) builder.getArg(BEAN_TARGET, bc);
151 |  |
           }
152 | 0 |
           catch (ClassCastException e)
153 |  |
           {
154 | 0 |
              throw new NotVariableBuildException(myDescr.name, e);
155 | 0 |
           }
156 | 0 |
           targetName = target.getName();
157 | 0 |
           _className = (String) builder.getArg(BEAN_CLASS_NAME, bc);
158 | 0 |
           classForName(_className);
159 |  |
160 |  |
           // check if bean is declared as static
161 |  |
           // this implies global scope and no constructor invocation
162 | 0 |
           isStaticClass = (builder.getArg(BEAN_TYPE_STATIC) != null);
163 | 0 |
           if (isStaticClass)
164 | 0 |
              scope = BEAN_SCOPE_GLOBAL;
165 |  |
           else
166 |  |
           {
167 | 0 |
              scope = getScope(builder, bc);
168 |  |
              // initArgs is only valid for non-static beans
169 | 0 |
              initArgObj = builder.getArg(BEAN_INITARGS_VAL);
170 | 0 |
              if (initArgObj instanceof Builder)
171 | 0 |
                 initArgObj = ((Builder) initArgObj).build(bc);
172 |  |
           }
173 | 0 |
           onNewBlock = (Block) builder.getArg(BEAN_ON_NEW_BLOCK, bc);
174 |  |
175 | 0 |
           _log.debug("BeanDirective, target=" + target + ", className="
176 |  |
                    + _className + ", scope=" + scope + ", isStaticClass="
177 |  |
                    + isStaticClass + ", initArgs=" + initArgObj);
178 | 0 |
           return this;
179 |  |
        }
180 |  |
181 |  |
        public void write(FastWriter out, Context context) throws PropertyException,
182 |  |
                 IOException
183 |  |
        {
184 | 0 |
           Map appBeans = (Map) _broker.getBrokerLocal(APP_BEANS_KEY);
185 |  |
186 |  |
           // = beanConf.appBeans;
187 | 0 |
           boolean isNew = false;
188 |  |
189 |  |
           try
190 |  |
           {
191 | 0 |
              while (initArgObj instanceof Macro && initArgObj != UNDEF)
192 | 0 |
                 initArgObj = ((Macro) initArgObj).evaluate(context);
193 |  |
194 |  |
              // store init args in array
195 | 0 |
              if (initArgObj == null || initArgObj.getClass().isArray())
196 |  |
              {
197 | 0 |
                 initArgs = (Object[]) initArgObj;
198 |  |
              }
199 |  |
              else
200 |  |
              {
201 | 0 |
                 initArgs = new Object[]
202 |  |
                 { initArgObj };
203 |  |
              }
204 |  |
205 | 0 |
              Object o = null;
206 | 0 |
              Class c = null;
207 | 0 |
              switch (scope)
208 |  |
              {
209 |  |
                 case BEAN_SCOPE_GLOBAL:
210 | 0 |
                    synchronized (globalBeans)
211 |  |
                    {
212 | 0 |
                       o = globalBeans.get(targetName);
213 | 0 |
                       if (o == null)
214 |  |
                       {
215 | 0 |
                          if (isStaticClass)
216 |  |
                          {
217 | 0 |
                             c = context.getBroker().classForName(_className);
218 | 0 |
                             o = new org.webmacro.engine.StaticClassWrapper(c);
219 |  |
                          }
220 |  |
                          else
221 |  |
                          {
222 |  |
                             // c = Class.forName(_className);
223 |  |
                             // o = c.newInstance();
224 | 0 |
                             o = instantiate(_className, initArgs);
225 |  |
                          }
226 | 0 |
                          isNew = true;
227 | 0 |
                          globalBeans.put(targetName, o);
228 |  |
                       }
229 | 0 |
                    }
230 | 0 |
                    break;
231 |  |
232 |  |
                 case BEAN_SCOPE_APPLICATION:
233 | 0 |
                    synchronized (appBeans)
234 |  |
                    {
235 | 0 |
                       o = appBeans.get(targetName);
236 | 0 |
                       if (o == null)
237 |  |
                       {
238 | 0 |
                          o = instantiate(_className, initArgs);
239 | 0 |
                          isNew = true;
240 | 0 |
                          appBeans.put(targetName, o);
241 |  |
                       }
242 | 0 |
                    }
243 | 0 |
                    break;
244 |  |
245 |  |
                 case BEAN_SCOPE_SESSION:
246 | 0 |
                    javax.servlet.http.HttpSession session = (javax.servlet.http.HttpSession) context
247 |  |
                             .getProperty("Session");
248 |  |
                    // if (context instanceof WebContext){
249 | 0 |
                    if (session != null)
250 |  |
                    {
251 | 0 |
                       synchronized (session)
252 |  |
                       {
253 | 0 |
                          o = session.getAttribute(targetName);
254 | 0 |
                          if (o == null)
255 |  |
                          {
256 | 0 |
                             o = instantiate(_className, initArgs);
257 | 0 |
                             isNew = true;
258 | 0 |
                             session.setAttribute(targetName, o);
259 |  |
                          }
260 | 0 |
                       }
261 |  |
                    }
262 |  |
                    else
263 |  |
                    {
264 | 0 |
                       PropertyException e = new PropertyException(
265 |  |
                                "#bean usage error: session scope is only valid with servlets!");
266 | 0 |
                       _broker.getEvaluationExceptionHandler().evaluate(target,
267 |  |
                                context, e);
268 |  |
                    }
269 | 0 |
                    break;
270 |  |
                 default:
271 |  |
                    // make "page" the default scope
272 |  |
                    // case BEAN_SCOPE_PAGE:
273 |  |
                    // NOTE: page beans always overwrite anything in the context
274 |  |
                    // with the same name
275 | 0 |
                    o = instantiate(_className, initArgs);
276 | 0 |
                    isNew = true;
277 | 0 |
                    if (o != null)
278 |  |
                    {
279 | 0 |
                       Class[] paramTypes =
280 |  |
                       { Context.class };
281 |  |
                       try
282 |  |
                       {
283 | 0 |
                          java.lang.reflect.Method m = o.getClass().getMethod(
284 |  |
                                   "init", paramTypes);
285 | 0 |
                          if (m != null)
286 |  |
                          {
287 | 0 |
                             Object[] args =
288 |  |
                             { context };
289 | 0 |
                             m.invoke(o, args);
290 |  |
                          }
291 |  |
                       }
292 | 0 |
                       catch (Exception e)
293 |  |
                       { // ignore
294 | 0 |
                       }
295 |  |
                    }
296 |  |
                    break;
297 |  |
              }
298 |  |
299 | 0 |
              _log.debug("BeanDirective: Class " + _className + " loaded.");
300 | 0 |
              target.setValue(context, o);
301 |  |
           }
302 | 0 |
           catch (PropertyException e)
303 |  |
           {
304 | 0 |
              this._broker.getEvaluationExceptionHandler().evaluate(target, context,
305 |  |
                       e);
306 |  |
           }
307 | 0 |
           catch (Exception e)
308 |  |
           {
309 | 0 |
              String errorText = "BeanDirective: Unable to load bean " + target
310 |  |
                       + " of type " + _className;
311 | 0 |
              writeWarning(errorText, context, out);
312 | 0 |
           }
313 | 0 |
           if (isNew && onNewBlock != null)
314 | 0 |
              onNewBlock.write(out, context);
315 |  |
316 | 0 |
        }
317 |  |
318 |  |
        public void accept(TemplateVisitor v)
319 |  |
        {
320 | 0 |
           v.beginDirective(myDescr.name);
321 | 0 |
           v.visitDirectiveArg("BeanTarget", target);
322 | 0 |
           v.visitDirectiveArg("BeanClass", _className);
323 | 0 |
           v.visitDirectiveArg("BeanScope", new Integer(scope));
324 | 0 |
           v.visitDirectiveArg("BeanIsStatic", new Boolean(isStaticClass));
325 | 0 |
           v.visitDirectiveArg("BeanInitArgs", initArgs);
326 | 0 |
           v.endDirective();
327 | 0 |
        }
328 |  |
329 |  |
        private Object instantiate(String className, Object[] args) throws Exception
330 |  |
        {
331 | 0 |
           Class c = classForName(className);
332 | 0 |
           return instantiate(c, args);
333 |  |
        }
334 |  |
335 |  |
        private Object instantiate(Class c, Object[] args) throws Exception
336 |  |
        {
337 | 0 |
           return Instantiator.getInstance(_broker).instantiate(c, args);
338 |  |
        }
339 |  |
340 |  |
        private static int getScope(DirectiveBuilder builder, BuildContext bc)
341 |  |
                 throws org.webmacro.engine.BuildException
342 |  |
        {
343 | 0 |
           int scope = -1;
344 |  |
345 | 0 |
           for (int i = 0; i < BEAN_SCOPES.length; i++)
346 |  |
           {
347 | 0 |
              scope = BEAN_SCOPES[i];
348 | 0 |
              if (builder.getArg(scope) != null)
349 | 0 |
                 break;
350 |  |
           }
351 | 0 |
           return scope;
352 |  |
        }
353 |  |
354 |  |
        public static void init(Broker b)
355 |  |
        {
356 |  |
           // get configuration parameters
357 | 6 |
           synchronized (b)
358 |  |
           {
359 | 6 |
              b.setBrokerLocal(APP_BEANS_KEY, new HashMap(20));
360 | 6 |
           }
361 | 6 |
        }
362 |  |
363 |  |
        private Class classForName(String className) throws BuildException
364 |  |
        {
365 |  |
366 |  |
           Class c;
367 |  |
           try
368 |  |
           {
369 | 0 |
              c = Instantiator.getInstance(_broker).classForName(className);
370 |  |
           }
371 | 0 |
           catch (WebMacroException e)
372 |  |
           {
373 | 0 |
              throw new BuildException("BeanDirective failed to load class \""
374 |  |
                       + className + "\"", e);
375 | 0 |
           }
376 | 0 |
           return c;
377 |  |
        }
378 |  |
     }