View Javadoc

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