View Javadoc

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