View Javadoc

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