View Javadoc

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