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