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 }