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