Coverage Report - org.webmacro.directive.BeanDirective
 
Classes in this File Line Coverage Branch Coverage Complexity
BeanDirective
9%
11/115
0%
0/40
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  
 }