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          * @return the type of the injectable
349          */
350         protected void initInjection (XmlParser.Node node, String jndiName, Class valueClass)
351         {
352             Iterator  itor = node.iterator("injection-target");
353             
354             while(itor.hasNext())
355             {
356                 XmlParser.Node injectionNode = (XmlParser.Node)itor.next(); 
357                 String targetClassName = injectionNode.getString("injection-target-class", false, true);
358                 String targetName = injectionNode.getString("injection-target-name", false, true);
359                 if ((targetClassName==null) || targetClassName.equals(""))
360                 {
361                     Log.warn("No classname found in injection-target");
362                     continue;
363                 }
364                 if ((targetName==null) || targetName.equals(""))
365                 {
366                     Log.warn("No field or method name in injection-target");
367                     continue;
368                 }
369 
370                 InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
371                 // comments in the javaee_5.xsd file specify that the targetName is looked
372                 // for first as a java bean property, then if that fails, as a field
373                 try
374                 {
375                     Class clazz = _context.loadClass(targetClassName);
376                     Injection injection = new Injection();
377                     injection.setJndiName(jndiName);
378                     injection.setTarget(clazz, targetName, valueClass);
379                      injections.add(injection);
380                 }
381                 catch (ClassNotFoundException e)
382                 {
383                     Log.warn("Couldn't load injection target class "+targetClassName);
384                 }
385             }
386         }
387     }
388     
389   
390     
391     public void preConfigure (WebAppContext context)
392     throws Exception
393     {
394       //set up our special ServletHandler to remember injections and lifecycle callbacks
395         ServletHandler servletHandler = new ServletHandler();
396         SecurityHandler securityHandler = context.getSecurityHandler();
397         org.eclipse.jetty.servlet.ServletHandler existingHandler = context.getServletHandler(); 
398         servletHandler.setFilters(existingHandler.getFilters());
399         servletHandler.setFilterMappings(existingHandler.getFilterMappings());    
400         servletHandler.setServlets(existingHandler.getServlets());
401         servletHandler.setServletMappings(existingHandler.getServletMappings());
402         context.setServletHandler(servletHandler);
403         securityHandler.setHandler(servletHandler);  
404         
405         LifeCycleCallbackCollection callbacks = new LifeCycleCallbackCollection();
406         context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks);
407         InjectionCollection injections = new InjectionCollection();
408         context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
409         RunAsCollection runAsCollection = new RunAsCollection();
410         context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);  
411     }
412    
413     public void postConfigure(WebAppContext context) throws Exception
414     {
415         context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, null);
416         context.setAttribute(InjectionCollection.INJECTION_COLLECTION, null);
417         context.setAttribute(RunAsCollection.RUNAS_COLLECTION, null); 
418     }
419 
420     public void configure (WebAppContext context)
421     throws Exception
422     {
423         bindUserTransaction(context);
424         
425         WebXmlProcessor webXmlProcessor = (WebXmlProcessor)context.getAttribute(WebXmlProcessor.WEB_PROCESSOR); 
426         if (webXmlProcessor == null)
427            throw new IllegalStateException ("No processor for web xml");
428 
429         //TODO: When webdefaults.xml, web.xml, fragments and web-override.xml are merged into an effective web.xml this 
430         //will change
431         PlusWebXmlProcessor plusProcessor = new PlusWebXmlProcessor(context);
432         plusProcessor.process(webXmlProcessor.getWebDefault());
433         plusProcessor.process(webXmlProcessor.getWebXml());
434 
435         //Process plus-elements of each descriptor
436         for (Descriptor frag: webXmlProcessor.getFragments())
437         {
438             plusProcessor.process(frag);
439         }
440 
441         //process the override-web.xml descriptor
442         plusProcessor.process(webXmlProcessor.getOverrideWeb());
443         
444         
445         //configure injections and callbacks to be called by the FilterHolder and ServletHolder
446         //when they lazily instantiate the Filter/Servlet.
447         ((ServletHandler)context.getServletHandler()).setInjections((InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION));
448         ((ServletHandler)context.getServletHandler()).setCallbacks((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION));
449         
450         //do any injects on the listeners that were created and then
451         //also callback any postConstruct lifecycle methods
452         injectAndCallPostConstructCallbacks(context);
453     }
454     
455     public void deconfigure (WebAppContext context)
456     throws Exception
457     {
458         //call any preDestroy methods on the listeners
459         callPreDestroyCallbacks(context);       
460     }
461      
462     
463     
464     protected void injectAndCallPostConstructCallbacks(WebAppContext context)
465     throws Exception
466     {
467         InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
468         RunAsCollection runAsCollection = (RunAsCollection)context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
469         LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
470         SecurityHandler securityHandler = context.getSecurityHandler();
471         
472         //look thru the servlets to apply any runAs annotations
473         //NOTE: that any run-as in web.xml will already have been applied
474         if (runAsCollection != null)
475         {
476             ServletHolder[] holders = context.getServletHandler().getServlets();
477             for (int i=0;holders!=null && i<holders.length;i++)
478             {
479                 runAsCollection.setRunAs(holders[i], securityHandler);
480             }
481         }
482 
483         EventListener[] listeners = context.getEventListeners();
484         for (int i=0;listeners!=null && i<listeners.length;i++)
485         {
486             if (injections != null)
487                 injections.inject(listeners[i]);
488             if (callbacks != null)
489                 callbacks.callPostConstructCallback(listeners[i]);
490         }
491     }
492     
493     
494     protected void callPreDestroyCallbacks (WebAppContext context)
495     throws Exception
496     {   
497         LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
498 
499         if (callbacks != null)
500         {
501             EventListener[] listeners = context.getEventListeners();
502             for (int i=0; listeners!=null && i<listeners.length;i++)
503             {
504                 callbacks.callPreDestroyCallback(listeners[i]);
505             }
506         }
507     }
508    
509 }