View Javadoc

    1   // ========================================================================
    2   // $Id: AbstractConfiguration.java 5533 2009-09-17 03:44:34Z janb $
    3   // Copyright 2006 Mort Bay Consulting Pty. Ltd.
    4   // ------------------------------------------------------------------------
    5   // Licensed under the Apache License, Version 2.0 (the "License");
    6   // you may not use this file except in compliance with the License.
    7   // You may obtain a copy of the License at
    8   // http://www.apache.org/licenses/LICENSE-2.0
    9   // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  // ========================================================================
    15
    16  package org.mortbay.jetty.plus.webapp;
    17
    18
    19  import java.util.EventListener;
    20  import java.util.Iterator;
    21
    22  import javax.servlet.UnavailableException;
    23
    24
    25  import org.mortbay.jetty.plus.annotation.Injection;
    26  import org.mortbay.jetty.plus.annotation.InjectionCollection;
    27  import org.mortbay.jetty.plus.annotation.LifeCycleCallback;
    28  import org.mortbay.jetty.plus.annotation.LifeCycleCallbackCollection;
    29  import org.mortbay.jetty.plus.annotation.PostConstructCallback;
    30  import org.mortbay.jetty.plus.annotation.PreDestroyCallback;
    31  import org.mortbay.jetty.plus.annotation.RunAsCollection;
    32  import org.mortbay.jetty.plus.servlet.ServletHandler;
    33  import org.mortbay.jetty.security.SecurityHandler;
    34  import org.mortbay.jetty.servlet.FilterHolder;
    35  import org.mortbay.jetty.servlet.FilterMapping;
    36  import org.mortbay.jetty.servlet.ServletHolder;
    37  import org.mortbay.jetty.servlet.ServletMapping;
    38  import org.mortbay.jetty.webapp.WebAppContext;
    39  import org.mortbay.jetty.webapp.WebXmlConfiguration;
    40  import org.mortbay.log.Log;
    41  import org.mortbay.util.TypeUtil;
    42  import org.mortbay.xml.XmlParser;
    43
    44
    45
    46  /**
    47   * Configuration
    48   *
    49   *
    50   */
    51  public abstract class AbstractConfiguration extends WebXmlConfiguration
    52  {
    53
    54      protected LifeCycleCallbackCollection _callbacks = new LifeCycleCallbackCollection();
    55      protected InjectionCollection _injections = new InjectionCollection();
    56      protected RunAsCollection _runAsCollection = new RunAsCollection();
    57
    58      private boolean _metadataComplete = true;
    59
    60      public abstract void bindEnvEntry (String name, Object value) throws Exception;
    61
    62      public abstract void bindResourceRef (String name, Class type) throws Exception;
    63
    64      public abstract void bindResourceEnvRef (String name, Class type) throws Exception;
    65
    66      public abstract void bindUserTransaction () throws Exception;
    67
    68      public abstract void bindMessageDestinationRef (String name, Class type)  throws Exception;
    69
    70
    71      public void setWebAppContext (WebAppContext context)
    72      {
    73          super.setWebAppContext(context);
    74
    75          //set up our special ServletHandler to remember injections and lifecycle callbacks
    76          ServletHandler servletHandler = new ServletHandler();
    77          SecurityHandler securityHandler = getWebAppContext().getSecurityHandler();
    78          org.mortbay.jetty.servlet.ServletHandler existingHandler = getWebAppContext().getServletHandler();
    79          servletHandler.setFilters(existingHandler.getFilters());
    80          servletHandler.setFilterMappings(existingHandler.getFilterMappings());
    81          servletHandler.setServlets(existingHandler.getServlets());
    82          servletHandler.setServletMappings(existingHandler.getServletMappings());
    83          getWebAppContext().setServletHandler(servletHandler);
    84          securityHandler.setHandler(servletHandler);
    85      }
    86
    87      public void configureDefaults ()
    88      throws Exception
    89      {
    90          super.configureDefaults();
    91      }
    92
    93      public void configureWebApp ()
    94      throws Exception
    95      {
    96          super.configureWebApp();
    97          bindUserTransaction();
    98      }
    99
    100     public void deconfigureWebApp()
    101     throws Exception
    102     {
    103         //call any preDestroy methods on the listeners
    104         callPreDestroyCallbacks();
    105
    106         super.deconfigureWebApp();
    107     }
    108
    109     public void configure(String webXml)
    110     throws Exception
    111     {
    112         //parse web.xml
    113         super.configure(webXml);
    114
    115         //parse classes for annotations, if necessary
    116         if (!_metadataComplete)
    117         {
    118             if (Log.isDebugEnabled()) Log.debug("Processing annotations");
    119             parseAnnotations();
    120         }
    121         //do any injects on the listeners that were created and then
    122         //also callback any postConstruct lifecycle methods
    123         injectAndCallPostConstructCallbacks();
    124     }
    125
    126
    127
    128
    129
    130     protected void initialize(XmlParser.Node config)
    131     throws ClassNotFoundException,UnavailableException
    132     {
    133         super.initialize(config);
    134
    135         //configure injections and callbacks to be called by the FilterHolder and ServletHolder
    136         //when they lazily instantiate the Filter/Servlet.
    137         ((ServletHandler)getWebAppContext().getServletHandler()).setInjections(_injections);
    138         ((ServletHandler)getWebAppContext().getServletHandler()).setCallbacks(_callbacks);
    139
    140         //find out if we need to process annotations
    141         //  servlet 2.5 web.xml && metadata-complete==false
    142         if (_version == 25)
    143             _metadataComplete = Boolean.valueOf((String)config.getAttribute("metadata-complete", "false")).booleanValue();
    144     }
    145
    146
    147     protected void initWebXmlElement(String element,XmlParser.Node node) throws Exception
    148     {
    149         if ("env-entry".equals(element))
    150         {
    151             initEnvEntry (node);
    152         }
    153         else if ("resource-ref".equals(element))
    154         {
    155             //resource-ref entries are ONLY for connection factories
    156             //the resource-ref says how the app will reference the jndi lookup relative
    157             //to java:comp/env, but it is up to the deployer to map this reference to
    158             //a real resource in the environment. At the moment, we insist that the
    159             //jetty.xml file name of the resource has to be exactly the same as the
    160             //name in web.xml deployment descriptor, but it shouldn't have to be
    161             initResourceRef(node);
    162         }
    163         else if ("resource-env-ref".equals(element))
    164         {
    165             //resource-env-ref elements are a non-connection factory type of resource
    166             //the app looks them up relative to java:comp/env
    167             //again, need a way for deployer to link up app naming to real naming.
    168             //Again, we insist now that the name of the resource in jetty.xml is
    169             //the same as web.xml
    170             initResourceEnvRef(node);
    171         }
    172         else if ("message-destination-ref".equals(element))
    173         {
    174             initMessageDestinationRef(node);
    175         }
    176         else if ("post-construct".equals(element))
    177         {
    178             //post-construct is the name of a class and method to call after all
    179             //resources have been setup but before the class is put into use
    180             initPostConstruct(node);
    181         }
    182         else if ("pre-destroy".equals(element))
    183         {
    184             //pre-destroy is the name of a class and method to call just as
    185             //the instance is being destroyed
    186             initPreDestroy(node);
    187         }
    188         else
    189         {
    190             super.initWebXmlElement(element, node);
    191         }
    192
    193     }
    194
    195     /**
    196      * JavaEE 5.4.1.3
    197      *
    198      *
    199      * @param node
    200      * @throws Exception
    201      */
    202     protected void initEnvEntry (XmlParser.Node node)
    203     throws Exception
    204     {
    205         String name=node.getString("env-entry-name",false,true);
    206         String type = node.getString("env-entry-type",false,true);
    207         String valueStr = node.getString("env-entry-value",false,true);
    208
    209         //if there's no value there's no point in making a jndi entry
    210         //nor processing injection entries
    211         if (valueStr==null || valueStr.equals(""))
    212         {
    213             Log.warn("No value for env-entry-name "+name);
    214             return;
    215         }
    216
    217         //the javaee_5.xsd says that the env-entry-type is optional
    218         //if there is an <injection> element, because you can get
    219         //type from the element, but what to do if there is more
    220         //than one <injection> element, do you just pick the type
    221         //of the first one?
    222
    223         //check for <injection> elements
    224         initInjection (node, name, TypeUtil.fromName(type));
    225
    226         //bind the entry into jndi
    227         Object value = TypeUtil.valueOf(type,valueStr);
    228         bindEnvEntry(name, value);
    229
    230     }
    231
    232
    233     /**
    234      * Common Annotations Spec section 2.3:
    235      *  resource-ref is for:
    236      *    - javax.sql.DataSource
    237      *    - javax.jms.ConnectionFactory
    238      *    - javax.jms.QueueConnectionFactory
    239      *    - javax.jms.TopicConnectionFactory
    240      *    - javax.mail.Session
    241      *    - java.net.URL
    242      *    - javax.resource.cci.ConnectionFactory
    243      *    - org.omg.CORBA_2_3.ORB
    244      *    - any other connection factory defined by a resource adapter
    245      * @param node
    246      * @throws Exception
    247      */
    248     protected void initResourceRef (XmlParser.Node node)
    249     throws Exception
    250     {
    251         String jndiName = node.getString("res-ref-name",false,true);
    252         String type = node.getString("res-type", false, true);
    253         String auth = node.getString("res-auth", false, true);
    254         String shared = node.getString("res-sharing-scope", false, true);
    255
    256         //check for <injection> elements
    257         Class typeClass = TypeUtil.fromName(type);
    258         if (typeClass==null)
    259             typeClass = getWebAppContext().loadClass(type);
    260         initInjection (node, jndiName, typeClass);
    261
    262         bindResourceRef(jndiName, typeClass);
    263     }
    264
    265
    266     /**
    267      * Common Annotations Spec section 2.3:
    268      *   resource-env-ref is for:
    269      *     - javax.transaction.UserTransaction
    270      *     - javax.resource.cci.InteractionSpec
    271      *     - anything else that is not a connection factory
    272      * @param node
    273      * @throws Exception
    274      */
    275     protected void initResourceEnvRef (XmlParser.Node node)
    276     throws Exception
    277     {
    278         String jndiName = node.getString("resource-env-ref-name",false,true);
    279         String type = node.getString("resource-env-ref-type", false, true);
    280
    281         //check for <injection> elements
    282
    283         //JavaEE Spec sec 5.7.1.3 says the resource-env-ref-type
    284         //is mandatory, but the schema says it is optional!
    285         Class typeClass = TypeUtil.fromName(type);
    286         if (typeClass==null)
    287             typeClass = getWebAppContext().loadClass(type);
    288         initInjection (node, jndiName, typeClass);
    289
    290         bindResourceEnvRef(jndiName, typeClass);
    291     }
    292
    293
    294     /**
    295      * Common Annotations Spec section 2.3:
    296      *   message-destination-ref is for:
    297      *     - javax.jms.Queue
    298      *     - javax.jms.Topic
    299      * @param node
    300      * @throws Exception
    301      */
    302     protected void initMessageDestinationRef (XmlParser.Node node)
    303     throws Exception
    304     {
    305         String jndiName = node.getString("message-destination-ref-name",false,true);
    306         String type = node.getString("message-destination-type",false,true);
    307         String usage = node.getString("message-destination-usage",false,true);
    308
    309         Class typeClass = TypeUtil.fromName(type);
    310         if (typeClass==null)
    311             typeClass = getWebAppContext().loadClass(type);
    312         initInjection(node, jndiName, typeClass);
    313
    314         bindMessageDestinationRef(jndiName, typeClass);
    315     }
    316
    317
    318
    319     /**
    320      * Process &lt;post-construct&gt;
    321      * @param node
    322      */
    323     protected void initPostConstruct(XmlParser.Node node)
    324     {
    325         String className = node.getString("lifecycle-callback-class", false, true);
    326         String methodName = node.getString("lifecycle-callback-method", false, true);
    327
    328         if (className==null || className.equals(""))
    329         {
    330             Log.warn("No lifecycle-callback-class specified");
    331             return;
    332         }
    333         if (methodName==null || methodName.equals(""))
    334         {
    335             Log.warn("No lifecycle-callback-method specified for class "+className);
    336             return;
    337         }
    338
    339         try
    340         {
    341             Class clazz = getWebAppContext().loadClass(className);
    342             LifeCycleCallback callback = new PostConstructCallback();
    343             callback.setTarget(clazz, methodName);
    344             _callbacks.add(callback);
    345         }
    346         catch (ClassNotFoundException e)
    347         {
    348             Log.warn("Couldn't load post-construct target class "+className);
    349         }
    350     }
    351
    352
    353     /**
    354      * Process &lt;pre-destroy&gt;
    355      * @param node
    356      */
    357     protected void initPreDestroy(XmlParser.Node node)
    358     {
    359         String className = node.getString("lifecycle-callback-class", false, true);
    360         String methodName = node.getString("lifecycle-callback-method", false, true);
    361         if (className==null || className.equals(""))
    362         {
    363             Log.warn("No lifecycle-callback-class specified for pre-destroy");
    364             return;
    365         }
    366         if (methodName==null || methodName.equals(""))
    367         {
    368             Log.warn("No lifecycle-callback-method specified for pre-destroy class "+className);
    369             return;
    370         }
    371
    372         try
    373         {
    374             Class clazz = getWebAppContext().loadClass(className);
    375             LifeCycleCallback callback = new PreDestroyCallback();
    376             callback.setTarget(clazz, methodName);
    377             _callbacks.add(callback);
    378         }
    379         catch (ClassNotFoundException e)
    380         {
    381             Log.warn("Couldn't load pre-destory target class "+className);
    382         }
    383     }
    384
    385
    386     /**
    387      * Iterate over the &lt;injection-target&gt; entries for a node
    388      *
    389      * @param node
    390      * @param jndiName
    391      * @param valueClass
    392      * @return the type of the injectable
    393      */
    394     protected void initInjection (XmlParser.Node node, String jndiName, Class valueClass)
    395     {
    396         Iterator  itor = node.iterator("injection-target");
    397
    398         while(itor.hasNext())
    399         {
    400             XmlParser.Node injectionNode = (XmlParser.Node)itor.next();
    401             String targetClassName = injectionNode.getString("injection-target-class", false, true);
    402             String targetName = injectionNode.getString("injection-target-name", false, true);
    403             if ((targetClassName==null) || targetClassName.equals(""))
    404             {
    405                 Log.warn("No classname found in injection-target");
    406                 continue;
    407             }
    408             if ((targetName==null) || targetName.equals(""))
    409             {
    410                 Log.warn("No field or method name in injection-target");
    411                 continue;
    412             }
    413
    414             // comments in the javaee_5.xsd file specify that the targetName is looked
    415             // for first as a java bean property, then if that fails, as a field
    416             try
    417             {
    418                 Class clazz = getWebAppContext().loadClass(targetClassName);
    419                 Injection injection = new Injection();
    420                 injection.setTargetClass(clazz);
    421                 injection.setJndiName(jndiName);
    422                 injection.setTarget(clazz, targetName, valueClass);
    423                  _injections.add(injection);
    424             }
    425             catch (ClassNotFoundException e)
    426             {
    427                 Log.warn("Couldn't load injection target class "+targetClassName);
    428             }
    429         }
    430     }
    431
    432
    433     /**
    434      * Parse all classes that are mentioned in web.xml (servlets, filters, listeners)
    435      * for annotations.
    436      *
    437      *
    438      *
    439      * @throws Exception
    440      */
    441     protected abstract void parseAnnotations () throws Exception;
    442
    443
    444
    445     protected void injectAndCallPostConstructCallbacks()
    446     throws Exception
    447     {
    448         //look thru the servlets to apply any runAs annotations
    449         //NOTE: that any run-as in web.xml will already have been applied
    450         ServletHolder[] holders = getWebAppContext().getServletHandler().getServlets();
    451         for (int i=0;holders!=null && i<holders.length;i++)
    452         {
    453             _runAsCollection.setRunAs(holders[i]);
    454         }
    455
    456
    457         EventListener[] listeners = getWebAppContext().getEventListeners();
    458         for (int i=0;listeners!=null && i<listeners.length;i++)
    459         {
    460             _injections.inject(listeners[i]);
    461             _callbacks.callPostConstructCallback(listeners[i]);
    462         }
    463     }
    464
    465
    466     protected void callPreDestroyCallbacks ()
    467     throws Exception
    468     {
    469         EventListener[] listeners = getWebAppContext().getEventListeners();
    470         for (int i=0; listeners!=null && i<listeners.length;i++)
    471         {
    472             _callbacks.callPreDestroyCallback(listeners[i]);
    473         }
    474     }
    475
    476 }