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