View Javadoc

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