View Javadoc

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