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