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         _tmpDir=dir;
1214         setAttribute(TEMPDIR,_tmpDir);            
1215     }
1216 
1217     /* ------------------------------------------------------------ */
1218     @ManagedAttribute(value="temporary directory location", readonly=true)
1219     public File getTempDirectory ()
1220     {
1221         return _tmpDir;
1222     }
1223 
1224     /**
1225      * If true the temp directory for this 
1226      * webapp will be kept when the webapp stops. Otherwise,
1227      * it will be deleted.
1228      * 
1229      * @param delete
1230      */
1231     public void setPersistTempDirectory(boolean persist)
1232     {
1233         _persistTmpDir = persist;
1234     }
1235     
1236     /**
1237      * @return
1238      */
1239     public boolean isPersistTempDirectory()
1240     {
1241         return _persistTmpDir;
1242     }
1243     
1244     
1245     /* ------------------------------------------------------------ */
1246     /**
1247      * @param war The war to set as a file name or URL
1248      */
1249     public void setWar(String war)
1250     {
1251         _war = war;
1252     }
1253 
1254     /* ------------------------------------------------------------ */
1255     /**
1256      * @return Comma or semicolon separated path of filenames or URLs
1257      * pointing to directories or jar files. Directories should end
1258      * with '/'.
1259      */
1260     @Override
1261     @ManagedAttribute(value="extra classpath for context classloader", readonly=true)
1262     public String getExtraClasspath()
1263     {
1264         return _extraClasspath;
1265     }
1266 
1267     /* ------------------------------------------------------------ */
1268     /**
1269      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1270      * pointing to directories or jar files. Directories should end
1271      * with '/'.
1272      */
1273     public void setExtraClasspath(String extraClasspath)
1274     {
1275         _extraClasspath=extraClasspath;
1276     }
1277 
1278     /* ------------------------------------------------------------ */
1279     public boolean isLogUrlOnStart()
1280     {
1281         return _logUrlOnStart;
1282     }
1283 
1284     /* ------------------------------------------------------------ */
1285     /**
1286      * Sets whether or not the web app name and URL is logged on startup
1287      *
1288      * @param logOnStart whether or not the log message is created
1289      */
1290     public void setLogUrlOnStart(boolean logOnStart)
1291     {
1292         this._logUrlOnStart = logOnStart;
1293     }
1294 
1295     /* ------------------------------------------------------------ */
1296     @Override
1297     public void setServer(Server server)
1298     {
1299         super.setServer(server);
1300     }
1301 
1302     /* ------------------------------------------------------------ */
1303     public boolean isAllowDuplicateFragmentNames()
1304     {
1305         return _allowDuplicateFragmentNames;
1306     }
1307 
1308     /* ------------------------------------------------------------ */
1309     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
1310     {
1311         _allowDuplicateFragmentNames = allowDuplicateFragmentNames;
1312     }
1313 
1314     /* ------------------------------------------------------------ */
1315     public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) {
1316         _throwUnavailableOnStartupException = throwIfStartupException;
1317     }
1318 
1319     /* ------------------------------------------------------------ */
1320     public boolean isThrowUnavailableOnStartupException () {
1321         return _throwUnavailableOnStartupException;
1322     }
1323 
1324     /* ------------------------------------------------------------ */
1325     @Override
1326     protected void startContext()
1327         throws Exception
1328     {
1329         configure();
1330 
1331         //resolve the metadata
1332         _metadata.resolve(this);
1333 
1334         startWebapp();
1335     }
1336 
1337     /* ------------------------------------------------------------ */
1338     protected void startWebapp()
1339         throws Exception
1340     {
1341         super.startContext();
1342     }
1343     
1344     /* ------------------------------------------------------------ */    
1345     @Override
1346     public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
1347     {
1348         Set<String> unchangedURLMappings = new HashSet<String>();
1349         //From javadoc for ServletSecurityElement:
1350         /*
1351         If a URL pattern of this ServletRegistration is an exact target of a security-constraint that 
1352         was established via the portable deployment descriptor, then this method does not change the 
1353         security-constraint for that pattern, and the pattern will be included in the return value.
1354 
1355         If a URL pattern of this ServletRegistration is an exact target of a security constraint 
1356         that was established via the ServletSecurity annotation or a previous call to this method, 
1357         then this method replaces the security constraint for that pattern.
1358 
1359         If a URL pattern of this ServletRegistration is neither the exact target of a security constraint 
1360         that was established via the ServletSecurity annotation or a previous call to this method, 
1361         nor the exact target of a security-constraint in the portable deployment descriptor, then 
1362         this method establishes the security constraint for that pattern from the argument ServletSecurityElement. 
1363          */
1364 
1365         Collection<String> pathMappings = registration.getMappings();
1366         if (pathMappings != null)
1367         {
1368             ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
1369 
1370             for (String pathSpec:pathMappings)
1371             {
1372                 Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
1373                
1374                 switch (origin)
1375                 {
1376                     case NotSet:
1377                     {
1378                         //No mapping for this url already established
1379                         List<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1380                         for (ConstraintMapping m:mappings)
1381                             ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
1382                         ((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
1383                         getMetaData().setOriginAPI("constraint.url."+pathSpec);
1384                         break;
1385                     }
1386                     case WebXml:
1387                     case WebDefaults:
1388                     case WebOverride:
1389                     case WebFragment:
1390                     {
1391                         //a mapping for this url was created in a descriptor, which overrides everything
1392                         unchangedURLMappings.add(pathSpec);
1393                         break;
1394                     }
1395                     case Annotation:
1396                     case API:
1397                     {
1398                         //mapping established via an annotation or by previous call to this method,
1399                         //replace the security constraint for this pattern
1400                         List<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
1401                        
1402                         List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
1403                         constraintMappings.addAll(freshMappings);
1404                            
1405                         ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
1406                         ((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
1407                         break;
1408                     }
1409                 }
1410             }
1411         }
1412         
1413         return unchangedURLMappings;
1414     }
1415 
1416 
1417 
1418     /* ------------------------------------------------------------ */
1419     public class Context extends ServletContextHandler.Context
1420     {
1421        
1422         /* ------------------------------------------------------------ */
1423         @Override
1424         public void checkListener(Class<? extends EventListener> listener) throws IllegalStateException
1425         {
1426             try
1427             {
1428                 super.checkListener(listener);
1429             }
1430             catch (IllegalArgumentException e)
1431             {
1432                 //not one of the standard servlet listeners, check our extended session listener types
1433                 boolean ok = false;
1434                 for (Class l:SessionHandler.SESSION_LISTENER_TYPES)
1435                 {
1436                     if (l.isAssignableFrom(listener))
1437                     {
1438                         ok = true;
1439                         break;
1440                     }
1441                 }
1442                 if (!ok)
1443                     throw new IllegalArgumentException("Inappropriate listener type "+listener.getName());
1444             }
1445         }
1446 
1447         /* ------------------------------------------------------------ */
1448         @Override
1449         public URL getResource(String path) throws MalformedURLException
1450         {
1451             Resource resource=WebAppContext.this.getResource(path);
1452             if (resource==null || !resource.exists())
1453                 return null;
1454 
1455             // Should we go to the original war?
1456             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1457             {
1458                 Resource[] resources = ((ResourceCollection)resource).getResources();
1459                 for (int i=resources.length;i-->0;)
1460                 {
1461                     if (resources[i].getName().startsWith("jar:file"))
1462                         return resources[i].getURL();
1463                 }
1464             }
1465 
1466             return resource.getURL();
1467         }
1468 
1469         /* ------------------------------------------------------------ */
1470         @Override
1471         public ServletContext getContext(String uripath)
1472         {
1473             ServletContext servletContext = super.getContext(uripath);
1474 
1475             if ( servletContext != null && _contextWhiteList != null )
1476             {
1477                 for ( String context : _contextWhiteList )
1478                 {
1479                     if ( context.equals(uripath) )
1480                     {
1481                         return servletContext;
1482                     }
1483                 }
1484 
1485                 return null;
1486             }
1487             else
1488             {
1489                 return servletContext;
1490             }
1491         }
1492         
1493     }
1494 
1495     /* ------------------------------------------------------------ */
1496     public MetaData getMetaData()
1497     {
1498         return _metadata;
1499     }
1500 }