Java tutorial
//======================================================================== //$Id: WebAppContext.java,v 1.5 2005/11/16 22:02:45 gregwilkins Exp $ //Copyright 2004-2006 Mort Bay Consulting Pty. Ltd. //------------------------------------------------------------------------ //Licensed under the Apache License, Version 2.0 (the "License"); //you may not use this file except in compliance with the License. //You may obtain a copy of the License at //http://www.apache.org/licenses/LICENSE-2.0 //Unless required by applicable law or agreed to in writing, software //distributed under the License is distributed on an "AS IS" BASIS, //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. //See the License for the specific language governing permissions and //limitations under the License. //======================================================================== package org.mortbay.jetty.webapp; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.security.PermissionCollection; import java.util.EventListener; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionListener; import org.mortbay.jetty.Connector; import org.mortbay.jetty.HandlerContainer; import org.mortbay.jetty.Server; import org.mortbay.jetty.deployer.ContextDeployer; import org.mortbay.jetty.deployer.WebAppDeployer; import org.mortbay.jetty.handler.ContextHandler; import org.mortbay.jetty.handler.ContextHandlerCollection; import org.mortbay.jetty.handler.ErrorHandler; import org.mortbay.jetty.handler.HandlerCollection; import org.mortbay.jetty.security.SecurityHandler; import org.mortbay.jetty.servlet.Context; import org.mortbay.jetty.servlet.ErrorPageErrorHandler; import org.mortbay.jetty.servlet.ServletHandler; import org.mortbay.jetty.servlet.SessionHandler; import org.mortbay.log.Log; import org.mortbay.resource.JarResource; import org.mortbay.resource.Resource; import org.mortbay.util.IO; import org.mortbay.util.LazyList; import org.mortbay.util.Loader; import org.mortbay.util.StringUtil; import org.mortbay.util.URIUtil; import org.mortbay.util.UrlEncoded; /* ------------------------------------------------------------ */ /** Web Application Context Handler. * The WebAppContext handler is an extension of ContextHandler that * coordinates the construction and configuration of nested handlers: * {@link org.mortbay.jetty.security.SecurityHandler}, {@link org.mortbay.jetty.servlet.SessionHandler} * and {@link org.mortbay.jetty.servlet.ServletHandler}. * The handlers are configured by pluggable configuration classes, with * the default being {@link org.mortbay.jetty.webapp.WebXmlConfiguration} and * {@link org.mortbay.jetty.webapp.JettyWebXmlConfiguration}. * * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base" * * @author gregw * */ public class WebAppContext extends Context { public final static String WEB_DEFAULTS_XML = "org/mortbay/jetty/webapp/webdefault.xml"; public final static String ERROR_PAGE = "org.mortbay.jetty.error_page"; private static String[] __dftConfigurationClasses = { "org.mortbay.jetty.webapp.WebInfConfiguration", "org.mortbay.jetty.webapp.WebXmlConfiguration", "org.mortbay.jetty.webapp.JettyWebXmlConfiguration", "org.mortbay.jetty.webapp.TagLibConfiguration" }; private String[] _configurationClasses = __dftConfigurationClasses; private Configuration[] _configurations; private String _defaultsDescriptor = WEB_DEFAULTS_XML; private String _descriptor = null; private String _overrideDescriptor = null; private boolean _distributable = false; private boolean _extractWAR = true; private boolean _copyDir = false; private boolean _logUrlOnStart = false; private boolean _parentLoaderPriority = Boolean.getBoolean("org.mortbay.jetty.webapp.parentLoaderPriority"); private PermissionCollection _permissions; private String[] _systemClasses = { "java.", "javax.servlet.", "javax.xml.", "org.mortbay.", "org.xml.", "org.w3c.", "org.apache.commons.logging.", "org.apache.log4j." }; private String[] _serverClasses = { "-org.mortbay.jetty.plus.jaas.", "org.mortbay.jetty.", "org.slf4j." }; // TODO hide all mortbay classes private File _tmpDir; private boolean _isExistingTmpDir; private String _war; private String _extraClasspath; private Throwable _unavailableException; private transient Map _resourceAliases; private transient boolean _ownClassLoader = false; private transient boolean _unavailable; public static ContextHandler getCurrentWebAppContext() { ContextHandler.SContext context = ContextHandler.getCurrentContext(); if (context != null) { ContextHandler handler = context.getContextHandler(); if (handler instanceof WebAppContext) return (ContextHandler) handler; } return null; } /* ------------------------------------------------------------ */ /** Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. * @param server Must not be <code>null</code> * @param webapps Directory file name or URL to look for auto * webapplication. * @param defaults The defaults xml filename or URL which is * loaded before any in the web app. Must respect the web.dtd. * If null the default defaults file is used. If the empty string, then * no defaults file is used. * @param extract If true, extract war files * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications * @exception IOException * @deprecated use {@link org.mortbay.jetty.deployer.WebAppDeployer} or {@link org.mortbay.jetty.deployer.ContextDeployer} */ public static void addWebApplications(Server server, String webapps, String defaults, boolean extract, boolean java2CompliantClassLoader) throws IOException { addWebApplications(server, webapps, defaults, __dftConfigurationClasses, extract, java2CompliantClassLoader); } /* ------------------------------------------------------------ */ /** Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp matches the rootWebApp it is added as the "/" context. * @param server Must not be <code>null</code>. * @param webapps Directory file name or URL to look for auto * webapplication. * @param defaults The defaults xml filename or URL which is * loaded before any in the web app. Must respect the web.dtd. * If null the default defaults file is used. If the empty string, then * no defaults file is used. * @param configurations Array of classnames of {@link Configuration} implementations to apply. * @param extract If true, extract war files * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications * @exception IOException * @throws IllegalAccessException * @throws InstantiationException * @deprecated use {@link org.mortbay.jetty.deployer.WebAppDeployer} or {@link org.mortbay.jetty.deployer.ContextDeployer} */ public static void addWebApplications(Server server, String webapps, String defaults, String[] configurations, boolean extract, boolean java2CompliantClassLoader) throws IOException { HandlerCollection contexts = (HandlerCollection) server .getChildHandlerByClass(ContextHandlerCollection.class); if (contexts == null) contexts = (HandlerCollection) server.getChildHandlerByClass(HandlerCollection.class); addWebApplications(contexts, webapps, defaults, configurations, extract, java2CompliantClassLoader); } /* ------------------------------------------------------------ */ /** Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp is called "root" it is added as the "/" context. * @param contexts A HandlerContainer to which the contexts will be added * @param webapps Directory file name or URL to look for auto * webapplication. * @param defaults The defaults xml filename or URL which is * loaded before any in the web app. Must respect the web.dtd. * If null the default defaults file is used. If the empty string, then * no defaults file is used. * @param configurations Array of classnames of {@link Configuration} implementations to apply. * @param extract If true, extract war files * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications * @exception IOException * @throws IllegalAccessException * @throws InstantiationException * @deprecated use {@link WebAppDeployer} or {@link ContextDeployer} */ public static void addWebApplications(HandlerContainer contexts, String webapps, String defaults, boolean extract, boolean java2CompliantClassLoader) throws IOException { addWebApplications(contexts, webapps, defaults, __dftConfigurationClasses, extract, java2CompliantClassLoader); } /* ------------------------------------------------------------ */ /** Add Web Applications. * Add auto webapplications to the server. The name of the * webapp directory or war is used as the context name. If the * webapp is called "root" it is added as the "/" context. * @param contexts A HandlerContainer to which the contexts will be added * @param webapps Directory file name or URL to look for auto * webapplication. * @param defaults The defaults xml filename or URL which is * loaded before any in the web app. Must respect the web.dtd. * If null the default defaults file is used. If the empty string, then * no defaults file is used. * @param configurations Array of classnames of {@link Configuration} implementations to apply. * @param extract If true, extract war files * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications * @exception IOException * @throws IllegalAccessException * @throws InstantiationException * @deprecated use {@link WebAppDeployer} or {@link ContextDeployer} */ public static void addWebApplications(HandlerContainer contexts, String webapps, String defaults, String[] configurations, boolean extract, boolean java2CompliantClassLoader) throws IOException { Log.warn("Deprecated configuration used for " + webapps); WebAppDeployer deployer = new WebAppDeployer(); deployer.setContexts(contexts); deployer.setWebAppDir(webapps); deployer.setConfigurationClasses(configurations); deployer.setExtract(extract); deployer.setParentLoaderPriority(java2CompliantClassLoader); try { deployer.start(); } catch (IOException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } /* ------------------------------------------------------------ */ public WebAppContext() { this(null, null, null, null); } /* ------------------------------------------------------------ */ /** * @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); setContextPath(contextPath); setWar(webApp); setErrorHandler(new ErrorPageErrorHandler()); } /* ------------------------------------------------------------ */ /** * @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); setWar(webApp); setErrorHandler(new ErrorPageErrorHandler()); } /* ------------------------------------------------------------ */ /** */ public WebAppContext(SecurityHandler securityHandler, SessionHandler sessionHandler, ServletHandler servletHandler, ErrorHandler errorHandler) { super(null, sessionHandler != null ? sessionHandler : new SessionHandler(), securityHandler != null ? securityHandler : new SecurityHandler(), servletHandler != null ? servletHandler : new ServletHandler(), null); setErrorHandler(errorHandler != null ? errorHandler : new ErrorPageErrorHandler()); } /* ------------------------------------------------------------ */ /** 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(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 alias) { if (_resourceAliases == null) return null; return (String) _resourceAliases.get(alias); } /* ------------------------------------------------------------ */ public String removeResourceAlias(String alias) { if (_resourceAliases == null) return null; return (String) _resourceAliases.remove(alias); } /* ------------------------------------------------------------ */ /* (non-Javadoc) * @see org.mortbay.jetty.handler.ContextHandler#setClassLoader(java.lang.ClassLoader) */ public void setClassLoader(ClassLoader classLoader) { super.setClassLoader(classLoader); if (classLoader != null && classLoader instanceof WebAppClassLoader) ((WebAppClassLoader) classLoader).setName(getDisplayName()); } /* ------------------------------------------------------------ */ public Resource getResource(String uriInContext) throws MalformedURLException { 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; } /* ------------------------------------------------------------ */ /** * @see org.mortbay.jetty.handler.ContextHandler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int) */ public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch) throws IOException, ServletException { if (_unavailable) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); } else super.handle(target, request, response, dispatch); } /* ------------------------------------------------------------ */ /* * @see org.mortbay.thread.AbstractLifeCycle#doStart() */ protected void doStart() throws Exception { try { // Setup configurations loadConfigurations(); for (int i = 0; i < _configurations.length; i++) _configurations[i].setWebAppContext(this); // 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 class loader is: " + loader); loader = loader.getParent(); while (loader != null) { Log.debug("Parent class loader is: " + loader); loader = loader.getParent(); } } for (int i = 0; i < _configurations.length; i++) _configurations[i].configureClassLoader(); getTempDirectory(); super.doStart(); 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; _unavailable = true; } } /* ------------------------------------------------------------ */ /* * Dumps the current web app name and URL to the log */ public 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()); } } /* ------------------------------------------------------------ */ /* * @see org.mortbay.thread.AbstractLifeCycle#doStop() */ protected void doStop() throws Exception { super.doStop(); try { // Configure classloader for (int i = _configurations.length; i-- > 0;) _configurations[i].deconfigureWebApp(); _configurations = null; // restore security handler if (_securityHandler.getHandler() == null) { _sessionHandler.setHandler(_securityHandler); _securityHandler.setHandler(_servletHandler); } // delete temp directory if we had to create it or if it isn't called work if (_tmpDir != null && !_isExistingTmpDir && !isTempWorkDirectory()) //_tmpDir!=null && !"work".equals(_tmpDir.getName())) { IO.delete(_tmpDir); _tmpDir = null; } } finally { if (_ownClassLoader) setClassLoader(null); _unavailable = false; _unavailableException = null; } } /* ------------------------------------------------------------ */ /** * @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. */ public String getOverrideDescriptor() { return _overrideDescriptor; } /* ------------------------------------------------------------ */ /** * @return Returns the permissions. */ public PermissionCollection getPermissions() { return _permissions; } /* ------------------------------------------------------------ */ /** * @return Returns the serverClasses. */ public String[] getServerClasses() { return _serverClasses; } /* ------------------------------------------------------------ */ /** * @return Returns the systemClasses. */ public String[] getSystemClasses() { return _systemClasses; } /* ------------------------------------------------------------ */ /** * Get a temporary directory in which to unpack the war etc etc. * The algorithm for determining this is to check these alternatives * in the order shown: * * <p>A. Try to use an explicit directory specifically for this webapp:</p> * <ol> * <li> * Iff an explicit directory is set for this webapp, use it. Do NOT set * delete on exit. * </li> * <li> * Iff javax.servlet.context.tempdir context attribute is set for * this webapp && exists && writeable, then use it. Do NOT set delete on exit. * </li> * </ol> * * <p>B. Create a directory based on global settings. The new directory * will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost * Work out where to create this directory: * <ol> * <li> * Iff $(jetty.home)/work exists create the directory there. Do NOT * set delete on exit. Do NOT delete contents if dir already exists. * </li> * <li> * Iff WEB-INF/work exists create the directory there. Do NOT set * delete on exit. Do NOT delete contents if dir already exists. * </li> * <li> * Else create dir in $(java.io.tmpdir). Set delete on exit. Delete * contents if dir already exists. * </li> * </ol> * * @return */ public File getTempDirectory() { if (_tmpDir != null && _tmpDir.isDirectory() && _tmpDir.canWrite()) return _tmpDir; // Initialize temporary directory // // I'm afraid that this is very much black magic. // but if you can think of better.... Object t = getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR); if (t != null && (t instanceof File)) { _tmpDir = (File) t; if (_tmpDir.isDirectory() && _tmpDir.canWrite()) return _tmpDir; } if (t != null && (t instanceof String)) { try { _tmpDir = new File((String) t); if (_tmpDir.isDirectory() && _tmpDir.canWrite()) { if (Log.isDebugEnabled()) Log.debug("Converted to File " + _tmpDir + " for " + this); setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, _tmpDir); return _tmpDir; } } catch (Exception e) { Log.warn(Log.EXCEPTION, e); } } // No tempdir so look for a work directory to use as tempDir base File work = null; try { File w = new File(System.getProperty("jetty.home"), "work"); if (w.exists() && w.canWrite() && w.isDirectory()) work = w; else if (getBaseResource() != null) { Resource web_inf = getWebInf(); if (web_inf != null && web_inf.exists()) { w = new File(web_inf.getFile(), "work"); if (w.exists() && w.canWrite() && w.isDirectory()) work = w; } } } catch (Exception e) { Log.ignore(e); } // No tempdir set so make one! try { String temp = getCanonicalNameForWebAppTmpDir(); if (work != null) _tmpDir = new File(work, temp); else { _tmpDir = new File(System.getProperty("java.io.tmpdir"), temp); if (_tmpDir.exists()) { if (Log.isDebugEnabled()) Log.debug("Delete existing temp dir " + _tmpDir + " for " + this); if (!IO.delete(_tmpDir)) { if (Log.isDebugEnabled()) Log.debug("Failed to delete temp dir " + _tmpDir); } if (_tmpDir.exists()) { String old = _tmpDir.toString(); _tmpDir = File.createTempFile(temp + "_", ""); if (_tmpDir.exists()) _tmpDir.delete(); Log.warn("Can't reuse " + old + ", using " + _tmpDir); } } } if (!_tmpDir.exists()) _tmpDir.mkdir(); //if not in a dir called "work" then we want to delete it on jvm exit if (!isTempWorkDirectory()) _tmpDir.deleteOnExit(); if (Log.isDebugEnabled()) Log.debug("Created temp dir " + _tmpDir + " for " + this); } catch (Exception e) { _tmpDir = null; Log.ignore(e); } if (_tmpDir == null) { try { // that didn't work, so try something simpler (ish) _tmpDir = File.createTempFile("JettyContext", ""); if (_tmpDir.exists()) _tmpDir.delete(); _tmpDir.mkdir(); _tmpDir.deleteOnExit(); if (Log.isDebugEnabled()) Log.debug("Created temp dir " + _tmpDir + " for " + this); } catch (IOException e) { Log.warn("tmpdir", e); System.exit(1); } } setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, _tmpDir); return _tmpDir; } /** * Check if the _tmpDir itself is called "work", or if the _tmpDir * is in a directory called "work". * @return */ public boolean isTempWorkDirectory() { if (_tmpDir == null) return false; if (_tmpDir.getName().equalsIgnoreCase("work")) return true; File t = _tmpDir.getParentFile(); if (t == null) return false; return (t.getName().equalsIgnoreCase("work")); } /* ------------------------------------------------------------ */ /** * @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 { resolveWebApp(); // 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) */ public boolean isCopyWebDir() { return _copyDir; } /* ------------------------------------------------------------ */ /** * @return Returns the java2compliant. */ public boolean isParentLoaderPriority() { return _parentLoaderPriority; } /* ------------------------------------------------------------ */ protected void loadConfigurations() throws Exception { if (_configurations != null) return; if (_configurationClasses == null) _configurationClasses = __dftConfigurationClasses; _configurations = new Configuration[_configurationClasses.length]; for (int i = 0; i < _configurations.length; i++) { _configurations[i] = (Configuration) Loader.loadClass(this.getClass(), _configurationClasses[i]) .newInstance(); } } /* ------------------------------------------------------------ */ protected boolean isProtectedTarget(String target) { while (target.startsWith("//")) target = URIUtil.compactPath(target); return StringUtil.startsWithIgnoreCase(target, "/web-inf") || StringUtil.startsWithIgnoreCase(target, "/meta-inf"); } /* ------------------------------------------------------------ */ public String toString() { return this.getClass().getName() + "@" + Integer.toHexString(hashCode()) + "{" + getContextPath() + "," + (_war == null ? getResourceBase() : _war) + "}"; } /* ------------------------------------------------------------ */ /** Resolve Web App directory * If the BaseResource has not been set, use the war resource to * derive a webapp resource (expanding WAR if required). */ protected void resolveWebApp() throws IOException { Resource web_app = super.getBaseResource(); if (web_app == null) { if (_war == null || _war.length() == 0) _war = getResourceBase(); // Set dir or WAR web_app = Resource.newResource(_war); // Accept aliases for WAR files if (web_app.getAlias() != null) { Log.debug(web_app + " anti-aliased to " + web_app.getAlias()); web_app = Resource.newResource(web_app.getAlias()); } if (Log.isDebugEnabled()) Log.debug("Try webapp=" + web_app + ", exists=" + web_app.exists() + ", directory=" + web_app.isDirectory()); // Is the WAR usable directly? if (web_app.exists() && !web_app.isDirectory() && !web_app.toString().startsWith("jar:")) { // No - then lets see if it can be turned into a jar URL. Resource jarWebApp = Resource.newResource("jar:" + web_app + "!/"); if (jarWebApp.exists() && jarWebApp.isDirectory()) { web_app = jarWebApp; _war = web_app.toString(); } } // If we should extract or the URL is still not usable if (web_app.exists() && ((_copyDir && web_app.getFile() != null && web_app.getFile().isDirectory()) || (_extractWAR && web_app.getFile() != null && !web_app.getFile().isDirectory()) || (_extractWAR && web_app.getFile() == null) || !web_app.isDirectory())) { // Then extract it if necessary. File extractedWebAppDir = new File(getTempDirectory(), "webapp"); if (web_app.getFile() != null && web_app.getFile().isDirectory()) { // Copy directory Log.info("Copy " + web_app.getFile() + " to " + extractedWebAppDir); IO.copyDir(web_app.getFile(), extractedWebAppDir); } else { if (!extractedWebAppDir.exists()) { //it hasn't been extracted before so extract it extractedWebAppDir.mkdir(); Log.info("Extract " + _war + " to " + extractedWebAppDir); JarResource.extract(web_app, extractedWebAppDir, false); } else { //only extract if the war file is newer if (web_app.lastModified() > extractedWebAppDir.lastModified()) { extractedWebAppDir.delete(); extractedWebAppDir.mkdir(); Log.info("Extract " + _war + " to " + extractedWebAppDir); JarResource.extract(web_app, extractedWebAppDir, false); } } } web_app = Resource.newResource(extractedWebAppDir.getCanonicalPath()); } // Now do we have something usable? if (!web_app.exists() || !web_app.isDirectory()) { Log.warn("Web application not found " + _war); throw new java.io.FileNotFoundException(_war); } if (Log.isDebugEnabled()) Log.debug("webapp=" + web_app); // ResourcePath super.setBaseResource(web_app); } } /* ------------------------------------------------------------ */ /** * @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) { _configurationClasses = configurations == null ? null : (String[]) configurations.clone(); } /* ------------------------------------------------------------ */ /** * @param configurations The configurations to set. */ public void setConfigurations(Configuration[] configurations) { _configurations = configurations == null ? null : (Configuration[]) configurations.clone(); } /* ------------------------------------------------------------ */ /** * 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 defaultsDescriptor The overrideDescritpor to set. */ public void setOverrideDescriptor(String overrideDescriptor) { _overrideDescriptor = 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; } /* ------------------------------------------------------------ */ 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); } } } /* ------------------------------------------------------------ */ /** Add EventListener * Conveniance method that calls {@link #setEventListeners(EventListener[])} * @param listener */ public void addEventListener(EventListener listener) { setEventListeners( (EventListener[]) LazyList.addToArray(getEventListeners(), listener, EventListener.class)); } /* ------------------------------------------------------------ */ /** * @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 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; } /* ------------------------------------------------------------ */ /** * @param serverClasses The serverClasses to set. */ public void setServerClasses(String[] serverClasses) { _serverClasses = serverClasses == null ? null : (String[]) serverClasses.clone(); } /* ------------------------------------------------------------ */ /** * @param systemClasses The systemClasses to set. */ public void setSystemClasses(String[] systemClasses) { _systemClasses = systemClasses == null ? null : (String[]) systemClasses.clone(); } /* ------------------------------------------------------------ */ /** 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(); } else if (dir != null) _isExistingTmpDir = true; if (dir != null && (!dir.exists() || !dir.isDirectory() || !dir.canWrite())) throw new IllegalArgumentException("Bad temp directory: " + dir); _tmpDir = dir; setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, _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; } /* ------------------------------------------------------------ */ protected void startContext() throws Exception { // Configure defaults for (int i = 0; i < _configurations.length; i++) _configurations[i].configureDefaults(); // Is there a WEB-INF work directory Resource web_inf = getWebInf(); if (web_inf != null) { Resource work = web_inf.addPath("work"); if (work.exists() && work.isDirectory() && work.getFile() != null && work.getFile().canWrite() && getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR) == null) setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, work.getFile()); } // Configure webapp for (int i = 0; i < _configurations.length; i++) _configurations[i].configureWebApp(); super.startContext(); } /** * Create a canonical name for a webapp tmp directory. * The form of the name is: * "Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36 hashcode of whole string * * host and port uniquely identify the server * context and virtual host uniquely identify the webapp * @return */ private String getCanonicalNameForWebAppTmpDir() { StringBuffer canonicalName = new StringBuffer(); canonicalName.append("Jetty"); //get the host and the port from the first connector Connector[] connectors = getServer().getConnectors(); //Get the host canonicalName.append("_"); String host = (connectors == null || connectors[0] == null ? "" : connectors[0].getHost()); if (host == null) host = "0.0.0.0"; canonicalName.append(host.replace('.', '_')); //Get the port canonicalName.append("_"); //try getting the real port being listened on int port = (connectors == null || connectors[0] == null ? 0 : connectors[0].getLocalPort()); //if not available (eg no connectors or connector not started), //try getting one that was configured. if (port < 0) port = connectors[0].getPort(); canonicalName.append(port); //Resource base canonicalName.append("_"); try { Resource resource = super.getBaseResource(); if (resource == null) { if (_war == null || _war.length() == 0) resource = Resource.newResource(getResourceBase()); // Set dir or WAR resource = Resource.newResource(_war); } String tmp = URIUtil.decodePath(resource.getURL().getPath()); if (tmp.endsWith("/")) tmp = tmp.substring(0, tmp.length() - 1); if (tmp.endsWith("!")) tmp = tmp.substring(0, tmp.length() - 1); //get just the last part which is the filename int i = tmp.lastIndexOf("/"); canonicalName.append(tmp.substring(i + 1, tmp.length())); } catch (Exception e) { Log.warn("Can't generate resourceBase as part of webapp tmp dir name", e); } //Context name canonicalName.append("_"); String contextPath = getContextPath(); contextPath = contextPath.replace('/', '_'); contextPath = contextPath.replace('\\', '_'); canonicalName.append(contextPath); //Virtual host (if there is one) canonicalName.append("_"); String[] vhosts = getVirtualHosts(); canonicalName.append((vhosts == null || vhosts[0] == null ? "" : vhosts[0])); //base36 hash of the whole string for uniqueness String hash = Integer.toString(canonicalName.toString().hashCode(), 36); canonicalName.append("_"); canonicalName.append(hash); // sanitize for (int i = 0; i < canonicalName.length(); i++) { char c = canonicalName.charAt(i); if (!Character.isJavaIdentifierPart(c)) canonicalName.setCharAt(i, '.'); } return canonicalName.toString(); } }