View Javadoc

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