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