View Javadoc

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