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