View Javadoc

1   // ========================================================================
2   // Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses.
12  // ========================================================================
13  
14  package org.eclipse.jetty.webapp;
15  
16  import java.io.File;
17  import java.io.IOException;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.security.PermissionCollection;
21  import java.util.EventListener;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.jar.JarFile;
26  
27  import javax.servlet.http.HttpSessionActivationListener;
28  import javax.servlet.http.HttpSessionAttributeListener;
29  import javax.servlet.http.HttpSessionBindingListener;
30  import javax.servlet.http.HttpSessionListener;
31  
32  import org.eclipse.jetty.security.SecurityHandler;
33  import org.eclipse.jetty.server.Connector;
34  import org.eclipse.jetty.server.HandlerContainer;
35  import org.eclipse.jetty.server.Server;
36  import org.eclipse.jetty.server.handler.ContextHandler;
37  import org.eclipse.jetty.server.handler.ErrorHandler;
38  import org.eclipse.jetty.server.session.SessionHandler;
39  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
40  import org.eclipse.jetty.servlet.ServletContextHandler;
41  import org.eclipse.jetty.servlet.ServletHandler;
42  import org.eclipse.jetty.util.LazyList;
43  import org.eclipse.jetty.util.Loader;
44  import org.eclipse.jetty.util.StringUtil;
45  import org.eclipse.jetty.util.URIUtil;
46  import org.eclipse.jetty.util.log.Log;
47  import org.eclipse.jetty.util.resource.Resource;
48  import org.eclipse.jetty.util.resource.ResourceCollection;
49  
50  /* ------------------------------------------------------------ */
51  /** Web Application Context Handler.
52   * The WebAppContext handler is an extension of ContextHandler that
53   * coordinates the construction and configuration of nested handlers:
54   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
55   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
56   * The handlers are configured by pluggable configuration classes, with
57   * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
58   * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
59   * 
60   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
61   * 
62   * 
63   *
64   */
65  public class WebAppContext extends ServletContextHandler
66  {
67      public static final String TEMPDIR = "javax.servlet.context.tempdir";
68      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
69      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
70      public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration";
71      public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
72      public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
73      
74      private static String[] __dftConfigurationClasses =
75      {
76          "org.eclipse.jetty.webapp.WebInfConfiguration",
77          "org.eclipse.jetty.webapp.WebXmlConfiguration",
78          "org.eclipse.jetty.webapp.MetaInfConfiguration",
79          "org.eclipse.jetty.webapp.FragmentConfiguration",
80          "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
81          "org.eclipse.jetty.webapp.TagLibConfiguration"
82      } ;
83      
84      // System classes are classes that cannot be replaced by 
85      // the web application, and they are *always* loaded via 
86      // system classloader.
87      private final static String[] __dftSystemClasses = 
88      {
89          "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) 
90          "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
91          "org.xml.",                         // needed by javax.xml
92          "org.w3c.",                         // needed by javax.xml
93          "apache.commons.logging.",          // special case 
94          "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
95          "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
96          "org.eclipse.jetty.plus.jaas.",     // webapp cannot change jaas classes
97          "org.eclipse.jetty.websocket.",     // WebSocket is a jetty extension
98          "org.eclipse.jetty.servlet.DefaultServlet"  // webapp cannot change default servlets
99      } ;
100     
101     // Server classes are classes that are hidden from being
102     // loaded by the web application using system classloader,
103     // so if web application needs to load any of such classes,
104     // it has to include them in its distribution.
105     private final static String[] __dftServerClasses = 
106     {
107             "-org.eclipse.jetty.continuation.", // don't hide continuation classes
108             "-org.eclipse.jetty.jndi.",         // don't hide naming classes
109             "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas classes
110             "-org.eclipse.jetty.websocket.",    // don't hide websocket extension
111             "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
112             "org.eclipse.jetty."                // hide other jetty classes
113     } ; 
114 
115     private String[] _configurationClasses = __dftConfigurationClasses;
116     private ClasspathPattern _systemClasses = null;
117     private ClasspathPattern _serverClasses = null;
118 
119     private Configuration[] _configurations;
120     private String _defaultsDescriptor=WEB_DEFAULTS_XML;
121     private String _descriptor=null;
122     private String _overrideDescriptor=null;
123     private boolean _distributable=false;
124     private boolean _extractWAR=true;
125     private boolean _copyDir=false;
126     private boolean _logUrlOnStart =false;
127     private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
128     private PermissionCollection _permissions;
129 
130     private File _tmpDir;
131     private String _war;
132     private String _extraClasspath;
133     private Throwable _unavailableException;
134     
135     private Map _resourceAliases;
136     private boolean _ownClassLoader=false;
137     private boolean _configurationDiscovered=true;
138     private boolean _configurationClassesSet=false;
139     private boolean _configurationsSet=false;
140 
141     public static WebAppContext getCurrentWebAppContext()
142     {
143         ContextHandler.Context context=ContextHandler.getCurrentContext();
144         if (context!=null)
145         {
146             ContextHandler handler = context.getContextHandler();
147             if (handler instanceof WebAppContext)
148                 return (WebAppContext)handler;
149         }
150         return null;
151     }
152     
153     /* ------------------------------------------------------------ */
154     public WebAppContext()
155     {
156         super(SESSIONS|SECURITY); 
157         _scontext=new Context();
158         setErrorHandler(new ErrorPageErrorHandler());
159     }
160     
161     /* ------------------------------------------------------------ */
162     /**
163      * @param contextPath The context path
164      * @param webApp The URL or filename of the webapp directory or war file.
165      */
166     public WebAppContext(String webApp,String contextPath)
167     {
168         super(null,contextPath,SESSIONS|SECURITY);
169         _scontext=new Context();
170         setContextPath(contextPath);
171         setWar(webApp);
172         setErrorHandler(new ErrorPageErrorHandler());
173     }
174     
175     /* ------------------------------------------------------------ */
176     /**
177      * @param parent The parent HandlerContainer.
178      * @param contextPath The context path
179      * @param webApp The URL or filename of the webapp directory or war file.
180      */
181     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
182     {
183         super(parent,contextPath,SESSIONS|SECURITY);
184         _scontext=new Context();
185         setWar(webApp);
186         setErrorHandler(new ErrorPageErrorHandler());
187     }
188 
189     /* ------------------------------------------------------------ */
190     /**
191      */
192     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler)
193     {
194         super(null,sessionHandler,securityHandler,servletHandler,errorHandler);
195         _scontext=new Context();
196         setErrorHandler(errorHandler!=null?errorHandler:new ErrorPageErrorHandler());
197     }
198 
199     /* ------------------------------------------------------------ */
200     /**
201      * @param servletContextName The servletContextName to set.
202      */
203     @Override
204     public void setDisplayName(String servletContextName)
205     {
206         super.setDisplayName(servletContextName);
207         ClassLoader cl = getClassLoader();
208         if (cl!=null && cl instanceof WebAppClassLoader)
209             ((WebAppClassLoader)cl).setName(servletContextName);
210     }
211     
212     /* ------------------------------------------------------------ */
213     /** Get an exception that caused the webapp to be unavailable
214      * @return A throwable if the webapp is unavailable or null
215      */
216     public Throwable getUnavailableException()
217     {
218         return _unavailableException;
219     }
220 
221     
222     /* ------------------------------------------------------------ */
223     /** Set Resource Alias.
224      * Resource aliases map resource uri's within a context.
225      * They may optionally be used by a handler when looking for
226      * a resource.
227      * @param alias
228      * @param uri
229      */
230     public void setResourceAlias(String alias, String uri)
231     {
232         if (_resourceAliases == null)
233             _resourceAliases= new HashMap(5);
234         _resourceAliases.put(alias, uri);
235     }
236 
237     /* ------------------------------------------------------------ */
238     public Map getResourceAliases()
239     {
240         if (_resourceAliases == null)
241             return null;
242         return _resourceAliases;
243     }
244     
245     /* ------------------------------------------------------------ */
246     public void setResourceAliases(Map map)
247     {
248         _resourceAliases = map;
249     }
250     
251     /* ------------------------------------------------------------ */
252     public String getResourceAlias(String alias)
253     {
254         if (_resourceAliases == null)
255             return null;
256         return (String)_resourceAliases.get(alias);
257     }
258 
259     /* ------------------------------------------------------------ */
260     public String removeResourceAlias(String alias)
261     {
262         if (_resourceAliases == null)
263             return null;
264         return (String)_resourceAliases.remove(alias);
265     }
266 
267     /* ------------------------------------------------------------ */
268     /* (non-Javadoc)
269      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
270      */
271     @Override
272     public void setClassLoader(ClassLoader classLoader)
273     {
274         super.setClassLoader(classLoader);
275         if (classLoader!=null && classLoader instanceof WebAppClassLoader)
276             ((WebAppClassLoader)classLoader).setName(getDisplayName());
277     }
278     
279     /* ------------------------------------------------------------ */
280     @Override
281     public Resource getResource(String uriInContext) throws MalformedURLException
282     {
283         IOException ioe= null;
284         Resource resource= null;
285         int loop=0;
286         while (uriInContext!=null && loop++<100)
287         {
288             try
289             {
290                 resource= super.getResource(uriInContext);
291                 if (resource != null && resource.exists())
292                     return resource;
293                 
294                 uriInContext = getResourceAlias(uriInContext);
295             }
296             catch (IOException e)
297             {
298                 Log.ignore(e);
299                 if (ioe==null)
300                     ioe= e;
301             }
302         }
303 
304         if (ioe != null && ioe instanceof MalformedURLException)
305             throw (MalformedURLException)ioe;
306 
307         return resource;
308     }
309     
310 
311     /* ------------------------------------------------------------ */
312     /** Is the context Automatically configured.
313      * 
314      * @return true if configuration discovery.
315      */
316     public boolean isConfigurationDiscovered()
317     {
318         return _configurationDiscovered;
319     }
320 
321     /* ------------------------------------------------------------ */
322     /** Set the configuration discovery mode.
323      * If configuration discovery is set to true, then the JSR315
324      * servlet 3.0 discovered configuration features are enabled.
325      * These are:<ul>
326      * <li>Web Fragments</li>
327      * <li>META-INF/resource directories</li>
328      * </ul>
329      * @param discovered true if configuration discovery is enabled for automatic configuration from the context
330      */
331     public void setConfigurationDiscovered(boolean discovered)
332     {
333         _configurationDiscovered = discovered;
334     }
335 
336     /* ------------------------------------------------------------ */
337     /*
338      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
339      */
340     @Override
341     protected void doStart() throws Exception
342     {
343         try
344         {
345             // Setup configurations
346             loadConfigurations();
347 
348             // Setup system classes
349             loadSystemClasses();
350             
351             // Setup server classes
352             loadServerClasses();
353 
354             // Configure classloader
355             _ownClassLoader=false;
356             if (getClassLoader()==null)
357             {
358                 WebAppClassLoader classLoader = new WebAppClassLoader(this);
359                 setClassLoader(classLoader);
360                 _ownClassLoader=true;
361             }
362 
363             if (Log.isDebugEnabled())
364             {
365                 ClassLoader loader = getClassLoader();
366                 Log.debug("Thread Context class loader is: " + loader);
367                 loader=loader.getParent();
368                 while(loader!=null)
369                 {
370                     Log.debug("Parent class loader is: " + loader);
371                     loader=loader.getParent();
372                 }
373             }
374             
375           
376 
377             // Prepare for configuration
378             for (int i=0;i<_configurations.length;i++)
379                 _configurations[i].preConfigure(this);
380             
381             super.doStart();
382             
383             // Clean up after configuration
384             for (int i=0;i<_configurations.length;i++)
385                 _configurations[i].postConfigure(this);
386 
387 
388             if (isLogUrlOnStart())
389                 dumpUrl();
390         }
391         catch (Exception e)
392         {
393             //start up of the webapp context failed, make sure it is not started
394             Log.warn("Failed startup of context "+this, e);
395             _unavailableException=e;
396             setAvailable(false);
397         }
398     }
399 
400     /* ------------------------------------------------------------ */
401     /*
402      * Dumps the current web app name and URL to the log
403      */
404     public void dumpUrl()
405     {
406         Connector[] connectors = getServer().getConnectors();
407         for (int i=0;i<connectors.length;i++)
408         {
409             String connectorName = connectors[i].getName();
410             String displayName = getDisplayName();
411             if (displayName == null)
412                 displayName = "WebApp@"+connectors.hashCode();
413            
414             Log.info(displayName + " at http://" + connectorName + getContextPath());
415         }
416     }
417 
418     /* ------------------------------------------------------------ */
419     /*
420      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
421      */
422     @Override
423     protected void doStop() throws Exception
424     {
425         super.doStop();
426 
427         try
428         {
429             for (int i=_configurations.length;i-->0;)
430                 _configurations[i].deconfigure(this);
431             
432             _configurations=null;
433             
434         }
435         finally
436         {
437             if (_ownClassLoader)
438                 setClassLoader(null);
439 
440             setAvailable(true);
441             _unavailableException=null;
442         }
443     }
444     
445     /* ------------------------------------------------------------ */
446     /**
447      * @return Returns the configurations.
448      */
449     public String[] getConfigurationClasses()
450     {
451         return _configurationClasses;
452     }
453     
454     /* ------------------------------------------------------------ */
455     /**
456      * @return Returns the configurations.
457      */
458     public Configuration[] getConfigurations()
459     {
460         return _configurations;
461     }
462     
463     /* ------------------------------------------------------------ */
464     /**
465      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
466      * @return Returns the defaultsDescriptor.
467      */
468     public String getDefaultsDescriptor()
469     {
470         return _defaultsDescriptor;
471     }
472     
473     /* ------------------------------------------------------------ */
474     /**
475      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
476      * @return Returns the Override Descriptor.
477      */
478     public String getOverrideDescriptor()
479     {
480         return _overrideDescriptor;
481     }
482     
483     /* ------------------------------------------------------------ */
484     /**
485      * @return Returns the permissions.
486      */
487     public PermissionCollection getPermissions()
488     {
489         return _permissions;
490     }
491 
492     /* ------------------------------------------------------------ */
493     /**
494      * @see #setServerClasses(String[])
495      * @return Returns the serverClasses.
496      */
497     public String[] getServerClasses()
498     {
499         if (_serverClasses == null)
500             loadServerClasses();
501         
502         return _serverClasses.getPatterns();
503     }
504 
505     public void addServerClass(String classname)
506     {
507         if (_serverClasses == null)
508             loadServerClasses();
509         
510         _serverClasses.addPattern(classname);
511     }
512     
513     /* ------------------------------------------------------------ */
514     /**
515      * @see #setSystemClasses(String[])
516      * @return Returns the systemClasses.
517      */
518     public String[] getSystemClasses()
519     {
520         if (_systemClasses == null)
521             loadSystemClasses();
522         
523         return _systemClasses.getPatterns();
524     }
525     
526     public void addSystemClass(String classname)
527     {
528         if (_systemClasses == null)
529             loadSystemClasses();
530         
531         _systemClasses.addPattern(classname);
532     }
533     
534     /* ------------------------------------------------------------ */
535     public boolean isServerClass(String name)
536     {
537         if (_serverClasses == null)
538             loadServerClasses();
539         
540         return _serverClasses.match(name);
541     }
542 
543     /* ------------------------------------------------------------ */
544     public boolean isSystemClass(String name)
545     {
546         if (_systemClasses == null)
547             loadSystemClasses();
548         
549         return _systemClasses.match(name);
550     }
551 
552     private void loadSystemClasses()
553     {
554         if (_systemClasses != null)
555             return;
556         
557         //look for a Server attribute with the list of System classes
558         //to apply to every web application. If not present, use our defaults.
559         Server server = getServer();
560         if (server != null)
561         {
562             Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
563             if (systemClasses != null && systemClasses instanceof String[])
564                 _systemClasses = ClasspathPattern.fromArray((String[])systemClasses);
565         }
566         
567         if (_systemClasses == null)
568             _systemClasses = ClasspathPattern.fromArray(__dftSystemClasses);
569     }
570     
571     private void loadServerClasses()
572     {
573         if (_serverClasses != null)
574             return;
575         
576         //look for a Server attribute with the list of Server classes
577         //to apply to every web application. If not present, use our defaults.
578         Server server = getServer();
579         if (server != null)
580         {
581             Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
582             if (serverClasses != null || serverClasses instanceof String[])
583                 _serverClasses = ClasspathPattern.fromArray((String[])serverClasses);
584         }
585         
586         if (_serverClasses == null)
587             _serverClasses = ClasspathPattern.fromArray(__dftServerClasses);
588     }
589     
590     /* ------------------------------------------------------------ */
591     /**
592      * @return Returns the war as a file or URL string (Resource)
593      */
594     public String getWar()
595     {
596         if (_war==null)
597             _war=getResourceBase();
598         return _war;
599     }
600 
601     /* ------------------------------------------------------------ */
602     public Resource getWebInf() throws IOException
603     {
604         if (super.getBaseResource() == null)
605             return null;
606 
607         // Iw there a WEB-INF directory?
608         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
609         if (!web_inf.exists() || !web_inf.isDirectory())
610             return null;
611         
612         return web_inf;
613     }
614     
615     /* ------------------------------------------------------------ */
616     /**
617      * @return Returns the distributable.
618      */
619     public boolean isDistributable()
620     {
621         return _distributable;
622     }
623 
624     /* ------------------------------------------------------------ */
625     /**
626      * @return Returns the extractWAR.
627      */
628     public boolean isExtractWAR()
629     {
630         return _extractWAR;
631     }
632 
633     /* ------------------------------------------------------------ */
634     /**
635      * @return True if the webdir is copied (to allow hot replacement of jars)
636      */
637     public boolean isCopyWebDir()
638     {
639         return _copyDir;
640     }
641     
642     /* ------------------------------------------------------------ */
643     /**
644      * @return Returns the java2compliant.
645      */
646     public boolean isParentLoaderPriority()
647     {
648         return _parentLoaderPriority;
649     }
650     
651     
652     /* ------------------------------------------------------------ */
653     public String[] getDefaultConfigurationClasses ()
654     {
655         return __dftConfigurationClasses;
656     }
657     
658     
659     /* ------------------------------------------------------------ */
660     protected void loadConfigurations()
661     	throws Exception
662     {
663         //if the configuration instances have been set explicitly, use them
664         if (_configurations!=null)
665             return;
666 
667         //if the configuration classnames have been set explicitly use them
668         if (!_configurationClassesSet)
669             _configurationClasses=__dftConfigurationClasses;
670 
671         _configurations = new Configuration[_configurationClasses.length];
672         for (int i = 0; i < _configurationClasses.length; i++)
673         {
674             _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
675         }
676     }
677     
678     /* ------------------------------------------------------------ */
679     @Override
680     protected boolean isProtectedTarget(String target)
681     {
682         while (target.startsWith("//"))
683             target=URIUtil.compactPath(target);
684          
685         return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
686     }
687     
688 
689     /* ------------------------------------------------------------ */
690     @Override
691     public String toString()
692     {
693         return super.toString()+(_war==null?"":(","+_war));
694     }
695     
696    
697        
698 
699     /* ------------------------------------------------------------ */
700     /**
701      * @param configurations The configuration class names.  If setConfigurations is not called
702      * these classes are used to create a configurations array.
703      */
704     public void setConfigurationClasses(String[] configurations)
705     {
706         _configurationClasses = configurations==null?null:(String[])configurations.clone();
707         _configurationClassesSet = true;
708     }
709     
710     /* ------------------------------------------------------------ */
711     /**
712      * @param configurations The configurations to set.
713      */
714     public void setConfigurations(Configuration[] configurations)
715     {
716         _configurations = configurations==null?null:(Configuration[])configurations.clone();
717         _configurationsSet = true;
718     }
719 
720     /* ------------------------------------------------------------ */
721     /**
722      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
723      * @param defaultsDescriptor The defaultsDescriptor to set.
724      */
725     public void setDefaultsDescriptor(String defaultsDescriptor)
726     {
727         _defaultsDescriptor = defaultsDescriptor;
728     }
729 
730     /* ------------------------------------------------------------ */
731     /**
732      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
733      * @param overrideDescriptor The overrideDescritpor to set.
734      */
735     public void setOverrideDescriptor(String overrideDescriptor)
736     {
737         _overrideDescriptor = overrideDescriptor;
738     }
739 
740     /* ------------------------------------------------------------ */
741     /**
742      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
743      */
744     public String getDescriptor()
745     {
746         return _descriptor;
747     }
748 
749     /* ------------------------------------------------------------ */
750     /**
751      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
752      */
753     public void setDescriptor(String descriptor)
754     {
755         _descriptor=descriptor;
756     }
757     
758     /* ------------------------------------------------------------ */
759     /**
760      * @param distributable The distributable to set.
761      */
762     public void setDistributable(boolean distributable)
763     {
764         this._distributable = distributable;
765     }
766 
767     /* ------------------------------------------------------------ */
768     @Override
769     public void setEventListeners(EventListener[] eventListeners)
770     {
771         if (_sessionHandler!=null)
772             _sessionHandler.clearEventListeners();
773             
774         super.setEventListeners(eventListeners);
775       
776         for (int i=0; eventListeners!=null && i<eventListeners.length;i ++)
777         {
778             EventListener listener = eventListeners[i];
779             
780             if ((listener instanceof HttpSessionActivationListener)
781                             || (listener instanceof HttpSessionAttributeListener)
782                             || (listener instanceof HttpSessionBindingListener)
783                             || (listener instanceof HttpSessionListener))
784             {
785                 if (_sessionHandler!=null)
786                     _sessionHandler.addEventListener(listener);
787             }
788             
789         }
790     }
791 
792     /* ------------------------------------------------------------ */
793     /** Add EventListener
794      * Conveniance method that calls {@link #setEventListeners(EventListener[])}
795      * @param listener
796      */
797     @Override
798     public void addEventListener(EventListener listener)
799     {
800         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));
801     }
802 
803     
804     /* ------------------------------------------------------------ */
805     /**
806      * @param extractWAR True if war files are extracted
807      */
808     public void setExtractWAR(boolean extractWAR)
809     {
810         _extractWAR = extractWAR;
811     }
812     
813     /* ------------------------------------------------------------ */
814     /**
815      * 
816      * @param copy True if the webdir is copied (to allow hot replacement of jars)
817      */
818     public void setCopyWebDir(boolean copy)
819     {
820         _copyDir = copy;
821     }
822 
823     /* ------------------------------------------------------------ */
824     /**
825      * @param java2compliant The java2compliant to set.
826      */
827     public void setParentLoaderPriority(boolean java2compliant)
828     {
829         _parentLoaderPriority = java2compliant;
830     }
831 
832     /* ------------------------------------------------------------ */
833     /**
834      * @param permissions The permissions to set.
835      */
836     public void setPermissions(PermissionCollection permissions)
837     {
838         _permissions = permissions;
839     }
840 
841     /* ------------------------------------------------------------ */
842     /**
843      * Set the server classes patterns.
844      * <p>
845      * Server classes/packages are classes used to implement the server and are hidden
846      * from the context.  If the context needs to load these classes, it must have its
847      * own copy of them in WEB-INF/lib or WEB-INF/classes.
848      * A class pattern is a string of one of the forms:<dl>
849      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
850      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
851      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
852      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
853      * </dl>
854      * @param serverClasses The serverClasses to set.
855      */
856     public void setServerClasses(String[] serverClasses)
857     {
858         _serverClasses = ClasspathPattern.fromArray(serverClasses);
859     }
860     
861     /* ------------------------------------------------------------ */
862     /**
863      * Set the system classes patterns.
864      * <p>
865      * System classes/packages are classes provided by the JVM and that
866      * cannot be replaced by classes of the same name from WEB-INF,
867      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
868      * A class pattern is a string of one of the forms:<dl>
869      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
870      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
871      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
872      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
873      * </dl>
874      * @param systemClasses The systemClasses to set.
875      */
876     public void setSystemClasses(String[] systemClasses)
877     {
878         _systemClasses = ClasspathPattern.fromArray(systemClasses);
879     }
880     
881 
882     /* ------------------------------------------------------------ */
883     /** Set temporary directory for context.
884      * The javax.servlet.context.tempdir attribute is also set.
885      * @param dir Writable temporary directory.
886      */
887     public void setTempDirectory(File dir)
888     {
889         if (isStarted())
890             throw new IllegalStateException("Started");
891 
892         if (dir!=null)
893         {
894             try{dir=new File(dir.getCanonicalPath());}
895             catch (IOException e){Log.warn(Log.EXCEPTION,e);}
896         }
897 
898         if (dir!=null && !dir.exists())
899         {
900             dir.mkdir();
901             dir.deleteOnExit();
902         }
903 
904         if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
905             throw new IllegalArgumentException("Bad temp directory: "+dir);
906 
907         _tmpDir=dir;
908         setAttribute(TEMPDIR,_tmpDir);
909     }
910     
911     public File getTempDirectory ()
912     {
913         return _tmpDir;
914     }
915     
916     /* ------------------------------------------------------------ */
917     /**
918      * @param war The war to set as a file name or URL
919      */
920     public void setWar(String war)
921     {
922         _war = war;
923     }
924 
925 
926     /* ------------------------------------------------------------ */
927     /**
928      * @return Comma or semicolon separated path of filenames or URLs
929      * pointing to directories or jar files. Directories should end
930      * with '/'.
931      */
932     public String getExtraClasspath()
933     {
934         return _extraClasspath;
935     }
936 
937     /* ------------------------------------------------------------ */
938     /**
939      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
940      * pointing to directories or jar files. Directories should end
941      * with '/'.
942      */
943     public void setExtraClasspath(String extraClasspath)
944     {
945         _extraClasspath=extraClasspath;
946     }
947 
948     /* ------------------------------------------------------------ */
949     public boolean isLogUrlOnStart()
950     {
951         return _logUrlOnStart;
952     }
953 
954     /* ------------------------------------------------------------ */
955     /**
956      * Sets whether or not the web app name and URL is logged on startup
957      *
958      * @param logOnStart whether or not the log message is created
959      */
960     public void setLogUrlOnStart(boolean logOnStart)
961     {
962         this._logUrlOnStart = logOnStart;
963     }
964     
965     
966     /* ------------------------------------------------------------ */
967     @Override
968     public void setServer(Server server)
969     {
970         super.setServer(server);
971         //if we haven't been given a set of configuration instances to 
972         //use, and we haven't been given a set of configuration classes
973         //to use, use the configuration classes that came from the
974         //Server (if there are any)
975         if (!_configurationsSet && !_configurationClassesSet && server != null)
976         {
977             String[] serverConfigs = (String[])server.getAttribute(SERVER_CONFIG);
978             if (serverConfigs != null)
979                 setConfigurationClasses(serverConfigs);
980         }
981     }
982 
983     /* ------------------------------------------------------------ */
984     @Override
985     protected void startContext()
986         throws Exception
987     {
988  
989         
990         // Configure webapp
991         for (int i=0;i<_configurations.length;i++)
992             _configurations[i].configure(this);
993 
994         
995         super.startContext();
996     }
997     
998     /* ------------------------------------------------------------ */
999     public class Context extends ServletContextHandler.Context
1000     {
1001         /* ------------------------------------------------------------ */
1002         public URL getResource(String path) throws MalformedURLException
1003         {
1004             Resource resource=WebAppContext.this.getResource(path);
1005             if (resource==null || !resource.exists())
1006                 return null;
1007             
1008             // Should we go to the original war?
1009             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1010             {
1011                 Resource[] resources = ((ResourceCollection)resource).getResources();
1012                 for (int i=resources.length;i-->0;)
1013                 {
1014                     if (resources[i].getName().startsWith("jar:file"))
1015                         return resources[i].getURL();
1016                 }
1017             }
1018                 
1019             return resource.getURL();
1020         }
1021     }
1022 
1023 }