1   // ========================================================================
2   // Copyright 2003-2005 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // Licensed under the Apache License, Version 2.0 (the "License");
5   // you may not use this file except in compliance with the License.
6   // You may obtain a copy of the License at
7   // http://www.apache.org/licenses/LICENSE-2.0
8   // Unless required by applicable law or agreed to in writing, software
9   // distributed under the License is distributed on an "AS IS" BASIS,
10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11  // See the License for the specific language governing permissions and
12  // limitations under the License.
13  // ========================================================================
14  package org.mortbay.jetty.webapp;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.lang.reflect.Method;
19  import java.net.MalformedURLException;
20  import java.net.URL;
21  import java.util.ArrayList;
22  import java.util.EventListener;
23  import java.util.HashMap;
24  import java.util.Iterator;
25  import java.util.Map;
26  import java.util.jar.JarEntry;
27  import java.util.regex.Pattern;
28  
29  import javax.servlet.Servlet;
30  import javax.servlet.UnavailableException;
31  
32  import org.mortbay.jetty.Authenticator;
33  import org.mortbay.jetty.Dispatcher;
34  import org.mortbay.jetty.Handler;
35  import org.mortbay.jetty.UserRealm;
36  import org.mortbay.jetty.handler.ContextHandler;
37  import org.mortbay.jetty.security.BasicAuthenticator;
38  import org.mortbay.jetty.security.Constraint;
39  import org.mortbay.jetty.security.ConstraintMapping;
40  import org.mortbay.jetty.security.DigestAuthenticator;
41  import org.mortbay.jetty.security.FormAuthenticator;
42  import org.mortbay.jetty.servlet.ErrorPageErrorHandler;
43  import org.mortbay.jetty.servlet.FilterHolder;
44  import org.mortbay.jetty.servlet.FilterMapping;
45  import org.mortbay.jetty.servlet.ServletHandler;
46  import org.mortbay.jetty.servlet.ServletHolder;
47  import org.mortbay.jetty.servlet.ServletMapping;
48  import org.mortbay.log.Log;
49  import org.mortbay.resource.Resource;
50  import org.mortbay.util.LazyList;
51  import org.mortbay.util.Loader;
52  import org.mortbay.xml.XmlParser;
53  /* ------------------------------------------------------------------------------- */
54  /**
55   * Configure by parsing default web.xml and web.xml
56   * 
57   * @author gregw
58   */
59  public class WebXmlConfiguration implements Configuration
60  {
61      protected WebAppContext _context;
62      protected XmlParser _xmlParser;
63      protected Object _filters;
64      protected Object _filterMappings;
65      protected Object _servlets;
66      protected Object _servletMappings;
67      protected Object _welcomeFiles;
68      protected Object _constraintMappings;
69      protected Object _listeners;
70      protected Map _errorPages;
71      protected boolean _hasJSP;
72      protected String _jspServletName;
73      protected String _jspServletClass;
74      protected boolean _defaultWelcomeFileList;
75      protected ServletHandler _servletHandler;
76      protected int _version;
77      protected boolean _metaDataComplete = false;
78      private URL _webxml;
79      
80      public WebXmlConfiguration() throws ClassNotFoundException
81      {
82          // Get parser
83          _xmlParser=webXmlParser();
84      }
85  
86      public static XmlParser webXmlParser() throws ClassNotFoundException
87      {
88          XmlParser xmlParser=new XmlParser();
89          //set up cache of DTDs and schemas locally        
90          URL dtd22=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_2.dtd",true);
91          URL dtd23=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_3.dtd",true);
92          URL j2ee14xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_1_4.xsd",true);
93          URL webapp24xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_4.xsd",true);
94          URL webapp25xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_2_5.xsd",true);
95          URL webapp30xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/web-app_3_0.xsd",true);
96          URL schemadtd=Loader.getResource(Servlet.class,"javax/servlet/resources/XMLSchema.dtd",true);
97          URL xmlxsd=Loader.getResource(Servlet.class,"javax/servlet/resources/xml.xsd",true);
98          URL webservice11xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/j2ee_web_services_client_1_1.xsd",true);
99          URL webservice12xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/javaee_web_services_client_1_2.xsd",true);
100         URL datatypesdtd=Loader.getResource(Servlet.class,"javax/servlet/resources/datatypes.dtd",true);
101 
102         
103         URL jsp20xsd=null;
104         URL jsp21xsd=null;
105         
106         try
107         {
108             Class jsp_page = Loader.loadClass(WebXmlConfiguration.class,"javax.servlet.jsp.JspPage");
109             jsp20xsd=jsp_page.getResource("/javax/servlet/resources/jsp_2_0.xsd");
110             jsp21xsd=jsp_page.getResource("/javax/servlet/resources/jsp_2_1.xsd");
111         }
112         catch(Exception e)
113         {
114             Log.ignore(e);
115         }
116         finally
117         {
118             if (jsp20xsd==null)
119                 jsp20xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/jsp_2_0.xsd",true);
120             if (jsp21xsd==null)
121                 jsp21xsd=Loader.getResource(Servlet.class,"javax/servlet/resources/jsp_2_1.xsd",true);
122         }
123         
124         redirect(xmlParser,"web-app_2_2.dtd",dtd22);
125         redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN",dtd22);
126         redirect(xmlParser,"web.dtd",dtd23);
127         redirect(xmlParser,"web-app_2_3.dtd",dtd23);
128         redirect(xmlParser,"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN",dtd23);
129         redirect(xmlParser,"XMLSchema.dtd",schemadtd);
130         redirect(xmlParser,"http://www.w3.org/2001/XMLSchema.dtd",schemadtd);
131         redirect(xmlParser,"-//W3C//DTD XMLSCHEMA 200102//EN",schemadtd);
132         redirect(xmlParser,"jsp_2_0.xsd",jsp20xsd);
133         redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/jsp_2_0.xsd",jsp20xsd);
134         redirect(xmlParser,"jsp_2_1.xsd",jsp21xsd);
135         redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/jsp_2_1.xsd",jsp21xsd);
136         redirect(xmlParser,"j2ee_1_4.xsd",j2ee14xsd);
137         redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/j2ee_1_4.xsd",j2ee14xsd);
138         redirect(xmlParser,"web-app_2_4.xsd",webapp24xsd);
139         redirect(xmlParser,"http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd",webapp24xsd);
140         redirect(xmlParser,"web-app_2_5.xsd",webapp25xsd);
141         redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd",webapp25xsd);
142         redirect(xmlParser,"web-app_3_0.xsd",webapp30xsd);
143         redirect(xmlParser,"http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd",webapp30xsd);
144         redirect(xmlParser,"xml.xsd",xmlxsd);
145         redirect(xmlParser,"http://www.w3.org/2001/xml.xsd",xmlxsd);
146         redirect(xmlParser,"datatypes.dtd",datatypesdtd);
147         redirect(xmlParser,"http://www.w3.org/2001/datatypes.dtd",datatypesdtd);
148         redirect(xmlParser,"j2ee_web_services_client_1_1.xsd",webservice11xsd);
149         redirect(xmlParser,"http://www.ibm.com/webservices/xsd/j2ee_web_services_client_1_1.xsd",webservice11xsd);
150         redirect(xmlParser,"javaee_web_services_client_1_2.xsd",webservice12xsd);
151         redirect(xmlParser,"http://www.ibm.com/webservices/xsd/javaee_web_services_client_1_2.xsd",webservice12xsd);
152 
153         return xmlParser;
154     }
155     
156     /* ------------------------------------------------------------------------------- */
157     private static void redirect(XmlParser parser,String resource, URL source)
158     {
159         if (source!=null)
160             parser.redirectEntity(resource,source);
161     }
162 
163     /* ------------------------------------------------------------------------------- */
164     public void setWebAppContext (WebAppContext context)
165     {
166         _context = context;
167     }
168 
169     /* ------------------------------------------------------------------------------- */
170     public WebAppContext getWebAppContext()
171     {
172         return _context;
173     }
174 
175     /* ------------------------------------------------------------------------------- */
176     /** Configure ClassPath.
177      */
178     public  void configureClassLoader()
179     throws Exception
180     {
181     }
182 
183     /* ------------------------------------------------------------------------------- */
184     /** 
185      * Process webdefaults.xml
186      * 
187      * @see org.mortbay.jetty.webapp.Configuration#configureDefaults()
188      */
189     public void configureDefaults() throws Exception
190     {
191         //cannot configure if the context is already started
192         if (_context.isStarted())
193         {
194             if (Log.isDebugEnabled()){Log.debug("Cannot configure webapp after it is started");}
195             return;
196         }
197         String defaultsDescriptor=getWebAppContext().getDefaultsDescriptor();
198         if(defaultsDescriptor!=null&&defaultsDescriptor.length()>0)
199         {
200             Resource dftResource=Resource.newSystemResource(defaultsDescriptor);
201             if(dftResource==null)
202                 dftResource=_context.newResource(defaultsDescriptor);
203             
204             //don't initialize the version and therefore the metadata from webdefault.xml
205             XmlParser.Node config=null;
206             config=_xmlParser.parse(dftResource.getURL().toString());
207             initialize(config);
208             _defaultWelcomeFileList=_welcomeFiles!=null;
209         }
210     }
211 
212     /* ------------------------------------------------------------------------------- */
213     /** 
214      * Process web.xml
215      * @see org.mortbay.jetty.webapp.Configuration#configureWebApp()
216      */
217     public void configureWebApp() throws Exception
218     {
219         //cannot configure if the context is already started
220         if (_context.isStarted())
221         {
222             if (Log.isDebugEnabled())
223                 Log.debug("Cannot configure webapp after it is started");
224             return;
225         }
226 
227         _webxml=findWebXml();
228         if (_webxml!=null)
229             configure(_webxml.toString());
230        
231         String overrideDescriptor=getWebAppContext().getOverrideDescriptor();
232         if(overrideDescriptor!=null&&overrideDescriptor.length()>0)
233         {
234             Resource orideResource=Resource.newSystemResource(overrideDescriptor);
235             if(orideResource==null)
236                 orideResource=_context.newResource(overrideDescriptor);
237             _xmlParser.setValidating(false);
238             configure(orideResource.getURL().toString());
239         }
240         
241         //TODO is this before or after the overrides?
242         configureWebFragments();
243     }
244     
245     
246     /* ------------------------------------------------------------------------------- */
247     /**
248      * Look for any web.xml fragments in META-INF of jars in WEB-INF/lib
249      * @throws Exception
250      */
251     public void configureWebFragments () throws Exception
252     {        
253         Log.debug("metadata-complete "+_metaDataComplete);
254         
255         //if metadata-complete is true in web.xml, do not search for fragments
256         if (_metaDataComplete)
257             return;
258         
259         //either there is no web.xml, or it set metadata-complete to false, so
260         //we need to look for fragments in WEB-INF/lib
261         //Check to see if a specific search pattern has been set.
262         String tmp = (String)_context.getInitParameter("org.mortbay.jetty.webapp.WebXmlFragmentPattern");
263         Pattern webFragPattern = (tmp==null?null:Pattern.compile(tmp));
264       
265         JarScanner fragScanner = new JarScanner ()
266         {
267             public void processEntry(URL jarUrl, JarEntry entry)
268             {
269                 try
270                 {
271                     String name = entry.getName();
272                     if (name.toLowerCase().equals("meta-inf/web.xml"))
273                     {
274                         Resource webXmlFrag = _context.newResource("jar:"+jarUrl+"!/"+name);
275                         Log.debug("web.xml fragment found {}", webXmlFrag);
276                         //Process web.xml
277                         //web-fragment
278                         // servlet
279                         // servlet-mapping
280                         // filter
281                         // filter-mapping
282                         // listener                        
283                         XmlParser.Node config=null;
284                         config=_xmlParser.parse(webXmlFrag.toString());
285                         initialize(config);
286                     }
287                 }
288                 catch (Exception e)
289                 {
290                     Log.warn("Problem processing jar entry "+entry, e);
291                 }
292             }
293         };
294        
295         fragScanner.scan (webFragPattern, Thread.currentThread().getContextClassLoader(), true, false);
296     }
297 
298     /* ------------------------------------------------------------------------------- */
299     protected URL findWebXml() throws IOException, MalformedURLException
300     {
301         String descriptor=getWebAppContext().getDescriptor();
302         if (descriptor!=null)
303         {
304             Resource web= _context.newResource(descriptor);
305             if (web.exists()&& !web.isDirectory())
306                 return web.getURL();
307         }
308 
309         Resource web_inf=getWebAppContext().getWebInf();
310         if(web_inf!=null && web_inf.isDirectory())
311         {
312             // do web.xml file
313             Resource web=web_inf.addPath("web.xml");
314             if(web.exists()) 
315                 return web.getURL();
316             Log.debug("No WEB-INF/web.xml in "+getWebAppContext().getWar()
317                     +". Serving files and default/dynamic servlets only");
318         }
319         return null;
320     }
321     
322     /* ------------------------------------------------------------------------------- */
323     public void configure(String webXml) throws Exception
324     {
325         XmlParser.Node config=null;
326         config=_xmlParser.parse(webXml);
327         initializeVersion(config);
328         initialize(config);
329     }
330 
331     /* ------------------------------------------------------------------------------- */
332     public void deconfigureWebApp() throws Exception
333     {
334         // TODO preserve any configuration that pre-existed.
335 
336         _servletHandler = getWebAppContext().getServletHandler();
337 
338         _servletHandler.setFilters(null);
339         _servletHandler.setFilterMappings(null);
340         _servletHandler.setServlets(null);
341         _servletHandler.setServletMappings(null);
342 
343         getWebAppContext().setEventListeners(null);
344         getWebAppContext().setWelcomeFiles(null);
345         if (getWebAppContext().getSecurityHandler() != null)
346             getWebAppContext().getConstraintsSecurityHandler().setConstraintMappings(null);
347 
348         if (getWebAppContext().getErrorHandler() instanceof ErrorPageErrorHandler)
349             ((ErrorPageErrorHandler)getWebAppContext().getErrorHandler()).setErrorPages(null);
350 
351         // TODO remove classpaths from classloader
352     }
353     
354     /* ------------------------------------------------------------ */
355     protected void initializeVersion (XmlParser.Node config)
356     {
357         String version=config.getAttribute("version","DTD");
358         if ("2.5".equals(version))
359             _version=25;
360         else if ("2.4".equals(version))
361             _version=24;
362         else if ("3.0".equals(version))
363             _version=30;
364         else if ("DTD".equals(version))
365         {
366             _version=23;
367             String dtd=_xmlParser.getDTD();
368             if (dtd!=null && dtd.indexOf("web-app_2_2")>=0)
369                 _version=22;
370         }
371        
372         if (_version < 25)
373             _metaDataComplete = true; //does not apply before 2.5
374         else
375             _metaDataComplete = Boolean.valueOf((String)config.getAttribute("metadata-complete", "false")).booleanValue();
376   
377         Log.debug("Calculated metadatacomplete = "+_metaDataComplete+" with version="+version);
378         
379         _context.setAttribute("metadata-complete", String.valueOf(_metaDataComplete));   
380     }
381     
382     
383     /* ------------------------------------------------------------ */
384     protected void initialize(XmlParser.Node config) throws ClassNotFoundException,UnavailableException
385     {  
386         _servletHandler = getWebAppContext().getServletHandler();
387         // Get any existing servlets and mappings.
388         _filters=LazyList.array2List(_servletHandler.getFilters());
389         _filterMappings=LazyList.array2List(_servletHandler.getFilterMappings());
390         _servlets=LazyList.array2List(_servletHandler.getServlets());
391         _servletMappings=LazyList.array2List(_servletHandler.getServletMappings());
392 
393         _listeners = LazyList.array2List(getWebAppContext().getEventListeners());
394         _welcomeFiles = LazyList.array2List(getWebAppContext().getWelcomeFiles());
395         _constraintMappings = LazyList.array2List(getWebAppContext().getConstraintsSecurityHandler().getConstraintMappings());
396 
397         _errorPages = getWebAppContext().getErrorHandler() instanceof ErrorPageErrorHandler ?
398                         ((ErrorPageErrorHandler)getWebAppContext().getErrorHandler()).getErrorPages():null;
399 
400      
401         
402         Iterator iter=config.iterator();
403         XmlParser.Node node=null;
404         while(iter.hasNext())
405         {
406             try
407             {
408                 Object o=iter.next();
409                 if(!(o instanceof XmlParser.Node))
410                     continue;
411                 node=(XmlParser.Node)o;
412                 String name=node.getTag();
413                 initWebXmlElement(name,node);
414             }
415             catch(ClassNotFoundException e)
416             {
417                 throw e;
418             }
419             catch(Exception e)
420             {
421                 Log.warn("Configuration problem at "+node,e);
422                 throw new UnavailableException("Configuration problem");
423             }
424         }
425         
426         _servletHandler.setFilters((FilterHolder[])LazyList.toArray(_filters,FilterHolder.class));
427         _servletHandler.setFilterMappings((FilterMapping[])LazyList.toArray(_filterMappings,FilterMapping.class));
428         _servletHandler.setServlets((ServletHolder[])LazyList.toArray(_servlets,ServletHolder.class));
429         _servletHandler.setServletMappings((ServletMapping[])LazyList.toArray(_servletMappings,ServletMapping.class));
430 
431         getWebAppContext().setEventListeners((EventListener[])LazyList.toArray(_listeners,EventListener.class));
432         getWebAppContext().setWelcomeFiles((String[])LazyList.toArray(_welcomeFiles,String.class));
433         getWebAppContext().getConstraintsSecurityHandler().setConstraintMappings((ConstraintMapping[])LazyList.toArray(_constraintMappings, ConstraintMapping.class));
434 
435         if (_errorPages!=null && getWebAppContext().getErrorHandler() instanceof ErrorPageErrorHandler)
436             ((ErrorPageErrorHandler)getWebAppContext().getErrorHandler()).setErrorPages(_errorPages);
437 
438     }
439 
440     /* ------------------------------------------------------------ */
441     /**
442      * Handle web.xml element. This method is called for each top level element within the web.xml
443      * file. It may be specialized by derived WebAppHandlers to provide additional
444      * configuration and handling.
445      *
446      * @param element The element name
447      * @param node The node containing the element.
448      */
449     protected void initWebXmlElement(String element,XmlParser.Node node) throws Exception
450     {
451         if("display-name".equals(element))
452             initDisplayName(node);
453         else if("description".equals(element))
454         {}
455         else if("context-param".equals(element))
456             initContextParam(node);
457         else if("servlet".equals(element))
458             initServlet(node);
459         else if("servlet-mapping".equals(element))
460             initServletMapping(node);
461         else if("session-config".equals(element))
462             initSessionConfig(node);
463         else if("mime-mapping".equals(element))
464             initMimeConfig(node);
465         else if("welcome-file-list".equals(element))
466             initWelcomeFileList(node);
467         else if("locale-encoding-mapping-list".equals(element))
468             initLocaleEncodingList(node);
469         else if("error-page".equals(element))
470             initErrorPage(node);
471         else if("taglib".equals(element))
472             initTagLib(node);
473         else if("jsp-config".equals(element))
474             initJspConfig(node);
475         else if("resource-ref".equals(element))
476         {
477             if(Log.isDebugEnabled())
478                 Log.debug("No implementation: "+node);
479         }
480         else if("security-constraint".equals(element))
481             initSecurityConstraint(node);
482         else if("login-config".equals(element))
483             initLoginConfig(node);
484         else if("security-role".equals(element))
485             initSecurityRole(node);
486         else if("filter".equals(element))
487             initFilter(node);
488         else if("filter-mapping".equals(element))
489             initFilterMapping(node);
490         else if("listener".equals(element))
491             initListener(node);
492         else if("distributable".equals(element))
493             initDistributable(node);
494         else if("web-fragment".equals(element))
495         {}
496         else
497         {
498             if(Log.isDebugEnabled())
499             {
500                 Log.debug("Element {} not handled in {}",element,this);
501                 Log.debug(node.toString());
502             }
503         }
504     }
505 
506     /* ------------------------------------------------------------ */
507     protected void initDisplayName(XmlParser.Node node)
508     {
509         getWebAppContext().setDisplayName(node.toString(false,true));
510     }
511 
512     /* ------------------------------------------------------------ */
513     protected void initContextParam(XmlParser.Node node)
514     {
515         String name=node.getString("param-name",false,true);
516         String value=node.getString("param-value",false,true);
517         if(Log.isDebugEnabled())
518             Log.debug("ContextParam: "+name+"="+value);
519         getWebAppContext().getInitParams().put(name, value);
520     }
521 
522     /* ------------------------------------------------------------ */
523     protected void initFilter(XmlParser.Node node)
524     {
525         String name=node.getString("filter-name",false,true);
526         FilterHolder holder= _servletHandler.getFilter(name);
527         if (holder==null)
528         {
529             holder=_servletHandler.newFilterHolder();
530             holder.setName(name);
531             _filters=LazyList.add(_filters,holder);
532         }
533         
534         String filter_class=node.getString("filter-class",false,true);
535         if (filter_class!=null)
536             holder.setClassName(filter_class);
537 
538         Iterator iter=node.iterator("init-param");
539         while(iter.hasNext())
540         {
541             XmlParser.Node paramNode=(XmlParser.Node)iter.next();
542             String pname=paramNode.getString("param-name",false,true);
543             String pvalue=paramNode.getString("param-value",false,true);
544             holder.setInitParameter(pname, pvalue);
545         }
546         
547     }
548 
549     /* ------------------------------------------------------------ */
550     protected void initFilterMapping(XmlParser.Node node)
551     {
552         String filter_name=node.getString("filter-name",false,true);
553 
554 
555 
556         FilterMapping mapping = new FilterMapping();
557 
558         mapping.setFilterName(filter_name);
559 
560         ArrayList paths = new ArrayList();
561         Iterator iter=node.iterator("url-pattern");
562         while(iter.hasNext())
563         {
564             String p=((XmlParser.Node)iter.next()).toString(false,true);
565             p=normalizePattern(p);
566             paths.add(p);
567         }
568         mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
569 
570 
571         ArrayList names = new ArrayList();
572         iter=node.iterator("servlet-name");
573         while(iter.hasNext())
574         {
575             String n=((XmlParser.Node)iter.next()).toString(false,true);
576             names.add(n);
577         }
578         mapping.setServletNames((String[])names.toArray(new String[names.size()]));
579 
580 
581         int dispatcher=Handler.DEFAULT;
582         iter=node.iterator("dispatcher");
583         while(iter.hasNext())
584         {
585             String d=((XmlParser.Node)iter.next()).toString(false,true);
586             dispatcher|=Dispatcher.type(d);
587         }
588         mapping.setDispatches(dispatcher);
589 
590         iter=node.iterator("life-cycle");
591         while(iter.hasNext())
592         {
593             String l=((XmlParser.Node)iter.next()).toString(false,true);
594             if ("initial".equalsIgnoreCase(l))
595                 mapping.setInitialLifeCycle(true);
596             if ("redispatch".equalsIgnoreCase(l))
597                 mapping.setRedispatchLifeCycle(true);
598         }
599         mapping.setDispatches(dispatcher);
600         
601         _filterMappings=LazyList.add(_filterMappings,mapping);
602     }
603 
604     /* ------------------------------------------------------------ */
605     protected String normalizePattern(String p)
606     {
607         if (p!=null && p.length()>0 && !p.startsWith("/") && !p.startsWith("*"))
608             return "/"+p;
609         return p;
610     }
611 
612     /* ------------------------------------------------------------ */
613     protected void initServlet(XmlParser.Node node) 
614     {
615         String id=node.getAttribute("id");
616 
617         // initialize holder
618         String servlet_name=node.getString("servlet-name",false,true);
619         ServletHolder holder = _servletHandler.getServlet(servlet_name);
620         if (holder==null)
621         {
622             holder=_servletHandler.newServletHolder();
623             holder.setName(servlet_name);
624             _servlets=LazyList.add(_servlets,holder);
625         }
626         
627         // init params
628         Iterator iParamsIter=node.iterator("init-param");
629         while(iParamsIter.hasNext())
630         {
631             XmlParser.Node paramNode=(XmlParser.Node)iParamsIter.next();
632             String pname=paramNode.getString("param-name",false,true);
633             String pvalue=paramNode.getString("param-value",false,true);
634             holder.setInitParameter(pname,pvalue);
635         }
636         
637         String servlet_class=node.getString("servlet-class",false,true);
638         
639         // Handle JSP
640         if (id!=null && id.equals("jsp"))
641         {
642             _jspServletName=servlet_name;
643             _jspServletClass=servlet_class;
644             try
645             {
646                 Loader.loadClass(this.getClass(), servlet_class);
647                 _hasJSP=true;
648 
649                 //inject a jetty-jasper log bridge
650                 try
651                 {
652                     Class jspLogFactoryClass = Loader.loadClass(this.getClass(), "com.sun.org.apache.commons.logging.LogFactory");
653                     Class jspLogClass = Loader.loadClass(this.getClass(), "com.sun.org.apache.commons.logging.Log");
654                     try
655                     {
656                         Method setLogImplMethod = jspLogFactoryClass.getDeclaredMethod("setLogImplClassName", new Class[]{String.class});
657                         setLogImplMethod.invoke(null, new Object[]{"com.sun.org.apache.commons.logging.impl.JettyLog"});
658                     }
659                     catch (Exception e)
660                     {
661                         Log.warn(e);
662                     }
663                 }
664                 catch (ClassNotFoundException e)
665                 {
666                     Log.ignore(e);
667                 }
668             }
669             catch(ClassNotFoundException e)
670             {
671                 Log.info("NO JSP Support for {}, did not find {}",_context.getContextPath(),servlet_class);
672                 _hasJSP=false;
673                 _jspServletClass=servlet_class="org.mortbay.jetty.servlet.NoJspServlet";
674             }
675             if (holder.getInitParameter("scratchdir")==null)
676             {
677                 File tmp=getWebAppContext().getTempDirectory();
678                 File scratch=new File(tmp,"jsp");
679                 if (!scratch.exists())
680                     scratch.mkdir();
681                 holder.setInitParameter("scratchdir",scratch.getAbsolutePath());
682                 
683                 if ("?".equals(holder.getInitParameter("classpath")))
684                 {
685                     String classpath=getWebAppContext().getClassPath();
686                     Log.debug("classpath="+classpath);
687                     if (classpath!=null)
688                         holder.setInitParameter("classpath",classpath);
689                 }
690             }
691         }
692         if (servlet_class!=null)
693             holder.setClassName(servlet_class);
694         
695         
696         // Handler JSP file
697         String jsp_file=node.getString("jsp-file",false,true);
698         if (jsp_file!=null)
699         {
700             holder.setForcedPath(jsp_file);
701             holder.setClassName(_jspServletClass);
702         }
703 
704         // handle startup
705         XmlParser.Node startup=node.get("load-on-startup");
706         if(startup!=null)
707         {
708             String s=startup.toString(false,true).toLowerCase();
709             if(s.startsWith("t"))
710             {
711                 Log.warn("Deprecated boolean load-on-startup.  Please use integer");
712                 holder.setInitOrder(1);
713             }
714             else
715             {
716                 int order=0;
717                 try
718                 {
719                     if(s!=null&&s.trim().length()>0)
720                         order=Integer.parseInt(s);
721                 }
722                 catch(Exception e)
723                 {
724                     Log.warn("Cannot parse load-on-startup "+s+". Please use integer");
725                     Log.ignore(e);
726                 }
727                 holder.setInitOrder(order);
728             }
729         }
730         
731         Iterator sRefsIter=node.iterator("security-role-ref");
732         while(sRefsIter.hasNext())
733         {
734             XmlParser.Node securityRef=(XmlParser.Node)sRefsIter.next();
735             String roleName=securityRef.getString("role-name",false,true);
736             String roleLink=securityRef.getString("role-link",false,true);
737             if(roleName!=null&&roleName.length()>0&&roleLink!=null&&roleLink.length()>0)
738             {
739                 if(Log.isDebugEnabled())
740                     Log.debug("link role "+roleName+" to "+roleLink+" for "+this);
741                 holder.setUserRoleLink(roleName,roleLink);
742             }
743             else
744             {
745                 Log.warn("Ignored invalid security-role-ref element: "+"servlet-name="+holder.getName()+", "+securityRef);
746             }
747         }
748         
749         XmlParser.Node run_as=node.get("run-as");
750         if(run_as!=null)
751         {
752             String roleName=run_as.getString("role-name",false,true);
753             if(roleName!=null)
754                 holder.setRunAs(roleName);
755         }
756         
757     }
758 
759     /* ------------------------------------------------------------ */
760     protected void initServletMapping(XmlParser.Node node)
761     {
762         String servlet_name = node.getString("servlet-name",false,true);
763         ServletMapping mapping = new ServletMapping();
764         mapping.setServletName(servlet_name);
765 
766         ArrayList paths = new ArrayList();
767         Iterator iter=node.iterator("url-pattern");
768         while(iter.hasNext())
769         {
770             String p=((XmlParser.Node)iter.next()).toString(false,true);
771             p=normalizePattern(p);
772             paths.add(p);
773         }
774         mapping.setPathSpecs((String[])paths.toArray(new String[paths.size()]));
775 
776         _servletMappings=LazyList.add(_servletMappings,mapping);
777     }
778 
779     /* ------------------------------------------------------------ */
780     protected void initListener(XmlParser.Node node)
781     {
782         String className=node.getString("listener-class",false,true);
783         Object listener=null;
784         try
785         {
786             Class listenerClass=getWebAppContext().loadClass(className);
787             listener=newListenerInstance(listenerClass);
788             if(!(listener instanceof EventListener))
789             {
790                 Log.warn("Not an EventListener: "+listener);
791                 return;
792             }
793             _listeners=LazyList.add(_listeners, listener);
794         }
795         catch(Exception e)
796         {
797             Log.warn("Could not instantiate listener "+className,e);
798             return;
799         }
800     }
801     
802     /* ------------------------------------------------------------ */
803     protected Object newListenerInstance(Class clazz) 
804         throws InstantiationException, IllegalAccessException 
805     {
806                      return clazz.newInstance();
807     }
808     
809     /* ------------------------------------------------------------ */
810     protected void initDistributable(XmlParser.Node node)
811     {
812         // the element has no content, so its simple presence
813         // indicates that the webapp is distributable...
814         WebAppContext wac=getWebAppContext();
815         if (!wac.isDistributable())
816             wac.setDistributable(true);
817     }
818 
819     /* ------------------------------------------------------------ */
820     protected void initSessionConfig(XmlParser.Node node)
821     {
822         XmlParser.Node tNode=node.get("session-timeout");
823         if(tNode!=null)
824         {
825             int timeout=Integer.parseInt(tNode.toString(false,true));
826             getWebAppContext().getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout*60);
827         }
828     }
829 
830     /* ------------------------------------------------------------ */
831     protected void initMimeConfig(XmlParser.Node node)
832     {
833         String extension=node.getString("extension",false,true);
834         if(extension!=null&&extension.startsWith("."))
835             extension=extension.substring(1);
836         String mimeType=node.getString("mime-type",false,true);
837         getWebAppContext().getMimeTypes().addMimeMapping(extension, mimeType);
838     }
839 
840     /* ------------------------------------------------------------ */
841     protected void initWelcomeFileList(XmlParser.Node node)
842     {
843         if (_defaultWelcomeFileList)
844             _welcomeFiles=null; // erase welcome files from default web.xml 
845         
846         _defaultWelcomeFileList=false;
847         Iterator iter=node.iterator("welcome-file");
848         while(iter.hasNext())
849         {
850             XmlParser.Node indexNode=(XmlParser.Node)iter.next();
851             String welcome=indexNode.toString(false,true);
852             _welcomeFiles=LazyList.add(_welcomeFiles,welcome);
853         }
854     }
855 
856     /* ------------------------------------------------------------ */
857     protected void initLocaleEncodingList(XmlParser.Node node)
858     {
859         Iterator iter=node.iterator("locale-encoding-mapping");
860         while(iter.hasNext())
861         {
862             XmlParser.Node mapping=(XmlParser.Node)iter.next();
863             String locale=mapping.getString("locale",false,true);
864             String encoding=mapping.getString("encoding",false,true);
865             getWebAppContext().addLocaleEncoding(locale,encoding);
866         }
867     }
868 
869     /* ------------------------------------------------------------ */
870     protected void initErrorPage(XmlParser.Node node)
871     {
872         String error=node.getString("error-code",false,true);
873         if(error==null||error.length()==0)
874             error=node.getString("exception-type",false,true);
875         String location=node.getString("location",false,true);
876 
877         if (_errorPages==null)
878             _errorPages=new HashMap();
879         _errorPages.put(error,location);
880     }
881 
882     /* ------------------------------------------------------------ */
883     protected void initTagLib(XmlParser.Node node)
884     {
885         String uri=node.getString("taglib-uri",false,true);
886         String location=node.getString("taglib-location",false,true);
887 
888         getWebAppContext().setResourceAlias(uri,location);
889     }
890 
891     /* ------------------------------------------------------------ */
892     protected void initJspConfig(XmlParser.Node node)
893     {
894         for (int i=0;i<node.size();i++)
895         {
896             Object o=node.get(i);
897             if (o instanceof XmlParser.Node && "taglib".equals(((XmlParser.Node)o).getTag()))
898                 initTagLib((XmlParser.Node)o);
899         }
900         
901         // Map URLs from jsp property groups to JSP servlet.
902         // this is more JSP stupidness creaping into the servlet spec
903         Iterator iter=node.iterator("jsp-property-group");
904         Object paths=null;
905         while(iter.hasNext())
906         {
907             XmlParser.Node group=(XmlParser.Node)iter.next();
908             Iterator iter2 = group.iterator("url-pattern");
909             while (iter2.hasNext())
910             {
911                 String url = ((XmlParser.Node) iter2.next()).toString(false, true);
912                 url=normalizePattern(url);
913                 paths=LazyList.add(paths,url);
914             }
915         }
916 
917         if (LazyList.size(paths)>0)
918         {
919             String jspName=getJSPServletName();
920             if (jspName!=null)
921             {
922                 ServletMapping mapping = new ServletMapping();
923                 mapping.setServletName(jspName);
924                 mapping.setPathSpecs(LazyList.toStringArray(paths));
925                 _servletMappings=LazyList.add(_servletMappings,mapping);
926             }
927         }
928     }
929 
930     /* ------------------------------------------------------------ */
931     protected void initSecurityConstraint(XmlParser.Node node)
932     {
933         Constraint scBase = new Constraint();
934 
935         try
936         {
937             XmlParser.Node auths = node.get("auth-constraint");
938             
939             if (auths != null)
940             {
941                 scBase.setAuthenticate(true);
942                 // auth-constraint
943                 Iterator iter = auths.iterator("role-name");
944                 Object roles=null;
945                 while (iter.hasNext())
946                 {
947                     String role = ((XmlParser.Node) iter.next()).toString(false, true);
948                     roles=LazyList.add(roles,role);
949                 }
950                 scBase.setRoles(LazyList.toStringArray(roles));
951             }
952             
953             XmlParser.Node data = node.get("user-data-constraint");
954             if (data != null)
955             {
956                 data = data.get("transport-guarantee");
957                 String guarantee = data.toString(false, true).toUpperCase();
958                 if (guarantee == null || guarantee.length() == 0 || "NONE".equals(guarantee))
959                     scBase.setDataConstraint(Constraint.DC_NONE);
960                 else if ("INTEGRAL".equals(guarantee))
961                     scBase.setDataConstraint(Constraint.DC_INTEGRAL);
962                 else if ("CONFIDENTIAL".equals(guarantee))
963                     scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
964                 else
965                 {
966                     Log.warn("Unknown user-data-constraint:" + guarantee);
967                     scBase.setDataConstraint(Constraint.DC_CONFIDENTIAL);
968                 }
969             }
970             Iterator iter = node.iterator("web-resource-collection");
971             while (iter.hasNext())
972             {
973                 XmlParser.Node collection = (XmlParser.Node) iter.next();
974                 String name = collection.getString("web-resource-name", false, true);
975                 Constraint sc = (Constraint) scBase.clone();
976                 sc.setName(name);
977 
978 
979                 Iterator iter2 = collection.iterator("url-pattern");
980                 while (iter2.hasNext())
981                 {
982                     String url = ((XmlParser.Node) iter2.next()).toString(false, true);
983                     url=normalizePattern(url);
984                     
985                     Iterator iter3 = collection.iterator("http-method");
986                     if (iter3.hasNext())
987                     {
988                         while (iter3.hasNext())
989                         {
990                             String method=((XmlParser.Node) iter3.next()).toString(false, true);
991                             ConstraintMapping mapping = new ConstraintMapping();
992                             mapping.setMethod(method);
993                             mapping.setPathSpec(url);
994                             mapping.setConstraint(sc);
995                             _constraintMappings=LazyList.add(_constraintMappings,mapping);
996                         }
997                     }
998                     else
999                     {
1000                         ConstraintMapping mapping = new ConstraintMapping();
1001                         mapping.setPathSpec(url);
1002                         mapping.setConstraint(sc);
1003                         _constraintMappings=LazyList.add(_constraintMappings,mapping);
1004                     }
1005                 }
1006             }
1007         }
1008         catch (CloneNotSupportedException e)
1009         {
1010             Log.warn(e);
1011         }
1012 
1013     }
1014 
1015     /* ------------------------------------------------------------ */
1016     protected void initLoginConfig(XmlParser.Node node) throws Exception
1017     {
1018         XmlParser.Node method=node.get("auth-method");
1019         FormAuthenticator _formAuthenticator=null;
1020         if(method!=null)
1021         {
1022             Authenticator authenticator=null;
1023             String m=method.toString(false,true);
1024             if(Constraint.__FORM_AUTH.equals(m))
1025                 authenticator=_formAuthenticator=new FormAuthenticator();
1026             else if(Constraint.__BASIC_AUTH.equals(m))
1027                 authenticator=new BasicAuthenticator();
1028             else if(Constraint.__DIGEST_AUTH.equals(m))
1029                 authenticator=new DigestAuthenticator();
1030             else if(Constraint.__CERT_AUTH.equals(m) || 
1031                     Constraint.__CERT_AUTH2.equals(m))
1032                 authenticator=(Authenticator)Loader.loadClass(WebXmlConfiguration.class,"org.mortbay.jetty.security.ClientCertAuthenticator").newInstance();
1033             else
1034                 Log.warn("UNKNOWN AUTH METHOD: "+m);
1035             getWebAppContext().getConstraintsSecurityHandler().setAuthenticator(authenticator);
1036         }
1037         XmlParser.Node name=node.get("realm-name");
1038 
1039         UserRealm[] realms=ContextHandler.getCurrentContext().getContextHandler().getServer().getUserRealms();
1040 
1041         String realm_name=name==null?"default":name.toString(false,true);
1042 
1043         UserRealm realm=getWebAppContext().getSecurityHandler().getUserRealm();
1044         for (int i=0;realm==null && realms!=null && i<realms.length; i++)
1045         {
1046             if (realms[i]!=null && realm_name.equals(realms[i].getName()))
1047                 realm=realms[i];
1048         }
1049 
1050         if (realm==null)
1051         {
1052             String msg = "Unknown realm: "+realm_name;
1053             Log.warn(msg);
1054         }
1055         else
1056             getWebAppContext().getConstraintsSecurityHandler().setUserRealm(realm);
1057 
1058         
1059         XmlParser.Node formConfig=node.get("form-login-config");
1060         if(formConfig!=null)
1061         {
1062             if(_formAuthenticator==null)
1063                 Log.warn("FORM Authentication miss-configured");
1064             else
1065             {
1066                 XmlParser.Node loginPage=formConfig.get("form-login-page");
1067                 if(loginPage!=null)
1068                     _formAuthenticator.setLoginPage(loginPage.toString(false,true));
1069                 XmlParser.Node errorPage=formConfig.get("form-error-page");
1070                 if(errorPage!=null)
1071                 {
1072                     String ep=errorPage.toString(false,true);
1073                     _formAuthenticator.setErrorPage(ep);
1074                 }
1075             }
1076         }
1077     }
1078 
1079     /* ------------------------------------------------------------ */
1080     protected void initSecurityRole(XmlParser.Node node)
1081     {}
1082 
1083  
1084     
1085 
1086     /* ------------------------------------------------------------ */
1087     protected String getJSPServletName()
1088     {
1089         if (_jspServletName==null)
1090         {
1091             Map.Entry entry= _context.getServletHandler().getHolderEntry("test.jsp");
1092             if (entry!=null)
1093             {
1094                 ServletHolder holder=(ServletHolder)entry.getValue();
1095                 _jspServletName=holder.getName();
1096             }
1097         }
1098         return _jspServletName;
1099     }
1100 }