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