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