View Javadoc

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