View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.webapp;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.security.PermissionCollection;
21  import java.util.ArrayList;
22  import java.util.Collections;
23  import java.util.EventListener;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.servlet.http.HttpSessionActivationListener;
29  import javax.servlet.http.HttpSessionAttributeListener;
30  import javax.servlet.http.HttpSessionBindingListener;
31  import javax.servlet.http.HttpSessionListener;
32  
33  import org.eclipse.jetty.security.SecurityHandler;
34  import org.eclipse.jetty.server.Connector;
35  import org.eclipse.jetty.server.HandlerContainer;
36  import org.eclipse.jetty.server.Server;
37  import org.eclipse.jetty.server.handler.ContextHandler;
38  import org.eclipse.jetty.server.handler.ErrorHandler;
39  import org.eclipse.jetty.server.session.SessionHandler;
40  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
41  import org.eclipse.jetty.servlet.ServletContextHandler;
42  import org.eclipse.jetty.servlet.ServletHandler;
43  import org.eclipse.jetty.util.LazyList;
44  import org.eclipse.jetty.util.Loader;
45  import org.eclipse.jetty.util.MultiException;
46  import org.eclipse.jetty.util.StringUtil;
47  import org.eclipse.jetty.util.URIUtil;
48  import org.eclipse.jetty.util.log.Log;
49  import org.eclipse.jetty.util.resource.Resource;
50  import org.eclipse.jetty.util.resource.ResourceCollection;
51  
52  /* ------------------------------------------------------------ */
53  /** Web Application Context Handler.
54   * The WebAppContext handler is an extension of ContextHandler that
55   * coordinates the construction and configuration of nested handlers:
56   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
57   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
58   * The handlers are configured by pluggable configuration classes, with
59   * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
60   * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
61   * 
62   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
63   * 
64   */
65  public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
66  {
67      public static final String TEMPDIR = "javax.servlet.context.tempdir";
68      public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir";
69      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
70      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
71      public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration";
72      public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
73      public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
74      
75      private static String[] __dftConfigurationClasses =
76      {
77          "org.eclipse.jetty.webapp.WebInfConfiguration",
78          "org.eclipse.jetty.webapp.WebXmlConfiguration",
79          "org.eclipse.jetty.webapp.MetaInfConfiguration",
80          "org.eclipse.jetty.webapp.FragmentConfiguration",
81          "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
82          "org.eclipse.jetty.webapp.TagLibConfiguration"
83      } ;
84      
85      // System classes are classes that cannot be replaced by 
86      // the web application, and they are *always* loaded via 
87      // system classloader.
88      public final static String[] __dftSystemClasses = 
89      {
90          "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) 
91          "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
92          "org.xml.",                         // needed by javax.xml
93          "org.w3c.",                         // needed by javax.xml
94          "org.apache.commons.logging.",      // TODO: review if special case still needed 
95          "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
96          "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
97          "org.eclipse.jetty.plus.jaas.",     // webapp cannot change jaas classes
98          "org.eclipse.jetty.websocket.",     // WebSocket is a jetty extension
99          "org.eclipse.jetty.servlet.DefaultServlet"  // webapp cannot change default servlets
100     } ;
101     
102     // Server classes are classes that are hidden from being
103     // loaded by the web application using system classloader,
104     // so if web application needs to load any of such classes,
105     // it has to include them in its distribution.
106     public final static String[] __dftServerClasses = 
107     {
108         "-org.eclipse.jetty.continuation.", // don't hide continuation classes
109         "-org.eclipse.jetty.jndi.",         // don't hide naming classes
110         "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas classes
111         "-org.eclipse.jetty.websocket.",    // don't hide websocket extension
112         "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
113         "org.eclipse.jetty."                // hide other jetty classes
114     } ; 
115 
116     private String[] _configurationClasses = __dftConfigurationClasses;
117     private ClasspathPattern _systemClasses = null;
118     private ClasspathPattern _serverClasses = null;
119 
120     private Configuration[] _configurations;
121     private String _defaultsDescriptor=WEB_DEFAULTS_XML;
122     private String _descriptor=null;
123     private final List<String> _overrideDescriptors = new ArrayList<String>();
124     private boolean _distributable=false;
125     private boolean _extractWAR=true;
126     private boolean _copyDir=false;
127     private boolean _copyWebInf=true; // TODO change to false?
128     private boolean _logUrlOnStart =false;
129     private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
130     private PermissionCollection _permissions;
131 
132     private File _tmpDir;
133     private String _war;
134     private String _extraClasspath;
135     private Throwable _unavailableException;
136     
137     private Map _resourceAliases;
138     private boolean _ownClassLoader=false;
139     private boolean _configurationDiscovered=true;
140     private boolean _configurationClassesSet=false;
141     private boolean _configurationsSet=false;
142     private boolean _allowDuplicateFragmentNames = false;
143     
144   
145     private MetaData _metadata=new MetaData();
146 
147     public static WebAppContext getCurrentWebAppContext()
148     {
149         ContextHandler.Context context=ContextHandler.getCurrentContext();
150         if (context!=null)
151         {
152             ContextHandler handler = context.getContextHandler();
153             if (handler instanceof WebAppContext)
154                 return (WebAppContext)handler;
155         }
156         return null;
157     }
158     
159     /* ------------------------------------------------------------ */
160     public WebAppContext()
161     {
162         super(SESSIONS|SECURITY); 
163         _scontext=new Context();
164         setErrorHandler(new ErrorPageErrorHandler());    
165     }
166     
167     /* ------------------------------------------------------------ */
168     /**
169      * @param contextPath The context path
170      * @param webApp The URL or filename of the webapp directory or war file.
171      */
172     public WebAppContext(String webApp,String contextPath)
173     {
174         super(null,contextPath,SESSIONS|SECURITY);
175         _scontext=new Context();
176         setContextPath(contextPath);
177         setWar(webApp);
178         setErrorHandler(new ErrorPageErrorHandler()); 
179     }
180     
181     /* ------------------------------------------------------------ */
182     /**
183      * @param parent The parent HandlerContainer.
184      * @param contextPath The context path
185      * @param webApp The URL or filename of the webapp directory or war file.
186      */
187     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
188     {
189         super(parent,contextPath,SESSIONS|SECURITY);
190         _scontext=new Context();
191         setWar(webApp);
192         setErrorHandler(new ErrorPageErrorHandler());   
193     }
194 
195     /* ------------------------------------------------------------ */
196 
197     /**
198      * This constructor is used in the geronimo integration.
199      *
200      * @param sessionHandler SessionHandler for this web app
201      * @param securityHandler SecurityHandler for this web app
202      * @param servletHandler ServletHandler for this web app
203      * @param errorHandler ErrorHandler for this web app
204      */
205     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) {
206         super(null, sessionHandler, securityHandler, servletHandler, errorHandler);
207         _scontext = new Context();
208         setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
209     }
210     
211     /* ------------------------------------------------------------ */
212     /**
213      * @param servletContextName The servletContextName to set.
214      */
215     @Override
216     public void setDisplayName(String servletContextName)
217     {
218         super.setDisplayName(servletContextName);
219         ClassLoader cl = getClassLoader();
220         if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null)
221             ((WebAppClassLoader)cl).setName(servletContextName);
222     }
223     
224     /* ------------------------------------------------------------ */
225     /** Get an exception that caused the webapp to be unavailable
226      * @return A throwable if the webapp is unavailable or null
227      */
228     public Throwable getUnavailableException()
229     {
230         return _unavailableException;
231     }
232 
233     
234     /* ------------------------------------------------------------ */
235     /** Set Resource Alias.
236      * Resource aliases map resource uri's within a context.
237      * They may optionally be used by a handler when looking for
238      * a resource.
239      * @param alias
240      * @param uri
241      */
242     public void setResourceAlias(String alias, String uri)
243     {
244         if (_resourceAliases == null)
245             _resourceAliases= new HashMap(5);
246         _resourceAliases.put(alias, uri);
247     }
248 
249     /* ------------------------------------------------------------ */
250     public Map getResourceAliases()
251     {
252         if (_resourceAliases == null)
253             return null;
254         return _resourceAliases;
255     }
256     
257     /* ------------------------------------------------------------ */
258     public void setResourceAliases(Map map)
259     {
260         _resourceAliases = map;
261     }
262     
263     /* ------------------------------------------------------------ */
264     public String getResourceAlias(String alias)
265     {
266         if (_resourceAliases == null)
267             return null;
268         return (String)_resourceAliases.get(alias);
269     }
270 
271     /* ------------------------------------------------------------ */
272     public String removeResourceAlias(String alias)
273     {
274         if (_resourceAliases == null)
275             return null;
276         return (String)_resourceAliases.remove(alias);
277     }
278 
279     /* ------------------------------------------------------------ */
280     /* (non-Javadoc)
281      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
282      */
283     @Override
284     public void setClassLoader(ClassLoader classLoader)
285     {
286         super.setClassLoader(classLoader);
287         
288 //        if ( !(classLoader instanceof WebAppClassLoader) )
289 //        {
290 //            Log.info("NOTE: detected a classloader which is not an instance of WebAppClassLoader being set on WebAppContext, some typical class and resource locations may be missing on: " + toString() );
291 //        }
292         
293         if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
294             ((WebAppClassLoader)classLoader).setName(getDisplayName());
295     }
296     
297     /* ------------------------------------------------------------ */
298     @Override
299     public Resource getResource(String uriInContext) throws MalformedURLException
300     {
301         if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH))
302             throw new MalformedURLException(uriInContext);
303 
304         IOException ioe= null;
305         Resource resource= null;
306         int loop=0;
307         while (uriInContext!=null && loop++<100)
308         {
309             try
310             {
311                 resource= super.getResource(uriInContext);
312                 if (resource != null && resource.exists())
313                     return resource;
314                 
315                 uriInContext = getResourceAlias(uriInContext);
316             }
317             catch (IOException e)
318             {
319                 Log.ignore(e);
320                 if (ioe==null)
321                     ioe= e;
322             }
323         }
324 
325         if (ioe != null && ioe instanceof MalformedURLException)
326             throw (MalformedURLException)ioe;
327 
328         return resource;
329     }
330     
331 
332     /* ------------------------------------------------------------ */
333     /** Is the context Automatically configured.
334      * 
335      * @return true if configuration discovery.
336      */
337     public boolean isConfigurationDiscovered()
338     {
339         return _configurationDiscovered;
340     }
341 
342     /* ------------------------------------------------------------ */
343     /** Set the configuration discovery mode.
344      * If configuration discovery is set to true, then the JSR315
345      * servlet 3.0 discovered configuration features are enabled.
346      * These are:<ul>
347      * <li>Web Fragments</li>
348      * <li>META-INF/resource directories</li>
349      * </ul>
350      * @param discovered true if configuration discovery is enabled for automatic configuration from the context
351      */
352     public void setConfigurationDiscovered(boolean discovered)
353     {
354         _configurationDiscovered = discovered;
355     }
356     
357     /* ------------------------------------------------------------ */
358     /** Pre configure the web application.
359      * <p>
360      * The method is normally called from {@link #start()}. It performs
361      * the discovery of the configurations to be applied to this context,
362      * specifically:<ul>
363      * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
364      * <li>Setup the default System classes by calling {@link #loadSystemClasses()}
365      * <li>Setup the default Server classes by calling <code>loadServerClasses()</code>
366      * <li>Instantiates a classload (if one is not already set)
367      * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all 
368      * Configuration instances.
369      * </ul>
370      * @throws Exception
371      */
372     public void preConfigure() throws Exception
373     {
374         // Setup configurations
375         loadConfigurations();
376 
377         // Setup system classes
378         loadSystemClasses();
379         
380         // Setup server classes
381         loadServerClasses();
382 
383         // Configure classloader
384         _ownClassLoader=false;
385         if (getClassLoader()==null)
386         {
387             WebAppClassLoader classLoader = new WebAppClassLoader(this);
388             setClassLoader(classLoader);
389             _ownClassLoader=true;
390         }
391 
392         if (Log.isDebugEnabled())
393         {
394             ClassLoader loader = getClassLoader();
395             Log.debug("Thread Context class loader is: " + loader);
396             loader=loader.getParent();
397             while(loader!=null)
398             {
399                 Log.debug("Parent class loader is: " + loader);
400                 loader=loader.getParent();
401             }
402         }
403       
404         // Prepare for configuration     
405         for (int i=0;i<_configurations.length;i++)
406             _configurations[i].preConfigure(this);
407     }
408 
409     /* ------------------------------------------------------------ */
410     public void configure() throws Exception
411     {
412         // Configure webapp
413         for (int i=0;i<_configurations.length;i++)
414             _configurations[i].configure(this);
415     }
416     
417     /* ------------------------------------------------------------ */
418     public void postConfigure() throws Exception
419     {   
420         // Clean up after configuration
421         for (int i=0;i<_configurations.length;i++)
422             _configurations[i].postConfigure(this);
423     }
424     
425     /* ------------------------------------------------------------ */
426     /*
427      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
428      */
429     @Override
430     protected void doStart() throws Exception
431     {
432         try
433         {
434             _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
435             preConfigure();
436             super.doStart();
437             postConfigure();
438 
439             if (isLogUrlOnStart())
440                 dumpUrl();
441         }
442         catch (Exception e)
443         {
444             //start up of the webapp context failed, make sure it is not started
445             Log.warn("Failed startup of context "+this, e);
446             _unavailableException=e;
447             setAvailable(false);
448         }
449     }
450     
451     /* ------------------------------------------------------------ */
452     /*
453      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
454      */
455     @Override
456     protected void doStop() throws Exception
457     {
458         super.doStop();
459 
460         try
461         {
462             for (int i=_configurations.length;i-->0;)
463                 _configurations[i].deconfigure(this);
464             
465             if (_metadata != null)
466                 _metadata.clear();
467             _metadata=new MetaData();
468             
469         }
470         finally
471         {
472             if (_ownClassLoader)
473                 setClassLoader(null);
474 
475             setAvailable(true);
476             _unavailableException=null;
477         }
478     }
479     
480     /* ------------------------------------------------------------ */
481     @Override
482     public void destroy()
483     {
484         // Prepare for configuration     
485         MultiException mx=new MultiException();
486         if (_configurations!=null)
487         {
488             for (int i=_configurations.length;i-->0;)
489             {
490                 try
491                 {   
492                     _configurations[i].destroy(this);
493                 }
494                 catch(Exception e)
495                 {
496                     mx.add(e);
497                 }
498             }
499         }
500         _configurations=null;
501         super.destroy();
502         mx.ifExceptionThrowRuntime();
503     }
504 
505 
506     /* ------------------------------------------------------------ */
507     /*
508      * Dumps the current web app name and URL to the log
509      */
510     private void dumpUrl()
511     {
512         Connector[] connectors = getServer().getConnectors();
513         for (int i=0;i<connectors.length;i++)
514         {
515             String connectorName = connectors[i].getName();
516             String displayName = getDisplayName();
517             if (displayName == null)
518                 displayName = "WebApp@"+connectors.hashCode();
519            
520             Log.info(displayName + " at http://" + connectorName + getContextPath());
521         }
522     }
523 
524     /* ------------------------------------------------------------ */
525     /**
526      * @return Returns the configurations.
527      */
528     public String[] getConfigurationClasses()
529     {
530         return _configurationClasses;
531     }
532     
533     /* ------------------------------------------------------------ */
534     /**
535      * @return Returns the configurations.
536      */
537     public Configuration[] getConfigurations()
538     {
539         return _configurations;
540     }
541     
542     /* ------------------------------------------------------------ */
543     /**
544      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
545      * @return Returns the defaultsDescriptor.
546      */
547     public String getDefaultsDescriptor()
548     {
549         return _defaultsDescriptor;
550     }
551     
552     /* ------------------------------------------------------------ */
553     /**
554      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
555      * @return Returns the Override Descriptor.
556      * @deprecated use {@link #getOverrideDescriptors()}
557      */
558     public String getOverrideDescriptor()
559     {
560         if (_overrideDescriptors.size()!=1)
561             return null;
562         return _overrideDescriptors.get(0);
563     }
564     
565     /* ------------------------------------------------------------ */
566     /**
567      * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
568      * @return Returns the Override Descriptor list
569      */
570     public List<String> getOverrideDescriptors()
571     {
572         return Collections.unmodifiableList(_overrideDescriptors);
573     }
574     
575     /* ------------------------------------------------------------ */
576     /**
577      * @return Returns the permissions.
578      */
579     public PermissionCollection getPermissions()
580     {
581         return _permissions;
582     }
583 
584     /* ------------------------------------------------------------ */
585     /**
586      * @see #setServerClasses(String[])
587      * @return Returns the serverClasses.
588      */
589     public String[] getServerClasses()
590     {
591         if (_serverClasses == null)
592             loadServerClasses();
593         
594         return _serverClasses.getPatterns();
595     }
596 
597     public void addServerClass(String classname)
598     {
599         if (_serverClasses == null)
600             loadServerClasses();
601         
602         _serverClasses.addPattern(classname);
603     }
604     
605     /* ------------------------------------------------------------ */
606     /**
607      * @see #setSystemClasses(String[])
608      * @return Returns the systemClasses.
609      */
610     public String[] getSystemClasses()
611     {
612         if (_systemClasses == null)
613             loadSystemClasses();
614         
615         return _systemClasses.getPatterns();
616     }
617     
618     /* ------------------------------------------------------------ */
619     public void addSystemClass(String classname)
620     {
621         if (_systemClasses == null)
622             loadSystemClasses();
623         
624         _systemClasses.addPattern(classname);
625     }
626     
627     /* ------------------------------------------------------------ */
628     public boolean isServerClass(String name)
629     {
630         if (_serverClasses == null)
631             loadServerClasses();
632         
633         return _serverClasses.match(name);
634     }
635 
636     /* ------------------------------------------------------------ */
637     public boolean isSystemClass(String name)
638     {
639         if (_systemClasses == null)
640             loadSystemClasses();
641         
642         return _systemClasses.match(name);
643     }
644     
645     /* ------------------------------------------------------------ */
646     protected void loadSystemClasses()
647     {
648         if (_systemClasses != null)
649             return;
650         
651         //look for a Server attribute with the list of System classes
652         //to apply to every web application. If not present, use our defaults.
653         Server server = getServer();
654         if (server != null)
655         {
656             Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
657             if (systemClasses != null && systemClasses instanceof String[])
658                 _systemClasses = new ClasspathPattern((String[])systemClasses);
659         }
660         
661         if (_systemClasses == null)
662             _systemClasses = new ClasspathPattern(__dftSystemClasses);
663     }
664     
665     /* ------------------------------------------------------------ */
666     private void loadServerClasses()
667     {
668         if (_serverClasses != null)
669             return;
670         
671         //look for a Server attribute with the list of Server classes
672         //to apply to every web application. If not present, use our defaults.
673         Server server = getServer();
674         if (server != null)
675         {
676             Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
677             if (serverClasses != null || serverClasses instanceof String[])
678                 _serverClasses = new ClasspathPattern((String[])serverClasses);
679         }
680         
681         if (_serverClasses == null)
682             _serverClasses = new ClasspathPattern(__dftServerClasses);
683     }
684     
685     /* ------------------------------------------------------------ */
686     /**
687      * @return Returns the war as a file or URL string (Resource)
688      */
689     public String getWar()
690     {
691         if (_war==null)
692             _war=getResourceBase();
693         return _war;
694     }
695 
696     /* ------------------------------------------------------------ */
697     public Resource getWebInf() throws IOException
698     {
699         if (super.getBaseResource() == null)
700             return null;
701 
702         // Iw there a WEB-INF directory?
703         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
704         if (!web_inf.exists() || !web_inf.isDirectory())
705             return null;
706         
707         return web_inf;
708     }
709     
710     /* ------------------------------------------------------------ */
711     /**
712      * @return Returns the distributable.
713      */
714     public boolean isDistributable()
715     {
716         return _distributable;
717     }
718 
719     /* ------------------------------------------------------------ */
720     /**
721      * @return Returns the extractWAR.
722      */
723     public boolean isExtractWAR()
724     {
725         return _extractWAR;
726     }
727 
728     /* ------------------------------------------------------------ */
729     /**
730      * @return True if the webdir is copied (to allow hot replacement of jars on windows)
731      */
732     public boolean isCopyWebDir()
733     {
734         return _copyDir;
735     }
736 
737     /* ------------------------------------------------------------ */
738     /**
739      * @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
740      */
741     public boolean isCopyWebInf()
742     {
743         return _copyWebInf;
744     }
745 
746     /* ------------------------------------------------------------ */
747     /**
748      * @return True if the classloader should delegate first to the parent 
749      * classloader (standard java behaviour) or false if the classloader 
750      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet 
751      * spec recommendation).
752      */
753     public boolean isParentLoaderPriority()
754     {
755         return _parentLoaderPriority;
756     }
757     
758     
759     /* ------------------------------------------------------------ */
760     public String[] getDefaultConfigurationClasses ()
761     {
762         return __dftConfigurationClasses;
763     }
764     
765     
766     /* ------------------------------------------------------------ */
767     protected void loadConfigurations()
768     	throws Exception
769     {
770         //if the configuration instances have been set explicitly, use them
771         if (_configurations!=null)
772             return;
773 
774         //if the configuration classnames have been set explicitly use them
775         if (!_configurationClassesSet)
776             _configurationClasses=__dftConfigurationClasses;
777 
778         _configurations = new Configuration[_configurationClasses.length];
779         for (int i = 0; i < _configurationClasses.length; i++)
780         {
781             _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
782         }
783     }
784     
785     /* ------------------------------------------------------------ */
786     @Override
787     protected boolean isProtectedTarget(String target)
788     {
789         while (target.startsWith("//"))
790             target=URIUtil.compactPath(target);
791          
792         return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
793     }
794     
795 
796     /* ------------------------------------------------------------ */
797     @Override
798     public String toString()
799     {
800         return super.toString()+(_war==null?"":(","+_war));
801     }
802 
803     /* ------------------------------------------------------------ */
804     /**
805      * @param configurations The configuration class names.  If setConfigurations is not called
806      * these classes are used to create a configurations array.
807      */
808     public void setConfigurationClasses(String[] configurations)
809     {
810         if (isStarted())
811             throw new IllegalStateException();
812         _configurationClasses = configurations==null?null:(String[])configurations.clone();
813         _configurationClassesSet = true;
814         _configurations=null;
815     }
816     
817     /* ------------------------------------------------------------ */
818     /**
819      * @param configurations The configurations to set.
820      */
821     public void setConfigurations(Configuration[] configurations)
822     {
823         if (isStarted())
824             throw new IllegalStateException();
825         _configurations = configurations==null?null:(Configuration[])configurations.clone();
826         _configurationsSet = true;
827     }
828 
829     /* ------------------------------------------------------------ */
830     /**
831      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
832      * @param defaultsDescriptor The defaultsDescriptor to set.
833      */
834     public void setDefaultsDescriptor(String defaultsDescriptor)
835     {
836         _defaultsDescriptor = defaultsDescriptor;
837     }
838 
839     /* ------------------------------------------------------------ */
840     /**
841      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
842      * @param overrideDescriptor The overrideDescritpor to set.
843      * @deprecated use {@link #setOverrideDescriptors(List)}
844      */
845     public void setOverrideDescriptor(String overrideDescriptor)
846     {
847         _overrideDescriptors.clear();
848         _overrideDescriptors.add(overrideDescriptor);
849     }
850     
851     /* ------------------------------------------------------------ */
852     /**
853      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
854      * @param overrideDescriptors The overrideDescriptors (file or URL) to set.
855      */
856     public void setOverrideDescriptors(List<String> overrideDescriptors)
857     {
858         _overrideDescriptors.clear();
859         _overrideDescriptors.addAll(overrideDescriptors);
860     }
861     
862     /* ------------------------------------------------------------ */
863     /**
864      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
865      * @param overrideDescriptor The overrideDescriptor (file or URL) to add.
866      */
867     public void addOverrideDescriptor(String overrideDescriptor)
868     {
869         _overrideDescriptors.add(overrideDescriptor);
870     }
871 
872     /* ------------------------------------------------------------ */
873     /**
874      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
875      */
876     public String getDescriptor()
877     {
878         return _descriptor;
879     }
880 
881     /* ------------------------------------------------------------ */
882     /**
883      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
884      */
885     public void setDescriptor(String descriptor)
886     {
887         _descriptor=descriptor;
888     }
889     
890     /* ------------------------------------------------------------ */
891     /**
892      * @param distributable The distributable to set.
893      */
894     public void setDistributable(boolean distributable)
895     {
896         this._distributable = distributable;
897     }
898 
899     /* ------------------------------------------------------------ */
900     @Override
901     public void setEventListeners(EventListener[] eventListeners)
902     {
903         if (_sessionHandler!=null)
904             _sessionHandler.clearEventListeners();
905             
906         super.setEventListeners(eventListeners);
907       
908         for (int i=0; eventListeners!=null && i<eventListeners.length;i ++)
909         {
910             EventListener listener = eventListeners[i];
911             
912             if ((listener instanceof HttpSessionActivationListener)
913                             || (listener instanceof HttpSessionAttributeListener)
914                             || (listener instanceof HttpSessionBindingListener)
915                             || (listener instanceof HttpSessionListener))
916             {
917                 if (_sessionHandler!=null)
918                     _sessionHandler.addEventListener(listener);
919             }
920             
921         }
922     }
923 
924     /* ------------------------------------------------------------ */
925     /** Add EventListener
926      * Conveniance method that calls {@link #setEventListeners(EventListener[])}
927      * @param listener
928      */
929     @Override
930     public void addEventListener(EventListener listener)
931     {   
932         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));
933     }
934 
935     
936     /* ------------------------------------------------------------ */
937     /**
938      * @param extractWAR True if war files are extracted
939      */
940     public void setExtractWAR(boolean extractWAR)
941     {
942         _extractWAR = extractWAR;
943     }
944     
945     /* ------------------------------------------------------------ */
946     /**
947      * @param copy True if the webdir is copied (to allow hot replacement of jars)
948      */
949     public void setCopyWebDir(boolean copy)
950     {
951         _copyDir = copy;
952     }
953 
954     /* ------------------------------------------------------------ */
955     /**
956      * @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
957      */
958     public void setCopyWebInf(boolean copyWebInf)
959     {
960         _copyWebInf = copyWebInf;
961     }
962 
963     /* ------------------------------------------------------------ */
964     /**
965      * @param java2compliant The java2compliant to set.
966      */
967     public void setParentLoaderPriority(boolean java2compliant)
968     {
969         _parentLoaderPriority = java2compliant;
970     }
971 
972     /* ------------------------------------------------------------ */
973     /**
974      * @param permissions The permissions to set.
975      */
976     public void setPermissions(PermissionCollection permissions)
977     {
978         _permissions = permissions;
979     }
980 
981     /* ------------------------------------------------------------ */
982     /**
983      * Set the server classes patterns.
984      * <p>
985      * Server classes/packages are classes used to implement the server and are hidden
986      * from the context.  If the context needs to load these classes, it must have its
987      * own copy of them in WEB-INF/lib or WEB-INF/classes.
988      * A class pattern is a string of one of the forms:<dl>
989      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
990      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
991      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
992      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
993      * </dl>
994      * @param serverClasses The serverClasses to set.
995      */
996     public void setServerClasses(String[] serverClasses)
997     {
998         _serverClasses = new ClasspathPattern(serverClasses);
999     }
1000     
1001     /* ------------------------------------------------------------ */
1002     /**
1003      * Set the system classes patterns.
1004      * <p>
1005      * System classes/packages are classes provided by the JVM and that
1006      * cannot be replaced by classes of the same name from WEB-INF,
1007      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
1008      * A class pattern is a string of one of the forms:<dl>
1009      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1010      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1011      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1012      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1013      * </dl>
1014      * @param systemClasses The systemClasses to set.
1015      */
1016     public void setSystemClasses(String[] systemClasses)
1017     {
1018         _systemClasses = new ClasspathPattern(systemClasses);
1019     }
1020     
1021 
1022     /* ------------------------------------------------------------ */
1023     /** Set temporary directory for context.
1024      * The javax.servlet.context.tempdir attribute is also set.
1025      * @param dir Writable temporary directory.
1026      */
1027     public void setTempDirectory(File dir)
1028     {
1029         if (isStarted())
1030             throw new IllegalStateException("Started");
1031 
1032         if (dir!=null)
1033         {
1034             try{dir=new File(dir.getCanonicalPath());}
1035             catch (IOException e){Log.warn(Log.EXCEPTION,e);}
1036         }
1037 
1038         if (dir!=null && !dir.exists())
1039         {
1040             dir.mkdir();
1041             dir.deleteOnExit();
1042         }
1043 
1044         if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
1045             throw new IllegalArgumentException("Bad temp directory: "+dir);
1046         
1047         try
1048         {
1049             if (dir!=null)
1050                 dir=dir.getCanonicalFile();
1051         }
1052         catch(Exception e)
1053         {
1054             Log.warn(e);
1055         }
1056         _tmpDir=dir;
1057         setAttribute(TEMPDIR,_tmpDir);
1058     }
1059     
1060     /* ------------------------------------------------------------ */
1061     public File getTempDirectory ()
1062     {
1063         return _tmpDir;
1064     }
1065     
1066     /* ------------------------------------------------------------ */
1067     /**
1068      * @param war The war to set as a file name or URL
1069      */
1070     public void setWar(String war)
1071     {
1072         _war = war;
1073     }
1074 
1075 
1076     /* ------------------------------------------------------------ */
1077     /**
1078      * @return Comma or semicolon separated path of filenames or URLs
1079      * pointing to directories or jar files. Directories should end
1080      * with '/'.
1081      */
1082     public String getExtraClasspath()
1083     {
1084         return _extraClasspath;
1085     }
1086 
1087     /* ------------------------------------------------------------ */
1088     /**
1089      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1090      * pointing to directories or jar files. Directories should end
1091      * with '/'.
1092      */
1093     public void setExtraClasspath(String extraClasspath)
1094     {
1095         _extraClasspath=extraClasspath;
1096     }
1097 
1098     /* ------------------------------------------------------------ */
1099     public boolean isLogUrlOnStart()
1100     {
1101         return _logUrlOnStart;
1102     }
1103 
1104     /* ------------------------------------------------------------ */
1105     /**
1106      * Sets whether or not the web app name and URL is logged on startup
1107      *
1108      * @param logOnStart whether or not the log message is created
1109      */
1110     public void setLogUrlOnStart(boolean logOnStart)
1111     {
1112         this._logUrlOnStart = logOnStart;
1113     }
1114     
1115     
1116     /* ------------------------------------------------------------ */
1117     @Override
1118     public void setServer(Server server)
1119     {
1120         super.setServer(server);
1121         //if we haven't been given a set of configuration instances to 
1122         //use, and we haven't been given a set of configuration classes
1123         //to use, use the configuration classes that came from the
1124         //Server (if there are any)
1125         if (!_configurationsSet && !_configurationClassesSet && server != null)
1126         {
1127             String[] serverConfigs = (String[])server.getAttribute(SERVER_CONFIG);
1128             if (serverConfigs != null)
1129                 setConfigurationClasses(serverConfigs);
1130         }
1131     }
1132     
1133     
1134     /* ------------------------------------------------------------ */
1135     public boolean isAllowDuplicateFragmentNames()
1136     {
1137         return _allowDuplicateFragmentNames;
1138     }
1139     
1140     
1141     /* ------------------------------------------------------------ */
1142     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
1143     {
1144         _allowDuplicateFragmentNames = allowDuplicateFragmentNames;
1145     }
1146 
1147 
1148     /* ------------------------------------------------------------ */
1149     @Override
1150     protected void startContext()
1151         throws Exception
1152     {
1153         configure();
1154         
1155         //resolve the metadata
1156         _metadata.resolve(this);
1157         
1158         super.startContext();
1159     }
1160     
1161     /* ------------------------------------------------------------ */
1162     public class Context extends ServletContextHandler.Context
1163     {
1164         /* ------------------------------------------------------------ */
1165         public URL getResource(String path) throws MalformedURLException
1166         {
1167             Resource resource=WebAppContext.this.getResource(path);
1168             if (resource==null || !resource.exists())
1169                 return null;
1170             
1171             // Should we go to the original war?
1172             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1173             {
1174                 Resource[] resources = ((ResourceCollection)resource).getResources();
1175                 for (int i=resources.length;i-->0;)
1176                 {
1177                     if (resources[i].getName().startsWith("jar:file"))
1178                         return resources[i].getURL();
1179                 }
1180             }
1181                 
1182             return resource.getURL();
1183         }
1184     }
1185 
1186     /* ------------------------------------------------------------ */
1187     public MetaData getMetaData()
1188     {
1189         return _metadata;
1190     }
1191 
1192 }