• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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