org.eclipse.jetty.webapp.WebAppContext Maven / Gradle / Ivy
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.webapp;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.PermissionCollection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration.Dynamic;
import javax.servlet.ServletSecurityElement;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingListener;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.http.HttpSessionListener;
import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.ClassLoaderDump;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HandlerContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.session.SessionHandler;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.DumpableCollection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceCollection;
/**
* Web Application Context Handler.
*
* The WebAppContext handler is an extension of ContextHandler that
* coordinates the construction and configuration of nested handlers:
* {@link org.eclipse.jetty.security.ConstraintSecurityHandler}, {@link org.eclipse.jetty.server.session.SessionHandler}
* and {@link org.eclipse.jetty.servlet.ServletHandler}.
* The handlers are configured by pluggable configuration classes, with
* the default being {@link org.eclipse.jetty.webapp.WebXmlConfiguration} and
* {@link org.eclipse.jetty.webapp.JettyWebXmlConfiguration}.
*
*/
@ManagedObject("Web Application ContextHandler")
public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context
{
static final Logger LOG = Log.getLogger(WebAppContext.class);
public static final String TEMPDIR = "javax.servlet.context.tempdir";
public static final String BASETEMPDIR = "org.eclipse.jetty.webapp.basetempdir";
public final static String WEB_DEFAULTS_XML="org/eclipse/jetty/webapp/webdefault.xml";
public final static String ERROR_PAGE="org.eclipse.jetty.server.error_page";
public final static String SERVER_SYS_CLASSES = "org.eclipse.jetty.webapp.systemClasses";
public final static String SERVER_SRV_CLASSES = "org.eclipse.jetty.webapp.serverClasses";
private String[] __dftProtectedTargets = {"/web-inf", "/meta-inf"};
public static final String[] DEFAULT_CONFIGURATION_CLASSES =
{
"org.eclipse.jetty.webapp.WebInfConfiguration",
"org.eclipse.jetty.webapp.WebXmlConfiguration",
"org.eclipse.jetty.webapp.MetaInfConfiguration",
"org.eclipse.jetty.webapp.FragmentConfiguration",
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration"
} ;
// System classes are classes that cannot be replaced by
// the web application, and they are *always* loaded via
// system classloader.
// TODO This centrally managed list of features that are exposed/hidden needs to be replaced
// with a more automatic distributed mechanism
public final static String[] __dftSystemClasses =
{
"java.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"javax.", // Java SE classes (per servlet spec v2.5 / SRV.9.7.2)
"org.xml.", // needed by javax.xml
"org.w3c.", // needed by javax.xml
"org.eclipse.jetty.jmx.", // webapp cannot change jmx classes
"org.eclipse.jetty.util.annotation.", // webapp cannot change jmx annotations
"org.eclipse.jetty.continuation.", // webapp cannot change continuation classes
"org.eclipse.jetty.jndi.", // webapp cannot change naming classes
"org.eclipse.jetty.jaas.", // webapp cannot change jaas classes
"org.eclipse.jetty.websocket.", // webapp cannot change / replace websocket classes
"org.eclipse.jetty.util.log.", // webapp should use server log
"org.eclipse.jetty.servlet.DefaultServlet", // webapp cannot change default servlets
"org.eclipse.jetty.jsp.JettyJspServlet", //webapp cannot change jetty jsp servlet
"org.eclipse.jetty.servlets.PushCacheFilter", //must be loaded by container classpath
"org.eclipse.jetty.servlets.PushSessionCacheFilter" //must be loaded by container classpath
} ;
// Server classes are classes that are hidden from being
// loaded by the web application using system classloader,
// so if web application needs to load any of such classes,
// it has to include them in its distribution.
// TODO This centrally managed list of features that are exposed/hidden needs to be replaced
// with a more automatic distributed mechanism
// TODO should be white list rather than black list
public final static String[] __dftServerClasses =
{
"-org.eclipse.jetty.server.session.SessionData", //don't hide SessionData for de/serialization purposes
"-org.eclipse.jetty.jmx.", // don't hide jmx classes
"-org.eclipse.jetty.util.annotation.", // don't hide jmx annotation
"-org.eclipse.jetty.continuation.", // don't hide continuation classes
"-org.eclipse.jetty.jndi.", // don't hide naming classes
"-org.eclipse.jetty.jaas.", // don't hide jaas classes
"-org.eclipse.jetty.servlets.", // don't hide jetty servlets
"-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet
"-org.eclipse.jetty.servlet.NoJspServlet", // don't hide noJspServlet servlet
"-org.eclipse.jetty.jsp.", //don't hide jsp servlet
"-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners
"-org.eclipse.jetty.websocket.", // don't hide websocket classes from webapps (allow webapp to use ones from system classloader)
"-org.eclipse.jetty.apache.", // don't hide jetty apache impls
"-org.eclipse.jetty.util.log.", // don't hide server log
"-org.eclipse.jetty.alpn.", // don't hide ALPN
"org.objectweb.asm.", // hide asm used by jetty
"org.eclipse.jdt.", // hide jdt used by jetty
"org.eclipse.jetty." // hide other jetty classes
} ;
private final List _configurationClasses = new ArrayList<>();
private ClasspathPattern _systemClasses = null;
private ClasspathPattern _serverClasses = null;
private final List _configurations = new ArrayList<>();
private String _defaultsDescriptor=WEB_DEFAULTS_XML;
private String _descriptor=null;
private final List _overrideDescriptors = new ArrayList<>();
private boolean _distributable=false;
private boolean _extractWAR=true;
private boolean _copyDir=false;
private boolean _copyWebInf=false;
private boolean _logUrlOnStart =false;
private boolean _parentLoaderPriority= Boolean.getBoolean("org.eclipse.jetty.server.webapp.parentLoaderPriority");
private PermissionCollection _permissions;
private String[] _contextWhiteList = null;
private File _tmpDir;
private boolean _persistTmpDir = false;
private String _war;
private String _extraClasspath;
private Throwable _unavailableException;
private Map _resourceAliases;
private boolean _ownClassLoader=false;
private boolean _configurationDiscovered=false;
private boolean _allowDuplicateFragmentNames = false;
private boolean _throwUnavailableOnStartupException = false;
private MetaData _metadata=new MetaData();
public static WebAppContext getCurrentWebAppContext()
{
ContextHandler.Context context=ContextHandler.getCurrentContext();
if (context!=null)
{
ContextHandler handler = context.getContextHandler();
if (handler instanceof WebAppContext)
return (WebAppContext)handler;
}
return null;
}
/* ------------------------------------------------------------ */
public WebAppContext()
{
this(null,null,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
}
/* ------------------------------------------------------------ */
/**
* @param contextPath The context path
* @param webApp The URL or filename of the webapp directory or war file.
*/
public WebAppContext(String webApp,String contextPath)
{
this(null,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
setWar(webApp);
}
/* ------------------------------------------------------------ */
/**
* @param contextPath The context path
* @param webApp The URL or filename of the webapp directory or war file.
*/
public WebAppContext(Resource webApp, String contextPath)
{
this(null,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
setWarResource(webApp);
}
/* ------------------------------------------------------------ */
/**
* @param parent The parent HandlerContainer.
* @param contextPath The context path
* @param webApp The URL or filename of the webapp directory or war file.
*/
public WebAppContext(HandlerContainer parent, String webApp, String contextPath)
{
this(parent,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
setWar(webApp);
}
/* ------------------------------------------------------------ */
/**
* @param parent The parent HandlerContainer.
* @param contextPath The context path
* @param webApp The webapp directory or war file.
*/
public WebAppContext(HandlerContainer parent, Resource webApp, String contextPath)
{
this(parent,contextPath,null,null,null,new ErrorPageErrorHandler(),SESSIONS|SECURITY);
setWarResource(webApp);
}
/* ------------------------------------------------------------ */
/**
* This constructor is used in the geronimo integration.
*
* @param sessionHandler SessionHandler for this web app
* @param securityHandler SecurityHandler for this web app
* @param servletHandler ServletHandler for this web app
* @param errorHandler ErrorHandler for this web app
*/
public WebAppContext(SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler)
{
this(null, null, sessionHandler, securityHandler, servletHandler, errorHandler,0);
}
/* ------------------------------------------------------------ */
/**
* This constructor is used in the geronimo integration.
*
* @param parent the parent handler
* @param contextPath the context path
* @param sessionHandler SessionHandler for this web app
* @param securityHandler SecurityHandler for this web app
* @param servletHandler ServletHandler for this web app
* @param errorHandler ErrorHandler for this web app
* @param options the options ({@link ServletContextHandler#SESSIONS} and/or {@link ServletContextHandler#SECURITY})
*/
public WebAppContext(HandlerContainer parent, String contextPath, SessionHandler sessionHandler, SecurityHandler securityHandler, ServletHandler servletHandler, ErrorHandler errorHandler,int options)
{
super(parent, contextPath,sessionHandler, securityHandler, servletHandler, errorHandler,options);
_scontext = new Context();
setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler());
setProtectedTargets(__dftProtectedTargets);
}
/* ------------------------------------------------------------ */
/**
* @param servletContextName The servletContextName to set.
*/
@Override
public void setDisplayName(String servletContextName)
{
super.setDisplayName(servletContextName);
ClassLoader cl = getClassLoader();
if (cl!=null && cl instanceof WebAppClassLoader && servletContextName!=null)
((WebAppClassLoader)cl).setName(servletContextName);
}
/* ------------------------------------------------------------ */
/** Get an exception that caused the webapp to be unavailable
* @return A throwable if the webapp is unavailable or null
*/
public Throwable getUnavailableException()
{
return _unavailableException;
}
/* ------------------------------------------------------------ */
/**
* Set Resource Alias.
* Resource aliases map resource uri's within a context.
* They may optionally be used by a handler when looking for
* a resource.
* @param alias the alias for a resource
* @param uri the uri for the resource
*/
public void setResourceAlias(String alias, String uri)
{
if (_resourceAliases == null)
_resourceAliases= new HashMap(5);
_resourceAliases.put(alias, uri);
}
/* ------------------------------------------------------------ */
public Map getResourceAliases()
{
if (_resourceAliases == null)
return null;
return _resourceAliases;
}
/* ------------------------------------------------------------ */
public void setResourceAliases(Map map)
{
_resourceAliases = map;
}
/* ------------------------------------------------------------ */
public String getResourceAlias(String path)
{
if (_resourceAliases == null)
return null;
String alias = _resourceAliases.get(path);
int slash=path.length();
while (alias==null)
{
slash=path.lastIndexOf("/",slash-1);
if (slash<0)
break;
String match=_resourceAliases.get(path.substring(0,slash+1));
if (match!=null)
alias=match+path.substring(slash+1);
}
return alias;
}
/* ------------------------------------------------------------ */
public String removeResourceAlias(String alias)
{
if (_resourceAliases == null)
return null;
return _resourceAliases.remove(alias);
}
/* ------------------------------------------------------------ */
/* (non-Javadoc)
* @see org.eclipse.jetty.server.server.handler.ContextHandler#setClassLoader(java.lang.ClassLoader)
*/
@Override
public void setClassLoader(ClassLoader classLoader)
{
super.setClassLoader(classLoader);
String name = getDisplayName();
if (name==null)
name=getContextPath();
if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null)
((WebAppClassLoader)classLoader).setName(name);
}
/* ------------------------------------------------------------ */
@Override
public Resource getResource(String uriInContext) throws MalformedURLException
{
if (uriInContext==null || !uriInContext.startsWith(URIUtil.SLASH))
throw new MalformedURLException(uriInContext);
IOException ioe= null;
Resource resource= null;
int loop=0;
while (uriInContext!=null && loop++<100)
{
try
{
resource= super.getResource(uriInContext);
if (resource != null && resource.exists())
return resource;
uriInContext = getResourceAlias(uriInContext);
}
catch (IOException e)
{
LOG.ignore(e);
if (ioe==null)
ioe= e;
}
}
if (ioe != null && ioe instanceof MalformedURLException)
throw (MalformedURLException)ioe;
return resource;
}
/* ------------------------------------------------------------ */
/** Is the context Automatically configured.
*
* @return true if configuration discovery.
*/
public boolean isConfigurationDiscovered()
{
return _configurationDiscovered;
}
/* ------------------------------------------------------------ */
/** Set the configuration discovery mode.
* If configuration discovery is set to true, then the JSR315
* servlet 3.0 discovered configuration features are enabled.
* These are:
* - Web Fragments
* - META-INF/resource directories
*
* @param discovered true if configuration discovery is enabled for automatic configuration from the context
*/
public void setConfigurationDiscovered(boolean discovered)
{
_configurationDiscovered = discovered;
}
/* ------------------------------------------------------------ */
/**
* Pre configure the web application.
*
* The method is normally called from {@link #start()}. It performs
* the discovery of the configurations to be applied to this context,
* specifically:
*
* - Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}.
*
- Setup the default System classes by calling {@link #loadSystemClasses()}
*
- Setup the default Server classes by calling
loadServerClasses()
* - Instantiates a classload (if one is not already set)
*
- Calls the {@link Configuration#preConfigure(WebAppContext)} method of all
* Configuration instances.
*
* @throws Exception if unable to pre configure
*/
public void preConfigure() throws Exception
{
// Setup configurations
loadConfigurations();
// Setup system classes
loadSystemClasses();
// Setup server classes
loadServerClasses();
// Configure classloader
_ownClassLoader=false;
if (getClassLoader()==null)
{
WebAppClassLoader classLoader = new WebAppClassLoader(this);
setClassLoader(classLoader);
_ownClassLoader=true;
}
if (LOG.isDebugEnabled())
{
ClassLoader loader = getClassLoader();
LOG.debug("Thread Context classloader {}",loader);
loader=loader.getParent();
while(loader!=null)
{
LOG.debug("Parent class loader: {} ",loader);
loader=loader.getParent();
}
}
// Prepare for configuration
for (Configuration configuration : _configurations)
{
LOG.debug("preConfigure {} with {}",this,configuration);
configuration.preConfigure(this);
}
}
/* ------------------------------------------------------------ */
public void configure() throws Exception
{
// Configure webapp
for (Configuration configuration : _configurations)
{
LOG.debug("configure {} with {}",this,configuration);
configuration.configure(this);
}
}
/* ------------------------------------------------------------ */
public void postConfigure() throws Exception
{
// Clean up after configuration
for (Configuration configuration : _configurations)
{
LOG.debug("postConfigure {} with {}",this,configuration);
configuration.postConfigure(this);
}
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.thread.AbstractLifeCycle#doStart()
*/
@Override
protected void doStart() throws Exception
{
try
{
_metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames());
Boolean validate = (Boolean)getAttribute(MetaData.VALIDATE_XML);
_metadata.setValidateXml((validate!=null && validate.booleanValue()));
preConfigure();
super.doStart();
postConfigure();
if (isLogUrlOnStart())
dumpUrl();
}
catch (Throwable t)
{
// start up of the webapp context failed, make sure it is not started
LOG.warn("Failed startup of context "+this, t);
_unavailableException=t;
setAvailable(false); // webapp cannot be accessed (results in status code 503)
if (isThrowUnavailableOnStartupException())
throw t;
}
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.thread.AbstractLifeCycle#doStop()
*/
@Override
protected void doStop() throws Exception
{
super.doStop();
}
/* ------------------------------------------------------------ */
@Override
public void destroy()
{
// Prepare for configuration
MultiException mx=new MultiException();
if (_configurations!=null)
{
for (int i=_configurations.size();i-->0;)
{
try
{
_configurations.get(i).destroy(this);
}
catch(Exception e)
{
mx.add(e);
}
}
}
_configurations.clear();
super.destroy();
mx.ifExceptionThrowRuntime();
}
/* ------------------------------------------------------------ */
/*
* Dumps the current web app name and URL to the log
*/
private void dumpUrl()
{
Connector[] connectors = getServer().getConnectors();
for (int i=0;i getOverrideDescriptors()
{
return Collections.unmodifiableList(_overrideDescriptors);
}
/* ------------------------------------------------------------ */
/**
* @return Returns the permissions.
*/
@Override
public PermissionCollection getPermissions()
{
return _permissions;
}
/* ------------------------------------------------------------ */
/**
* @see #setServerClasses(String[])
* @return Returns the serverClasses.
*/
@ManagedAttribute(value="classes and packages hidden by the context classloader", readonly=true)
public String[] getServerClasses()
{
if (_serverClasses == null)
loadServerClasses();
return _serverClasses.getPatterns();
}
/* ------------------------------------------------------------ */
/**
* @return The ClasspathPattern used to match Server (hidden) classes
*/
public ClasspathPattern getServerClasspathPattern()
{
if (_serverClasses == null)
loadServerClasses();
return _serverClasses;
}
/* ------------------------------------------------------------ */
/**
* @deprecated Use {@link #getServerClasspathPattern()}.{@link ClasspathPattern#add(String)}
* @param classOrPackageOrLocation pattern (see {@link ClasspathPattern}
*/
@Deprecated
public void addServerClass(String classOrPackageOrLocation)
{
if (_serverClasses == null)
loadServerClasses();
_serverClasses.add(classOrPackageOrLocation);
}
/* ------------------------------------------------------------ */
/** Prepend to the list of Server classes.
* @param classOrPackage A pattern.
* @see #setServerClasses(String[])
* @see Jetty Documentation: Classloading
* @deprecated Use {@link #getServerClasspathPattern()}.{@link ClasspathPattern#add(String)}
*/
@Deprecated
public void prependServerClass(String classOrPackage)
{
if (_serverClasses == null)
loadServerClasses();
_serverClasses.add(classOrPackage);
}
/* ------------------------------------------------------------ */
/**
* @see #setSystemClasses(String[])
* @return Returns the systemClasses.
*/
@ManagedAttribute(value="classes and packages given priority by context classloader", readonly=true)
public String[] getSystemClasses()
{
if (_systemClasses == null)
loadSystemClasses();
return _systemClasses.getPatterns();
}
/* ------------------------------------------------------------ */
/**
* @return The ClasspathPattern used to match System (protected) classes
*/
public ClasspathPattern getSystemClasspathPattern()
{
if (_systemClasses == null)
loadSystemClasses();
return _systemClasses;
}
/* ------------------------------------------------------------ */
/**
*
* @param classOrPackage pattern
* @deprecated Use {@link #getSystemClasspathPattern()}.{@link ClasspathPattern#add(String)}
*/
@Deprecated
public void addSystemClass(String classOrPackage)
{
if (_systemClasses == null)
loadSystemClasses();
_systemClasses.add(classOrPackage);
}
/* ------------------------------------------------------------ */
/** Prepend to the list of System classes.
* @param classOrPackage A pattern.
* @see #setSystemClasses(String[])
* @see Jetty Documentation: Classloading
* @deprecated Use {@link #getSystemClasspathPattern()}.{@link ClasspathPattern#add(String)}
*/
@Deprecated
public void prependSystemClass(String classOrPackage)
{
if (_systemClasses == null)
loadSystemClasses();
_systemClasses.add(classOrPackage);
}
/* ------------------------------------------------------------ */
/**
* @param name class name
* @return true if matched by {@link #getServerClasspathPattern()}
* @deprecated Use {@link #getServerClasspathPattern()}.{@link ClasspathPattern#match(String)}
*/
@Deprecated
public boolean isServerClass(String name)
{
if (_serverClasses == null)
loadServerClasses();
return _serverClasses.match(name);
}
/* ------------------------------------------------------------ */
/**
* @param name class name
* @return true if matched by {@link #getServerClasspathPattern()}
* @deprecated Use {@link #getSystemClasspathPattern()}.{@link ClasspathPattern#match(String)}
*/
@Deprecated
public boolean isSystemClass(String name)
{
if (_systemClasses == null)
loadSystemClasses();
return _systemClasses.match(name);
}
/* ------------------------------------------------------------ */
@Override
public boolean isServerClass(Class> clazz)
{
if (_serverClasses == null)
loadServerClasses();
boolean result = _serverClasses.match(clazz);
if (LOG.isDebugEnabled())
LOG.debug("isServerClass=={} {}",result,clazz);
return result;
}
/* ------------------------------------------------------------ */
@Override
public boolean isSystemClass(Class> clazz)
{
if (_systemClasses == null)
loadSystemClasses();
boolean result = _systemClasses.match(clazz);
if (LOG.isDebugEnabled())
LOG.debug("isSystemClass=={} {}",result,clazz);
return result;
}
/* ------------------------------------------------------------ */
@Override
public boolean isServerResource(String name, URL url)
{
if (_serverClasses == null)
loadServerClasses();
boolean result = _serverClasses.match(name,url);
if (LOG.isDebugEnabled())
LOG.debug("isServerResource=={} {} {}",result,name,url);
return result;
}
/* ------------------------------------------------------------ */
@Override
public boolean isSystemResource(String name, URL url)
{
if (_systemClasses == null)
loadSystemClasses();
boolean result = _systemClasses.match(name,url);
if (LOG.isDebugEnabled())
LOG.debug("isSystemResource=={} {} {}",result,name,url);
return result;
}
/* ------------------------------------------------------------ */
protected void loadSystemClasses()
{
if (_systemClasses != null)
return;
//look for a Server attribute with the list of System classes
//to apply to every web application. If not present, use our defaults.
Server server = getServer();
if (server != null)
{
Object systemClasses = server.getAttribute(SERVER_SYS_CLASSES);
if (systemClasses instanceof String[])
_systemClasses = new ClasspathPattern((String[])systemClasses);
else if (systemClasses instanceof ClasspathPattern)
_systemClasses = new ClasspathPattern(((ClasspathPattern)systemClasses).getPatterns());
}
if (_systemClasses == null)
_systemClasses = new ClasspathPattern(__dftSystemClasses);
}
/* ------------------------------------------------------------ */
protected void loadServerClasses()
{
if (_serverClasses != null)
{
return;
}
// look for a Server attribute with the list of Server classes
// to apply to every web application. If not present, use our defaults.
Server server = getServer();
if (server != null)
{
Object serverClasses = server.getAttribute(SERVER_SRV_CLASSES);
if (serverClasses instanceof String[])
_serverClasses = new ClasspathPattern((String[])serverClasses);
else if (serverClasses instanceof ClasspathPattern)
_serverClasses = new ClasspathPattern(((ClasspathPattern)serverClasses).getPatterns());
}
if (_serverClasses == null)
{
_serverClasses = new ClasspathPattern(__dftServerClasses);
}
}
/* ------------------------------------------------------------ */
/**
* @return Returns the war as a file or URL string (Resource).
* The war may be different to the @link {@link #getResourceBase()}
* if the war has been expanded and/or copied.
*/
@ManagedAttribute(value="war file location", readonly=true)
public String getWar()
{
if (_war==null)
_war=getResourceBase();
return _war;
}
/* ------------------------------------------------------------ */
public Resource getWebInf() throws IOException
{
if (super.getBaseResource() == null)
return null;
// Iw there a WEB-INF directory?
Resource web_inf= super.getBaseResource().addPath("WEB-INF/");
if (!web_inf.exists() || !web_inf.isDirectory())
return null;
return web_inf;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the distributable.
*/
@ManagedAttribute("web application distributable")
public boolean isDistributable()
{
return _distributable;
}
/* ------------------------------------------------------------ */
/**
* @return Returns the extractWAR.
*/
@ManagedAttribute(value="extract war", readonly=true)
public boolean isExtractWAR()
{
return _extractWAR;
}
/* ------------------------------------------------------------ */
/**
* @return True if the webdir is copied (to allow hot replacement of jars on windows)
*/
@ManagedAttribute(value="webdir copied on deploy (allows hot replacement on windows)", readonly=true)
public boolean isCopyWebDir()
{
return _copyDir;
}
/* ------------------------------------------------------------ */
/**
* @return True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
*/
public boolean isCopyWebInf()
{
return _copyWebInf;
}
/* ------------------------------------------------------------ */
/**
* @return True if the classloader should delegate first to the parent
* classloader (standard java behaviour) or false if the classloader
* should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
* spec recommendation). Default is false or can be set by the system
* property org.eclipse.jetty.server.webapp.parentLoaderPriority
*/
@Override
@ManagedAttribute(value="parent classloader given priority", readonly=true)
public boolean isParentLoaderPriority()
{
return _parentLoaderPriority;
}
/* ------------------------------------------------------------ */
public static String[] getDefaultConfigurationClasses ()
{
return DEFAULT_CONFIGURATION_CLASSES;
}
/* ------------------------------------------------------------ */
public String[] getDefaultServerClasses ()
{
return __dftServerClasses;
}
/* ------------------------------------------------------------ */
public String[] getDefaultSystemClasses ()
{
return __dftSystemClasses;
}
/* ------------------------------------------------------------ */
protected void loadConfigurations()
throws Exception
{
//if the configuration instances have been set explicitly, use them
if (_configurations.size()>0)
return;
if (_configurationClasses.size()==0) {
_configurationClasses.addAll(Configuration.ClassList.serverDefault(getServer()));
}
for (String configClass : _configurationClasses)
_configurations.add((Configuration)Loader.loadClass(configClass).getDeclaredConstructor().newInstance());
}
/* ------------------------------------------------------------ */
@Override
public String toString()
{
if (_war!=null)
{
String war=_war;
if (war.indexOf("/webapps/")>=0)
war=war.substring(war.indexOf("/webapps/")+8);
return super.toString()+"{"+war+"}";
}
return super.toString();
}
/* ------------------------------------------------------------ */
@Override
public void dump(Appendable out, String indent) throws IOException
{
List system_classes=null;
if (_systemClasses!=null)
{
system_classes=new ArrayList<>(_systemClasses);
Collections.sort(system_classes);
}
List server_classes=null;
if (_serverClasses!=null)
{
server_classes=new ArrayList<>(_serverClasses);
Collections.sort(server_classes);
}
dumpBeans(out,indent,
Collections.singletonList(new ClassLoaderDump(getClassLoader())),
Collections.singletonList(new DumpableCollection("Systemclasses "+this,system_classes)),
Collections.singletonList(new DumpableCollection("Serverclasses "+this,server_classes)),
Collections.singletonList(new DumpableCollection("Configurations "+this,_configurations)),
Collections.singletonList(new DumpableCollection("Handler attributes "+this,((AttributesMap)getAttributes()).getAttributeEntrySet())),
Collections.singletonList(new DumpableCollection("Context attributes "+this,((Context)getServletContext()).getAttributeEntrySet())),
Collections.singletonList(new DumpableCollection("Initparams "+this,getInitParams().entrySet()))
);
}
/* ------------------------------------------------------------ */
/**
* @param configurations The configuration class names. If setConfigurations is not called
* these classes are used to create a configurations array.
*/
public void setConfigurationClasses(String[] configurations)
{
if (isStarted())
throw new IllegalStateException();
_configurationClasses.clear();
if (configurations!=null)
_configurationClasses.addAll(Arrays.asList(configurations));
_configurations.clear();
}
public void setConfigurationClasses(List configurations)
{
setConfigurationClasses(configurations.toArray(new String[configurations.size()]));
}
/* ------------------------------------------------------------ */
/**
* @param configurations The configurations to set.
*/
public void setConfigurations(Configuration[] configurations)
{
if (isStarted())
throw new IllegalStateException();
_configurations.clear();
if (configurations!=null)
_configurations.addAll(Arrays.asList(configurations));
}
/* ------------------------------------------------------------ */
/**
* The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
* @param defaultsDescriptor The defaultsDescriptor to set.
*/
public void setDefaultsDescriptor(String defaultsDescriptor)
{
_defaultsDescriptor = defaultsDescriptor;
}
/* ------------------------------------------------------------ */
/**
* The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
* @param overrideDescriptor The overrideDescritpor to set.
*/
public void setOverrideDescriptor(String overrideDescriptor)
{
_overrideDescriptors.clear();
_overrideDescriptors.add(overrideDescriptor);
}
/* ------------------------------------------------------------ */
/**
* The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
* @param overrideDescriptors The overrideDescriptors (file or URL) to set.
*/
public void setOverrideDescriptors(List overrideDescriptors)
{
_overrideDescriptors.clear();
_overrideDescriptors.addAll(overrideDescriptors);
}
/* ------------------------------------------------------------ */
/**
* The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
* @param overrideDescriptor The overrideDescriptor (file or URL) to add.
*/
public void addOverrideDescriptor(String overrideDescriptor)
{
_overrideDescriptors.add(overrideDescriptor);
}
/* ------------------------------------------------------------ */
/**
* @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
*/
@ManagedAttribute(value="standard web.xml descriptor", readonly=true)
public String getDescriptor()
{
return _descriptor;
}
/* ------------------------------------------------------------ */
/**
* @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
*/
public void setDescriptor(String descriptor)
{
_descriptor=descriptor;
}
/* ------------------------------------------------------------ */
/**
* @param distributable The distributable to set.
*/
public void setDistributable(boolean distributable)
{
this._distributable = distributable;
}
/* ------------------------------------------------------------ */
@Override
public void setEventListeners(EventListener[] eventListeners)
{
if (_sessionHandler!=null)
_sessionHandler.clearEventListeners();
super.setEventListeners(eventListeners);
}
@Override
public void removeEventListener(EventListener listener)
{
super.removeEventListener(listener);
if ((listener instanceof HttpSessionActivationListener)
|| (listener instanceof HttpSessionAttributeListener)
|| (listener instanceof HttpSessionBindingListener)
|| (listener instanceof HttpSessionListener)
|| (listener instanceof HttpSessionIdListener))
{
if (_sessionHandler!=null)
_sessionHandler.removeEventListener(listener);
}
}
/* ------------------------------------------------------------ */
/**
* @param extractWAR True if war files are extracted
*/
public void setExtractWAR(boolean extractWAR)
{
_extractWAR = extractWAR;
}
/* ------------------------------------------------------------ */
/**
* @param copy True if the webdir is copied (to allow hot replacement of jars)
*/
public void setCopyWebDir(boolean copy)
{
_copyDir = copy;
}
/* ------------------------------------------------------------ */
/**
* @param copyWebInf True if the web-inf lib and classes directories are copied (to allow hot replacement of jars on windows)
*/
public void setCopyWebInf(boolean copyWebInf)
{
_copyWebInf = copyWebInf;
}
/* ------------------------------------------------------------ */
/**
* @param java2compliant True if the classloader should delegate first to the parent
* classloader (standard java behaviour) or false if the classloader
* should first try to load from WEB-INF/lib or WEB-INF/classes (servlet
* spec recommendation). Default is false or can be set by the system
* property org.eclipse.jetty.server.webapp.parentLoaderPriority
*/
public void setParentLoaderPriority(boolean java2compliant)
{
_parentLoaderPriority = java2compliant;
}
/* ------------------------------------------------------------ */
/**
* @param permissions The permissions to set.
*/
public void setPermissions(PermissionCollection permissions)
{
_permissions = permissions;
}
/**
* Set the context white list
*
* In certain circumstances you want may want to deny access of one webapp from another
* when you may not fully trust the webapp. Setting this white list will enable a
* check when a servlet called {@link org.eclipse.jetty.servlet.ServletContextHandler.Context#getContext(String)}, validating that the uriInPath
* for the given webapp has been declaratively allows access to the context.
*
* @param contextWhiteList the whitelist of contexts for {@link org.eclipse.jetty.servlet.ServletContextHandler.Context#getContext(String)}
*/
public void setContextWhiteList(String[] contextWhiteList)
{
_contextWhiteList = contextWhiteList;
}
/* ------------------------------------------------------------ */
/**
* Set the server classes patterns.
*
* Server classes/packages are classes used to implement the server and are hidden
* from the context. If the context needs to load these classes, it must have its
* own copy of them in WEB-INF/lib or WEB-INF/classes.
* A {@link ClasspathPattern} is used to match the server classes.
* @param serverClasses The serverClasses to set.
*/
public void setServerClasses(String[] serverClasses)
{
_serverClasses = new ClasspathPattern(serverClasses);
}
/* ------------------------------------------------------------ */
/**
* Set the system classes patterns.
*
* System classes/packages are classes provided by the JVM and that
* cannot be replaced by classes of the same name from WEB-INF,
* regardless of the value of {@link #setParentLoaderPriority(boolean)}.
* A {@link ClasspathPattern} is used to match the system classes.
* @param systemClasses The systemClasses to set.
*/
public void setSystemClasses(String[] systemClasses)
{
_systemClasses = new ClasspathPattern(systemClasses);
}
/* ------------------------------------------------------------ */
/** Set temporary directory for context.
* The javax.servlet.context.tempdir attribute is also set.
* @param dir Writable temporary directory.
*/
public void setTempDirectory(File dir)
{
if (isStarted())
throw new IllegalStateException("Started");
if (dir!=null)
{
try{dir=new File(dir.getCanonicalPath());}
catch (IOException e){LOG.warn(Log.EXCEPTION,e);}
}
_tmpDir=dir;
setAttribute(TEMPDIR,_tmpDir);
}
/* ------------------------------------------------------------ */
@ManagedAttribute(value="temporary directory location", readonly=true)
public File getTempDirectory ()
{
return _tmpDir;
}
/**
* If true the temp directory for this
* webapp will be kept when the webapp stops. Otherwise,
* it will be deleted.
*
* @param persist true to persist the temp directory on shutdown / exit of the webapp
*/
public void setPersistTempDirectory(boolean persist)
{
_persistTmpDir = persist;
}
/**
* @return true if tmp directory will persist between startups of the webapp
*/
public boolean isPersistTempDirectory()
{
return _persistTmpDir;
}
/* ------------------------------------------------------------ */
/**
* Set the war of the webapp. From this value a {@link #setResourceBase(String)}
* value is computed by {@link WebInfConfiguration}, which may be changed from
* the war URI by unpacking and/or copying.
* @param war The war to set as a file name or URL.
*/
public void setWar(String war)
{
_war = war;
}
/* ------------------------------------------------------------ */
/**
* Set the war of the webapp as a {@link Resource}.
* @see #setWar(String)
* @param war The war to set as a Resource.
*/
public void setWarResource(Resource war)
{
setWar(war==null?null:war.toString());
}
/* ------------------------------------------------------------ */
/**
* @return Comma or semicolon separated path of filenames or URLs
* pointing to directories or jar files. Directories should end
* with '/'.
*/
@Override
@ManagedAttribute(value="extra classpath for context classloader", readonly=true)
public String getExtraClasspath()
{
return _extraClasspath;
}
/* ------------------------------------------------------------ */
/**
* @param extraClasspath Comma or semicolon separated path of filenames or URLs
* pointing to directories or jar files. Directories should end
* with '/'.
*/
public void setExtraClasspath(String extraClasspath)
{
_extraClasspath=extraClasspath;
}
/* ------------------------------------------------------------ */
public boolean isLogUrlOnStart()
{
return _logUrlOnStart;
}
/* ------------------------------------------------------------ */
/**
* Sets whether or not the web app name and URL is logged on startup
*
* @param logOnStart whether or not the log message is created
*/
public void setLogUrlOnStart(boolean logOnStart)
{
this._logUrlOnStart = logOnStart;
}
/* ------------------------------------------------------------ */
@Override
public void setServer(Server server)
{
super.setServer(server);
}
/* ------------------------------------------------------------ */
public boolean isAllowDuplicateFragmentNames()
{
return _allowDuplicateFragmentNames;
}
/* ------------------------------------------------------------ */
public void setAllowDuplicateFragmentNames(boolean allowDuplicateFragmentNames)
{
_allowDuplicateFragmentNames = allowDuplicateFragmentNames;
}
/* ------------------------------------------------------------ */
public void setThrowUnavailableOnStartupException (boolean throwIfStartupException) {
_throwUnavailableOnStartupException = throwIfStartupException;
}
/* ------------------------------------------------------------ */
public boolean isThrowUnavailableOnStartupException () {
return _throwUnavailableOnStartupException;
}
/* ------------------------------------------------------------ */
@Override
protected void startContext()
throws Exception
{
configure();
//resolve the metadata
_metadata.resolve(this);
startWebapp();
}
/* ------------------------------------------------------------ */
@Override
protected void stopContext() throws Exception
{
stopWebapp();
try
{
for (int i=_configurations.size();i-->0;)
_configurations.get(i).deconfigure(this);
if (_metadata != null)
_metadata.clear();
_metadata=new MetaData();
}
finally
{
if (_ownClassLoader)
{
ClassLoader loader = getClassLoader();
if (loader != null && loader instanceof URLClassLoader)
((URLClassLoader)loader).close();
setClassLoader(null);
}
setAvailable(true);
_unavailableException=null;
}
}
/* ------------------------------------------------------------ */
protected void startWebapp()
throws Exception
{
super.startContext();
}
/* ------------------------------------------------------------ */
protected void stopWebapp() throws Exception
{
super.stopContext();
}
/* ------------------------------------------------------------ */
@Override
public Set setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement)
{
Set unchangedURLMappings = new HashSet();
//From javadoc for ServletSecurityElement:
/*
If a URL pattern of this ServletRegistration is an exact target of a security-constraint that
was established via the portable deployment descriptor, then this method does not change the
security-constraint for that pattern, and the pattern will be included in the return value.
If a URL pattern of this ServletRegistration is an exact target of a security constraint
that was established via the ServletSecurity annotation or a previous call to this method,
then this method replaces the security constraint for that pattern.
If a URL pattern of this ServletRegistration is neither the exact target of a security constraint
that was established via the ServletSecurity annotation or a previous call to this method,
nor the exact target of a security-constraint in the portable deployment descriptor, then
this method establishes the security constraint for that pattern from the argument ServletSecurityElement.
*/
Collection pathMappings = registration.getMappings();
if (pathMappings != null)
{
ConstraintSecurityHandler.createConstraint(registration.getName(), servletSecurityElement);
for (String pathSpec:pathMappings)
{
Origin origin = getMetaData().getOrigin("constraint.url."+pathSpec);
switch (origin)
{
case NotSet:
{
//No mapping for this url already established
List mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
for (ConstraintMapping m:mappings)
((ConstraintAware)getSecurityHandler()).addConstraintMapping(m);
((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
getMetaData().setOriginAPI("constraint.url."+pathSpec);
break;
}
case WebXml:
case WebDefaults:
case WebOverride:
case WebFragment:
{
//a mapping for this url was created in a descriptor, which overrides everything
unchangedURLMappings.add(pathSpec);
break;
}
case Annotation:
case API:
{
//mapping established via an annotation or by previous call to this method,
//replace the security constraint for this pattern
List constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings());
List freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement);
constraintMappings.addAll(freshMappings);
((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings);
((ConstraintAware)getSecurityHandler()).checkPathsWithUncoveredHttpMethods();
break;
}
}
}
}
return unchangedURLMappings;
}
/* ------------------------------------------------------------ */
public class Context extends ServletContextHandler.Context
{
/* ------------------------------------------------------------ */
@Override
public void checkListener(Class extends EventListener> listener) throws IllegalStateException
{
try
{
super.checkListener(listener);
}
catch (IllegalArgumentException e)
{
//not one of the standard servlet listeners, check our extended session listener types
boolean ok = false;
for (Class> l:SessionHandler.SESSION_LISTENER_TYPES)
{
if (l.isAssignableFrom(listener))
{
ok = true;
break;
}
}
if (!ok)
throw new IllegalArgumentException("Inappropriate listener type "+listener.getName());
}
}
/* ------------------------------------------------------------ */
@Override
public URL getResource(String path) throws MalformedURLException
{
Resource resource=WebAppContext.this.getResource(path);
if (resource==null || !resource.exists())
return null;
// Should we go to the original war?
if (resource.isDirectory() && resource instanceof ResourceCollection && !WebAppContext.this.isExtractWAR())
{
Resource[] resources = ((ResourceCollection)resource).getResources();
for (int i=resources.length;i-->0;)
{
if (resources[i].getName().startsWith("jar:file"))
return resources[i].getURI().toURL();
}
}
return resource.getURI().toURL();
}
/* ------------------------------------------------------------ */
@Override
public ServletContext getContext(String uripath)
{
ServletContext servletContext = super.getContext(uripath);
if ( servletContext != null && _contextWhiteList != null )
{
for ( String context : _contextWhiteList )
{
if ( context.equals(uripath) )
{
return servletContext;
}
}
return null;
}
else
{
return servletContext;
}
}
}
/* ------------------------------------------------------------ */
public MetaData getMetaData()
{
return _metadata;
}
/* ------------------------------------------------------------ */
public static void addServerClasses(Server server,String... pattern )
{
if (pattern == null || pattern.length == 0)
return;
// look for a Server attribute with the list of Server classes
// to apply to every web application. If not present, use our defaults.
Object o = server.getAttribute(SERVER_SRV_CLASSES);
if (o instanceof ClasspathPattern)
{
((ClasspathPattern)o).add(pattern);
return;
}
String[] server_classes;
if (o instanceof String[])
server_classes = (String[])o;
else
server_classes = __dftServerClasses;
int l = server_classes.length;
server_classes = Arrays.copyOf(server_classes,l+pattern.length);
System.arraycopy(pattern,0,server_classes,l,pattern.length);
server.setAttribute(SERVER_SRV_CLASSES,server_classes);
}
/* ------------------------------------------------------------ */
public static void addSystemClasses(Server server,String... pattern )
{
if (pattern == null || pattern.length == 0)
return;
// look for a Server attribute with the list of System classes
// to apply to every web application. If not present, use our defaults.
Object o = server.getAttribute(SERVER_SYS_CLASSES);
if (o instanceof ClasspathPattern)
{
((ClasspathPattern)o).add(pattern);
return;
}
String[] system_classes;
if (o instanceof String[])
system_classes = (String[])o;
else
system_classes = __dftSystemClasses;
int l = system_classes.length;
system_classes = Arrays.copyOf(system_classes,l+pattern.length);
System.arraycopy(pattern,0,system_classes,l,pattern.length);
server.setAttribute(SERVER_SYS_CLASSES,system_classes);
}
}