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.ArrayList;
22  import java.util.Collections;
23  import java.util.EventListener;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.servlet.ServletContext;
29  import javax.servlet.http.HttpSessionActivationListener;
30  import javax.servlet.http.HttpSessionAttributeListener;
31  import javax.servlet.http.HttpSessionBindingListener;
32  import javax.servlet.http.HttpSessionListener;
33  
34  import org.eclipse.jetty.security.SecurityHandler;
35  import org.eclipse.jetty.server.Connector;
36  import org.eclipse.jetty.server.HandlerContainer;
37  import org.eclipse.jetty.server.Server;
38  import org.eclipse.jetty.server.handler.ContextHandler;
39  import org.eclipse.jetty.server.handler.ErrorHandler;
40  import org.eclipse.jetty.server.session.SessionHandler;
41  import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
42  import org.eclipse.jetty.servlet.ServletContextHandler;
43  import org.eclipse.jetty.servlet.ServletHandler;
44  import org.eclipse.jetty.util.LazyList;
45  import org.eclipse.jetty.util.Loader;
46  import org.eclipse.jetty.util.MultiException;
47  import org.eclipse.jetty.util.StringUtil;
48  import org.eclipse.jetty.util.URIUtil;
49  import org.eclipse.jetty.util.log.Log;
50  import org.eclipse.jetty.util.resource.Resource;
51  import org.eclipse.jetty.util.resource.ResourceCollection;
52  
53  /* ------------------------------------------------------------ */
54  /** Web Application Context Handler.
55   * The WebAppContext handler is an extension of ContextHandler that
56   * coordinates the construction and configuration of nested handlers:
57   * {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
58   * and {@link org.eclipse.jetty.servlet.ServletHandler}.
59   * The handlers are configured by pluggable configuration classes, with
60   * the default being  {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
61   * {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
62   * 
63   * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
64   * 
65   */
66  public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
67  {
68      public static final String TEMPDIR = "javax.servlet.context.tempdir";
69      public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir";
70      public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
71      public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
72      public final static String SERVER_CONFIG = "org.eclipse.jetty.webapp.configuration";
73      public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
74      public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
75      
76      private static String[] __dftConfigurationClasses =
77      {
78          "org.eclipse.jetty.webapp.WebInfConfiguration",
79          "org.eclipse.jetty.webapp.WebXmlConfiguration",
80          "org.eclipse.jetty.webapp.MetaInfConfiguration",
81          "org.eclipse.jetty.webapp.FragmentConfiguration",
82          "org.eclipse.jetty.webapp.JettyWebXmlConfiguration"//,
83          //"org.eclipse.jetty.webapp.TagLibConfiguration"
84      } ;
85      
86      // System classes are classes that cannot be replaced by 
87      // the web application, and they are *always* loaded via 
88      // system classloader.
89      public final static String[] __dftSystemClasses = 
90      {
91          "java.",                            // Java SE classes (per servlet spec v2.5 / SRV.9.7.2) 
92          "javax.",                           // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
93          "org.xml.",                         // needed by javax.xml
94          "org.w3c.",                         // needed by javax.xml
95          "org.apache.commons.logging.",      // TODO: review if special case still needed 
96          "org.eclipse.jetty.continuation.",  // webapp cannot change continuation classes
97          "org.eclipse.jetty.jndi.",          // webapp cannot change naming classes
98          "org.eclipse.jetty.plus.jaas.",     // webapp cannot change jaas classes
99          "org.eclipse.jetty.websocket.",     // WebSocket is a jetty extension
100         "org.eclipse.jetty.servlet.DefaultServlet"  // webapp cannot change default servlets
101     } ;
102     
103     // Server classes are classes that are hidden from being
104     // loaded by the web application using system classloader,
105     // so if web application needs to load any of such classes,
106     // it has to include them in its distribution.
107     public final static String[] __dftServerClasses = 
108     {
109         "-org.eclipse.jetty.continuation.", // don't hide continuation classes
110         "-org.eclipse.jetty.jndi.",         // don't hide naming classes
111         "-org.eclipse.jetty.plus.jaas.",    // don't hide jaas classes
112         "-org.eclipse.jetty.websocket.",    // don't hide websocket extension
113         "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
114         "-org.eclipse.jetty.servlet.listener.", //don't hide useful listeners
115         "org.eclipse.jetty."                // hide other jetty classes
116     } ; 
117 
118     private String[] _configurationClasses = __dftConfigurationClasses;
119     private ClasspathPattern _systemClasses = null;
120     private ClasspathPattern _serverClasses = null;
121 
122     private Configuration[] _configurations;
123     private String _defaultsDescriptor=WEB_DEFAULTS_XML;
124     private String _descriptor=null;
125     private final List<String> _overrideDescriptors = new ArrayList<String>();
126     private boolean _distributable=false;
127     private boolean _extractWAR=true;
128     private boolean _copyDir=false;
129     private boolean _copyWebInf=true; // TODO change to false?
130     private boolean _logUrlOnStart =false;
131     private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
132     private PermissionCollection _permissions;
133 
134     private String[] _contextWhiteList = null;
135     
136     private File _tmpDir;
137     private String _war;
138     private String _extraClasspath;
139     private Throwable _unavailableException;
140     
141     private Map<String, String> _resourceAliases;
142     private boolean _ownClassLoader=false;
143     private boolean _configurationDiscovered=true;
144     private boolean _configurationClassesSet=false;
145     private boolean _configurationsSet=false;
146     private boolean _allowDuplicateFragmentNames = false;
147     private boolean _throwUnavailableOnStartupException = false;
148     
149     private MetaData _metadata=new MetaData();
150 
151     public static WebAppContext getCurrentWebAppContext()
152     {
153         ContextHandler.Context context=ContextHandler.getCurrentContext();
154         if (context!=null)
155         {
156             ContextHandler handler = context.getContextHandler();
157             if (handler instanceof WebAppContext)
158                 return (WebAppContext)handler;
159         }
160         return null;
161     }
162     
163     /* ------------------------------------------------------------ */
164     public WebAppContext()
165     {
166         super(SESSIONS|SECURITY); 
167         _scontext=new Context();
168         setErrorHandler(new ErrorPageErrorHandler());    
169     }
170     
171     /* ------------------------------------------------------------ */
172     /**
173      * @param contextPath The context path
174      * @param webApp The URL or filename of the webapp directory or war file.
175      */
176     public WebAppContext(String webApp,String contextPath)
177     {
178         super(null,contextPath,SESSIONS|SECURITY);
179         _scontext=new Context();
180         setContextPath(contextPath);
181         setWar(webApp);
182         setErrorHandler(new ErrorPageErrorHandler()); 
183     }
184     
185     /* ------------------------------------------------------------ */
186     /**
187      * @param parent The parent HandlerContainer.
188      * @param contextPath The context path
189      * @param webApp The URL or filename of the webapp directory or war file.
190      */
191     public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
192     {
193         super(parent,contextPath,SESSIONS|SECURITY);
194         _scontext=new Context();
195         setWar(webApp);
196         setErrorHandler(new ErrorPageErrorHandler());   
197     }
198 
199     /* ------------------------------------------------------------ */
200 
201     /**
202      * This constructor is used in the geronimo integration.
203      *
204      * @param sessionHandler SessionHandler for this web app
205      * @param securityHandler SecurityHandler for this web app
206      * @param servletHandler ServletHandler for this web app
207      * @param errorHandler ErrorHandler for this web app
208      */
209     public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler) {
210         super(null, sessionHandler, securityHandler, servletHandler, errorHandler);
211         _scontext = new Context();
212         setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
213     }
214     
215     /* ------------------------------------------------------------ */
216     /**
217      * @param servletContextName The servletContextName to set.
218      */
219     @Override
220     public void setDisplayName(String servletContextName)
221     {
222         super.setDisplayName(servletContextName);
223         ClassLoader cl = getClassLoader();
224         if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null)
225             ((WebAppClassLoader)cl).setName(servletContextName);
226     }
227     
228     /* ------------------------------------------------------------ */
229     /** Get an exception that caused the webapp to be unavailable
230      * @return A throwable if the webapp is unavailable or null
231      */
232     public Throwable getUnavailableException()
233     {
234         return _unavailableException;
235     }
236 
237     
238     /* ------------------------------------------------------------ */
239     /** Set Resource Alias.
240      * Resource aliases map resource uri's within a context.
241      * They may optionally be used by a handler when looking for
242      * a resource.
243      * @param alias
244      * @param uri
245      */
246     public void setResourceAlias(String alias, String uri)
247     {
248         if (_resourceAliases == null)
249             _resourceAliases= new HashMap<String, String>(5);
250         _resourceAliases.put(alias, uri);
251     }
252 
253     /* ------------------------------------------------------------ */
254     public Map<String, String> getResourceAliases()
255     {
256         if (_resourceAliases == null)
257             return null;
258         return _resourceAliases;
259     }
260     
261     /* ------------------------------------------------------------ */
262     public void setResourceAliases(Map<String, String> map)
263     {
264         _resourceAliases = map;
265     }
266     
267     /* ------------------------------------------------------------ */
268     public String getResourceAlias(String alias)
269     {
270         if (_resourceAliases == null)
271             return null;
272         return _resourceAliases.get(alias);
273     }
274 
275     /* ------------------------------------------------------------ */
276     public String removeResourceAlias(String alias)
277     {
278         if (_resourceAliases == null)
279             return null;
280         return _resourceAliases.remove(alias);
281     }
282 
283     /* ------------------------------------------------------------ */
284     /* (non-Javadoc)
285      * @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
286      */
287     @Override
288     public void setClassLoader(ClassLoader classLoader)
289     {
290         super.setClassLoader(classLoader);
291         
292 //        if ( !(classLoader instanceof WebAppClassLoader) )
293 //        {
294 //            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() );
295 //        }
296         
297         if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
298             ((WebAppClassLoader)classLoader).setName(getDisplayName());
299     }
300     
301     /* ------------------------------------------------------------ */
302     @Override
303     public Resource getResource(String uriInContext) throws MalformedURLException
304     {
305         if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH))
306             throw new MalformedURLException(uriInContext);
307 
308         IOException ioe= null;
309         Resource resource= null;
310         int loop=0;
311         while (uriInContext!=null && loop++<100)
312         {
313             try
314             {
315                 resource= super.getResource(uriInContext);
316                 if (resource != null && resource.exists())
317                     return resource;
318                 
319                 uriInContext = getResourceAlias(uriInContext);
320             }
321             catch (IOException e)
322             {
323                 Log.ignore(e);
324                 if (ioe==null)
325                     ioe= e;
326             }
327         }
328 
329         if (ioe != null && ioe instanceof MalformedURLException)
330             throw (MalformedURLException)ioe;
331 
332         return resource;
333     }
334     
335 
336     /* ------------------------------------------------------------ */
337     /** Is the context Automatically configured.
338      * 
339      * @return true if configuration discovery.
340      */
341     public boolean isConfigurationDiscovered()
342     {
343         return _configurationDiscovered;
344     }
345 
346     /* ------------------------------------------------------------ */
347     /** Set the configuration discovery mode.
348      * If configuration discovery is set to true, then the JSR315
349      * servlet 3.0 discovered configuration features are enabled.
350      * These are:<ul>
351      * <li>Web Fragments</li>
352      * <li>META-INF/resource directories</li>
353      * </ul>
354      * @param discovered true if configuration discovery is enabled for automatic configuration from the context
355      */
356     public void setConfigurationDiscovered(boolean discovered)
357     {
358         _configurationDiscovered = discovered;
359     }
360     
361     /* ------------------------------------------------------------ */
362     /** Pre configure the web application.
363      * <p>
364      * The method is normally called from {@link #start()}. It performs
365      * the discovery of the configurations to be applied to this context,
366      * specifically:<ul>
367      * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
368      * <li>Setup the default System classes by calling {@link #loadSystemClasses()}
369      * <li>Setup the default Server classes by calling <code>loadServerClasses()</code>
370      * <li>Instantiates a classload (if one is not already set)
371      * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all 
372      * Configuration instances.
373      * </ul>
374      * @throws Exception
375      */
376     public void preConfigure() throws Exception
377     {
378         // Setup configurations
379         loadConfigurations();
380 
381         // Setup system classes
382         loadSystemClasses();
383         
384         // Setup server classes
385         loadServerClasses();
386 
387         // Configure classloader
388         _ownClassLoader=false;
389         if (getClassLoader()==null)
390         {
391             WebAppClassLoader classLoader = new WebAppClassLoader(this);
392             setClassLoader(classLoader);
393             _ownClassLoader=true;
394         }
395 
396         if (Log.isDebugEnabled())
397         {
398             ClassLoader loader = getClassLoader();
399             Log.debug("Thread Context class loader is: " + loader);
400             loader=loader.getParent();
401             while(loader!=null)
402             {
403                 Log.debug("Parent class loader is: " + loader);
404                 loader=loader.getParent();
405             }
406         }
407       
408         // Prepare for configuration     
409         for (int i=0;i<_configurations.length;i++)
410         {
411             Log.debug("preConfigure {} with {}",this,_configurations[i]);
412             _configurations[i].preConfigure(this);
413         }
414     }
415 
416     /* ------------------------------------------------------------ */
417     public void configure() throws Exception
418     {
419         // Configure webapp
420         for (int i=0;i<_configurations.length;i++)
421         {
422             Log.debug("configure {} with {}",this,_configurations[i]);
423             _configurations[i].configure(this);
424         }
425     }
426     
427     /* ------------------------------------------------------------ */
428     public void postConfigure() throws Exception
429     {   
430         // Clean up after configuration
431         for (int i=0;i<_configurations.length;i++)
432         {
433             Log.debug("postConfigure {} with {}",this,_configurations[i]);
434             _configurations[i].postConfigure(this);
435         }
436     }
437     
438     /* ------------------------------------------------------------ */
439     /*
440      * @see org.eclipse.thread.AbstractLifeCycle#doStart()
441      */
442     @Override
443     protected void doStart() throws Exception
444     {
445         try
446         {
447             _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
448             preConfigure();
449             super.doStart();
450             postConfigure();
451 
452             if (isLogUrlOnStart())
453                 dumpUrl();
454         }
455         catch (Exception e)
456         {
457             //start up of the webapp context failed, make sure it is not started
458             Log.warn("Failed startup of context "+this, e);
459             _unavailableException=e;
460             setAvailable(false);
461             if (isThrowUnavailableOnStartupException())
462                 throw e;
463         }
464     }
465     
466     /* ------------------------------------------------------------ */
467     /*
468      * @see org.eclipse.thread.AbstractLifeCycle#doStop()
469      */
470     @Override
471     protected void doStop() throws Exception
472     {
473         super.doStop();
474 
475         try
476         {
477             for (int i=_configurations.length;i-->0;)
478                 _configurations[i].deconfigure(this);
479             
480             if (_metadata != null)
481                 _metadata.clear();
482             _metadata=new MetaData();
483             
484         }
485         finally
486         {
487             if (_ownClassLoader)
488                 setClassLoader(null);
489 
490             setAvailable(true);
491             _unavailableException=null;
492         }
493     }
494     
495     /* ------------------------------------------------------------ */
496     @Override
497     public void destroy()
498     {
499         // Prepare for configuration     
500         MultiException mx=new MultiException();
501         if (_configurations!=null)
502         {
503             for (int i=_configurations.length;i-->0;)
504             {
505                 try
506                 {   
507                     _configurations[i].destroy(this);
508                 }
509                 catch(Exception e)
510                 {
511                     mx.add(e);
512                 }
513             }
514         }
515         _configurations=null;
516         super.destroy();
517         mx.ifExceptionThrowRuntime();
518     }
519 
520 
521     /* ------------------------------------------------------------ */
522     /*
523      * Dumps the current web app name and URL to the log
524      */
525     private void dumpUrl()
526     {
527         Connector[] connectors = getServer().getConnectors();
528         for (int i=0;i<connectors.length;i++)
529         {
530             String connectorName = connectors[i].getName();
531             String displayName = getDisplayName();
532             if (displayName == null)
533                 displayName = "WebApp@"+connectors.hashCode();
534            
535             Log.info(displayName + " at http://" + connectorName + getContextPath());
536         }
537     }
538 
539     /* ------------------------------------------------------------ */
540     /**
541      * @return Returns the configurations.
542      */
543     public String[] getConfigurationClasses()
544     {
545         return _configurationClasses;
546     }
547     
548     /* ------------------------------------------------------------ */
549     /**
550      * @return Returns the configurations.
551      */
552     public Configuration[] getConfigurations()
553     {
554         return _configurations;
555     }
556     
557     /* ------------------------------------------------------------ */
558     /**
559      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
560      * @return Returns the defaultsDescriptor.
561      */
562     public String getDefaultsDescriptor()
563     {
564         return _defaultsDescriptor;
565     }
566     
567     /* ------------------------------------------------------------ */
568     /**
569      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
570      * @return Returns the Override Descriptor.
571      * @deprecated use {@link #getOverrideDescriptors()}
572      */
573     public String getOverrideDescriptor()
574     {
575         if (_overrideDescriptors.size()!=1)
576             return null;
577         return _overrideDescriptors.get(0);
578     }
579     
580     /* ------------------------------------------------------------ */
581     /**
582      * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
583      * @return Returns the Override Descriptor list
584      */
585     public List<String> getOverrideDescriptors()
586     {
587         return Collections.unmodifiableList(_overrideDescriptors);
588     }
589     
590     /* ------------------------------------------------------------ */
591     /**
592      * @return Returns the permissions.
593      */
594     public PermissionCollection getPermissions()
595     {
596         return _permissions;
597     }
598 
599     /* ------------------------------------------------------------ */
600     /**
601      * @see #setServerClasses(String[])
602      * @return Returns the serverClasses.
603      */
604     public String[] getServerClasses()
605     {
606         if (_serverClasses == null)
607             loadServerClasses();
608         
609         return _serverClasses.getPatterns();
610     }
611 
612     public void addServerClass(String classname)
613     {
614         if (_serverClasses == null)
615             loadServerClasses();
616         
617         _serverClasses.addPattern(classname);
618     }
619     
620     /* ------------------------------------------------------------ */
621     /**
622      * @see #setSystemClasses(String[])
623      * @return Returns the systemClasses.
624      */
625     public String[] getSystemClasses()
626     {
627         if (_systemClasses == null)
628             loadSystemClasses();
629         
630         return _systemClasses.getPatterns();
631     }
632     
633     /* ------------------------------------------------------------ */
634     public void addSystemClass(String classname)
635     {
636         if (_systemClasses == null)
637             loadSystemClasses();
638         
639         _systemClasses.addPattern(classname);
640     }
641     
642     /* ------------------------------------------------------------ */
643     public boolean isServerClass(String name)
644     {
645         if (_serverClasses == null)
646             loadServerClasses();
647         
648         return _serverClasses.match(name);
649     }
650 
651     /* ------------------------------------------------------------ */
652     public boolean isSystemClass(String name)
653     {
654         if (_systemClasses == null)
655             loadSystemClasses();
656         
657         return _systemClasses.match(name);
658     }
659     
660     /* ------------------------------------------------------------ */
661     protected void loadSystemClasses()
662     {
663         if (_systemClasses != null)
664             return;
665         
666         //look for a Server attribute with the list of System classes
667         //to apply to every web application. If not present, use our defaults.
668         Server server = getServer();
669         if (server != null)
670         {
671             Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
672             if (systemClasses != null && systemClasses instanceof String[])
673                 _systemClasses = new ClasspathPattern((String[])systemClasses);
674         }
675         
676         if (_systemClasses == null)
677             _systemClasses = new ClasspathPattern(__dftSystemClasses);
678     }
679     
680     /* ------------------------------------------------------------ */
681     private void loadServerClasses()
682     {
683         if (_serverClasses != null)
684             return;
685         
686         //look for a Server attribute with the list of Server classes
687         //to apply to every web application. If not present, use our defaults.
688         Server server = getServer();
689         if (server != null)
690         {
691             Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
692             if (serverClasses != null || serverClasses instanceof String[])
693                 _serverClasses = new ClasspathPattern((String[])serverClasses);
694         }
695         
696         if (_serverClasses == null)
697             _serverClasses = new ClasspathPattern(__dftServerClasses);
698     }
699     
700     /* ------------------------------------------------------------ */
701     /**
702      * @return Returns the war as a file or URL string (Resource)
703      */
704     public String getWar()
705     {
706         if (_war==null)
707             _war=getResourceBase();
708         return _war;
709     }
710 
711     /* ------------------------------------------------------------ */
712     public Resource getWebInf() throws IOException
713     {
714         if (super.getBaseResource() == null)
715             return null;
716 
717         // Iw there a WEB-INF directory?
718         Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
719         if (!web_inf.exists() || !web_inf.isDirectory())
720             return null;
721         
722         return web_inf;
723     }
724     
725     /* ------------------------------------------------------------ */
726     /**
727      * @return Returns the distributable.
728      */
729     public boolean isDistributable()
730     {
731         return _distributable;
732     }
733 
734     /* ------------------------------------------------------------ */
735     /**
736      * @return Returns the extractWAR.
737      */
738     public boolean isExtractWAR()
739     {
740         return _extractWAR;
741     }
742 
743     /* ------------------------------------------------------------ */
744     /**
745      * @return True if the webdir is copied (to allow hot replacement of jars on windows)
746      */
747     public boolean isCopyWebDir()
748     {
749         return _copyDir;
750     }
751 
752     /* ------------------------------------------------------------ */
753     /**
754      * @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
755      */
756     public boolean isCopyWebInf()
757     {
758         return _copyWebInf;
759     }
760 
761     /* ------------------------------------------------------------ */
762     /**
763      * @return True if the classloader should delegate first to the parent 
764      * classloader (standard java behaviour) or false if the classloader 
765      * should first try to load from WEB-INF/lib or WEB-INF/classes (servlet 
766      * spec recommendation).
767      */
768     public boolean isParentLoaderPriority()
769     {
770         return _parentLoaderPriority;
771     }
772     
773     
774     /* ------------------------------------------------------------ */
775     public String[] getDefaultConfigurationClasses ()
776     {
777         return __dftConfigurationClasses;
778     }
779     
780     /* ------------------------------------------------------------ */
781     public String[] getDefaultServerClasses ()
782     {
783         return __dftServerClasses;
784     }
785     
786     /* ------------------------------------------------------------ */
787     public String[] getDefaultSystemClasses ()
788     {
789         return __dftSystemClasses;
790     }
791     
792     /* ------------------------------------------------------------ */
793     protected void loadConfigurations()
794     	throws Exception
795     {
796         //if the configuration instances have been set explicitly, use them
797         if (_configurations!=null)
798             return;
799 
800         //if the configuration classnames have been set explicitly use them
801         if (!_configurationClassesSet)
802             _configurationClasses=__dftConfigurationClasses;
803 
804         _configurations = new Configuration[_configurationClasses.length];
805         for (int i = 0; i < _configurationClasses.length; i++)
806         {
807             _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance();
808         }
809     }
810     
811     /* ------------------------------------------------------------ */
812     @Override
813     protected boolean isProtectedTarget(String target)
814     {
815         while (target.startsWith("//"))
816             target=URIUtil.compactPath(target);
817          
818         return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
819     }
820     
821 
822     /* ------------------------------------------------------------ */
823     @Override
824     public String toString()
825     {
826         return super.toString()+(_war==null?"":(","+_war));
827     }
828 
829     /* ------------------------------------------------------------ */
830     /**
831      * @param configurations The configuration class names.  If setConfigurations is not called
832      * these classes are used to create a configurations array.
833      */
834     public void setConfigurationClasses(String[] configurations)
835     {
836         if (isRunning())
837             throw new IllegalStateException();
838         _configurationClasses = configurations==null?null:(String[])configurations.clone();
839         _configurationClassesSet = true;
840         _configurations=null;
841     }
842     
843     /* ------------------------------------------------------------ */
844     /**
845      * @param configurations The configurations to set.
846      */
847     public void setConfigurations(Configuration[] configurations)
848     {
849         if (isRunning())
850             throw new IllegalStateException();
851         _configurations = configurations==null?null:(Configuration[])configurations.clone();
852         _configurationsSet = true;
853     }
854 
855     /* ------------------------------------------------------------ */
856     /**
857      * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
858      * @param defaultsDescriptor The defaultsDescriptor to set.
859      */
860     public void setDefaultsDescriptor(String defaultsDescriptor)
861     {
862         _defaultsDescriptor = defaultsDescriptor;
863     }
864 
865     /* ------------------------------------------------------------ */
866     /**
867      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
868      * @param overrideDescriptor The overrideDescritpor to set.
869      * @deprecated use {@link #setOverrideDescriptors(List)}
870      */
871     public void setOverrideDescriptor(String overrideDescriptor)
872     {
873         _overrideDescriptors.clear();
874         _overrideDescriptors.add(overrideDescriptor);
875     }
876     
877     /* ------------------------------------------------------------ */
878     /**
879      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
880      * @param overrideDescriptors The overrideDescriptors (file or URL) to set.
881      */
882     public void setOverrideDescriptors(List<String> overrideDescriptors)
883     {
884         _overrideDescriptors.clear();
885         _overrideDescriptors.addAll(overrideDescriptors);
886     }
887     
888     /* ------------------------------------------------------------ */
889     /**
890      * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
891      * @param overrideDescriptor The overrideDescriptor (file or URL) to add.
892      */
893     public void addOverrideDescriptor(String overrideDescriptor)
894     {
895         _overrideDescriptors.add(overrideDescriptor);
896     }
897 
898     /* ------------------------------------------------------------ */
899     /**
900      * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
901      */
902     public String getDescriptor()
903     {
904         return _descriptor;
905     }
906 
907     /* ------------------------------------------------------------ */
908     /**
909      * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
910      */
911     public void setDescriptor(String descriptor)
912     {
913         _descriptor=descriptor;
914     }
915     
916     /* ------------------------------------------------------------ */
917     /**
918      * @param distributable The distributable to set.
919      */
920     public void setDistributable(boolean distributable)
921     {
922         this._distributable = distributable;
923     }
924 
925     /* ------------------------------------------------------------ */
926     @Override
927     public void setEventListeners(EventListener[] eventListeners)
928     {
929         if (_sessionHandler!=null)
930             _sessionHandler.clearEventListeners();
931             
932         super.setEventListeners(eventListeners);
933       
934         for (int i=0; eventListeners!=null && i<eventListeners.length;i ++)
935         {
936             EventListener listener = eventListeners[i];
937             
938             if ((listener instanceof HttpSessionActivationListener)
939                             || (listener instanceof HttpSessionAttributeListener)
940                             || (listener instanceof HttpSessionBindingListener)
941                             || (listener instanceof HttpSessionListener))
942             {
943                 if (_sessionHandler!=null)
944                     _sessionHandler.addEventListener(listener);
945             }
946             
947         }
948     }
949 
950     /* ------------------------------------------------------------ */
951     /** Add EventListener
952      * Conveniance method that calls {@link #setEventListeners(EventListener[])}
953      * @param listener
954      */
955     @Override
956     public void addEventListener(EventListener listener)
957     {   
958         setEventListeners((EventListener[])LazyList.addToArray(getEventListeners(), listener, EventListener.class));
959     }
960 
961     
962     /* ------------------------------------------------------------ */
963     /**
964      * @param extractWAR True if war files are extracted
965      */
966     public void setExtractWAR(boolean extractWAR)
967     {
968         _extractWAR = extractWAR;
969     }
970     
971     /* ------------------------------------------------------------ */
972     /**
973      * @param copy True if the webdir is copied (to allow hot replacement of jars)
974      */
975     public void setCopyWebDir(boolean copy)
976     {
977         _copyDir = copy;
978     }
979 
980     /* ------------------------------------------------------------ */
981     /**
982      * @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
983      */
984     public void setCopyWebInf(boolean copyWebInf)
985     {
986         _copyWebInf = copyWebInf;
987     }
988 
989     /* ------------------------------------------------------------ */
990     /**
991      * @param java2compliant The java2compliant to set.
992      */
993     public void setParentLoaderPriority(boolean java2compliant)
994     {
995         _parentLoaderPriority = java2compliant;
996     }
997 
998     /* ------------------------------------------------------------ */
999     /**
1000      * @param permissions The permissions to set.
1001      */
1002     public void setPermissions(PermissionCollection permissions)
1003     {
1004         _permissions = permissions;
1005     }
1006     
1007     /**
1008      * Set the context white list
1009      * 
1010      * In certain circumstances you want may want to deny access of one webapp from another
1011      * when you may not fully trust the webapp.  Setting this white list will enable a
1012      * check when a servlet called getContext(String), validating that the uriInPath
1013      * for the given webapp has been declaratively allows access to the context.
1014      * @param contextWhiteList
1015      */
1016     public void setContextWhiteList(String[] contextWhiteList)
1017     {
1018         _contextWhiteList = contextWhiteList;
1019     }
1020 
1021     /* ------------------------------------------------------------ */
1022     /**
1023      * Set the server classes patterns.
1024      * <p>
1025      * Server classes/packages are classes used to implement the server and are hidden
1026      * from the context.  If the context needs to load these classes, it must have its
1027      * own copy of them in WEB-INF/lib or WEB-INF/classes.
1028      * A class pattern is a string of one of the forms:<dl>
1029      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1030      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1031      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1032      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1033      * </dl>
1034      * @param serverClasses The serverClasses to set.
1035      */
1036     public void setServerClasses(String[] serverClasses)
1037     {
1038         _serverClasses = new ClasspathPattern(serverClasses);
1039     }
1040     
1041     /* ------------------------------------------------------------ */
1042     /**
1043      * Set the system classes patterns.
1044      * <p>
1045      * System classes/packages are classes provided by the JVM and that
1046      * cannot be replaced by classes of the same name from WEB-INF,
1047      * regardless of the value of {@link #setParentLoaderPriority(boolean)}.
1048      * A class pattern is a string of one of the forms:<dl>
1049      * <dt>org.package.Classname</dt><dd>Match a specific class</dd>
1050      * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd>
1051      * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd>
1052      * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd>
1053      * </dl>
1054      * @param systemClasses The systemClasses to set.
1055      */
1056     public void setSystemClasses(String[] systemClasses)
1057     {
1058         _systemClasses = new ClasspathPattern(systemClasses);
1059     }
1060     
1061 
1062     /* ------------------------------------------------------------ */
1063     /** Set temporary directory for context.
1064      * The javax.servlet.context.tempdir attribute is also set.
1065      * @param dir Writable temporary directory.
1066      */
1067     public void setTempDirectory(File dir)
1068     {
1069         if (isStarted())
1070             throw new IllegalStateException("Started");
1071 
1072         if (dir!=null)
1073         {
1074             try{dir=new File(dir.getCanonicalPath());}
1075             catch (IOException e){Log.warn(Log.EXCEPTION,e);}
1076         }
1077 
1078         if (dir!=null && !dir.exists())
1079         {
1080             dir.mkdir();
1081             dir.deleteOnExit();
1082         }
1083 
1084         if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite()))
1085             throw new IllegalArgumentException("Bad temp directory: "+dir);
1086         
1087         try
1088         {
1089             if (dir!=null)
1090                 dir=dir.getCanonicalFile();
1091         }
1092         catch(Exception e)
1093         {
1094             Log.warn(e);
1095         }
1096         _tmpDir=dir;
1097         setAttribute(TEMPDIR,_tmpDir);
1098     }
1099     
1100     /* ------------------------------------------------------------ */
1101     public File getTempDirectory ()
1102     {
1103         return _tmpDir;
1104     }
1105     
1106     /* ------------------------------------------------------------ */
1107     /**
1108      * @param war The war to set as a file name or URL
1109      */
1110     public void setWar(String war)
1111     {
1112         _war = war;
1113     }
1114 
1115     /* ------------------------------------------------------------ */
1116     /**
1117      * @return Comma or semicolon separated path of filenames or URLs
1118      * pointing to directories or jar files. Directories should end
1119      * with '/'.
1120      */
1121     public String getExtraClasspath()
1122     {
1123         return _extraClasspath;
1124     }
1125 
1126     /* ------------------------------------------------------------ */
1127     /**
1128      * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1129      * pointing to directories or jar files. Directories should end
1130      * with '/'.
1131      */
1132     public void setExtraClasspath(String extraClasspath)
1133     {
1134         _extraClasspath=extraClasspath;
1135     }
1136 
1137     /* ------------------------------------------------------------ */
1138     public boolean isLogUrlOnStart()
1139     {
1140         return _logUrlOnStart;
1141     }
1142 
1143     /* ------------------------------------------------------------ */
1144     /**
1145      * Sets whether or not the web app name and URL is logged on startup
1146      *
1147      * @param logOnStart whether or not the log message is created
1148      */
1149     public void setLogUrlOnStart(boolean logOnStart)
1150     {
1151         this._logUrlOnStart = logOnStart;
1152     }
1153     
1154     
1155     /* ------------------------------------------------------------ */
1156     @Override
1157     public void setServer(Server server)
1158     {
1159         super.setServer(server);
1160         //if we haven't been given a set of configuration instances to 
1161         //use, and we haven't been given a set of configuration classes
1162         //to use, use the configuration classes that came from the
1163         //Server (if there are any)
1164         if (!_configurationsSet && !_configurationClassesSet && server != null)
1165         {
1166             String[] serverConfigs = (String[])server.getAttribute(SERVER_CONFIG);
1167             if (serverConfigs != null)
1168                 setConfigurationClasses(serverConfigs);
1169         }
1170     }
1171     
1172     
1173     /* ------------------------------------------------------------ */
1174     public boolean isAllowDuplicateFragmentNames()
1175     {
1176         return _allowDuplicateFragmentNames;
1177     }
1178     
1179     
1180     /* ------------------------------------------------------------ */
1181     public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
1182     {
1183         _allowDuplicateFragmentNames = allowDuplicateFragmentNames;
1184     }
1185 
1186     
1187     /* ------------------------------------------------------------ */
1188     public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) {
1189         _throwUnavailableOnStartupException = throwIfStartupException;
1190     }
1191     
1192 
1193     /* ------------------------------------------------------------ */
1194     public boolean isThrowUnavailableOnStartupException () {
1195         return _throwUnavailableOnStartupException;
1196     }
1197     
1198     /* ------------------------------------------------------------ */
1199     @Override
1200     protected void startContext()
1201         throws Exception
1202     {
1203         configure();
1204         
1205         //resolve the metadata
1206         _metadata.resolve(this);
1207         
1208         super.startContext();
1209     }
1210     
1211     /* ------------------------------------------------------------ */
1212     public class Context extends ServletContextHandler.Context
1213     {
1214         /* ------------------------------------------------------------ */
1215         public URL getResource(String path) throws MalformedURLException
1216         {
1217             Resource resource=WebAppContext.this.getResource(path);
1218             if (resource==null || !resource.exists())
1219                 return null;
1220             
1221             // Should we go to the original war?
1222             if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
1223             {
1224                 Resource[] resources = ((ResourceCollection)resource).getResources();
1225                 for (int i=resources.length;i-->0;)
1226                 {
1227                     if (resources[i].getName().startsWith("jar:file"))
1228                         return resources[i].getURL();
1229                 }
1230             }
1231                 
1232             return resource.getURL();
1233         }
1234 
1235         /* ------------------------------------------------------------ */
1236         @Override
1237         public ServletContext getContext(String uripath)
1238         {
1239             ServletContext servletContext = super.getContext(uripath);
1240             
1241             if ( servletContext != null && _contextWhiteList != null )
1242             {
1243                 for ( String context : _contextWhiteList )
1244                 {
1245                     if ( context.equals(uripath) )
1246                     {
1247                         return servletContext;
1248                     }
1249                 }
1250                 
1251                 return null;
1252             }
1253             else
1254             {
1255                 return servletContext;
1256             }
1257         }
1258 
1259     }
1260 
1261     /* ------------------------------------------------------------ */
1262     public MetaData getMetaData()
1263     {
1264         return _metadata;
1265     }
1266 
1267 }