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