View Javadoc

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