View Javadoc

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