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