View Javadoc

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