View Javadoc

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