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.webapp;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.net.URL;
19  import java.net.URLClassLoader;
20  import java.util.ArrayList;
21  import java.util.EnumSet;
22  import java.util.EventListener;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.servlet.ServletException;
28  
29  import org.eclipse.jetty.http.security.Constraint;
30  import org.eclipse.jetty.security.ConstraintAware;
31  import org.eclipse.jetty.security.ConstraintMapping;
32  import org.eclipse.jetty.security.authentication.FormAuthenticator;
33  import org.eclipse.jetty.server.DispatcherType;
34  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
35  import org.eclipse.jetty.servlet.FilterHolder;
36  import org.eclipse.jetty.servlet.FilterMapping;
37  import org.eclipse.jetty.servlet.ServletContextHandler;
38  import org.eclipse.jetty.servlet.ServletHolder;
39  import org.eclipse.jetty.servlet.ServletMapping;
40  import org.eclipse.jetty.util.LazyList;
41  import org.eclipse.jetty.util.Loader;
42  import org.eclipse.jetty.util.log.Log;
43  import org.eclipse.jetty.util.log.Logger;
44  import org.eclipse.jetty.util.resource.Resource;
45  import org.eclipse.jetty.xml.XmlParser;
46  
47  /**
48   * StandardDescriptorProcessor
49   *
50   * Process a web.xml, web-defaults.xml, web-overrides.xml, web-fragment.xml.
51   */
52  public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
53  {
54      private static final Logger LOG = Log.getLogger(StandardDescriptorProcessor.class);
55  
56      public static final String STANDARD_PROCESSOR = "org.eclipse.jetty.standardDescriptorProcessor";
57      
58      
59      
60      public StandardDescriptorProcessor ()
61      {
62   
63          try
64          {
65              registerVisitor("context-param", this.getClass().getDeclaredMethod("visitContextParam", __signature));
66              registerVisitor("display-name", this.getClass().getDeclaredMethod("visitDisplayName", __signature));
67              registerVisitor("servlet", this.getClass().getDeclaredMethod("visitServlet",  __signature));
68              registerVisitor("servlet-mapping", this.getClass().getDeclaredMethod("visitServletMapping",  __signature));
69              registerVisitor("session-config", this.getClass().getDeclaredMethod("visitSessionConfig",  __signature));
70              registerVisitor("mime-mapping", this.getClass().getDeclaredMethod("visitMimeMapping",  __signature)); 
71              registerVisitor("welcome-file-list", this.getClass().getDeclaredMethod("visitWelcomeFileList",  __signature));
72              registerVisitor("locale-encoding-mapping-list", this.getClass().getDeclaredMethod("visitLocaleEncodingList",  __signature));
73              registerVisitor("error-page", this.getClass().getDeclaredMethod("visitErrorPage",  __signature));
74              registerVisitor("taglib", this.getClass().getDeclaredMethod("visitTagLib",  __signature));
75              registerVisitor("jsp-config", this.getClass().getDeclaredMethod("visitJspConfig",  __signature));
76              registerVisitor("security-constraint", this.getClass().getDeclaredMethod("visitSecurityConstraint",  __signature));
77              registerVisitor("login-config", this.getClass().getDeclaredMethod("visitLoginConfig",  __signature));
78              registerVisitor("security-role", this.getClass().getDeclaredMethod("visitSecurityRole",  __signature));
79              registerVisitor("filter", this.getClass().getDeclaredMethod("visitFilter",  __signature));
80              registerVisitor("filter-mapping", this.getClass().getDeclaredMethod("visitFilterMapping",  __signature));
81              registerVisitor("listener", this.getClass().getDeclaredMethod("visitListener",  __signature));
82              registerVisitor("distributable", this.getClass().getDeclaredMethod("visitDistributable",  __signature));
83          }
84          catch (Exception e)
85          {
86              throw new IllegalStateException(e);
87          }
88      }
89  
90      
91      
92      /**
93       * {@inheritDoc}
94       */
95      public void start(WebAppContext context, Descriptor descriptor)
96      { 
97      }
98      
99      
100     
101     /** 
102      * {@inheritDoc}
103      */
104     public void end(WebAppContext context, Descriptor descriptor)
105     {
106     }
107     
108     /**
109      * @param context
110      * @param descriptor
111      * @param node
112      */
113     public void visitContextParam (WebAppContext context, Descriptor descriptor, XmlParser.Node node)
114     {
115         String name = node.getString("param-name", false, true);
116         String value = node.getString("param-value", false, true);
117         Origin o = context.getMetaData().getOrigin("context-param."+name);
118         switch (o)
119         {
120             case NotSet:
121             {
122                 //just set it
123                 context.getInitParams().put(name, value);
124                 context.getMetaData().setOrigin("context-param."+name, descriptor);
125                 break;
126             }
127             case WebXml:
128             case WebDefaults:
129             case WebOverride:
130             {
131                 //previously set by a web xml, allow other web xml files to override
132                 if (!(descriptor instanceof FragmentDescriptor))
133                 {
134                     context.getInitParams().put(name, value);
135                     context.getMetaData().setOrigin("context-param."+name, descriptor); 
136                 }
137                 break;
138             }
139             case WebFragment:
140             {
141                 //previously set by a web-fragment, this fragment's value must be the same
142                 if (descriptor instanceof FragmentDescriptor)
143                 {
144                     if (!((String)context.getInitParams().get(name)).equals(value))
145                         throw new IllegalStateException("Conflicting context-param "+name+"="+value+" in "+descriptor.getResource());
146                 }
147                 break;
148             }
149         }
150         if (LOG.isDebugEnabled()) LOG.debug("ContextParam: " + name + "=" + value);
151 
152     }
153     
154 
155     /* ------------------------------------------------------------ */
156     /**
157      * @param context
158      * @param descriptor
159      * @param node
160      */
161     protected void visitDisplayName(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
162     {
163         //Servlet Spec 3.0 p. 74 Ignore from web-fragments
164         if (!(descriptor instanceof FragmentDescriptor))
165         {
166             context.setDisplayName(node.toString(false, true));
167             context.getMetaData().setOrigin("display-name", descriptor);
168         }
169     }
170     
171     
172     /**
173      * @param context
174      * @param descriptor
175      * @param node
176      */
177     protected void visitServlet(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
178     {
179         String id = node.getAttribute("id");
180 
181         // initialize holder
182         String servlet_name = node.getString("servlet-name", false, true);
183         ServletHolder holder = context.getServletHandler().getServlet(servlet_name);
184           
185         /*
186          * If servlet of that name does not already exist, create it.
187          */
188         if (holder == null)
189         {
190             holder = context.getServletHandler().newServletHolder();
191             holder.setName(servlet_name);
192             context.getServletHandler().addServlet(holder);
193         }
194 
195         // init params  
196         Iterator<?> iParamsIter = node.iterator("init-param");
197         while (iParamsIter.hasNext())
198         {
199             XmlParser.Node paramNode = (XmlParser.Node) iParamsIter.next();
200             String pname = paramNode.getString("param-name", false, true);
201             String pvalue = paramNode.getString("param-value", false, true);
202             
203             Origin origin = context.getMetaData().getOrigin(servlet_name+".servlet.init-param."+pname);
204             
205             switch (origin)
206             {
207                 case NotSet:
208                 {
209                     //init-param not already set, so set it
210                     
211                     holder.setInitParameter(pname, pvalue); 
212                     context.getMetaData().setOrigin(servlet_name+".servlet.init-param."+pname, descriptor);
213                     break;
214                 }
215                 case WebXml:
216                 case WebDefaults:
217                 case WebOverride:
218                 {
219                     //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
220                     //otherwise just ignore it
221                     if (!(descriptor instanceof FragmentDescriptor))
222                     {
223                         holder.setInitParameter(pname, pvalue); 
224                         context.getMetaData().setOrigin(servlet_name+".servlet.init-param."+pname, descriptor);
225                     }
226                     break;
227                 }
228                 case WebFragment:
229                 {
230                     //previously set by a web-fragment, make sure that the value matches, otherwise its an error
231                     if (!holder.getInitParameter(pname).equals(pvalue))
232                         throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource());
233                     break;
234                 }
235             }  
236         }
237 
238         String servlet_class = node.getString("servlet-class", false, true);
239 
240         // Handle JSP
241         String jspServletName=null;
242         String jspServletClass=null;;
243         boolean hasJSP=false;
244         if (id != null && id.equals("jsp"))
245         {
246             jspServletName = servlet_name;
247             jspServletClass = servlet_class;
248             try
249             {
250                 Loader.loadClass(this.getClass(), servlet_class);
251                 hasJSP = true;
252             }
253             catch (ClassNotFoundException e)
254             {
255                 LOG.info("NO JSP Support for {}, did not find {}", context.getContextPath(), servlet_class);
256                 jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet";
257             }
258             if (holder.getInitParameter("scratchdir") == null)
259             {
260                 File tmp = context.getTempDirectory();
261                 File scratch = new File(tmp, "jsp");
262                 if (!scratch.exists()) scratch.mkdir();
263                 holder.setInitParameter("scratchdir", scratch.getAbsolutePath());
264 
265                 if ("?".equals(holder.getInitParameter("classpath")))
266                 {
267                     String classpath = context.getClassPath();
268                     LOG.debug("classpath=" + classpath);
269                     if (classpath != null) 
270                         holder.setInitParameter("classpath", classpath);
271                 }
272             }
273 
274             /* Set the webapp's classpath for Jasper */
275             context.setAttribute("org.apache.catalina.jsp_classpath", context.getClassPath());
276 
277             /* Set the system classpath for Jasper */
278             holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context)); 
279         }
280         
281         //Set the servlet-class
282         if (servlet_class != null) 
283         {
284             ((WebDescriptor)descriptor).addClassName(servlet_class);
285             
286             Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.servlet-class");
287             switch (o)
288             {
289                 case NotSet:
290                 {
291                     //the class of the servlet has not previously been set, so set it
292                     holder.setClassName(servlet_class);
293                     context.getMetaData().setOrigin(servlet_name+".servlet.servlet-class", descriptor);
294                     break;
295                 }
296                 case WebXml:
297                 case WebDefaults:
298                 case WebOverride:
299                 {
300                     //the class of the servlet was set by a web xml file, only allow web-override/web-default to change it
301                     if (!(descriptor instanceof FragmentDescriptor))
302                     {
303                         holder.setClassName(servlet_class);
304                         context.getMetaData().setOrigin(servlet_name+".servlet.servlet-class", descriptor);
305                     }
306                     break;
307                 }
308                 case WebFragment:
309                 {
310                     //the class was set by another fragment, ensure this fragment's value is the same
311                     if (!servlet_class.equals(holder.getClassName()))
312                         throw new IllegalStateException("Conflicting servlet-class "+servlet_class+" in "+descriptor.getResource());
313                     break;
314                 }
315             }          
316         }
317 
318         // Handler JSP file
319         String jsp_file = node.getString("jsp-file", false, true);
320         if (jsp_file != null)
321         {
322             holder.setForcedPath(jsp_file);
323             holder.setClassName(jspServletClass);
324             //set the system classpath explicitly for the holder that will represent the JspServlet instance
325             holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context)); 
326         }
327 
328         // handle load-on-startup 
329         XmlParser.Node startup = node.get("load-on-startup");
330         if (startup != null)
331         {
332             String s = startup.toString(false, true).toLowerCase();
333             int order = 0;
334             if (s.startsWith("t"))
335             {
336                 LOG.warn("Deprecated boolean load-on-startup.  Please use integer");
337                 order = 1; 
338             }
339             else
340             {
341                 try
342                 {
343                     if (s != null && s.trim().length() > 0) order = Integer.parseInt(s);
344                 }
345                 catch (Exception e)
346                 {
347                     LOG.warn("Cannot parse load-on-startup " + s + ". Please use integer");
348                     LOG.ignore(e);
349                 }
350             }
351 
352             Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.load-on-startup");
353             switch (o)
354             {
355                 case NotSet:
356                 {
357                     //not already set, so set it now
358                     holder.setInitOrder(order);
359                     context.getMetaData().setOrigin(servlet_name+".servlet.load-on-startup", descriptor);
360                     break;
361                 }
362                 case WebXml:
363                 case WebDefaults:
364                 case WebOverride:
365                 {
366                     //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it
367                     if (!(descriptor instanceof FragmentDescriptor))
368                     {
369                         holder.setInitOrder(order);
370                         context.getMetaData().setOrigin(servlet_name+".servlet.load-on-startup", descriptor);
371                     }
372                     break;
373                 }
374                 case WebFragment:
375                 {
376                     //it was already set by another fragment, if we're parsing a fragment, the values must match
377                     if (order != holder.getInitOrder())
378                         throw new IllegalStateException("Conflicting load-on-startup value in "+descriptor.getResource());
379                     break;
380                 }
381             } 
382         }
383 
384         Iterator sRefsIter = node.iterator("security-role-ref");
385         while (sRefsIter.hasNext())
386         {
387             XmlParser.Node securityRef = (XmlParser.Node) sRefsIter.next();
388             String roleName = securityRef.getString("role-name", false, true);
389             String roleLink = securityRef.getString("role-link", false, true);
390             if (roleName != null && roleName.length() > 0 && roleLink != null && roleLink.length() > 0)
391             {
392                 if (LOG.isDebugEnabled()) LOG.debug("link role " + roleName + " to " + roleLink + " for " + this);
393                 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.role-name."+roleName);
394                 switch (o)
395                 {
396                     case NotSet:
397                     {
398                         //set it
399                         holder.setUserRoleLink(roleName, roleLink);
400                         context.getMetaData().setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor);
401                         break;
402                     }
403                     case WebXml:
404                     case WebDefaults:
405                     case WebOverride:
406                     {
407                         //only another web xml descriptor (web-default,web-override web.xml) can override an already set value
408                         if (!(descriptor instanceof FragmentDescriptor))
409                         {
410                             holder.setUserRoleLink(roleName, roleLink);
411                             context.getMetaData().setOrigin(servlet_name+".servlet.role-name."+roleName, descriptor);
412                         }
413                         break;
414                     }
415                     case WebFragment:
416                     {
417                         if (!holder.getUserRoleLink(roleName).equals(roleLink))
418                             throw new IllegalStateException("Conflicting role-link for role-name "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource());
419                         break;
420                     }
421                 }
422             }
423             else
424             {
425                 LOG.warn("Ignored invalid security-role-ref element: " + "servlet-name=" + holder.getName() + ", " + securityRef);
426             }
427         }
428 
429         
430         XmlParser.Node run_as = node.get("run-as");
431         if (run_as != null)
432         { 
433             String roleName = run_as.getString("role-name", false, true);
434 
435             if (roleName != null)
436             {
437                 Origin o = context.getMetaData().getOrigin(servlet_name+".servlet.run-as");
438                 switch (o)
439                 {
440                     case NotSet:
441                     {
442                         //run-as not set, so set it
443                         holder.setRunAsRole(roleName);
444                         context.getMetaData().setOrigin(servlet_name+".servlet.run-as", descriptor);
445                         break;
446                     }
447                     case WebXml:
448                     case WebDefaults:
449                     case WebOverride:
450                     {
451                         //run-as was set by a web xml, only allow it to be changed if we're currently parsing another web xml(override/default)
452                         if (!(descriptor instanceof FragmentDescriptor))
453                         {
454                             holder.setRunAsRole(roleName);
455                             context.getMetaData().setOrigin(servlet_name+".servlet.run-as", descriptor);
456                         }
457                         break;
458                     }
459                     case WebFragment:
460                     {
461                         //run-as was set by another fragment, this fragment must show the same value
462                         if (!holder.getRunAsRole().equals(roleName))
463                             throw new IllegalStateException("Conflicting run-as role "+roleName+" for servlet "+servlet_name+" in "+descriptor.getResource());
464                         break;
465                     }    
466                 }
467             }
468         }
469     }
470     
471     
472 
473     /**
474      * @param context
475      * @param descriptor
476      * @param node
477      */
478     protected void visitServletMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
479     {
480         //Servlet Spec 3.0, p74
481         //servlet-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments.
482         //Maintenance update 3.0a to spec:
483         //  Updated 8.2.3.g.v to say <servlet-mapping> elements are additive across web-fragments. 
484         //  <servlet-mapping> declared in web.xml overrides the mapping for the servlet specified in the web-fragment.xml
485 
486         String servlet_name = node.getString("servlet-name", false, true); 
487         Origin origin = context.getMetaData().getOrigin(servlet_name+".servlet.mappings");
488         
489         switch (origin)
490         {
491             case NotSet:
492             {
493                 //no servlet mappings
494                 context.getMetaData().setOrigin(servlet_name+".servlet.mappings", descriptor);
495                 addServletMapping(servlet_name, node, context);
496                 break;
497             }
498             case WebXml:
499             case WebDefaults:
500             case WebOverride:
501             {
502                 //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
503                 //otherwise just ignore it
504                 if (!(descriptor instanceof FragmentDescriptor))
505                 {
506                    addServletMapping(servlet_name, node, context);
507                 }
508                 break;
509             }
510             case WebFragment:
511             {
512                 //mappings previously set by another web-fragment, so merge in this web-fragment's mappings
513                 addServletMapping(servlet_name, node, context);
514                 break;
515             }
516         }        
517     }
518     
519     
520     /**
521      * @param context
522      * @param descriptor
523      * @param node
524      */
525     protected void visitSessionConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
526     {
527         XmlParser.Node tNode = node.get("session-timeout");
528         if (tNode != null)
529         {
530             int timeout = Integer.parseInt(tNode.toString(false, true));
531             context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60);
532         }
533     }
534     
535     
536     
537     /**
538      * @param context
539      * @param descriptor
540      * @param node
541      */
542     protected void visitMimeMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
543     {
544         String extension = node.getString("extension", false, true);
545         if (extension != null && extension.startsWith(".")) 
546             extension = extension.substring(1);
547         String mimeType = node.getString("mime-type", false, true);
548         if (extension != null)
549         {
550             Origin o = context.getMetaData().getOrigin("extension."+extension);
551             switch (o)
552             {
553                 case NotSet:
554                 {
555                     //no mime-type set for the extension yet
556                     context.getMimeTypes().addMimeMapping(extension, mimeType);
557                     context.getMetaData().setOrigin("extension."+extension, descriptor);
558                     break;
559                 }
560                 case WebXml:
561                 case WebDefaults:
562                 case WebOverride:
563                 {
564                     //a mime-type was set for the extension in a web xml, only allow web-default/web-override to change
565                     if (!(descriptor instanceof FragmentDescriptor))
566                     {
567                         context.getMimeTypes().addMimeMapping(extension, mimeType);
568                         context.getMetaData().setOrigin("extension."+extension, descriptor);
569                     }
570                     break;
571                 }
572                 case WebFragment:
573                 {
574                     //a web-fragment set the value, all web-fragments must have the same value
575                     if (!context.getMimeTypes().getMimeByExtension("."+extension).equals(context.getMimeTypes().CACHE.lookup(mimeType)))
576                         throw new IllegalStateException("Conflicting mime-type "+mimeType+" for extension "+extension+" in "+descriptor.getResource());
577                     break;
578                 }
579             }
580         }
581     }
582     
583     /**
584      * @param context
585      * @param descriptor
586      * @param node
587      */
588     protected void visitWelcomeFileList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
589     {
590         Origin o = context.getMetaData().getOrigin("welcome-file-list");
591         switch (o)
592         {
593             case NotSet:
594             {
595                 context.getMetaData().setOrigin("welcome-file-list", descriptor);
596                 addWelcomeFiles(context,node);
597                 break;
598             }
599             case WebXml:
600             {
601                 //web.xml set the welcome-file-list, all other descriptors then just merge in
602                 addWelcomeFiles(context,node);
603                 break;
604             }
605             case WebDefaults:
606             {
607                 //if web-defaults set the welcome-file-list first and
608                 //we're processing web.xml then reset the welcome-file-list
609                 if (!(descriptor instanceof DefaultsDescriptor) && !(descriptor instanceof OverrideDescriptor) && !(descriptor instanceof FragmentDescriptor))
610                 {
611                     context.setWelcomeFiles(new String[0]);
612                 }
613                 addWelcomeFiles(context,node);
614                 break;
615             }
616             case WebOverride:
617             {
618                 //web-override set the list, all other descriptors just merge in
619                 addWelcomeFiles(context,node);
620                 break;
621             }
622             case WebFragment:
623             {
624                 //A web-fragment first set the welcome-file-list. Other descriptors just add. 
625                 addWelcomeFiles(context,node);
626                 break;
627             }
628         }
629     }
630     
631     /**
632      * @param context
633      * @param descriptor
634      * @param node
635      */
636     protected void visitLocaleEncodingList(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
637     {
638         Iterator<XmlParser.Node> iter = node.iterator("locale-encoding-mapping");
639         while (iter.hasNext())
640         {
641             XmlParser.Node mapping = iter.next();
642             String locale = mapping.getString("locale", false, true);
643             String encoding = mapping.getString("encoding", false, true);
644             
645             if (encoding != null)
646             {
647                 Origin o = context.getMetaData().getOrigin("locale-encoding."+locale);
648                 switch (o)
649                 {
650                     case NotSet:
651                     {
652                         //no mapping for the locale yet, so set it
653                         context.addLocaleEncoding(locale, encoding);
654                         context.getMetaData().setOrigin("locale-encoding."+locale, descriptor);
655                         break;
656                     }
657                     case WebXml:
658                     case WebDefaults:
659                     case WebOverride:
660                     {
661                         //a value was set in a web descriptor, only allow another web descriptor to change it (web-default/web-override)
662                         if (!(descriptor instanceof FragmentDescriptor))
663                         {
664                             context.addLocaleEncoding(locale, encoding);
665                             context.getMetaData().setOrigin("locale-encoding."+locale, descriptor);
666                         }
667                         break;
668                     }
669                     case WebFragment:
670                     {
671                         //a value was set by a web-fragment, all fragments must have the same value
672                         if (!encoding.equals(context.getLocaleEncoding(locale)))
673                             throw new IllegalStateException("Conflicting locale-encoding mapping for locale "+locale+" in "+descriptor.getResource());
674                         break;                    
675                     }
676                 }
677             }
678         }
679     }
680 
681     /**
682      * @param context
683      * @param descriptor
684      * @param node
685      */
686     protected void visitErrorPage(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
687     {
688         String error = node.getString("error-code", false, true);
689         int code=0;
690         if (error == null || error.length() == 0) 
691             error = node.getString("exception-type", false, true);
692         else
693             code=Integer.valueOf(error);
694         String location = node.getString("location", false, true);
695 
696         
697         ErrorPageErrorHandler handler = (ErrorPageErrorHandler)context.getErrorHandler();
698         
699         
700         Origin o = context.getMetaData().getOrigin("error."+error);
701         switch (o)
702         {
703             case NotSet:
704             {
705                 //no error page setup for this code or exception yet
706                 if (code>0)
707                     handler.addErrorPage(code,location);
708                 else
709                     handler.addErrorPage(error,location);
710                 context.getMetaData().setOrigin("error."+error, descriptor);
711                 break;
712             }
713             case WebXml:
714             case WebDefaults:
715             case WebOverride:
716             {
717                 //an error page setup was set in web.xml, only allow other web xml descriptors to override it
718                 if (!(descriptor instanceof FragmentDescriptor))
719                 {
720                     if (code>0)
721                         handler.addErrorPage(code,location);
722                     else
723                         handler.addErrorPage(error,location);
724                     context.getMetaData().setOrigin("error."+error, descriptor);
725                 }
726                 break;
727             }
728             case WebFragment:
729             {
730                 //another web fragment set the same error code or exception, if its different its an error
731                 if (!handler.getErrorPages().get(error).equals(location))
732                     throw new IllegalStateException("Conflicting error-code or exception-type "+error+" in "+descriptor.getResource());
733                 break;
734             }
735         }
736        
737     }
738     
739     /**
740      * @param context
741      * @param node
742      */
743     protected void addWelcomeFiles(WebAppContext context, XmlParser.Node node)
744     {
745         Iterator<XmlParser.Node> iter = node.iterator("welcome-file");
746         while (iter.hasNext())
747         {
748             XmlParser.Node indexNode = (XmlParser.Node) iter.next();
749             String welcome = indexNode.toString(false, true);
750             
751             //Servlet Spec 3.0 p. 74 welcome files are additive
752             context.setWelcomeFiles((String[])LazyList.addToArray(context.getWelcomeFiles(),welcome,String.class));
753         }
754     }
755     
756     
757     /**
758      * @param servletName
759      * @param node
760      * @param context
761      */
762     protected void addServletMapping (String servletName, XmlParser.Node node, WebAppContext context)
763     {
764         ServletMapping mapping = new ServletMapping();
765         mapping.setServletName(servletName);
766         
767         List<String> paths = new ArrayList<String>();
768         Iterator<XmlParser.Node> iter = node.iterator("url-pattern");
769         while (iter.hasNext())
770         {
771             String p = iter.next().toString(false, true);
772             p = normalizePattern(p);
773             paths.add(p);
774         }
775         mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
776         context.getServletHandler().addServletMapping(mapping);
777     }
778     
779     /**
780      * @param filterName
781      * @param node
782      * @param context
783      */
784     protected void addFilterMapping (String filterName, XmlParser.Node node, WebAppContext context)
785     {
786         FilterMapping mapping = new FilterMapping();
787         mapping.setFilterName(filterName);
788 
789         List<String> paths = new ArrayList<String>();
790         Iterator<XmlParser.Node>  iter = node.iterator("url-pattern");
791         while (iter.hasNext())
792         {
793             String p = iter.next().toString(false, true);
794             p = normalizePattern(p);
795             paths.add(p);
796         }
797         mapping.setPathSpecs((String[]) paths.toArray(new String[paths.size()]));
798 
799         List<String> names = new ArrayList<String>();
800         iter = node.iterator("servlet-name");
801         while (iter.hasNext())
802         {
803             String n = ((XmlParser.Node) iter.next()).toString(false, true);
804             names.add(n);
805         }
806         mapping.setServletNames((String[]) names.toArray(new String[names.size()]));
807 
808         
809         List<DispatcherType> dispatches = new ArrayList<DispatcherType>();
810         iter=node.iterator("dispatcher");
811         while(iter.hasNext())
812         {
813             String d=((XmlParser.Node)iter.next()).toString(false,true);
814             dispatches.add(FilterMapping.dispatch(d));
815         }
816         
817         if (dispatches.size()>0)
818             mapping.setDispatcherTypes(EnumSet.copyOf(dispatches));
819 
820         context.getServletHandler().addFilterMapping(mapping);
821     }
822     
823     
824     /**
825      * @param context
826      * @param descriptor
827      * @param node
828      */
829     protected void visitTagLib(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
830     {
831         //Additive across web.xml and web-fragment.xml
832         String uri = node.getString("taglib-uri", false, true);
833         String location = node.getString("taglib-location", false, true);
834 
835         context.setResourceAlias(uri, location);
836     }
837     
838     /**
839      * @param context
840      * @param descriptor
841      * @param node
842      */
843     protected void visitJspConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
844     {  
845         for (int i = 0; i < node.size(); i++)
846         {
847             Object o = node.get(i);
848             if (o instanceof XmlParser.Node && "taglib".equals(((XmlParser.Node) o).getTag())) 
849                 visitTagLib(context,descriptor, (XmlParser.Node) o);
850         }
851 
852         // Map URLs from jsp property groups to JSP servlet.
853         // this is more JSP stupidness creaping into the servlet spec
854         Iterator<XmlParser.Node> iter = node.iterator("jsp-property-group");
855         List<String> paths = new ArrayList<String>();
856         while (iter.hasNext())
857         {
858             XmlParser.Node group = iter.next();
859             Iterator<XmlParser.Node> iter2 = group.iterator("url-pattern");
860             while (iter2.hasNext())
861             {
862                 String url = iter2.next().toString(false, true);
863                 url = normalizePattern(url);
864                 paths.add( url);
865             }
866         }
867 
868         if (paths.size() > 0)
869         {
870             String jspName = "jsp";
871             Map.Entry entry = context.getServletHandler().getHolderEntry("test.jsp");
872             if (entry != null)
873             {
874                 ServletHolder holder = (ServletHolder) entry.getValue();
875                 jspName = holder.getName();
876             }
877             
878             if (jspName != null)
879             {
880                 ServletMapping mapping = new ServletMapping();
881                 mapping.setServletName(jspName);
882                 mapping.setPathSpecs(paths.toArray(new String[paths.size()]));
883                 context.getServletHandler().addServletMapping(mapping);
884             }
885         }
886     }
887     
888     /**
889      * @param context
890      * @param descriptor
891      * @param node
892      */
893     protected void visitSecurityConstraint(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
894     {
895         Constraint scBase = new Constraint();
896 
897         //ServletSpec 3.0, p74 security-constraints, as minOccurs > 1, are additive 
898         //across fragments
899         try
900         {
901             XmlParser.Node auths = node.get("auth-constraint");
902 
903             if (auths != null)
904             {
905                 scBase.setAuthenticate(true);
906                 // auth-constraint
907                 Iterator<XmlParser.Node> iter = auths.iterator("role-name");
908                 List<String> roles = new ArrayList<String>();
909                 while (iter.hasNext())
910                 {
911                     String role = iter.next().toString(false, true);
912                     roles.add(role);
913                 }
914                 scBase.setRoles(roles.toArray(new String[roles.size()]));
915             }
916 
917             XmlParser.Node data = node.get("user-data-constraint");
918             if (data != null)
919             {
920                 data = data.get("transport-guarantee");
921                 String guarantee = data.toString(false, true).toUpperCase();
922                 if (guarantee == null || guarantee.length() == 0 || "NONE".equals(guarantee))
923                     scBase.setDataConstraint(Constraint.DC_NONE);
924                 else if ("INTEGRAL".equals(guarantee))
925                     scBase.setDataConstraint(Constraint.DC_INTEGRAL);
926                 else if ("CONFIDENTIAL".equals(guarantee))
927                     scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
928                 else
929                 {
930                     LOG.warn("Unknown user-data-constraint:" + guarantee);
931                     scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
932                 }
933             }
934             Iterator<XmlParser.Node> iter = node.iterator("web-resource-collection");
935             while (iter.hasNext())
936             {
937                 XmlParser.Node collection =  iter.next();
938                 String name = collection.getString("web-resource-name", false, true);
939                 Constraint sc = (Constraint) scBase.clone();
940                 sc.setName(name);
941 
942                 Iterator<XmlParser.Node> iter2 = collection.iterator("url-pattern");
943                 while (iter2.hasNext())
944                 {
945                     String url = iter2.next().toString(false, true);
946                     url = normalizePattern(url);
947 
948                     Iterator<XmlParser.Node> iter3 = collection.iterator("http-method");
949                     if (iter3.hasNext())
950                     {
951                         while (iter3.hasNext())
952                         {
953                             String method = ((XmlParser.Node) iter3.next()).toString(false, true);
954                             ConstraintMapping mapping = new ConstraintMapping();
955                             mapping.setMethod(method);
956                             mapping.setPathSpec(url);
957                             mapping.setConstraint(sc);
958                                                         
959                             ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
960                         }
961                     }
962                     else
963                     {
964                         ConstraintMapping mapping = new ConstraintMapping();
965                         mapping.setPathSpec(url);
966                         mapping.setConstraint(sc);
967                         ((ConstraintAware)context.getSecurityHandler()).addConstraintMapping(mapping);
968                     }
969                 }
970             }
971         }
972         catch (CloneNotSupportedException e)
973         {
974             LOG.warn(e);
975         }
976     }
977     
978     /**
979      * @param context
980      * @param descriptor
981      * @param node
982      * @throws Exception
983      */
984     protected void visitLoginConfig(WebAppContext context, Descriptor descriptor, XmlParser.Node node) throws Exception
985     {
986         //ServletSpec 3.0 p74 says elements present 0/1 time if specified in web.xml take
987         //precendece over any web-fragment. If not specified in web.xml, then if specified
988         //in a web-fragment must be the same across all web-fragments.
989         XmlParser.Node method = node.get("auth-method");
990         if (method != null)
991         {
992             //handle auth-method merge
993             Origin o = context.getMetaData().getOrigin("auth-method");
994             switch (o)
995             {
996                 case NotSet:
997                 {
998                     //not already set, so set it now
999                     context.getSecurityHandler().setAuthMethod(method.toString(false, true));
1000                     context.getMetaData().setOrigin("auth-method", descriptor);
1001                     break;
1002                 }
1003                 case WebXml:
1004                 case WebDefaults:
1005                 case WebOverride:
1006                 {
1007                     //if it was already set by a web xml descriptor and we're parsing another web xml descriptor, then override it
1008                     if (!(descriptor instanceof FragmentDescriptor))
1009                     {
1010                         context.getSecurityHandler().setAuthMethod(method.toString(false, true));
1011                         context.getMetaData().setOrigin("auth-method", descriptor);
1012                     }
1013                     break;
1014                 }
1015                 case WebFragment:
1016                 {
1017                     //it was already set by another fragment, if we're parsing a fragment, the values must match
1018                     if (!context.getSecurityHandler().getAuthMethod().equals(method.toString(false, true)))
1019                         throw new IllegalStateException("Conflicting auth-method value in "+descriptor.getResource());
1020                     break;
1021                 }
1022             } 
1023             
1024             //handle realm-name merge
1025             XmlParser.Node name = node.get("realm-name");
1026             String nameStr = (name == null ? "default" : name.toString(false, true));
1027             o = context.getMetaData().getOrigin("realm-name");
1028             switch (o)
1029             {
1030                 case NotSet:
1031                 {
1032                     //no descriptor has set the realm-name yet, so set it
1033                     context.getSecurityHandler().setRealmName(nameStr);
1034                     context.getMetaData().setOrigin("realm-name", descriptor);
1035                     break;
1036                 }
1037                 case WebXml:
1038                 case WebDefaults:
1039                 case WebOverride:
1040                 {
1041                     //set by a web xml file (web.xml/web-default.xm/web-override.xml), only allow it to be changed by another web xml file
1042                     if (!(descriptor instanceof FragmentDescriptor))
1043                     {
1044                         context.getSecurityHandler().setRealmName(nameStr);
1045                         context.getMetaData().setOrigin("realm-name", descriptor); 
1046                     }
1047                     break;
1048                 }
1049                 case WebFragment:
1050                 {
1051                     //a fragment set it, and we must be parsing another fragment, so the values must match
1052                     if (!context.getSecurityHandler().getRealmName().equals(nameStr))
1053                         throw new IllegalStateException("Conflicting realm-name value in "+descriptor.getResource());
1054                     break;
1055                 }
1056             }
1057  
1058             if (Constraint.__FORM_AUTH.equals(context.getSecurityHandler().getAuthMethod()))
1059             {  
1060                 XmlParser.Node formConfig = node.get("form-login-config");
1061                 if (formConfig != null)
1062                 {
1063                     String loginPageName = null;
1064                     XmlParser.Node loginPage = formConfig.get("form-login-page");
1065                     if (loginPage != null) 
1066                         loginPageName = loginPage.toString(false, true);
1067                     String errorPageName = null;
1068                     XmlParser.Node errorPage = formConfig.get("form-error-page");
1069                     if (errorPage != null) 
1070                         errorPageName = errorPage.toString(false, true);
1071                     
1072                     //handle form-login-page
1073                     o = context.getMetaData().getOrigin("form-login-page");
1074                     switch (o)
1075                     {
1076                         case NotSet:
1077                         {
1078                             //Never been set before, so accept it
1079                             context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName);
1080                             context.getMetaData().setOrigin("form-login-page",descriptor);
1081                             break;
1082                         }
1083                         case WebXml:
1084                         case WebDefaults:
1085                         case WebOverride:
1086                         {
1087                             //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml)
1088                             if (!(descriptor instanceof FragmentDescriptor))
1089                             {
1090                                 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE,loginPageName);
1091                                 context.getMetaData().setOrigin("form-login-page",descriptor);
1092                             }
1093                             break;
1094                         }
1095                         case WebFragment:
1096                         {
1097                             //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree
1098                             if (!context.getSecurityHandler().getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE).equals(loginPageName))
1099                                 throw new IllegalStateException("Conflicting form-login-page value in "+descriptor.getResource());
1100                             break;
1101                         }
1102                     }
1103                     
1104                     //handle form-error-page
1105                     o = context.getMetaData().getOrigin("form-error-page");
1106                     switch (o)
1107                     {
1108                         case NotSet:
1109                         {
1110                             //Never been set before, so accept it
1111                             context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName);
1112                             context.getMetaData().setOrigin("form-error-page",descriptor);
1113                             break;
1114                         }
1115                         case WebXml:
1116                         case WebDefaults:
1117                         case WebOverride:
1118                         {
1119                             //a web xml descriptor previously set it, only allow another one to change it (web.xml/web-default.xml/web-override.xml)
1120                             if (!(descriptor instanceof FragmentDescriptor))
1121                             {
1122                                 context.getSecurityHandler().setInitParameter(FormAuthenticator.__FORM_ERROR_PAGE,errorPageName);
1123                                 context.getMetaData().setOrigin("form-error-page",descriptor);
1124                             }
1125                             break;
1126                         }
1127                         case WebFragment:
1128                         {
1129                             //a web-fragment previously set it. We must be parsing yet another web-fragment, so the values must agree
1130                             if (!context.getSecurityHandler().getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE).equals(errorPageName))
1131                                 throw new IllegalStateException("Conflicting form-error-page value in "+descriptor.getResource());
1132                             break;
1133                         }
1134                     }              
1135                 }
1136                 else
1137                 {
1138                     throw new IllegalStateException("!form-login-config");
1139                 }
1140             }
1141         }
1142     }
1143     
1144     /**
1145      * @param context
1146      * @param descriptor
1147      * @param node
1148      */
1149     protected void visitSecurityRole(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1150     {
1151         //ServletSpec 3.0, p74 elements with multiplicity >1 are additive when merged
1152         XmlParser.Node roleNode = node.get("role-name");
1153         String role = roleNode.toString(false, true);
1154         ((ConstraintAware)context.getSecurityHandler()).addRole(role);
1155     }
1156     
1157     
1158     /**
1159      * @param context
1160      * @param descriptor
1161      * @param node
1162      */
1163     protected void visitFilter(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1164     {
1165         String name = node.getString("filter-name", false, true);
1166         FilterHolder holder = context.getServletHandler().getFilter(name);
1167         if (holder == null)
1168         {
1169             holder = context.getServletHandler().newFilterHolder();
1170             holder.setName(name);
1171             context.getServletHandler().addFilter(holder);
1172         }
1173 
1174         String filter_class = node.getString("filter-class", false, true);
1175         if (filter_class != null) 
1176         {
1177             ((WebDescriptor)descriptor).addClassName(filter_class);
1178             
1179             Origin o = context.getMetaData().getOrigin(name+".filter.filter-class");
1180             switch (o)
1181             {
1182                 case NotSet:
1183                 {
1184                     //no class set yet
1185                     holder.setClassName(filter_class);
1186                     context.getMetaData().setOrigin(name+".filter.filter-class", descriptor);
1187                     break;
1188                 }
1189                 case WebXml:
1190                 case WebDefaults:
1191                 case WebOverride:
1192                 {
1193                     //filter class was set in web.xml, only allow other web xml descriptors (override/default) to change it
1194                     if (!(descriptor instanceof FragmentDescriptor))
1195                     {
1196                         holder.setClassName(filter_class);
1197                         context.getMetaData().setOrigin(name+".filter.filter-class", descriptor); 
1198                     }
1199                     break;
1200                 }
1201                 case WebFragment:
1202                 {
1203                     //the filter class was set up by a web fragment, all fragments must be the same
1204                     if (!holder.getClassName().equals(filter_class))
1205                         throw new IllegalStateException("Conflicting filter-class for filter "+name+" in "+descriptor.getResource());
1206                     break;
1207                 }
1208             }
1209            
1210         }
1211 
1212         Iterator<XmlParser.Node>  iter = node.iterator("init-param");
1213         while (iter.hasNext())
1214         {
1215             XmlParser.Node paramNode = iter.next();
1216             String pname = paramNode.getString("param-name", false, true);
1217             String pvalue = paramNode.getString("param-value", false, true);
1218             
1219             Origin origin = context.getMetaData().getOrigin(name+".filter.init-param."+pname);
1220             switch (origin)
1221             {
1222                 case NotSet:
1223                 {
1224                     //init-param not already set, so set it
1225                     holder.setInitParameter(pname, pvalue); 
1226                     context.getMetaData().setOrigin(name+".filter.init-param."+pname, descriptor);
1227                     break;
1228                 }
1229                 case WebXml:
1230                 case WebDefaults:
1231                 case WebOverride:
1232                 {
1233                     //previously set by a web xml descriptor, if we're parsing another web xml descriptor allow override
1234                     //otherwise just ignore it
1235                     if (!(descriptor instanceof FragmentDescriptor))
1236                     {
1237                         holder.setInitParameter(pname, pvalue); 
1238                         context.getMetaData().setOrigin(name+".filter.init-param."+pname, descriptor);
1239                     }
1240                     break;
1241                 }
1242                 case WebFragment:
1243                 {
1244                     //previously set by a web-fragment, make sure that the value matches, otherwise its an error
1245                     if (!holder.getInitParameter(pname).equals(pvalue))
1246                         throw new IllegalStateException("Mismatching init-param "+pname+"="+pvalue+" in "+descriptor.getResource());
1247                     break;
1248                 }
1249             }  
1250         }
1251 
1252         String async=node.getString("async-supported",false,true);
1253         if (async!=null)
1254             holder.setAsyncSupported(async.length()==0||Boolean.valueOf(async));
1255         if (async!=null)
1256         {
1257             boolean val = async.length()==0||Boolean.valueOf(async);
1258             Origin o = context.getMetaData().getOrigin(name+".filter.async-supported");
1259             switch (o)
1260             {
1261                 case NotSet:
1262                 {
1263                     //set it
1264                     holder.setAsyncSupported(val);
1265                     context.getMetaData().setOrigin(name+".filter.async-supported", descriptor);
1266                     break;
1267                 }
1268                 case WebXml:
1269                 case WebDefaults:
1270                 case WebOverride:
1271                 {
1272                     //async-supported set by previous web xml descriptor, only allow override if we're parsing another web descriptor(web.xml/web-override.xml/web-default.xml)
1273                     if (!(descriptor instanceof FragmentDescriptor))
1274                     {
1275                         holder.setAsyncSupported(val);
1276                         context.getMetaData().setOrigin(name+".filter.async-supported", descriptor);  
1277                     }             
1278                     break;
1279                 }
1280                 case WebFragment:
1281                 {
1282                     //async-supported set by another fragment, this fragment's value must match
1283                     if (holder.isAsyncSupported() != val)
1284                         throw new IllegalStateException("Conflicting async-supported="+async+" for filter "+name+" in "+descriptor.getResource());
1285                     break;
1286                 }
1287             }
1288         }
1289         
1290     }
1291 
1292     /**
1293      * @param context
1294      * @param descriptor
1295      * @param node
1296      */
1297     protected void visitFilterMapping(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1298     {
1299         //Servlet Spec 3.0, p74
1300         //filter-mappings are always additive, whether from web xml descriptors (web.xml/web-default.xml/web-override.xml) or web-fragments.
1301         //Maintenance update 3.0a to spec:
1302         //  Updated 8.2.3.g.v to say <servlet-mapping> elements are additive across web-fragments. 
1303       
1304         
1305         String filter_name = node.getString("filter-name", false, true);
1306         
1307         Origin origin = context.getMetaData().getOrigin(filter_name+".filter.mappings");
1308         
1309         switch (origin)
1310         {
1311             case NotSet:
1312             {
1313                 //no filtermappings for this filter yet defined
1314                 context.getMetaData().setOrigin(filter_name+".filter.mappings", descriptor);
1315                 addFilterMapping(filter_name, node, context);
1316                 break;
1317             }
1318             case WebDefaults:
1319             case WebOverride:
1320             case WebXml:
1321             {
1322                 //filter mappings defined in a web xml file. If we're processing a fragment, we ignore filter mappings.
1323                 if (!(descriptor instanceof FragmentDescriptor))
1324                 {
1325                    addFilterMapping(filter_name, node, context);
1326                 }
1327                 break;
1328             }
1329             case WebFragment:
1330             {
1331                 //filter mappings first defined in a web-fragment, allow other fragments to add
1332                 addFilterMapping(filter_name, node, context);
1333                 break;
1334             }
1335         }
1336     }
1337 
1338     
1339     /**
1340      * @param context
1341      * @param descriptor
1342      * @param node
1343      */
1344     protected void visitListener(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1345     {
1346         String className = node.getString("listener-class", false, true);
1347         EventListener listener = null;
1348         try
1349         {
1350             if (className != null && className.length()> 0)
1351             {
1352                 //Servlet Spec 3.0 p 74
1353                 //Duplicate listener declarations don't result in duplicate listener instances
1354                 EventListener[] listeners=context.getEventListeners();
1355                 if (listeners!=null)
1356                 {
1357                     for (EventListener l : listeners)
1358                     {
1359                         if (l.getClass().getName().equals(className))
1360                             return;
1361                     }
1362                 }
1363                 
1364                 ((WebDescriptor)descriptor).addClassName(className);
1365 
1366                 Class<? extends EventListener> listenerClass = (Class<? extends EventListener>)context.loadClass(className);
1367                 listener = newListenerInstance(context,listenerClass);
1368                 if (!(listener instanceof EventListener))
1369                 {
1370                     LOG.warn("Not an EventListener: " + listener);
1371                     return;
1372                 }
1373                 context.addEventListener(listener);
1374                 context.getMetaData().setOrigin(className+".listener", descriptor);
1375                 
1376             }
1377         }
1378         catch (Exception e)
1379         {
1380             LOG.warn("Could not instantiate listener " + className, e);
1381             return;
1382         }
1383     }
1384     
1385     /**
1386      * @param context
1387      * @param descriptor
1388      * @param node
1389      */
1390     protected void visitDistributable(WebAppContext context, Descriptor descriptor, XmlParser.Node node)
1391     {
1392         // the element has no content, so its simple presence
1393         // indicates that the webapp is distributable...
1394         //Servlet Spec 3.0 p.74  distributable only if all fragments are distributable
1395         ((WebDescriptor)descriptor).setDistributable(true);
1396     }
1397     
1398     /**
1399      * @param context
1400      * @param clazz
1401      * @return the new event listener
1402      * @throws ServletException
1403      * @throws InstantiationException
1404      * @throws IllegalAccessException
1405      */
1406     protected EventListener newListenerInstance(WebAppContext context,Class<? extends EventListener> clazz) throws ServletException, InstantiationException, IllegalAccessException
1407     {
1408         try
1409         {
1410             return ((ServletContextHandler.Context)context.getServletContext()).createListener(clazz);
1411         }
1412         catch (ServletException se)
1413         {
1414             Throwable cause = se.getRootCause();
1415             if (cause instanceof InstantiationException)
1416                 throw (InstantiationException)cause;
1417             if (cause instanceof IllegalAccessException)
1418                 throw (IllegalAccessException)cause;
1419             throw se;
1420         }
1421     }
1422     
1423     /**
1424      * @param p
1425      * @return the normalized pattern
1426      */
1427     protected String normalizePattern(String p)
1428     {
1429         if (p != null && p.length() > 0 && !p.startsWith("/") && !p.startsWith("*")) return "/" + p;
1430         return p;
1431     }
1432 
1433     /**
1434      * Generate the classpath (as a string) of all classloaders
1435      * above the webapp's classloader.
1436      * 
1437      * This is primarily used for jasper.
1438      * @return the system class path
1439      */
1440     protected String getSystemClassPath(WebAppContext context)
1441     {
1442         ClassLoader loader = context.getClassLoader();
1443         if (loader.getParent() != null)
1444             loader = loader.getParent();
1445 
1446         StringBuilder classpath=new StringBuilder();
1447         while (loader != null && (loader instanceof URLClassLoader))
1448         {
1449             URL[] urls = ((URLClassLoader)loader).getURLs();
1450             if (urls != null)
1451             {     
1452                 for (int i=0;i<urls.length;i++)
1453                 {
1454                     try
1455                     {
1456                         Resource resource = context.newResource(urls[i]);
1457                         File file=resource.getFile();
1458                         if (file!=null && file.exists())
1459                         {
1460                             if (classpath.length()>0)
1461                                 classpath.append(File.pathSeparatorChar);
1462                             classpath.append(file.getAbsolutePath());
1463                         }
1464                     }
1465                     catch (IOException e)
1466                     {
1467                         LOG.debug(e);
1468                     }
1469                 }
1470             }
1471             loader = loader.getParent();
1472         }
1473         return classpath.toString();
1474     }
1475 }