View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.webapp;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.MalformedURLException;
24  import java.net.URL;
25  import java.security.PermissionCollection;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.EventListener;
31  import java.util.HashMap;
32  import java.util.HashSet;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import javax.servlet.ServletContext;
38  import javax.servlet.ServletRegistration.Dynamic;
39  import javax.servlet.ServletSecurityElement;
40  import javax.servlet.http.HttpSessionActivationListener;
41  import javax.servlet.http.HttpSessionAttributeListener;
42  import javax.servlet.http.HttpSessionBindingListener;
43  import javax.servlet.http.HttpSessionIdListener;
44  import javax.servlet.http.HttpSessionListener;
45  
46  import org.eclipse.jetty.security.ConstraintAware;
47  import org.eclipse.jetty.security.ConstraintMapping;
48  import org.eclipse.jetty.security.ConstraintSecurityHandler;
49  import org.eclipse.jetty.security.SecurityHandler;
50  import org.eclipse.jetty.server.Connector;
51  import org.eclipse.jetty.server.HandlerContainer;
52  import org.eclipse.jetty.server.Server;
53  import org.eclipse.jetty.server.handler.ContextHandler;
54  import org.eclipse.jetty.server.handler.ErrorHandler;
55  import org.eclipse.jetty.server.session.SessionHandler;
56  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
57  import org.eclipse.jetty.servlet.ServletContextHandler;
58  import org.eclipse.jetty.servlet.ServletHandler;
59  import org.eclipse.jetty.util.Loader;
60  import org.eclipse.jetty.util.MultiException;
61  import org.eclipse.jetty.util.URIUtil;
62  import org.eclipse.jetty.util.annotation.ManagedAttribute;
63  import org.eclipse.jetty.util.annotation.ManagedObject;
64  import org.eclipse.jetty.util.log.Log;
65  import org.eclipse.jetty.util.log.Logger;
66  import org.eclipse.jetty.util.resource.Resource;
67  import org.eclipse.jetty.util.resource.ResourceCollection;
68  
69  /* ------------------------------------------------------------ */
70  /** Web Application Context Handler.
71   * The WebAppContext handler is an extension of ContextHandler that
72   * coordinates the construction and configuration of nested handlers:
73   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
74   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
75   * The handlers are configured by pluggable configuration classes, with
76   * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
77   * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
78   *
79   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
80   *
81   */
82  @ManagedObject("Web Application ContextHandler")
83  public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
84  {
85      private static final Logger LOG = Log.getLogger(WebAppContext.class);
86  
87      public static final String TEMPDIR = "javax.servlet.context.tempdir";
88      public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir";
89      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
90      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
91      public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
92      public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
93  
94      private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"};
95  
96      public static final String[] DEFAULT_CONFIGURATION_CLASSES =
97      {
98          "org.eclipse.jetty.webapp.WebInfConfiguration",
99          "org.eclipse.jetty.webapp.WebXmlConfiguration",
100         "org.eclipse.jetty.webapp.MetaInfConfiguration",
101         "org.eclipse.jetty.webapp.FragmentConfiguration",
102         "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
103     } ;
104 
105     // System classes are classes that cannot be replaced by
106     // the web application, and they are *always* loaded via
107     // system classloader.
108     public final static String[] __dftSystemClasses =
109     {
110         "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
111         "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
112         "org.xml.",                         // needed by javax.xml
113         "org.w3c.",                         // needed by javax.xml
114         "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
115         "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
116         "org.eclipse.jetty.jaas.",          // webapp cannot change jaas classes
117         "org.eclipse.jetty.websocket.",     // webapp cannot change / replace websocket classes
118         "org.eclipse.jetty.util.log.",      // webapp should use server log
119         "org.eclipse.jetty.servlet.ServletContextHandler.Decorator", // for CDI / weld use
120         "org.eclipse.jetty.servlet.DefaultServlet", // webapp cannot change default servlets
121         "org.eclipse.jetty.servlets.AsyncGzipFilter" // special case for AsyncGzipFilter
122     } ;
123 
124     // Server classes are classes that are hidden from being
125     // loaded by the web application using system classloader,
126     // so if web application needs to load any of such classes,
127     // it has to include them in its distribution.
128     public final static String[] __dftServerClasses =
129     {
130         "-org.eclipse.jetty.continuation.", // don't hide continuation classes
131         "-org.eclipse.jetty.jndi.",         // don't hide naming classes
132         "-org.eclipse.jetty.jaas.",         // don't hide jaas classes
133         "-org.eclipse.jetty.servlets.",     // don't hide jetty servlets
134         "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
135         "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
136         "-org.eclipse.jetty.websocket.",    // don't hide websocket classes from webapps (allow webapp to use ones from system classloader)
137         "-org.eclipse.jetty.apache.",       // don't hide jetty apache impls
138         "-org.eclipse.jetty.util.log.",     // don't hide server log 
139         "-org.eclipse.jetty.servlet.ServletContextHandler.Decorator", // don't hide CDI / weld interface  
140         "org.objectweb.asm.",               // hide asm used by jetty
141         "org.eclipse.jdt.",                 // hide jdt used by jetty
142         "org.eclipse.jetty."                // hide other jetty classes
143     } ;
144 
145     private final List<String> _configurationClasses = new ArrayList<>();
146     private ClasspathPattern _systemClasses = null;
147     private ClasspathPattern _serverClasses = null;
148 
149     private final List<Configuration> _configurations = new ArrayList<>();
150     private String _defaultsDescriptor=WEB_DEFAULTS_XML;
151     private String _descriptor=null;
152     private final List<String> _overrideDescriptors = new ArrayList<>();
153     private boolean _distributable=false;
154     private boolean _extractWAR=true;
155     private boolean _copyDir=false;
156     private boolean _copyWebInf=false;
157     private boolean _logUrlOnStart =false;
158     private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
159     private PermissionCollection _permissions;
160 
161     private String[] _contextWhiteList = null;
162 
163     private File _tmpDir;
164     private boolean _persistTmpDir = false;
165  
166     private String _war;
167     private String _extraClasspath;
168     private Throwable _unavailableException;
169 
170     private Map<String, String> _resourceAliases;
171     private boolean _ownClassLoader=false;
172     private boolean _configurationDiscovered=true;
173     private boolean _allowDuplicateFragmentNames = false;
174     private boolean _throwUnavailableOnStartupException = false;
175 
176 
177 
178     private MetaData _metadata=new MetaData();
179 
180     public static WebAppContext getCurrentWebAppContext()
181     {
182         ContextHandler.Context context=ContextHandler.getCurrentContext();
183         if (context!=null)
184         {
185             ContextHandler handler = context.getContextHandler();
186             if (handler instanceof WebAppContext)
187                 return (WebAppContext)handler;
188         }
189         return null;
190     }
191 
192     /* ------------------------------------------------------------ */
193     public WebAppContext()
194     {
195         this(null,null,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
196     }
197 
198     /* ------------------------------------------------------------ */
199     /**
200      * @param contextPath The context path
201      * @param webApp The URL or filename of the webapp directory or war file.
202      */
203     public WebAppContext(String webApp,String contextPath)
204     {
205         this(null,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
206         setWar(webApp);
207     }
208 
209     /* ------------------------------------------------------------ */
210     /**
211      * @param parent The parent HandlerContainer.
212      * @param contextPath The context path
213      * @param webApp The URL or filename of the webapp directory or war file.
214      */
215     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
216     {
217         this(parent,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
218         setWar(webApp);
219     }
220 
221     /* ------------------------------------------------------------ */
222 
223     /**
224      * This constructor is used in the geronimo integration.
225      *
226      * @param sessionHandler SessionHandler for this web app
227      * @param securityHandler SecurityHandler for this web app
228      * @param servletHandler ServletHandler for this web app
229      * @param errorHandler ErrorHandler for this web app
230      */
231     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) 
232     {
233         this(null, null, sessionHandler, securityHandler, servletHandler, errorHandler,0);
234     }
235 
236     /* ------------------------------------------------------------ */
237     /**
238      * This constructor is used in the geronimo integration.
239      *
240      * @param sessionHandler SessionHandler for this web app
241      * @param securityHandler SecurityHandler for this web app
242      * @param servletHandler ServletHandler for this web app
243      * @param errorHandler ErrorHandler for this web app
244      */
245     public WebAppContext(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler,int options) 
246     {
247         super(parent, contextPath,sessionHandler, securityHandler, servletHandler, errorHandler,options);
248         _scontext = new Context();
249         setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
250         setProtectedTargets(__dftProtectedTargets);
251     }
252 
253     /* ------------------------------------------------------------ */
254     /**
255      * @param servletContextName The servletContextName to set.
256      */
257     @Override
258     public void setDisplayName(String servletContextName)
259     {
260         super.setDisplayName(servletContextName);
261         ClassLoader cl = getClassLoader();
262         if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null)
263             ((WebAppClassLoader)cl).setName(servletContextName);
264     }
265 
266     /* ------------------------------------------------------------ */
267     /** Get an exception that caused the webapp to be unavailable
268      * @return A throwable if the webapp is unavailable or null
269      */
270     public Throwable getUnavailableException()
271     {
272         return _unavailableException;
273     }
274 
275 
276     /* ------------------------------------------------------------ */
277     /** Set Resource Alias.
278      * Resource aliases map resource uri's within a context.
279      * They may optionally be used by a handler when looking for
280      * a resource.
281      * @param alias
282      * @param uri
283      */
284     public void setResourceAlias(String alias, String uri)
285     {
286         if (_resourceAliases == null)
287             _resourceAliases= new HashMap<String, String>(5);
288         _resourceAliases.put(alias, uri);
289     }
290 
291     /* ------------------------------------------------------------ */
292     public Map<String, String> getResourceAliases()
293     {
294         if (_resourceAliases == null)
295             return null;
296         return _resourceAliases;
297     }
298 
299     /* ------------------------------------------------------------ */
300     public void setResourceAliases(Map<String, String> map)
301     {
302         _resourceAliases = map;
303     }
304 
305     /* ------------------------------------------------------------ */
306     public String getResourceAlias(String path)
307     {
308         if (_resourceAliases == null)
309             return null;
310         String alias = _resourceAliases.get(path);
311 
312         int slash=path.length();
313         while (alias==null)
314         {
315             slash=path.lastIndexOf("/",slash-1);
316             if (slash<0)
317                 break;
318             String match=_resourceAliases.get(path.substring(0,slash+1));
319             if (match!=null)
320                 alias=match+path.substring(slash+1);
321         }
322         return alias;
323     }
324 
325     /* ------------------------------------------------------------ */
326     public String removeResourceAlias(String alias)
327     {
328         if (_resourceAliases == null)
329             return null;
330         return _resourceAliases.remove(alias);
331     }
332 
333     /* ------------------------------------------------------------ */
334     /* (non-Javadoc)
335      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
336      */
337     @Override
338     public void setClassLoader(ClassLoader classLoader)
339     {
340         super.setClassLoader(classLoader);
341 
342 //        if ( !(classLoader instanceof WebAppClassLoader) )
343 //        {
344 //            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() );
345 //        }
346 
347         if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
348             ((WebAppClassLoader)classLoader).setName(getDisplayName());
349     }
350 
351     /* ------------------------------------------------------------ */
352     @Override
353     public Resource getResource(String uriInContext) throws MalformedURLException
354     {
355         if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH))
356             throw new MalformedURLException(uriInContext);
357 
358         IOException ioe= null;
359         Resource resource= null;
360         int loop=0;
361         while (uriInContext!=null && loop++<100)
362         {
363             try
364             {
365                 resource= super.getResource(uriInContext);
366                 if (resource != null && resource.exists())
367                     return resource;
368 
369                 uriInContext = getResourceAlias(uriInContext);
370             }
371             catch (IOException e)
372             {
373                 LOG.ignore(e);
374                 if (ioe==null)
375                     ioe= e;
376             }
377         }
378 
379         if (ioe != null && ioe instanceof MalformedURLException)
380             throw (MalformedURLException)ioe;
381 
382         return resource;
383     }
384 
385 
386     /* ------------------------------------------------------------ */
387     /** Is the context Automatically configured.
388      *
389      * @return true if configuration discovery.
390      */
391     public boolean isConfigurationDiscovered()
392     {
393         return _configurationDiscovered;
394     }
395 
396     /* ------------------------------------------------------------ */
397     /** Set the configuration discovery mode.
398      * If configuration discovery is set to true, then the JSR315
399      * servlet 3.0 discovered configuration features are enabled.
400      * These are:<ul>
401      * <li>Web Fragments</li>
402      * <li>META-INF/resource directories</li>
403      * </ul>
404      * @param discovered true if configuration discovery is enabled for automatic configuration from the context
405      */
406     public void setConfigurationDiscovered(boolean discovered)
407     {
408         _configurationDiscovered = discovered;
409     }
410 
411     /* ------------------------------------------------------------ */
412     /** Pre configure the web application.
413      * <p>
414      * The method is normally called from {@link #start()}. It performs
415      * the discovery of the configurations to be applied to this context,
416      * specifically:<ul>
417      * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
418      * <li>Setup the default System classes by calling {@link #loadSystemClasses()}
419      * <li>Setup the default Server classes by calling <code>loadServerClasses()</code>
420      * <li>Instantiates a classload (if one is not already set)
421      * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all
422      * Configuration instances.
423      * </ul>
424      * @throws Exception
425      */
426     public void preConfigure() throws Exception
427     {
428         // Setup configurations
429         loadConfigurations();
430 
431         // Setup system classes
432         loadSystemClasses();
433 
434         // Setup server classes
435         loadServerClasses();
436 
437         // Configure classloader
438         _ownClassLoader=false;
439         if (getClassLoader()==null)
440         {
441             WebAppClassLoader classLoader = new WebAppClassLoader(this);
442             setClassLoader(classLoader);
443             _ownClassLoader=true;
444         }
445 
446         if (LOG.isDebugEnabled())
447         {
448             ClassLoader loader = getClassLoader();
449             LOG.debug("Thread Context classloader {}",loader);
450             loader=loader.getParent();
451             while(loader!=null)
452             {
453                 LOG.debug("Parent class loader: {} ",loader);
454                 loader=loader.getParent();
455             }
456         }
457 
458         // Prepare for configuration
459         for (Configuration configuration : _configurations)
460         {
461             LOG.debug("preConfigure {} with {}",this,configuration);
462             configuration.preConfigure(this);
463         }
464     }
465 
466     /* ------------------------------------------------------------ */
467     public void configure() throws Exception
468     {
469         // Configure webapp
470         for (Configuration configuration : _configurations)
471         {
472             LOG.debug("configure {} with {}",this,configuration);
473             configuration.configure(this);
474         }
475     }
476 
477     /* ------------------------------------------------------------ */
478     public void postConfigure() throws Exception
479     {
480         // Clean up after configuration
481         for (Configuration configuration : _configurations)
482         {
483             LOG.debug("postConfigure {} with {}",this,configuration);
484             configuration.postConfigure(this);
485         }
486     }
487 
488     /* ------------------------------------------------------------ */
489     /*
490      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
491      */
492     @Override
493     protected void doStart() throws Exception
494     {
495         try
496         {
497             _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
498             preConfigure();
499             super.doStart();
500             postConfigure();
501 
502             if (isLogUrlOnStart())
503                 dumpUrl();
504         }
505         catch (Exception e)
506         {
507             //start up of the webapp context failed, make sure it is not started
508             LOG.warn("Failed startup of context "+this, e);
509             _unavailableException=e;
510             setAvailable(false);
511             if (isThrowUnavailableOnStartupException())
512                 throw e;
513         }
514     }
515 
516     /* ------------------------------------------------------------ */
517     /*
518      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
519      */
520     @Override
521     protected void doStop() throws Exception
522     {
523         super.doStop();
524 
525         try
526         {
527             for (int i=_configurations.size();i-->0;)
528                 _configurations.get(i).deconfigure(this);
529 
530             if (_metadata != null)
531                 _metadata.clear();
532             _metadata=new MetaData();
533 
534         }
535         finally
536         {
537             if (_ownClassLoader)
538                 setClassLoader(null);
539 
540             setAvailable(true);
541             _unavailableException=null;
542         }
543     }
544 
545     /* ------------------------------------------------------------ */
546     @Override
547     public void destroy()
548     {
549         // Prepare for configuration
550         MultiException mx=new MultiException();
551         if (_configurations!=null)
552         {
553             for (int i=_configurations.size();i-->0;)
554             {
555                 try
556                 {
557                     _configurations.get(i).destroy(this);
558                 }
559                 catch(Exception e)
560                 {
561                     mx.add(e);
562                 }
563             }
564         }
565         _configurations.clear();
566         super.destroy();
567         mx.ifExceptionThrowRuntime();
568     }
569 
570 
571     /* ------------------------------------------------------------ */
572     /*
573      * Dumps the current web app name and URL to the log
574      */
575     private void dumpUrl()
576     {
577         Connector[] connectors = getServer().getConnectors();
578         for (int i=0;i<connectors.length;i++)
579         {
580             String displayName = getDisplayName();
581             if (displayName == null)
582                 displayName = "WebApp@"+connectors.hashCode();
583 
584             LOG.info(displayName + " at http://" + connectors[i].toString() + getContextPath());
585         }
586     }
587 
588     /* ------------------------------------------------------------ */
589     /**
590      * @return Returns the configurations.
591      */
592     @ManagedAttribute(value="configuration classes used to configure webapp", readonly=true)
593     public String[] getConfigurationClasses()
594     {
595         return _configurationClasses.toArray(new String[_configurationClasses.size()]);
596     }
597 
598     /* ------------------------------------------------------------ */
599     /**
600      * @return Returns the configurations.
601      */
602     public Configuration[] getConfigurations()
603     {
604         return _configurations.toArray(new Configuration[_configurations.size()]);
605     }
606 
607     /* ------------------------------------------------------------ */
608     /**
609      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
610      * @return Returns the defaultsDescriptor.
611      */
612     @ManagedAttribute(value="default web.xml deascriptor applied before standard web.xml", readonly=true)
613     public String getDefaultsDescriptor()
614     {
615         return _defaultsDescriptor;
616     }
617 
618     /* ------------------------------------------------------------ */
619     /**
620      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
621      * @return Returns the Override Descriptor.
622      */
623     public String getOverrideDescriptor()
624     {
625         if (_overrideDescriptors.size()!=1)
626             return null;
627         return _overrideDescriptors.get(0);
628     }
629 
630     /* ------------------------------------------------------------ */
631     /**
632      * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
633      * @return Returns the Override Descriptor list
634      */
635     @ManagedAttribute(value="web.xml deascriptors applied after standard web.xml", readonly=true)
636     public List<String> getOverrideDescriptors()
637     {
638         return Collections.unmodifiableList(_overrideDescriptors);
639     }
640 
641     /* ------------------------------------------------------------ */
642     /**
643      * @return Returns the permissions.
644      */
645     @Override
646     public PermissionCollection getPermissions()
647     {
648         return _permissions;
649     }
650 
651     /* ------------------------------------------------------------ */
652     /**
653      * @see #setServerClasses(String[])
654      * @return Returns the serverClasses.
655      */
656     @ManagedAttribute(value="classes and packages hidden by the context classloader", readonly=true)
657     public String[] getServerClasses()
658     {
659         if (_serverClasses == null)
660             loadServerClasses();
661 
662         return _serverClasses.getPatterns();
663     }
664 
665     /* ------------------------------------------------------------ */
666     /** Add to the list of Server classes.
667      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
668      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
669      * or package has '-' it is excluded from the server classes and order is thus
670      * important when added system class patterns. This argument may also be a comma 
671      * separated list of classOrPackage patterns.
672      * @see #setServerClasses(String[])
673      * @see <a href="http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html">Jetty Documentation: Classloading</a>
674      */
675     public void addServerClass(String classOrPackage)
676     {
677         if (_serverClasses == null)
678             loadServerClasses();
679 
680         _serverClasses.addPattern(classOrPackage);
681     }
682 
683     /* ------------------------------------------------------------ */
684     /** Prepend to the list of Server classes.
685      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
686      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
687      * or package has '-' it is excluded from the server classes and order is thus
688      * important when added system class patterns. This argument may also be a comma 
689      * separated list of classOrPackage patterns.
690      * @see #setServerClasses(String[])
691      * @see <a href="http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html">Jetty Documentation: Classloading</a>
692      */
693     public void prependServerClass(String classOrPackage)
694     {
695         if (_serverClasses == null)
696             loadServerClasses();
697 
698         _serverClasses.prependPattern(classOrPackage);
699     }
700 
701     /* ------------------------------------------------------------ */
702     /**
703      * @see #setSystemClasses(String[])
704      * @return Returns the systemClasses.
705      */
706     @ManagedAttribute(value="classes and packages given priority by context classloader", readonly=true)
707     public String[] getSystemClasses()
708     {
709         if (_systemClasses == null)
710             loadSystemClasses();
711 
712         return _systemClasses.getPatterns();
713     }
714 
715     /* ------------------------------------------------------------ */
716     /** Add to the list of System classes.
717      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
718      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
719      * or package has '-' it is excluded from the system classes and order is thus
720      * important when added system class patterns.  This argument may also be a comma 
721      * separated list of classOrPackage patterns.
722      * @see #setSystemClasses(String[])
723      * @see <a href="http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html">Jetty Documentation: Classloading</a>
724      */
725     public void addSystemClass(String classOrPackage)
726     {
727         if (_systemClasses == null)
728             loadSystemClasses();
729 
730         _systemClasses.addPattern(classOrPackage);
731     }
732 
733 
734     /* ------------------------------------------------------------ */
735     /** Prepend to the list of System classes.
736      * @param classOrPackage A fully qualified class name (eg com.foo.MyClass) 
737      * or a qualified package name ending with '.' (eg com.foo.).  If the class 
738      * or package has '-' it is excluded from the system classes and order is thus
739      * important when added system class patterns.This argument may also be a comma 
740      * separated list of classOrPackage patterns.
741      * @see #setSystemClasses(String[])
742      * @see <a href="http://www.eclipse.org/jetty/documentation/current/jetty-classloading.html">Jetty Documentation: Classloading</a>
743      */
744     public void prependSystemClass(String classOrPackage)
745     {
746         if (_systemClasses == null)
747             loadSystemClasses();
748 
749         _systemClasses.prependPattern(classOrPackage);
750     }
751 
752     /* ------------------------------------------------------------ */
753     @Override
754     public boolean isServerClass(String name)
755     {
756         if (_serverClasses == null)
757             loadServerClasses();
758 
759         return _serverClasses.match(name);
760     }
761 
762     /* ------------------------------------------------------------ */
763     @Override
764     public boolean isSystemClass(String name)
765     {
766         if (_systemClasses == null)
767             loadSystemClasses();
768 
769         return _systemClasses.match(name);
770     }
771 
772     /* ------------------------------------------------------------ */
773     protected void loadSystemClasses()
774     {
775         if (_systemClasses != null)
776             return;
777 
778         //look for a Server attribute with the list of System classes
779         //to apply to every web application. If not present, use our defaults.
780         Server server = getServer();
781         if (server != null)
782         {
783             Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
784             if (systemClasses != null && systemClasses instanceof String[])
785                 _systemClasses = new ClasspathPattern((String[])systemClasses);
786         }
787 
788         if (_systemClasses == null)
789             _systemClasses = new ClasspathPattern(__dftSystemClasses);
790     }
791 
792     /* ------------------------------------------------------------ */
793     private void loadServerClasses()
794     {
795         if (_serverClasses != null)
796         {
797             return;
798         }
799 
800         // look for a Server attribute with the list of Server classes
801         // to apply to every web application. If not present, use our defaults.
802         Server server = getServer();
803         if (server != null)
804         {
805             Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
806             if (serverClasses != null && serverClasses instanceof String[])
807             {
808                 _serverClasses = new ClasspathPattern((String[])serverClasses);
809             }
810         }
811 
812         if (_serverClasses == null)
813         {
814             _serverClasses = new ClasspathPattern(__dftServerClasses);
815         }
816     }
817 
818     /* ------------------------------------------------------------ */
819     /**
820      * @return Returns the war as a file or URL string (Resource)
821      */
822     @ManagedAttribute(value="war file location", readonly=true)
823     public String getWar()
824     {
825         if (_war==null)
826             _war=getResourceBase();
827         return _war;
828     }
829 
830     /* ------------------------------------------------------------ */
831     public Resource getWebInf() throws IOException
832     {
833         if (super.getBaseResource() == null)
834             return null;
835 
836         // Iw there a WEB-INF directory?
837         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
838         if (!web_inf.exists() || !web_inf.isDirectory())
839             return null;
840 
841         return web_inf;
842     }
843 
844     /* ------------------------------------------------------------ */
845     /**
846      * @return Returns the distributable.
847      */
848     @ManagedAttribute("web application distributable")
849     public boolean isDistributable()
850     {
851         return _distributable;
852     }
853 
854     /* ------------------------------------------------------------ */
855     /**
856      * @return Returns the extractWAR.
857      */
858     @ManagedAttribute(value="extract war", readonly=true)
859     public boolean isExtractWAR()
860     {
861         return _extractWAR;
862     }
863 
864     /* ------------------------------------------------------------ */
865     /**
866      * @return True if the webdir is copied (to allow hot replacement of jars on windows)
867      */
868     @ManagedAttribute(value="webdir copied on deploy (allows hot replacement on windows)", readonly=true)
869     public boolean isCopyWebDir()
870     {
871         return _copyDir;
872     }
873 
874     /* ------------------------------------------------------------ */
875     /**
876      * @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
877      */
878     public boolean isCopyWebInf()
879     {
880         return _copyWebInf;
881     }
882 
883     /* ------------------------------------------------------------ */
884     /**
885      * @return True if the classloader should delegate first to the parent
886      * classloader (standard java behaviour) or false if the classloader
887      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
888      * spec recommendation). Default is false or can be set by the system 
889      * property org.eclipse.jetty.server.webapp.parentLoaderPriority
890      */
891     @Override
892     @ManagedAttribute(value="parent classloader given priority", readonly=true)
893     public boolean isParentLoaderPriority()
894     {
895         return _parentLoaderPriority;
896     }
897 
898 
899     /* ------------------------------------------------------------ */
900     public static String[] getDefaultConfigurationClasses ()
901     {
902         return DEFAULT_CONFIGURATION_CLASSES;
903     }
904 
905     /* ------------------------------------------------------------ */
906     public String[] getDefaultServerClasses ()
907     {
908         return __dftServerClasses;
909     }
910 
911     /* ------------------------------------------------------------ */
912     public String[] getDefaultSystemClasses ()
913     {
914         return __dftSystemClasses;
915     }
916 
917     /* ------------------------------------------------------------ */
918     protected void loadConfigurations()
919         throws Exception
920     {
921         //if the configuration instances have been set explicitly, use them
922         if (_configurations.size()>0)
923             return;
924         
925         if (_configurationClasses.size()==0)
926             _configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer()));
927         for (String configClass : _configurationClasses)
928             _configurations.add((Configuration)Loader.loadClass(this.getClass(), configClass).newInstance());
929     }
930 
931     /* ------------------------------------------------------------ */
932     @Override
933     public String toString()
934     {
935         if (_war!=null)
936         {
937             String war=_war;
938             if (war.indexOf("/webapps/")>=0)
939                 war=war.substring(war.indexOf("/webapps/")+8);
940             return super.toString()+"{"+war+"}";
941         }
942         return super.toString();
943     }
944 
945     /* ------------------------------------------------------------ */
946     /**
947      * @param configurations The configuration class names.  If setConfigurations is not called
948      * these classes are used to create a configurations array.
949      */
950     public void setConfigurationClasses(String[] configurations)
951     {
952         if (isStarted())
953             throw new IllegalStateException();
954         _configurationClasses.clear();
955         if (configurations!=null)
956             _configurationClasses.addAll(Arrays.asList(configurations));
957         _configurations.clear();
958     }
959 
960     public void setConfigurationClasses(List<String> configurations)
961     {
962         setConfigurationClasses(configurations.toArray(new String[configurations.size()]));
963     }
964     
965     /* ------------------------------------------------------------ */
966     /**
967      * @param configurations The configurations to set.
968      */
969     public void setConfigurations(Configuration[] configurations)
970     {
971         if (isStarted())
972             throw new IllegalStateException();
973         _configurations.clear();
974         if (configurations!=null)
975             _configurations.addAll(Arrays.asList(configurations));
976     }
977 
978     /* ------------------------------------------------------------ */
979     /**
980      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
981      * @param defaultsDescriptor The defaultsDescriptor to set.
982      */
983     public void setDefaultsDescriptor(String defaultsDescriptor)
984     {
985         _defaultsDescriptor = defaultsDescriptor;
986     }
987 
988     /* ------------------------------------------------------------ */
989     /**
990      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
991      * @param overrideDescriptor The overrideDescritpor to set.
992      */
993     public void setOverrideDescriptor(String overrideDescriptor)
994     {
995         _overrideDescriptors.clear();
996         _overrideDescriptors.add(overrideDescriptor);
997     }
998 
999     /* ------------------------------------------------------------ */
1000     /**
1001      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
1002      * @param overrideDescriptors The overrideDescriptors (file or URL) to set.
1003      */
1004     public void setOverrideDescriptors(List<String> overrideDescriptors)
1005     {
1006         _overrideDescriptors.clear();
1007         _overrideDescriptors.addAll(overrideDescriptors);
1008     }
1009 
1010     /* ------------------------------------------------------------ */
1011     /**
1012      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
1013      * @param overrideDescriptor The overrideDescriptor (file or URL) to add.
1014      */
1015     public void addOverrideDescriptor(String overrideDescriptor)
1016     {
1017         _overrideDescriptors.add(overrideDescriptor);
1018     }
1019 
1020     /* ------------------------------------------------------------ */
1021     /**
1022      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
1023      */
1024     @ManagedAttribute(value="standard web.xml descriptor", readonly=true)
1025     public String getDescriptor()
1026     {
1027         return _descriptor;
1028     }
1029 
1030     /* ------------------------------------------------------------ */
1031     /**
1032      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
1033      */
1034     public void setDescriptor(String descriptor)
1035     {
1036         _descriptor=descriptor;
1037     }
1038 
1039     /* ------------------------------------------------------------ */
1040     /**
1041      * @param distributable The distributable to set.
1042      */
1043     public void setDistributable(boolean distributable)
1044     {
1045         this._distributable = distributable;
1046     }
1047 
1048     /* ------------------------------------------------------------ */
1049     @Override
1050     public void setEventListeners(EventListener[] eventListeners)
1051     {
1052         if (_sessionHandler!=null)
1053             _sessionHandler.clearEventListeners();
1054 
1055         super.setEventListeners(eventListeners);
1056     }
1057 
1058     /* ------------------------------------------------------------ */
1059     /** Add EventListener
1060      * Convenience method that calls {@link #setEventListeners(EventListener[])}
1061      * @param listener
1062      */
1063     @Override
1064     public void addEventListener(EventListener listener)
1065     {
1066         super.addEventListener(listener);
1067         if ((listener instanceof HttpSessionActivationListener)
1068             || (listener instanceof HttpSessionAttributeListener)
1069             || (listener instanceof HttpSessionBindingListener)
1070             || (listener instanceof HttpSessionListener)
1071             || (listener instanceof HttpSessionIdListener))
1072         {
1073             if (_sessionHandler!=null)
1074                 _sessionHandler.addEventListener(listener);
1075         }
1076     }
1077     
1078     @Override
1079     public void removeEventListener(EventListener listener)
1080     {
1081         super.removeEventListener(listener);
1082         if ((listener instanceof HttpSessionActivationListener)
1083             || (listener instanceof HttpSessionAttributeListener)
1084             || (listener instanceof HttpSessionBindingListener)
1085             || (listener instanceof HttpSessionListener)
1086             || (listener instanceof HttpSessionIdListener))
1087         {
1088             if (_sessionHandler!=null)
1089                 _sessionHandler.removeEventListener(listener);
1090         }
1091         
1092     }
1093 
1094 
1095     /* ------------------------------------------------------------ */
1096     /**
1097      * @param extractWAR True if war files are extracted
1098      */
1099     public void setExtractWAR(boolean extractWAR)
1100     {
1101         _extractWAR = extractWAR;
1102     }
1103 
1104     /* ------------------------------------------------------------ */
1105     /**
1106      * @param copy True if the webdir is copied (to allow hot replacement of jars)
1107      */
1108     public void setCopyWebDir(boolean copy)
1109     {
1110         _copyDir = copy;
1111     }
1112 
1113     /* ------------------------------------------------------------ */
1114     /**
1115      * @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
1116      */
1117     public void setCopyWebInf(boolean copyWebInf)
1118     {
1119         _copyWebInf = copyWebInf;
1120     }
1121 
1122     /* ------------------------------------------------------------ */
1123     /**
1124      * @param java2compliant True if the classloader should delegate first to the parent
1125      * classloader (standard java behaviour) or false if the classloader
1126      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
1127      * spec recommendation).  Default is false or can be set by the system 
1128      * property org.eclipse.jetty.server.webapp.parentLoaderPriority
1129      */
1130     public void setParentLoaderPriority(boolean java2compliant)
1131     {
1132         _parentLoaderPriority = java2compliant;
1133     }
1134 
1135     /* ------------------------------------------------------------ */
1136     /**
1137      * @param permissions The permissions to set.
1138      */
1139     public void setPermissions(PermissionCollection permissions)
1140     {
1141         _permissions = permissions;
1142     }
1143 
1144     /**
1145      * Set the context white list
1146      *
1147      * In certain circumstances you want may want to deny access of one webapp from another
1148      * when you may not fully trust the webapp.  Setting this white list will enable a
1149      * check when a servlet called getContext(String), validating that the uriInPath
1150      * for the given webapp has been declaratively allows access to the context.
1151      * @param contextWhiteList
1152      */
1153     public void setContextWhiteList(String[] contextWhiteList)
1154     {
1155         _contextWhiteList = contextWhiteList;
1156     }
1157 
1158     /* ------------------------------------------------------------ */
1159     /**
1160      * Set the server classes patterns.
1161      * <p>
1162      * Server classes/packages are classes used to implement the server and are hidden
1163      * from the context.  If the context needs to load these classes, it must have its
1164      * own copy of them in WEB-INF/lib or WEB-INF/classes.
1165      * A class pattern is a string of one of the forms:<dl>
1166      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1167      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1168      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1169      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1170      * </dl>
1171      * @param serverClasses The serverClasses to set.
1172      */
1173     public void setServerClasses(String[] serverClasses)
1174     {
1175         _serverClasses = new ClasspathPattern(serverClasses);
1176     }
1177 
1178     /* ------------------------------------------------------------ */
1179     /**
1180      * Set the system classes patterns.
1181      * <p>
1182      * System classes/packages are classes provided by the JVM and that
1183      * cannot be replaced by classes of the same name from WEB-INF,
1184      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
1185      * A class pattern is a string of one of the forms:<dl>
1186      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1187      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1188      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1189      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1190      * </dl>
1191      * @param systemClasses The systemClasses to set.
1192      */
1193     public void setSystemClasses(String[] systemClasses)
1194     {
1195         _systemClasses = new ClasspathPattern(systemClasses);
1196     }
1197 
1198 
1199     /* ------------------------------------------------------------ */
1200     /** Set temporary directory for context.
1201      * The javax.servlet.context.tempdir attribute is also set.
1202      * @param dir Writable temporary directory.
1203      */
1204     public void setTempDirectory(File dir)
1205     {
1206         if (isStarted())
1207             throw new IllegalStateException("Started");
1208 
1209         if (dir!=null)
1210         {
1211             try{dir=new File(dir.getCanonicalPath());}
1212             catch (IOException e){LOG.warn(Log.EXCEPTION,e);}
1213         }
1214 
1215         _tmpDir=dir;
1216         setAttribute(TEMPDIR,_tmpDir);            
1217     }
1218 
1219     /* ------------------------------------------------------------ */
1220     @ManagedAttribute(value="temporary directory location", readonly=true)
1221     public File getTempDirectory ()
1222     {
1223         return _tmpDir;
1224     }
1225 
1226     /**
1227      * If true the temp directory for this 
1228      * webapp will be kept when the webapp stops. Otherwise,
1229      * it will be deleted.
1230      * 
1231      * @param delete
1232      */
1233     public void setPersistTempDirectory(boolean persist)
1234     {
1235         _persistTmpDir = persist;
1236     }
1237     
1238     /**
1239      * @return
1240      */
1241     public boolean isPersistTempDirectory()
1242     {
1243         return _persistTmpDir;
1244     }
1245     
1246     
1247     /* ------------------------------------------------------------ */
1248     /**
1249      * @param war The war to set as a file name or URL
1250      */
1251     public void setWar(String war)
1252     {
1253         _war = war;
1254     }
1255 
1256     /* ------------------------------------------------------------ */
1257     /**
1258      * @return Comma or semicolon separated path of filenames or URLs
1259      * pointing to directories or jar files. Directories should end
1260      * with '/'.
1261      */
1262     @Override
1263     @ManagedAttribute(value="extra classpath for context classloader", readonly=true)
1264     public String getExtraClasspath()
1265     {
1266         return _extraClasspath;
1267     }
1268 
1269     /* ------------------------------------------------------------ */
1270     /**
1271      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1272      * pointing to directories or jar files. Directories should end
1273      * with '/'.
1274      */
1275     public void setExtraClasspath(String extraClasspath)
1276     {
1277         _extraClasspath=extraClasspath;
1278     }
1279 
1280     /* ------------------------------------------------------------ */
1281     public boolean isLogUrlOnStart()
1282     {
1283         return _logUrlOnStart;
1284     }
1285 
1286     /* ------------------------------------------------------------ */
1287     /**
1288      * Sets whether or not the web app name and URL is logged on startup
1289      *
1290      * @param logOnStart whether or not the log message is created
1291      */
1292     public void setLogUrlOnStart(boolean logOnStart)
1293     {
1294         this._logUrlOnStart = logOnStart;
1295     }
1296 
1297     /* ------------------------------------------------------------ */
1298     @Override
1299     public void setServer(Server server)
1300     {
1301         super.setServer(server);
1302     }
1303 
1304     /* ------------------------------------------------------------ */
1305     public boolean isAllowDuplicateFragmentNames()
1306     {
1307         return _allowDuplicateFragmentNames;
1308     }
1309 
1310     /* ------------------------------------------------------------ */
1311     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
1312     {
1313         _allowDuplicateFragmentNames = allowDuplicateFragmentNames;
1314     }
1315 
1316     /* ------------------------------------------------------------ */
1317     public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) {
1318         _throwUnavailableOnStartupException = throwIfStartupException;
1319     }
1320 
1321     /* ------------------------------------------------------------ */
1322     public boolean isThrowUnavailableOnStartupException () {
1323         return _throwUnavailableOnStartupException;
1324     }
1325 
1326     /* ------------------------------------------------------------ */
1327     @Override
1328     protected void startContext()
1329         throws Exception
1330     {
1331         configure();
1332 
1333         //resolve the metadata
1334         _metadata.resolve(this);
1335 
1336         startWebapp();
1337     }
1338 
1339     /* ------------------------------------------------------------ */
1340     protected void startWebapp()
1341         throws Exception
1342     {
1343         super.startContext();
1344     }
1345     
1346     /* ------------------------------------------------------------ */    
1347     @Override
1348     public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
1349     {
1350         Set<String> unchangedURLMappings = new HashSet<String>();
1351         //From javadoc for ServletSecurityElement:
1352         /*
1353         If a URL pattern of this ServletRegistration is an exact target of a security-constraint that 
1354         was established via the portable deployment descriptor, then this method does not change the 
1355         security-constraint for that pattern, and the pattern will be included in the return value.
1356 
1357         If a URL pattern of this ServletRegistration is an exact target of a security constraint 
1358         that was established via the ServletSecurity annotation or a previous call to this method, 
1359         then this method replaces the security constraint for that pattern.
1360 
1361         If a URL pattern of this ServletRegistration is neither the exact target of a security constraint 
1362         that was established via the ServletSecurity annotation or a previous call to this method, 
1363         nor the exact target of a security-constraint in the portable deployment descriptor, then 
1364         this method establishes the security constraint for that pattern from the argument ServletSecurityElement. 
1365          */
1366 
1367         Collection<String> pathMappings = registration.getMappings();
1368         if (pathMappings != null)
1369         {
1370             ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
1371 
1372             for (String pathSpec:pathMappings)
1373             {
1374                 Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
1375                
1376                 switch (origin)
1377                 {
1378                     case NotSet:
1379                     {
1380                         //No mapping for this url already established
1381                         List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1382                         for (ConstraintMapping m:mappings)
1383                             ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
1384                         ((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
1385                         getMetaData().setOriginAPI("constraint.url."+pathSpec);
1386                         break;
1387                     }
1388                     case WebXml:
1389                     case WebDefaults:
1390                     case WebOverride:
1391                     case WebFragment:
1392                     {
1393                         //a mapping for this url was created in a descriptor, which overrides everything
1394                         unchangedURLMappings.add(pathSpec);
1395                         break;
1396                     }
1397                     case Annotation:
1398                     case API:
1399                     {
1400                         //mapping established via an annotation or by previous call to this method,
1401                         //replace the security constraint for this pattern
1402                         List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
1403                        
1404                         List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1405                         constraintMappings.addAll(freshMappings);
1406                            
1407                         ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
1408                         ((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
1409                         break;
1410                     }
1411                 }
1412             }
1413         }
1414         
1415         return unchangedURLMappings;
1416     }
1417 
1418 
1419 
1420     /* ------------------------------------------------------------ */
1421     public class Context extends ServletContextHandler.Context
1422     {
1423        
1424         /* ------------------------------------------------------------ */
1425         @Override
1426         public void checkListener(Class<? extends EventListener> listener) throws IllegalStateException
1427         {
1428             try
1429             {
1430                 super.checkListener(listener);
1431             }
1432             catch (IllegalArgumentException e)
1433             {
1434                 //not one of the standard servlet listeners, check our extended session listener types
1435                 boolean ok = false;
1436                 for (Class l:SessionHandler.SESSION_LISTENER_TYPES)
1437                 {
1438                     if (l.isAssignableFrom(listener))
1439                     {
1440                         ok = true;
1441                         break;
1442                     }
1443                 }
1444                 if (!ok)
1445                     throw new IllegalArgumentException("Inappropriate listener type "+listener.getName());
1446             }
1447         }
1448 
1449         /* ------------------------------------------------------------ */
1450         @Override
1451         public URL getResource(String path) throws MalformedURLException
1452         {
1453             Resource resource=WebAppContext.this.getResource(path);
1454             if (resource==null || !resource.exists())
1455                 return null;
1456 
1457             // Should we go to the original war?
1458             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1459             {
1460                 Resource[] resources = ((ResourceCollection)resource).getResources();
1461                 for (int i=resources.length;i-->0;)
1462                 {
1463                     if (resources[i].getName().startsWith("jar:file"))
1464                         return resources[i].getURL();
1465                 }
1466             }
1467 
1468             return resource.getURL();
1469         }
1470 
1471         /* ------------------------------------------------------------ */
1472         @Override
1473         public ServletContext getContext(String uripath)
1474         {
1475             ServletContext servletContext = super.getContext(uripath);
1476 
1477             if ( servletContext != null && _contextWhiteList != null )
1478             {
1479                 for ( String context : _contextWhiteList )
1480                 {
1481                     if ( context.equals(uripath) )
1482                     {
1483                         return servletContext;
1484                     }
1485                 }
1486 
1487                 return null;
1488             }
1489             else
1490             {
1491                 return servletContext;
1492             }
1493         }
1494         
1495     }
1496 
1497     /* ------------------------------------------------------------ */
1498     public MetaData getMetaData()
1499     {
1500         return _metadata;
1501     }
1502 }