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