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