| // |
| // ======================================================================== |
| // Copyright (c) 1995-2014 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.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.HttpMethodConstraintElement; |
| import javax.servlet.ServletContext; |
| import javax.servlet.ServletRegistration.Dynamic; |
| import javax.servlet.ServletSecurityElement; |
| import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic; |
| import javax.servlet.annotation.ServletSecurity.TransportGuarantee; |
| import javax.servlet.http.HttpSessionActivationListener; |
| import javax.servlet.http.HttpSessionAttributeListener; |
| import javax.servlet.http.HttpSessionBindingListener; |
| 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.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.LazyList; |
| import org.eclipse.jetty.util.Loader; |
| import org.eclipse.jetty.util.MultiException; |
| import org.eclipse.jetty.util.StringUtil; |
| import org.eclipse.jetty.util.URIUtil; |
| 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; |
| import org.eclipse.jetty.util.security.Constraint; |
| |
| /* ------------------------------------------------------------ */ |
| /** 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}. |
| * |
| * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base" |
| * |
| */ |
| public class WebAppContext extends ServletContextHandler implements WebAppClassLoader.Context |
| { |
| private 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_CONFIG = "org.eclipse.jetty.webapp.configuration"; |
| 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"}; |
| |
| private static String[] __dftConfigurationClasses = |
| { |
| "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"//, |
| //"org.eclipse.jetty.webapp.TagLibConfiguration" |
| } ; |
| |
| // System classes are classes that cannot be replaced by |
| // the web application, and they are *always* loaded via |
| // system classloader. |
| 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.apache.commons.logging.", // TODO: review if special case still needed |
| "org.eclipse.jetty.continuation.", // webapp cannot change continuation classes |
| "org.eclipse.jetty.jndi.", // webapp cannot change naming classes |
| "org.eclipse.jetty.plus.jaas.", // webapp cannot change jaas classes |
| "org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension |
| "org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension |
| "org.eclipse.jetty.websocket.WebSocketServlet", // webapp cannot change WebSocketServlet |
| "org.eclipse.jetty.servlet.DefaultServlet" // webapp cannot change default servlets |
| } ; |
| |
| // 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. |
| public final static String[] __dftServerClasses = |
| { |
| "-org.eclipse.jetty.continuation.", // don't hide continuation classes |
| "-org.eclipse.jetty.jndi.", // don't hide naming classes |
| "-org.eclipse.jetty.plus.jaas.", // don't hide jaas classes |
| "-org.eclipse.jetty.websocket.WebSocket", // WebSocket is a jetty extension |
| "-org.eclipse.jetty.websocket.WebSocketFactory", // WebSocket is a jetty extension |
| "-org.eclipse.jetty.websocket.WebSocketServlet", // don't hide WebSocketServlet |
| "-org.eclipse.jetty.servlet.DefaultServlet", // don't hide default servlet |
| "-org.eclipse.jetty.servlet.listener.", // don't hide useful listeners |
| "org.eclipse.jetty." // hide other jetty classes |
| } ; |
| |
| private String[] _configurationClasses = __dftConfigurationClasses; |
| private ClasspathPattern _systemClasses = null; |
| private ClasspathPattern _serverClasses = null; |
| |
| private Configuration[] _configurations; |
| private String _defaultsDescriptor=WEB_DEFAULTS_XML; |
| private String _descriptor=null; |
| private final List<String> _overrideDescriptors = new ArrayList<String>(); |
| private boolean _distributable=false; |
| private boolean _extractWAR=true; |
| private boolean _copyDir=false; |
| private boolean _copyWebInf=false; // TODO change to true? |
| 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 String _war; |
| private String _extraClasspath; |
| private Throwable _unavailableException; |
| |
| private Map<String, String> _resourceAliases; |
| private boolean _ownClassLoader=false; |
| private boolean _configurationDiscovered=true; |
| private boolean _configurationClassesSet=false; |
| private boolean _configurationsSet=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() |
| { |
| super(SESSIONS|SECURITY); |
| _scontext=new Context(); |
| setErrorHandler(new ErrorPageErrorHandler()); |
| setProtectedTargets(__dftProtectedTargets); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param contextPath The context path |
| * @param webApp The URL or filename of the webapp directory or war file. |
| */ |
| public WebAppContext(String webApp,String contextPath) |
| { |
| super(null,contextPath,SESSIONS|SECURITY); |
| _scontext=new Context(); |
| setContextPath(contextPath); |
| setWar(webApp); |
| setErrorHandler(new ErrorPageErrorHandler()); |
| setProtectedTargets(__dftProtectedTargets); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @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) |
| { |
| super(parent,contextPath,SESSIONS|SECURITY); |
| _scontext=new Context(); |
| setWar(webApp); |
| setErrorHandler(new ErrorPageErrorHandler()); |
| setProtectedTargets(__dftProtectedTargets); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| |
| /** |
| * 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) { |
| super(null, sessionHandler, securityHandler, servletHandler, errorHandler); |
| _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 |
| * @param uri |
| */ |
| public void setResourceAlias(String alias, String uri) |
| { |
| if (_resourceAliases == null) |
| _resourceAliases= new HashMap<String, String>(5); |
| _resourceAliases.put(alias, uri); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public Map<String, String> getResourceAliases() |
| { |
| if (_resourceAliases == null) |
| return null; |
| return _resourceAliases; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void setResourceAliases(Map<String, String> 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); |
| |
| // if ( !(classLoader instanceof WebAppClassLoader) ) |
| // { |
| // 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() ); |
| // } |
| |
| if (classLoader!=null && classLoader instanceof WebAppClassLoader && getDisplayName()!=null) |
| ((WebAppClassLoader)classLoader).setName(getDisplayName()); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @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:<ul> |
| * <li>Web Fragments</li> |
| * <li>META-INF/resource directories</li> |
| * </ul> |
| * @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. |
| * <p> |
| * The method is normally called from {@link #start()}. It performs |
| * the discovery of the configurations to be applied to this context, |
| * specifically:<ul> |
| * <li>Instantiate the {@link Configuration} instances with a call to {@link #loadConfigurations()}. |
| * <li>Setup the default System classes by calling {@link #loadSystemClasses()} |
| * <li>Setup the default Server classes by calling <code>loadServerClasses()</code> |
| * <li>Instantiates a classload (if one is not already set) |
| * <li>Calls the {@link Configuration#preConfigure(WebAppContext)} method of all |
| * Configuration instances. |
| * </ul> |
| * @throws Exception |
| */ |
| 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 (int i=0;i<_configurations.length;i++) |
| { |
| LOG.debug("preConfigure {} with {}",this,_configurations[i]); |
| _configurations[i].preConfigure(this); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void configure() throws Exception |
| { |
| // Configure webapp |
| for (int i=0;i<_configurations.length;i++) |
| { |
| LOG.debug("configure {} with {}",this,_configurations[i]); |
| _configurations[i].configure(this); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void postConfigure() throws Exception |
| { |
| // Clean up after configuration |
| for (int i=0;i<_configurations.length;i++) |
| { |
| LOG.debug("postConfigure {} with {}",this,_configurations[i]); |
| _configurations[i].postConfigure(this); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /* |
| * @see org.eclipse.thread.AbstractLifeCycle#doStart() |
| */ |
| @Override |
| protected void doStart() throws Exception |
| { |
| try |
| { |
| _metadata.setAllowDuplicateFragmentNames(isAllowDuplicateFragmentNames()); |
| preConfigure(); |
| super.doStart(); |
| postConfigure(); |
| |
| if (isLogUrlOnStart()) |
| dumpUrl(); |
| } |
| catch (Exception e) |
| { |
| //start up of the webapp context failed, make sure it is not started |
| LOG.warn("Failed startup of context "+this, e); |
| _unavailableException=e; |
| setAvailable(false); |
| if (isThrowUnavailableOnStartupException()) |
| throw e; |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /* |
| * @see org.eclipse.thread.AbstractLifeCycle#doStop() |
| */ |
| @Override |
| protected void doStop() throws Exception |
| { |
| super.doStop(); |
| |
| try |
| { |
| for (int i=_configurations.length;i-->0;) |
| _configurations[i].deconfigure(this); |
| |
| if (_metadata != null) |
| _metadata.clear(); |
| _metadata=new MetaData(); |
| |
| } |
| finally |
| { |
| if (_ownClassLoader) |
| setClassLoader(null); |
| |
| setAvailable(true); |
| _unavailableException=null; |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public void destroy() |
| { |
| // Prepare for configuration |
| MultiException mx=new MultiException(); |
| if (_configurations!=null) |
| { |
| for (int i=_configurations.length;i-->0;) |
| { |
| try |
| { |
| _configurations[i].destroy(this); |
| } |
| catch(Exception e) |
| { |
| mx.add(e); |
| } |
| } |
| } |
| _configurations=null; |
| 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<connectors.length;i++) |
| { |
| String connectorName = connectors[i].getName(); |
| String displayName = getDisplayName(); |
| if (displayName == null) |
| displayName = "WebApp@"+connectors.hashCode(); |
| |
| LOG.info(displayName + " at http://" + connectorName + getContextPath()); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the configurations. |
| */ |
| public String[] getConfigurationClasses() |
| { |
| return _configurationClasses; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the configurations. |
| */ |
| public Configuration[] getConfigurations() |
| { |
| return _configurations; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml |
| * @return Returns the defaultsDescriptor. |
| */ |
| public String getDefaultsDescriptor() |
| { |
| return _defaultsDescriptor; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml |
| * @return Returns the Override Descriptor. |
| * @deprecated use {@link #getOverrideDescriptors()} |
| */ |
| @Deprecated |
| public String getOverrideDescriptor() |
| { |
| if (_overrideDescriptors.size()!=1) |
| return null; |
| return _overrideDescriptors.get(0); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * An override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml |
| * @return Returns the Override Descriptor list |
| */ |
| public List<String> getOverrideDescriptors() |
| { |
| return Collections.unmodifiableList(_overrideDescriptors); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the permissions. |
| */ |
| public PermissionCollection getPermissions() |
| { |
| return _permissions; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see #setServerClasses(String[]) |
| * @return Returns the serverClasses. |
| */ |
| public String[] getServerClasses() |
| { |
| if (_serverClasses == null) |
| loadServerClasses(); |
| |
| return _serverClasses.getPatterns(); |
| } |
| |
| public void addServerClass(String classname) |
| { |
| if (_serverClasses == null) |
| loadServerClasses(); |
| |
| _serverClasses.addPattern(classname); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @see #setSystemClasses(String[]) |
| * @return Returns the systemClasses. |
| */ |
| public String[] getSystemClasses() |
| { |
| if (_systemClasses == null) |
| loadSystemClasses(); |
| |
| return _systemClasses.getPatterns(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public void addSystemClass(String classname) |
| { |
| if (_systemClasses == null) |
| loadSystemClasses(); |
| |
| _systemClasses.addPattern(classname); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public boolean isServerClass(String name) |
| { |
| if (_serverClasses == null) |
| loadServerClasses(); |
| |
| return _serverClasses.match(name); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public boolean isSystemClass(String name) |
| { |
| if (_systemClasses == null) |
| loadSystemClasses(); |
| |
| return _systemClasses.match(name); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| 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 != null && systemClasses instanceof String[]) |
| _systemClasses = new ClasspathPattern((String[])systemClasses); |
| } |
| |
| if (_systemClasses == null) |
| _systemClasses = new ClasspathPattern(__dftSystemClasses); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| private 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 != null && serverClasses instanceof String[]) |
| { |
| _serverClasses = new ClasspathPattern((String[])serverClasses); |
| } |
| } |
| |
| if (_serverClasses == null) |
| { |
| _serverClasses = new ClasspathPattern(__dftServerClasses); |
| } |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the war as a file or URL string (Resource) |
| */ |
| 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. |
| */ |
| public boolean isDistributable() |
| { |
| return _distributable; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Returns the extractWAR. |
| */ |
| public boolean isExtractWAR() |
| { |
| return _extractWAR; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return True if the webdir is copied (to allow hot replacement of jars on windows) |
| */ |
| 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). |
| */ |
| public boolean isParentLoaderPriority() |
| { |
| return _parentLoaderPriority; |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| public String[] getDefaultConfigurationClasses () |
| { |
| return __dftConfigurationClasses; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| 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!=null) |
| return; |
| |
| //if the configuration classnames have been set explicitly use them |
| if (!_configurationClassesSet) |
| _configurationClasses=__dftConfigurationClasses; |
| |
| _configurations = new Configuration[_configurationClasses.length]; |
| for (int i = 0; i < _configurationClasses.length; i++) |
| { |
| _configurations[i]=(Configuration)Loader.loadClass(this.getClass(), _configurationClasses[i]).newInstance(); |
| } |
| } |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public String toString() |
| { |
| return super.toString()+(_war==null?"":(","+_war)); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @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 (isRunning()) |
| throw new IllegalStateException(); |
| _configurationClasses = configurations==null?null:(String[])configurations.clone(); |
| _configurationClassesSet = true; |
| _configurations=null; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param configurations The configurations to set. |
| */ |
| public void setConfigurations(Configuration[] configurations) |
| { |
| if (isRunning()) |
| throw new IllegalStateException(); |
| _configurations = configurations==null?null:(Configuration[])configurations.clone(); |
| _configurationsSet = true; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * 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. |
| * @deprecated use {@link #setOverrideDescriptors(List)} |
| */ |
| @Deprecated |
| 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<String> 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. |
| */ |
| 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); |
| |
| for (int i=0; eventListeners!=null && i<eventListeners.length;i ++) |
| { |
| EventListener listener = eventListeners[i]; |
| |
| if ((listener instanceof HttpSessionActivationListener) |
| || (listener instanceof HttpSessionAttributeListener) |
| || (listener instanceof HttpSessionBindingListener) |
| || (listener instanceof HttpSessionListener)) |
| { |
| if (_sessionHandler!=null) |
| _sessionHandler.addEventListener(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 The java2compliant to set. |
| */ |
| 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 getContext(String), validating that the uriInPath |
| * for the given webapp has been declaratively allows access to the context. |
| * @param contextWhiteList |
| */ |
| public void setContextWhiteList(String[] contextWhiteList) |
| { |
| _contextWhiteList = contextWhiteList; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set the server classes patterns. |
| * <p> |
| * 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 class pattern is a string of one of the forms:<dl> |
| * <dt>org.package.Classname</dt><dd>Match a specific class</dd> |
| * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd> |
| * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd> |
| * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd> |
| * </dl> |
| * @param serverClasses The serverClasses to set. |
| */ |
| public void setServerClasses(String[] serverClasses) |
| { |
| _serverClasses = new ClasspathPattern(serverClasses); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * Set the system classes patterns. |
| * <p> |
| * 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 class pattern is a string of one of the forms:<dl> |
| * <dt>org.package.Classname</dt><dd>Match a specific class</dd> |
| * <dt>org.package.</dt><dd>Match a specific package hierarchy</dd> |
| * <dt>-org.package.Classname</dt><dd>Exclude a specific class</dd> |
| * <dt>-org.package.</dt><dd>Exclude a specific package hierarchy</dd> |
| * </dl> |
| * @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);} |
| } |
| |
| if (dir!=null && !dir.exists()) |
| { |
| dir.mkdir(); |
| dir.deleteOnExit(); |
| } |
| |
| if (dir!=null && ( !dir.exists() || !dir.isDirectory() || !dir.canWrite())) |
| throw new IllegalArgumentException("Bad temp directory: "+dir); |
| |
| try |
| { |
| if (dir!=null) |
| dir=dir.getCanonicalFile(); |
| } |
| catch(Exception e) |
| { |
| LOG.warn(e); |
| } |
| _tmpDir=dir; |
| setAttribute(TEMPDIR,_tmpDir); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| public File getTempDirectory () |
| { |
| return _tmpDir; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @param war The war to set as a file name or URL |
| */ |
| public void setWar(String war) |
| { |
| _war = war; |
| } |
| |
| /* ------------------------------------------------------------ */ |
| /** |
| * @return Comma or semicolon separated path of filenames or URLs |
| * pointing to directories or jar files. Directories should end |
| * with '/'. |
| */ |
| 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); |
| //if we haven't been given a set of configuration instances to |
| //use, and we haven't been given a set of configuration classes |
| //to use, use the configuration classes that came from the |
| //Server (if there are any) |
| if (!_configurationsSet && !_configurationClassesSet && server != null) |
| { |
| String[] serverConfigs = (String[])server.getAttribute(SERVER_CONFIG); |
| if (serverConfigs != null) |
| setConfigurationClasses(serverConfigs); |
| } |
| } |
| |
| |
| /* ------------------------------------------------------------ */ |
| 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); |
| |
| super.startContext(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @Override |
| public Set<String> setServletSecurity(Dynamic registration, ServletSecurityElement servletSecurityElement) |
| { |
| |
| Set<String> unchangedURLMappings = new HashSet<String>(); |
| //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<String> pathMappings = registration.getMappings(); |
| if (pathMappings != null) |
| { |
| Constraint constraint = 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<ConstraintMapping> mappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement); |
| for (ConstraintMapping m:mappings) |
| ((ConstraintAware)getSecurityHandler()).addConstraintMapping(m); |
| getMetaData().setOrigin("constraint.url."+pathSpec, Origin.API); |
| 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<ConstraintMapping> constraintMappings = ConstraintSecurityHandler.removeConstraintMappingsForPath(pathSpec, ((ConstraintAware)getSecurityHandler()).getConstraintMappings()); |
| |
| List<ConstraintMapping> freshMappings = ConstraintSecurityHandler.createConstraintsWithMappingsForPath(registration.getName(), pathSpec, servletSecurityElement); |
| constraintMappings.addAll(freshMappings); |
| |
| ((ConstraintSecurityHandler)getSecurityHandler()).setConstraintMappings(constraintMappings); |
| break; |
| } |
| } |
| } |
| } |
| |
| return unchangedURLMappings; |
| } |
| |
| |
| |
| /* ------------------------------------------------------------ */ |
| public class Context extends ServletContextHandler.Context |
| { |
| /* ------------------------------------------------------------ */ |
| @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].getURL(); |
| } |
| } |
| |
| return resource.getURL(); |
| } |
| |
| /* ------------------------------------------------------------ */ |
| @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; |
| } |
| |
| } |