View Javadoc

1   // ========================================================================
2   // Copyright (c) 2006-2010 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  import java.util.Iterator;
17  
18  import javax.naming.Context;
19  import javax.naming.InitialContext;
20  import javax.naming.NameNotFoundException;
21  
22  import org.eclipse.jetty.jndi.NamingUtil;
23  import org.eclipse.jetty.plus.annotation.Injection;
24  import org.eclipse.jetty.plus.annotation.InjectionCollection;
25  import org.eclipse.jetty.plus.annotation.LifeCycleCallback;
26  import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection;
27  import org.eclipse.jetty.plus.annotation.PostConstructCallback;
28  import org.eclipse.jetty.plus.annotation.PreDestroyCallback;
29  import org.eclipse.jetty.plus.annotation.RunAsCollection;
30  import org.eclipse.jetty.plus.jndi.EnvEntry;
31  import org.eclipse.jetty.plus.jndi.Link;
32  import org.eclipse.jetty.plus.jndi.NamingEntry;
33  import org.eclipse.jetty.plus.jndi.NamingEntryUtil;
34  import org.eclipse.jetty.util.TypeUtil;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.webapp.Descriptor;
37  import org.eclipse.jetty.webapp.FragmentDescriptor;
38  import org.eclipse.jetty.webapp.IterativeDescriptorProcessor;
39  import org.eclipse.jetty.webapp.WebAppContext;
40  import org.eclipse.jetty.webapp.MetaData.Origin;
41  import org.eclipse.jetty.xml.XmlParser;
42  
43  /**
44   * PlusDescriptorProcessor
45   *
46   *
47   */
48  
49  public class PlusDescriptorProcessor extends IterativeDescriptorProcessor
50  {
51      public PlusDescriptorProcessor ()
52      {
53          try
54          {
55              registerVisitor("env-entry", getClass().getDeclaredMethod("visitEnvEntry", __signature));
56              registerVisitor("resource-ref", getClass().getDeclaredMethod("visitResourceRef", __signature));
57              registerVisitor("resource-env-ref", getClass().getDeclaredMethod("visitResourceEnvRef", __signature));
58              registerVisitor("message-destination-ref", getClass().getDeclaredMethod("visitMessageDestinationRef", __signature));
59              registerVisitor("post-construct", getClass().getDeclaredMethod("visitPostConstruct", __signature));
60              registerVisitor("pre-destroy", getClass().getDeclaredMethod("visitPreDestroy", __signature));
61          }
62          catch (Exception e)
63          {
64              throw new IllegalStateException(e);
65          }
66      }
67  
68      /** 
69       * @see org.eclipse.jetty.webapp.IterativeDescriptorProcessor#start(WebAppContext, org.eclipse.jetty.webapp.Descriptor)
70       */
71      public void start(WebAppContext context, Descriptor descriptor)
72      { 
73  
74          InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
75          if (injections == null)
76          {
77              injections = new InjectionCollection();
78              context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
79          }
80  
81          LifeCycleCallbackCollection callbacks = (LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION);
82          if (callbacks == null)
83          {
84              callbacks = new LifeCycleCallbackCollection();
85              context.setAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION, callbacks);
86          }
87  
88          RunAsCollection runAsCollection = (RunAsCollection)context.getAttribute(RunAsCollection.RUNAS_COLLECTION);
89          if (runAsCollection == null)
90          {
91              runAsCollection = new RunAsCollection();
92              context.setAttribute(RunAsCollection.RUNAS_COLLECTION, runAsCollection);  
93          }
94      }
95  
96      
97      /** 
98       * {@inheritDoc}
99       */
100     public void end(WebAppContext context,Descriptor descriptor)
101     {
102     }
103 
104     
105    
106     
107     /**
108      * JavaEE 5.4.1.3 
109      * 
110      * @param node
111      * @throws Exception
112      */
113     public void visitEnvEntry (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
114     throws Exception
115     {
116         String name=node.getString("env-entry-name",false,true);
117         String type = node.getString("env-entry-type",false,true);
118         String valueStr = node.getString("env-entry-value",false,true);
119         
120         //if there's no value there's no point in making a jndi entry
121         //nor processing injection entries
122         if (valueStr==null || valueStr.equals(""))
123         {
124             Log.warn("No value for env-entry-name "+name);
125             return;
126         }
127         
128         Origin o = context.getMetaData().getOrigin("env-entry."+name);
129         switch (o)
130         {
131             case NotSet:
132             {
133                 //no descriptor has configured an env-entry of this name previously
134                 context.getMetaData().setOrigin("env-entry."+name, descriptor);
135                 //the javaee_5.xsd says that the env-entry-type is optional
136                 //if there is an <injection> element, because you can get
137                 //type from the element, but what to do if there is more
138                 //than one <injection> element, do you just pick the type
139                 //of the first one?
140                 addInjections (context, descriptor, node, name, TypeUtil.fromName(type));
141                 Object value = TypeUtil.valueOf(type,valueStr);
142                 bindEnvEntry(name, value);   
143                 break;
144             }
145             case WebXml:
146             case WebDefaults:
147             case WebOverride:
148             {
149                 //ServletSpec 3.0 p75. web.xml (or web-override/web-defaults) declared
150                 //the env-entry. A fragment is not allowed to change that, except unless
151                 //the web.xml did not declare any injections.
152                 if (!(descriptor instanceof FragmentDescriptor))
153                 {
154                     //We're processing web-defaults, web.xml or web-override. Any of them can
155                     //set or change the env-entry.
156                     context.getMetaData().setOrigin("env-entry."+name, descriptor);
157                     addInjections (context, descriptor, node, name, TypeUtil.fromName(type));
158                     Object value = TypeUtil.valueOf(type,valueStr);
159                     bindEnvEntry(name, value);   
160                 }
161                 else
162                 {
163                     //A web.xml declared the env-entry. Check to see if any injections have been
164                     //declared for it. If it was declared in web.xml then don't merge any injections.
165                     //If it was declared in a web-fragment, then we can keep merging fragments.
166                     Descriptor d = context.getMetaData().getOriginDescriptor("env-entry."+name+".injection");
167                     if (d==null || d instanceof FragmentDescriptor)
168                         addInjections(context, descriptor, node, name, TypeUtil.fromName(type));
169                 }
170                 break;
171             }
172             case WebFragment:
173             {
174                 //ServletSpec p.75. No declaration in web.xml, but in multiple web-fragments. Error.
175                 throw new IllegalStateException("Conflicting env-entry "+name+" in "+descriptor.getResource());
176             }
177         }
178     }
179     
180     
181     /**
182      * Common Annotations Spec section 2.3:
183      *  resource-ref is for:
184      *    - javax.sql.DataSource
185      *    - javax.jms.ConnectionFactory
186      *    - javax.jms.QueueConnectionFactory
187      *    - javax.jms.TopicConnectionFactory
188      *    - javax.mail.Session
189      *    - java.net.URL
190      *    - javax.resource.cci.ConnectionFactory
191      *    - org.omg.CORBA_2_3.ORB
192      *    - any other connection factory defined by a resource adapter
193      *    
194      * TODO
195      * If web.xml contains a resource-ref with injection targets, all resource-ref entries
196      * of the same name are ignored in web fragments. If web.xml does not contain any
197      * injection-targets, then they are merged from all the fragments. 
198      * If web.xml does not contain a resource-ref element of same name, but 2 fragments
199      * declare the same name it is an error.
200      * resource-ref entries are ONLY for connection factories
201      * the resource-ref says how the app will reference the jndi lookup relative
202      * to java:comp/env, but it is up to the deployer to map this reference to
203      * a real resource in the environment. At the moment, we insist that the
204      * jetty.xml file name of the resource has to be exactly the same as the
205      * name in web.xml deployment descriptor, but it shouldn't have to be
206      * 
207      * Maintenance update 3.0a to spec:
208      *   Update Section 8.2.3.h.ii with the following -  If a resource reference 
209      *   element is specified in two fragments, while absent from the main web.xml, 
210      *   and all the attributes and child elements of the resource reference element 
211      *   are identical, the resource reference will be merged  into the main web.xml. 
212      *   It is considered an error if a resource reference element has the same name 
213      *   specified in two fragments, while absent from the main web.xml and the attributes 
214      *   and child elements are not identical in the two fragments. For example, if two 
215      *   web fragments declare a <resource-ref> with the same <resource-ref-name> element 
216      *   but the type in one is specified as javax.sql.DataSource while the type in the 
217      *   other is that of a java mail resource, then an error must be reported and the 
218      *   application MUST fail to deploy.
219      * 
220      * @param node
221      * @throws Exception
222      */
223     public void visitResourceRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
224     throws Exception
225     {
226         String jndiName = node.getString("res-ref-name",false,true);
227         String type = node.getString("res-type", false, true);
228         String auth = node.getString("res-auth", false, true);
229         String shared = node.getString("res-sharing-scope", false, true);
230         
231         Origin o = context.getMetaData().getOrigin("resource-ref."+jndiName);
232         switch (o)
233         {
234             case NotSet:
235             {
236                 //No descriptor or annotation previously declared a resource-ref of this name.
237                 context.getMetaData().setOrigin("resource-ref."+jndiName, descriptor);
238                 
239                 //check for <injection> elements
240                 Class<?> typeClass = TypeUtil.fromName(type);
241                 if (typeClass==null)
242                     typeClass = context.loadClass(type);
243                 addInjections(context, descriptor, node, jndiName, typeClass);                  
244                 bindResourceRef(context,jndiName, typeClass);
245                 break;
246             }
247             case WebXml:
248             case WebDefaults:
249             case WebOverride:   
250             {
251                 //A web xml previously declared the resource-ref.    
252                 if (!(descriptor instanceof FragmentDescriptor))
253                 {
254                     //We're processing web-defaults, web.xml or web-override. Any of them can
255                     //set or change the resource-ref.
256                     context.getMetaData().setOrigin("resource-ref."+jndiName, descriptor);
257      
258                     //check for <injection> elements
259                     Class<?> typeClass = TypeUtil.fromName(type);
260                     if (typeClass==null)
261                         typeClass = context.loadClass(type);
262                     
263                     addInjections(context, descriptor, node, jndiName, typeClass);
264                    
265                     //bind the entry into jndi
266                     bindResourceRef(context,jndiName, typeClass);
267                 }
268                 else
269                 {
270                     //A web xml declared the resource-ref and we're processing a 
271                     //web-fragment. Check to see if any injections were declared for it by web.xml. 
272                     //If any injection was declared in web.xml then don't merge any injections.
273                     //If it was declared in a web-fragment, then we can keep merging fragments.
274                     Descriptor d = context.getMetaData().getOriginDescriptor("resource-ref."+jndiName+".injection");
275                     if (d==null || d instanceof FragmentDescriptor)
276                     { 
277                         Class<?> typeClass = TypeUtil.fromName(type);
278                         if (typeClass==null)
279                             typeClass = context.loadClass(type);
280                         addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
281                     }
282                 }
283                 break;
284             }
285             case WebFragment:
286             { 
287                 Descriptor otherFragment = context.getMetaData().getOriginDescriptor("resource-ref."+jndiName);
288                 XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
289                 Iterator iter = otherFragmentRoot.iterator();
290                 XmlParser.Node otherNode = null;
291                 while (iter.hasNext() && otherNode == null)
292                 {
293                     Object obj = iter.next();
294                     if (!(obj instanceof XmlParser.Node)) continue;
295                     XmlParser.Node n = (XmlParser.Node)obj;
296                     if ("resource-ref".equals(n.getTag()) && jndiName.equals(n.getString("res-ref-name",false,true)))
297                         otherNode = n;
298                 }
299                 
300                 //If declared in another web-fragment
301                 if (otherNode != null)
302                 {
303                     //declarations of the resource-ref must be the same in both fragment descriptors
304                     String otherType = otherNode.getString("res-type", false, true);
305                     String otherAuth = otherNode.getString("res-auth", false, true);
306                     String otherShared = otherNode.getString("res-sharing-scope", false, true);
307                     
308                     //otherType, otherAuth and otherShared must be the same as type, auth, shared
309                     type = (type == null?"":type);
310                     otherType = (otherType == null?"":otherType);
311                     auth = (auth == null?"":auth);
312                     otherAuth = (otherAuth == null?"":otherAuth);
313                     shared = (shared == null?"":shared);
314                     otherShared = (otherShared == null?"":otherShared);
315                     
316                     //ServletSpec p.75. No declaration of resource-ref in web xml, but different in multiple web-fragments. Error.
317                     if (!type.equals(otherType) || !auth.equals(otherAuth) || !shared.equals(otherShared))
318                         throw new IllegalStateException("Conflicting resource-ref "+jndiName+" in "+descriptor.getResource());
319                     //same in multiple web-fragments, merge the injections
320                     addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
321                 }
322                 else
323                     throw new IllegalStateException("resource-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
324                 
325             }
326         }
327       
328     }
329     
330     
331     /**
332      * Common Annotations Spec section 2.3:
333      *   resource-env-ref is for:
334      *     - javax.transaction.UserTransaction
335      *     - javax.resource.cci.InteractionSpec
336      *     - anything else that is not a connection factory
337      *     
338      * @param node
339      * @throws Exception
340      */
341     public void visitResourceEnvRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
342     throws Exception
343     {
344         String jndiName = node.getString("resource-env-ref-name",false,true);
345         String type = node.getString("resource-env-ref-type", false, true);
346         
347         Origin o = context.getMetaData().getOrigin("resource-env-ref."+jndiName);
348         switch (o)
349         {
350             case NotSet:
351             {
352                 //First declaration of resource-env-ref with this jndiName
353                 //JavaEE Spec sec 5.7.1.3 says the resource-env-ref-type
354                 //is mandatory, but the schema says it is optional!
355                 Class<?> typeClass = TypeUtil.fromName(type);
356                 if (typeClass==null)
357                     typeClass = context.loadClass(type);
358                 addInjections (context, descriptor, node, jndiName, typeClass);
359                 bindResourceEnvRef(context,jndiName, typeClass);
360              break;
361             }
362             case WebXml:
363             case WebDefaults:
364             case WebOverride:   
365             {
366                 //A resource-env-ref of this name has been declared first in a web xml.
367                 //Only allow other web-default, web.xml, web-override to change it.
368                 if (!(descriptor instanceof FragmentDescriptor))
369                 {
370                     //We're processing web-defaults, web.xml or web-override. Any of them can
371                     //set or change the resource-env-ref.
372                     context.getMetaData().setOrigin("resource-env-ref."+jndiName, descriptor);
373                     Class<?> typeClass = TypeUtil.fromName(type);
374                     if (typeClass==null)
375                         typeClass = context.loadClass(type);
376                     addInjections (context, descriptor, node, jndiName, typeClass);
377                     bindResourceEnvRef(context,jndiName, typeClass); 
378                 }
379                 else
380                 {
381                     //We're processing a web-fragment. It can only contribute injections if the
382                     //there haven't been any injections declared yet, or they weren't declared in a WebXml file.
383                     Descriptor d = context.getMetaData().getOriginDescriptor("resource-env-ref."+jndiName+".injection");
384                     if (d == null || d instanceof FragmentDescriptor)
385                     {
386                         Class<?> typeClass = TypeUtil.fromName(type);
387                         if (typeClass==null)
388                             typeClass = context.loadClass(type);
389                         addInjections (context, descriptor, node, jndiName, typeClass);
390                     }
391                 }
392                 break;
393             }
394             case WebFragment:
395             {
396                 Descriptor otherFragment = context.getMetaData().getOriginDescriptor("resource-env-ref."+jndiName);
397                 XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
398                 Iterator iter = otherFragmentRoot.iterator();
399                 XmlParser.Node otherNode = null;
400                 while (iter.hasNext() && otherNode == null)
401                 {
402                     Object obj = iter.next();
403                     if (!(obj instanceof XmlParser.Node)) continue;
404                     XmlParser.Node n = (XmlParser.Node)obj;
405                     if ("resource-env-ref".equals(n.getTag()) && jndiName.equals(n.getString("resource-env-ref-name",false,true)))
406                         otherNode = n;
407                 }
408                 if (otherNode != null)
409                 {
410                     //declarations of the resource-ref must be the same in both fragment descriptors
411                     String otherType = otherNode.getString("resource-env-ref-type", false, true);
412 
413                     //types must be the same
414                     type = (type == null?"":type);
415                     otherType = (otherType == null?"":otherType);
416 
417                     //ServletSpec p.75. No declaration of resource-ref in web xml, but different in multiple web-fragments. Error.
418                     if (!type.equals(otherType))
419                         throw new IllegalStateException("Conflicting resource-env-ref "+jndiName+" in "+descriptor.getResource());   
420                     
421                     //same in multiple web-fragments, merge the injections
422                     addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
423                 }
424                 else
425                     throw new IllegalStateException("resource-env-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
426             }
427         }
428     }
429 
430 
431     /**
432      * Common Annotations Spec section 2.3:
433      *   message-destination-ref is for:
434      *     - javax.jms.Queue
435      *     - javax.jms.Topic
436      * @param node
437      * @throws Exception
438      */
439     public void visitMessageDestinationRef (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
440     throws Exception
441     {
442         String jndiName = node.getString("message-destination-ref-name",false,true);
443         String type = node.getString("message-destination-type",false,true);
444         String usage = node.getString("message-destination-usage",false,true);
445         
446         Origin o = context.getMetaData().getOrigin("message-destination-ref."+jndiName);
447         switch (o)
448         {
449             case NotSet:
450             {       
451                 //A message-destination-ref of this name has not been previously declared
452                 Class<?> typeClass = TypeUtil.fromName(type);
453                 if (typeClass==null)
454                     typeClass = context.loadClass(type);
455                 addInjections(context, descriptor, node, jndiName, typeClass);   
456                 bindMessageDestinationRef(context,jndiName, typeClass);
457                 context.getMetaData().setOrigin("message-destination-ref."+jndiName, descriptor);
458                 break;
459             }
460             case WebXml:
461             case WebDefaults:
462             case WebOverride:   
463             {               
464                 //A message-destination-ref of this name has been declared first in a web xml.
465                 //Only allow other web-default, web.xml, web-override to change it.
466                 if (!(descriptor instanceof FragmentDescriptor))
467                 {
468                     Class<?> typeClass = TypeUtil.fromName(type);
469                     if (typeClass==null)
470                         typeClass = context.loadClass(type);
471                     addInjections(context, descriptor, node, jndiName, typeClass);   
472                     bindMessageDestinationRef(context,jndiName, typeClass);
473                     context.getMetaData().setOrigin("message-destination-ref."+jndiName, descriptor);
474                 }
475                 else
476                 {
477                     //A web-fragment has declared a message-destination-ref with the same name as a web xml.
478                     //It can only contribute injections, and only if the web xml didn't declare any.
479                     Descriptor d = context.getMetaData().getOriginDescriptor("message-destination-ref."+jndiName+".injection");
480                     if (d == null || d instanceof FragmentDescriptor)
481                     { 
482                         Class<?> typeClass = TypeUtil.fromName(type);
483                         if (typeClass==null)
484                             typeClass = context.loadClass(type);
485                         addInjections(context, descriptor, node, jndiName, typeClass);   
486                     }
487                 }
488                 break;
489             }
490             case WebFragment:
491             {
492                 Descriptor otherFragment = context.getMetaData().getOriginDescriptor("message-destination-ref."+jndiName);
493                 XmlParser.Node otherFragmentRoot = otherFragment.getRoot();
494                 Iterator iter = otherFragmentRoot.iterator();
495                 XmlParser.Node otherNode = null;
496                 while (iter.hasNext() && otherNode == null)
497                 {
498                     Object obj = iter.next();
499                     if (!(obj instanceof XmlParser.Node)) continue;
500                     XmlParser.Node n = (XmlParser.Node)obj;
501                     if ("message-destination-ref".equals(n.getTag()) && jndiName.equals(n.getString("message-destination-ref-name",false,true)))
502                         otherNode = n;
503                 }
504                 if (otherNode != null)
505                 {
506                     String otherType = node.getString("message-destination-type",false,true);
507                     String otherUsage = node.getString("message-destination-usage",false,true);
508 
509                     type = (type==null?"":type);
510                     usage = (usage==null?"":usage);
511                     if (!type.equals(otherType) || !usage.equalsIgnoreCase(otherUsage))
512                         throw new IllegalStateException("Conflicting message-destination-ref "+jndiName+" in "+descriptor.getResource());
513                     
514                     //same in multiple web-fragments, merge the injections
515                     addInjections(context, descriptor, node, jndiName, TypeUtil.fromName(type));
516                 }
517                 else
518                     throw new IllegalStateException("message-destination-ref."+jndiName+" not found in declaring descriptor "+otherFragment);
519             }
520         }
521 
522     }
523     
524     
525 
526     /**
527      * If web.xml has at least 1 post-construct, then all post-constructs in fragments
528      * are ignored. Otherwise, post-constructs from fragments are merged.
529      * post-construct is the name of a class and method to call after all
530      * resources have been setup but before the class is put into use
531      * @param node
532      */
533     public void visitPostConstruct(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
534     {
535         String className = node.getString("lifecycle-callback-class", false, true);
536         String methodName = node.getString("lifecycle-callback-method", false, true);
537         
538         if (className==null || className.equals(""))
539         {
540             Log.warn("No lifecycle-callback-class specified");
541             return;
542         }
543         if (methodName==null || methodName.equals(""))
544         {
545             Log.warn("No lifecycle-callback-method specified for class "+className);
546             return;
547         }
548        
549         //ServletSpec 3.0 p80 If web.xml declares a post-construct then all post-constructs
550         //in fragments must be ignored. Otherwise, they are additive.
551         Origin o = context.getMetaData().getOrigin("post-construct");
552         switch (o)
553         {
554             case NotSet:
555             {
556                 //No post-constructs have been declared previously.
557                 context.getMetaData().setOrigin("post-construct", descriptor);
558                 
559                 try
560                 {
561                     Class<?> clazz = context.loadClass(className);
562                     LifeCycleCallback callback = new PostConstructCallback();
563                     callback.setTarget(clazz, methodName);
564                     ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
565                 }
566                 catch (ClassNotFoundException e)
567                 {
568                     Log.warn("Couldn't load post-construct target class "+className);
569                 }
570                 break;
571             }
572             case WebXml:
573             case WebDefaults:
574             case WebOverride:   
575             {
576                 //A web xml first declared a post-construct. Only allow other web xml files (web-defaults, web-overrides etc)
577                 //to add to it
578                 if (!(descriptor instanceof FragmentDescriptor))
579                 {
580                     try
581                     {
582                         Class<?> clazz = context.loadClass(className);
583                         LifeCycleCallback callback = new PostConstructCallback();
584                         callback.setTarget(clazz, methodName);
585                         ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
586                     }
587                     catch (ClassNotFoundException e)
588                     {
589                         Log.warn("Couldn't load post-construct target class "+className);
590                     }
591                 }
592                 break;
593             }
594             case WebFragment:
595             {
596                 //A web-fragment first declared a post-construct. Allow all other web-fragments to merge in their post-constructs
597                 try
598                 {
599                     Class<?> clazz = context.loadClass(className);
600                     LifeCycleCallback callback = new PostConstructCallback();
601                     callback.setTarget(clazz, methodName);
602                     ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
603                 }
604                 catch (ClassNotFoundException e)
605                 {
606                     Log.warn("Couldn't load post-construct target class "+className);
607                 }
608                 break;
609             }
610         }
611       
612     }
613     
614 
615     /**
616      * 
617      * pre-destroy is the name of a class and method to call just as
618      * the instance is being destroyed
619      * @param node
620      */
621     public void visitPreDestroy(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
622     {
623         String className = node.getString("lifecycle-callback-class", false, true);
624         String methodName = node.getString("lifecycle-callback-method", false, true);
625         if (className==null || className.equals(""))
626         {
627             Log.warn("No lifecycle-callback-class specified for pre-destroy");
628             return;
629         }
630         if (methodName==null || methodName.equals(""))
631         {
632             Log.warn("No lifecycle-callback-method specified for pre-destroy class "+className);
633             return;
634         } 
635       
636         Origin o = context.getMetaData().getOrigin("pre-destroy");
637         switch(o)
638         {
639             case NotSet:
640             {
641                 //No pre-destroys have been declared previously. Record this descriptor
642                 //as the first declarer.
643                 context.getMetaData().setOrigin("pre-destroy", descriptor);
644                 try
645                 {
646                     Class<?> clazz = context.loadClass(className);
647                     LifeCycleCallback callback = new PreDestroyCallback();
648                     callback.setTarget(clazz, methodName);
649                     ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
650                 }
651                 catch (ClassNotFoundException e)
652                 {
653                     Log.warn("Couldn't load pre-destory target class "+className);
654                 }
655                 break;
656             }
657             case WebXml:
658             case WebDefaults:
659             case WebOverride:   
660             {
661                 //A web xml file previously declared a pre-destroy. Only allow other web xml files
662                 //(not web-fragments) to add to them.
663                 if (!(descriptor instanceof FragmentDescriptor))
664                 {
665                     try
666                     {
667                         Class<?> clazz = context.loadClass(className);
668                         LifeCycleCallback callback = new PreDestroyCallback();
669                         callback.setTarget(clazz, methodName);
670                         ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
671                     }
672                     catch (ClassNotFoundException e)
673                     {
674                         Log.warn("Couldn't load pre-destory target class "+className);
675                     } 
676                 }
677                 break;
678             }
679             case WebFragment:
680             {
681                 //No pre-destroys in web xml, so allow all fragments to merge their pre-destroys.
682                 try
683                 {
684                     Class<?> clazz = context.loadClass(className);
685                     LifeCycleCallback callback = new PreDestroyCallback();
686                     callback.setTarget(clazz, methodName);
687                     ((LifeCycleCallbackCollection)context.getAttribute(LifeCycleCallbackCollection.LIFECYCLE_CALLBACK_COLLECTION)).add(callback);
688                 }
689                 catch (ClassNotFoundException e)
690                 {
691                     Log.warn("Couldn't load pre-destory target class "+className);
692                 } 
693                 break;
694             }
695         }  
696     }
697     
698     
699     /**
700      * Iterate over the &lt;injection-target&gt; entries for a node
701      * 
702      * @param descriptor
703      * @param node
704      * @param jndiName
705      * @param valueClass
706      */
707     public void addInjections (WebAppContext context, Descriptor descriptor, XmlParser.Node node, String jndiName, Class<?> valueClass)
708     {
709         Iterator  itor = node.iterator("injection-target");
710         
711         while(itor.hasNext())
712         {
713             XmlParser.Node injectionNode = (XmlParser.Node)itor.next(); 
714             String targetClassName = injectionNode.getString("injection-target-class", false, true);
715             String targetName = injectionNode.getString("injection-target-name", false, true);
716             if ((targetClassName==null) || targetClassName.equals(""))
717             {
718                 Log.warn("No classname found in injection-target");
719                 continue;
720             }
721             if ((targetName==null) || targetName.equals(""))
722             {
723                 Log.warn("No field or method name in injection-target");
724                 continue;
725             }
726 
727             InjectionCollection injections = (InjectionCollection)context.getAttribute(InjectionCollection.INJECTION_COLLECTION);
728             if (injections == null)
729             {
730                 injections = new InjectionCollection();
731                 context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections);
732             }
733             // comments in the javaee_5.xsd file specify that the targetName is looked
734             // for first as a java bean property, then if that fails, as a field
735             try
736             {
737                 Class<?> clazz = context.loadClass(targetClassName);
738                 Injection injection = new Injection();
739                 injection.setJndiName(jndiName);
740                 injection.setTarget(clazz, targetName, valueClass);
741                 injections.add(injection);
742                 
743                 //Record which was the first descriptor to declare an injection for this name
744                 if (context.getMetaData().getOriginDescriptor(node.getTag()+"."+jndiName+".injection") == null)
745                     context.getMetaData().setOrigin(node.getTag()+"."+jndiName+".injection", descriptor);
746             }
747             catch (ClassNotFoundException e)
748             {
749                 Log.warn("Couldn't load injection target class "+targetClassName);
750             }
751         }
752     }
753     
754   
755 
756     
757     /** 
758      * @param name
759      * @param value
760      * @throws Exception
761      */
762     public void bindEnvEntry(String name, Object value) throws Exception
763     {    
764         InitialContext ic = null;
765         boolean bound = false;
766         //check to see if we bound a value and an EnvEntry with this name already
767         //when we processed the server and the webapp's naming environment
768         //@see EnvConfiguration.bindEnvEntries()
769         ic = new InitialContext();
770         try
771         {
772             NamingEntry ne = (NamingEntry)ic.lookup("java:comp/env/"+NamingEntryUtil.makeNamingEntryName(ic.getNameParser(""), name));
773             if (ne!=null && ne instanceof EnvEntry)
774             {
775                 EnvEntry ee = (EnvEntry)ne;
776                 bound = ee.isOverrideWebXml();
777             }
778         }
779         catch (NameNotFoundException e)
780         {
781             bound = false;
782         }
783 
784         if (!bound)
785         {
786             //either nothing was bound or the value from web.xml should override
787             Context envCtx = (Context)ic.lookup("java:comp/env");
788             NamingUtil.bind(envCtx, name, value);
789         }
790     }
791 
792     /** 
793      * Bind a resource reference.
794      * 
795      * If a resource reference with the same name is in a jetty-env.xml
796      * file, it will already have been bound.
797      * 
798      * @param name
799      * @throws Exception
800      */
801     public void bindResourceRef(WebAppContext context, String name, Class<?> typeClass)
802     throws Exception
803     {
804         bindEntry(context, name, typeClass);
805     }
806 
807     /** 
808      * @param name
809      * @throws Exception
810      */
811     public void bindResourceEnvRef(WebAppContext context, String name, Class<?> typeClass)
812     throws Exception
813     {
814         bindEntry(context, name, typeClass);
815     }
816     
817     
818     public void bindMessageDestinationRef(WebAppContext context, String name, Class<?> typeClass)
819     throws Exception
820     {
821         bindEntry(context, name, typeClass);
822     }
823    
824     
825     /**
826      * Bind a resource with the given name from web.xml of the given type
827      * with a jndi resource from either the server or the webapp's naming 
828      * environment.
829      * 
830      * As the servlet spec does not cover the mapping of names in web.xml with
831      * names from the execution environment, jetty uses the concept of a Link, which is
832      * a subclass of the NamingEntry class. A Link defines a mapping of a name
833      * from web.xml with a name from the execution environment (ie either the server or the
834      * webapp's naming environment).
835      * 
836      * @param name name of the resource from web.xml
837      * @param typeClass 
838      * @throws Exception
839      */
840     protected void bindEntry (WebAppContext context, String name, Class<?> typeClass)
841     throws Exception
842     {
843         String nameInEnvironment = name;
844         boolean bound = false;
845         
846         //check if the name in web.xml has been mapped to something else
847         //check a context-specific naming environment first
848         Object scope = context;
849         NamingEntry ne = NamingEntryUtil.lookupNamingEntry(scope, name);
850     
851         if (ne!=null && (ne instanceof Link))
852         {
853             //if we found a mapping, get out name it is mapped to in the environment
854             nameInEnvironment = ((Link)ne).getLink();
855             Link l = (Link)ne;
856         }
857 
858         //try finding that mapped name in the webapp's environment first
859         scope = context;
860         bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
861         
862         if (bound)
863             return;
864         
865         //try the server's environment
866         scope = context.getServer();
867         bound = NamingEntryUtil.bindToENC(scope, name, nameInEnvironment);
868         if (bound)
869             return;
870 
871         //try the jvm environment
872         bound = NamingEntryUtil.bindToENC(null, name, nameInEnvironment);
873         if (bound)
874             return;
875 
876         //There is no matching resource so try a default name.
877         //The default name syntax is: the [res-type]/default
878         //eg       javax.sql.DataSource/default
879         nameInEnvironment = typeClass.getName()+"/default";
880         //First try the server scope
881         NamingEntry defaultNE = NamingEntryUtil.lookupNamingEntry(context.getServer(), nameInEnvironment);
882         if (defaultNE==null)
883             defaultNE = NamingEntryUtil.lookupNamingEntry(null, nameInEnvironment);
884         
885         if (defaultNE!=null)
886             defaultNE.bindToENC(name);
887         else
888             throw new IllegalStateException("Nothing to bind for name "+nameInEnvironment);
889     }
890 
891  
892 }