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