0001: //========================================================================
0002: //$Id: WebAppContext.java,v 1.5 2005/11/16 22:02:45 gregwilkins Exp $
0003: //Copyright 2004-2006 Mort Bay Consulting Pty. Ltd.
0004: //------------------------------------------------------------------------
0005: //Licensed under the Apache License, Version 2.0 (the "License");
0006: //you may not use this file except in compliance with the License.
0007: //You may obtain a copy of the License at
0008: //http://www.apache.org/licenses/LICENSE-2.0
0009: //Unless required by applicable law or agreed to in writing, software
0010: //distributed under the License is distributed on an "AS IS" BASIS,
0011: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0012: //See the License for the specific language governing permissions and
0013: //limitations under the License.
0014: //========================================================================
0015:
0016: package org.mortbay.jetty.webapp;
0017:
0018: import java.io.File;
0019: import java.io.IOException;
0020: import java.net.MalformedURLException;
0021: import java.security.PermissionCollection;
0022: import java.util.EventListener;
0023: import java.util.HashMap;
0024: import java.util.Map;
0025:
0026: import javax.servlet.ServletException;
0027: import javax.servlet.http.HttpServletRequest;
0028: import javax.servlet.http.HttpServletResponse;
0029: import javax.servlet.http.HttpSessionActivationListener;
0030: import javax.servlet.http.HttpSessionAttributeListener;
0031: import javax.servlet.http.HttpSessionBindingListener;
0032: import javax.servlet.http.HttpSessionListener;
0033:
0034: import org.mortbay.jetty.Connector;
0035: import org.mortbay.jetty.HandlerContainer;
0036: import org.mortbay.jetty.Server;
0037: import org.mortbay.jetty.deployer.WebAppDeployer;
0038: import org.mortbay.jetty.handler.ContextHandler;
0039: import org.mortbay.jetty.handler.ContextHandlerCollection;
0040: import org.mortbay.jetty.handler.ErrorHandler;
0041: import org.mortbay.jetty.handler.HandlerCollection;
0042: import org.mortbay.jetty.security.SecurityHandler;
0043: import org.mortbay.jetty.servlet.Context;
0044: import org.mortbay.jetty.servlet.ErrorPageErrorHandler;
0045: import org.mortbay.jetty.servlet.ServletHandler;
0046: import org.mortbay.jetty.servlet.SessionHandler;
0047: import org.mortbay.log.Log;
0048: import org.mortbay.resource.JarResource;
0049: import org.mortbay.resource.Resource;
0050: import org.mortbay.util.IO;
0051: import org.mortbay.util.LazyList;
0052: import org.mortbay.util.Loader;
0053: import org.mortbay.util.StringUtil;
0054:
0055: /* ------------------------------------------------------------ */
0056: /** Web Application Context Handler.
0057: * The WebAppContext handler is an extension of ContextHandler that
0058: * coordinates the construction and configuration of nested handlers:
0059: * {@link org.mortbay.jetty.security.SecurityHandler}, {@link org.mortbay.jetty.servlet.SessionHandler}
0060: * and {@link org.mortbay.jetty.servlet.ServletHandler}.
0061: * The handlers are configured by pluggable configuration classes, with
0062: * the default being {@link org.mortbay.jetty.webapp.WebXmlConfiguration} and
0063: * {@link org.mortbay.jetty.webapp.JettyWebXmlConfiguration}.
0064: *
0065: * @org.apache.xbean.XBean description="Creates a servlet web application at a given context from a resource base"
0066: *
0067: * @author gregw
0068: *
0069: */
0070: public class WebAppContext extends Context {
0071: public final static String WEB_DEFAULTS_XML = "org/mortbay/jetty/webapp/webdefault.xml";
0072: public final static String ERROR_PAGE = "org.mortbay.jetty.error_page";
0073:
0074: private static String[] __dftConfigurationClasses = {
0075: "org.mortbay.jetty.webapp.WebInfConfiguration",
0076: "org.mortbay.jetty.webapp.WebXmlConfiguration",
0077: "org.mortbay.jetty.webapp.JettyWebXmlConfiguration",
0078: "org.mortbay.jetty.webapp.TagLibConfiguration" };
0079: private String[] _configurationClasses = __dftConfigurationClasses;
0080: private Configuration[] _configurations;
0081: private String _defaultsDescriptor = WEB_DEFAULTS_XML;
0082: private String _descriptor = null;
0083: private String _overrideDescriptor = null;
0084: private boolean _distributable = false;
0085: private boolean _extractWAR = true;
0086: private boolean _copyDir = false;
0087: private boolean _parentLoaderPriority = Boolean
0088: .getBoolean("org.mortbay.jetty.webapp.parentLoaderPriority");
0089: private PermissionCollection _permissions;
0090: private String[] _systemClasses = { "java.", "javax.servlet.",
0091: "javax.xml.", "org.mortbay.", "org.xml.", "org.w3c.",
0092: "org.apache.commons.logging.", "org.apache.log4j." };
0093: private String[] _serverClasses = {
0094: "-org.mortbay.jetty.plus.jaas.", "org.mortbay.jetty.",
0095: "org.slf4j." }; // TODO hide all mortbay classes
0096: private File _tmpDir;
0097: private boolean _isExistingTmpDir;
0098: private String _war;
0099: private String _extraClasspath;
0100:
0101: private transient Map _resourceAliases;
0102: private transient boolean _ownClassLoader = false;
0103: private transient boolean _unavailable;
0104:
0105: public static WebAppContext getCurrentWebAppContext() {
0106: ContextHandler.SContext context = ContextHandler
0107: .getCurrentContext();
0108: if (context != null) {
0109: ContextHandler handler = context.getContextHandler();
0110: if (handler instanceof WebAppContext)
0111: return (WebAppContext) handler;
0112: }
0113: return null;
0114: }
0115:
0116: /* ------------------------------------------------------------ */
0117: /** Add Web Applications.
0118: * Add auto webapplications to the server. The name of the
0119: * webapp directory or war is used as the context name. If the
0120: * webapp matches the rootWebApp it is added as the "/" context.
0121: * @param server Must not be <code>null</code>
0122: * @param webapps Directory file name or URL to look for auto
0123: * webapplication.
0124: * @param defaults The defaults xml filename or URL which is
0125: * loaded before any in the web app. Must respect the web.dtd.
0126: * If null the default defaults file is used. If the empty string, then
0127: * no defaults file is used.
0128: * @param extract If true, extract war files
0129: * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
0130: * @exception IOException
0131: * @deprecated use {@link org.mortbay.jetty.deployer.WebAppDeployer} or {@link org.mortbay.jetty.deployer.ContextDeployer}
0132: */
0133: public static void addWebApplications(Server server,
0134: String webapps, String defaults, boolean extract,
0135: boolean java2CompliantClassLoader) throws IOException {
0136: addWebApplications(server, webapps, defaults,
0137: __dftConfigurationClasses, extract,
0138: java2CompliantClassLoader);
0139: }
0140:
0141: /* ------------------------------------------------------------ */
0142: /** Add Web Applications.
0143: * Add auto webapplications to the server. The name of the
0144: * webapp directory or war is used as the context name. If the
0145: * webapp matches the rootWebApp it is added as the "/" context.
0146: * @param server Must not be <code>null</code>.
0147: * @param webapps Directory file name or URL to look for auto
0148: * webapplication.
0149: * @param defaults The defaults xml filename or URL which is
0150: * loaded before any in the web app. Must respect the web.dtd.
0151: * If null the default defaults file is used. If the empty string, then
0152: * no defaults file is used.
0153: * @param configurations Array of classnames of {@link Configuration} implementations to apply.
0154: * @param extract If true, extract war files
0155: * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
0156: * @exception IOException
0157: * @throws IllegalAccessException
0158: * @throws InstantiationException
0159: * @deprecated use {@link org.mortbay.jetty.deployer.WebAppDeployer} or {@link org.mortbay.jetty.deployer.ContextDeployer}
0160: */
0161: public static void addWebApplications(Server server,
0162: String webapps, String defaults, String[] configurations,
0163: boolean extract, boolean java2CompliantClassLoader)
0164: throws IOException {
0165: HandlerCollection contexts = (HandlerCollection) server
0166: .getChildHandlerByClass(ContextHandlerCollection.class);
0167: if (contexts == null)
0168: contexts = (HandlerCollection) server
0169: .getChildHandlerByClass(HandlerCollection.class);
0170:
0171: addWebApplications(contexts, webapps, defaults, configurations,
0172: extract, java2CompliantClassLoader);
0173: }
0174:
0175: /* ------------------------------------------------------------ */
0176: /** Add Web Applications.
0177: * Add auto webapplications to the server. The name of the
0178: * webapp directory or war is used as the context name. If the
0179: * webapp is called "root" it is added as the "/" context.
0180: * @param contexts A HandlerContainer to which the contexts will be added
0181: * @param webapps Directory file name or URL to look for auto
0182: * webapplication.
0183: * @param defaults The defaults xml filename or URL which is
0184: * loaded before any in the web app. Must respect the web.dtd.
0185: * If null the default defaults file is used. If the empty string, then
0186: * no defaults file is used.
0187: * @param configurations Array of classnames of {@link Configuration} implementations to apply.
0188: * @param extract If true, extract war files
0189: * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
0190: * @exception IOException
0191: * @throws IllegalAccessException
0192: * @throws InstantiationException
0193: * @deprecated use {@link WebAppDeployer} or {@link ContextDeployer}
0194: */
0195: public static void addWebApplications(HandlerContainer contexts,
0196: String webapps, String defaults, boolean extract,
0197: boolean java2CompliantClassLoader) throws IOException {
0198: addWebApplications(contexts, webapps, defaults,
0199: __dftConfigurationClasses, extract,
0200: java2CompliantClassLoader);
0201: }
0202:
0203: /* ------------------------------------------------------------ */
0204: /** Add Web Applications.
0205: * Add auto webapplications to the server. The name of the
0206: * webapp directory or war is used as the context name. If the
0207: * webapp is called "root" it is added as the "/" context.
0208: * @param contexts A HandlerContainer to which the contexts will be added
0209: * @param webapps Directory file name or URL to look for auto
0210: * webapplication.
0211: * @param defaults The defaults xml filename or URL which is
0212: * loaded before any in the web app. Must respect the web.dtd.
0213: * If null the default defaults file is used. If the empty string, then
0214: * no defaults file is used.
0215: * @param configurations Array of classnames of {@link Configuration} implementations to apply.
0216: * @param extract If true, extract war files
0217: * @param java2CompliantClassLoader True if java2 compliance is applied to all webapplications
0218: * @exception IOException
0219: * @throws IllegalAccessException
0220: * @throws InstantiationException
0221: * @deprecated use {@link WebAppDeployer} or {@link ContextDeployer}
0222: */
0223: public static void addWebApplications(HandlerContainer contexts,
0224: String webapps, String defaults, String[] configurations,
0225: boolean extract, boolean java2CompliantClassLoader)
0226: throws IOException {
0227: Log.warn("Deprecated configuration used for " + webapps);
0228: WebAppDeployer deployer = new WebAppDeployer();
0229: deployer.setContexts(contexts);
0230: deployer.setWebAppDir(webapps);
0231: deployer.setConfigurationClasses(configurations);
0232: deployer.setExtract(extract);
0233: deployer.setParentLoaderPriority(java2CompliantClassLoader);
0234: try {
0235: deployer.start();
0236: } catch (IOException e) {
0237: throw e;
0238: } catch (Exception e) {
0239: throw new RuntimeException(e);
0240: }
0241: }
0242:
0243: /* ------------------------------------------------------------ */
0244: public WebAppContext() {
0245: this (null, null, null, null);
0246: }
0247:
0248: /* ------------------------------------------------------------ */
0249: /**
0250: * @param contextPath The context path
0251: * @param webApp The URL or filename of the webapp directory or war file.
0252: */
0253: public WebAppContext(String webApp, String contextPath) {
0254: super (null, contextPath, SESSIONS | SECURITY);
0255: setContextPath(contextPath);
0256: setWar(webApp);
0257: setErrorHandler(new ErrorPageErrorHandler());
0258: }
0259:
0260: /* ------------------------------------------------------------ */
0261: /**
0262: * @param parent The parent HandlerContainer.
0263: * @param contextPath The context path
0264: * @param webApp The URL or filename of the webapp directory or war file.
0265: */
0266: public WebAppContext(HandlerContainer parent, String webApp,
0267: String contextPath) {
0268: super (parent, contextPath, SESSIONS | SECURITY);
0269: setWar(webApp);
0270: setErrorHandler(new ErrorPageErrorHandler());
0271: }
0272:
0273: /* ------------------------------------------------------------ */
0274: /**
0275: */
0276: public WebAppContext(SecurityHandler securityHandler,
0277: SessionHandler sessionHandler,
0278: ServletHandler servletHandler, ErrorHandler errorHandler) {
0279: super (null, sessionHandler != null ? sessionHandler
0280: : new SessionHandler(),
0281: securityHandler != null ? securityHandler
0282: : new SecurityHandler(),
0283: servletHandler != null ? servletHandler
0284: : new ServletHandler(), null);
0285:
0286: setErrorHandler(errorHandler != null ? errorHandler
0287: : new ErrorPageErrorHandler());
0288: }
0289:
0290: /* ------------------------------------------------------------ */
0291: /** Set Resource Alias.
0292: * Resource aliases map resource uri's within a context.
0293: * They may optionally be used by a handler when looking for
0294: * a resource.
0295: * @param alias
0296: * @param uri
0297: */
0298: public void setResourceAlias(String alias, String uri) {
0299: if (_resourceAliases == null)
0300: _resourceAliases = new HashMap(5);
0301: _resourceAliases.put(alias, uri);
0302: }
0303:
0304: /* ------------------------------------------------------------ */
0305: public Map getResourceAliases() {
0306: if (_resourceAliases == null)
0307: return null;
0308: return _resourceAliases;
0309: }
0310:
0311: /* ------------------------------------------------------------ */
0312: public void setResourceAliases(Map map) {
0313: _resourceAliases = map;
0314: }
0315:
0316: /* ------------------------------------------------------------ */
0317: public String getResourceAlias(String alias) {
0318: if (_resourceAliases == null)
0319: return null;
0320: return (String) _resourceAliases.get(alias);
0321: }
0322:
0323: /* ------------------------------------------------------------ */
0324: public String removeResourceAlias(String alias) {
0325: if (_resourceAliases == null)
0326: return null;
0327: return (String) _resourceAliases.remove(alias);
0328: }
0329:
0330: /* ------------------------------------------------------------ */
0331: public Resource getResource(String uriInContext)
0332: throws MalformedURLException {
0333: IOException ioe = null;
0334: Resource resource = null;
0335: int loop = 0;
0336: while (uriInContext != null && loop++ < 100) {
0337: try {
0338: resource = super .getResource(uriInContext);
0339: if (resource != null && resource.exists())
0340: return resource;
0341:
0342: uriInContext = getResourceAlias(uriInContext);
0343: } catch (IOException e) {
0344: Log.ignore(e);
0345: if (ioe == null)
0346: ioe = e;
0347: }
0348: }
0349:
0350: if (ioe != null && ioe instanceof MalformedURLException)
0351: throw (MalformedURLException) ioe;
0352:
0353: return resource;
0354: }
0355:
0356: /* ------------------------------------------------------------ */
0357: /**
0358: * @see org.mortbay.jetty.handler.ContextHandler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
0359: */
0360: public void handle(String target, HttpServletRequest request,
0361: HttpServletResponse response, int dispatch)
0362: throws IOException, ServletException {
0363: if (_unavailable) {
0364: response
0365: .sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
0366: } else
0367: super .handle(target, request, response, dispatch);
0368: }
0369:
0370: /* ------------------------------------------------------------ */
0371: /*
0372: * @see org.mortbay.thread.AbstractLifeCycle#doStart()
0373: */
0374: protected void doStart() throws Exception {
0375: try {
0376: // Setup configurations
0377: loadConfigurations();
0378:
0379: for (int i = 0; i < _configurations.length; i++)
0380: _configurations[i].setWebAppContext(this );
0381:
0382: // Configure classloader
0383: _ownClassLoader = false;
0384: if (getClassLoader() == null) {
0385: WebAppClassLoader classLoader = new WebAppClassLoader(
0386: this );
0387: setClassLoader(classLoader);
0388: _ownClassLoader = true;
0389: }
0390:
0391: if (Log.isDebugEnabled()) {
0392: ClassLoader loader = getClassLoader();
0393: Log.debug("Thread Context class loader is: " + loader);
0394: loader = loader.getParent();
0395: while (loader != null) {
0396: Log.debug("Parent class loader is: " + loader);
0397: loader = loader.getParent();
0398: }
0399: }
0400:
0401: for (int i = 0; i < _configurations.length; i++)
0402: _configurations[i].configureClassLoader();
0403:
0404: getTempDirectory();
0405:
0406: super .doStart();
0407: } catch (Exception e) {
0408: //start up of the webapp context failed, make sure it is not started
0409: Log.warn("Failed startup of context " + this , e);
0410: _unavailable = true;
0411: }
0412:
0413: }
0414:
0415: /* ------------------------------------------------------------ */
0416: /*
0417: * @see org.mortbay.thread.AbstractLifeCycle#doStop()
0418: */
0419: protected void doStop() throws Exception {
0420: super .doStop();
0421:
0422: try {
0423: // Configure classloader
0424: for (int i = _configurations.length; i-- > 0;)
0425: _configurations[i].deconfigureWebApp();
0426: _configurations = null;
0427:
0428: // restore security handler
0429: if (_securityHandler.getHandler() == null) {
0430: _sessionHandler.setHandler(_securityHandler);
0431: _securityHandler.setHandler(_servletHandler);
0432: }
0433:
0434: // delete temp directory if we had to create it or if it isn't called work
0435: if (!_isExistingTmpDir && !isTempWorkDirectory())//_tmpDir!=null && !"work".equals(_tmpDir.getName()))
0436: IO.delete(_tmpDir);
0437: } finally {
0438: if (_ownClassLoader)
0439: setClassLoader(null);
0440:
0441: _unavailable = false;
0442: }
0443: }
0444:
0445: /* ------------------------------------------------------------ */
0446: /**
0447: * @return Returns the configurations.
0448: */
0449: public String[] getConfigurationClasses() {
0450: return _configurationClasses;
0451: }
0452:
0453: /* ------------------------------------------------------------ */
0454: /**
0455: * @return Returns the configurations.
0456: */
0457: public Configuration[] getConfigurations() {
0458: return _configurations;
0459: }
0460:
0461: /* ------------------------------------------------------------ */
0462: /**
0463: * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
0464: * @return Returns the defaultsDescriptor.
0465: */
0466: public String getDefaultsDescriptor() {
0467: return _defaultsDescriptor;
0468: }
0469:
0470: /* ------------------------------------------------------------ */
0471: /**
0472: * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
0473: * @return Returns the Override Descriptor.
0474: */
0475: public String getOverrideDescriptor() {
0476: return _overrideDescriptor;
0477: }
0478:
0479: /* ------------------------------------------------------------ */
0480: /**
0481: * @return Returns the permissions.
0482: */
0483: public PermissionCollection getPermissions() {
0484: return _permissions;
0485: }
0486:
0487: /* ------------------------------------------------------------ */
0488: /**
0489: * @return Returns the serverClasses.
0490: */
0491: public String[] getServerClasses() {
0492: return _serverClasses;
0493: }
0494:
0495: /* ------------------------------------------------------------ */
0496: /**
0497: * @return Returns the systemClasses.
0498: */
0499: public String[] getSystemClasses() {
0500: return _systemClasses;
0501: }
0502:
0503: /* ------------------------------------------------------------ */
0504: /**
0505: * Get a temporary directory in which to unpack the war etc etc.
0506: * The algorithm for determining this is to check these alternatives
0507: * in the order shown:
0508: *
0509: * <p>A. Try to use an explicit directory specifically for this webapp:</p>
0510: * <ol>
0511: * <li>
0512: * Iff an explicit directory is set for this webapp, use it. Do NOT set
0513: * delete on exit.
0514: * </li>
0515: * <li>
0516: * Iff javax.servlet.context.tempdir context attribute is set for
0517: * this webapp && exists && writeable, then use it. Do NOT set delete on exit.
0518: * </li>
0519: * </ol>
0520: *
0521: * <p>B. Create a directory based on global settings. The new directory
0522: * will be called "Jetty_"+host+"_"+port+"__"+context+"_"+virtualhost
0523: * Work out where to create this directory:
0524: * <ol>
0525: * <li>
0526: * Iff $(jetty.home)/work exists create the directory there. Do NOT
0527: * set delete on exit. Do NOT delete contents if dir already exists.
0528: * </li>
0529: * <li>
0530: * Iff WEB-INF/work exists create the directory there. Do NOT set
0531: * delete on exit. Do NOT delete contents if dir already exists.
0532: * </li>
0533: * <li>
0534: * Else create dir in $(java.io.tmpdir). Set delete on exit. Delete
0535: * contents if dir already exists.
0536: * </li>
0537: * </ol>
0538: *
0539: * @return
0540: */
0541: public File getTempDirectory() {
0542: if (_tmpDir != null)
0543: return _tmpDir;
0544:
0545: // Initialize temporary directory
0546: //
0547: // I'm afraid that this is very much black magic.
0548: // but if you can think of better....
0549: Object t = getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR);
0550:
0551: if (t != null && (t instanceof File)) {
0552: _tmpDir = (File) t;
0553: if (_tmpDir.isDirectory() && _tmpDir.canWrite())
0554: return _tmpDir;
0555: }
0556:
0557: if (t != null && (t instanceof String)) {
0558: try {
0559: _tmpDir = new File((String) t);
0560:
0561: if (_tmpDir.isDirectory() && _tmpDir.canWrite()) {
0562: if (Log.isDebugEnabled())
0563: Log.debug("Converted to File " + _tmpDir
0564: + " for " + this );
0565: setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR,
0566: _tmpDir);
0567: return _tmpDir;
0568: }
0569: } catch (Exception e) {
0570: Log.warn(Log.EXCEPTION, e);
0571: }
0572: }
0573:
0574: // No tempdir so look for a work directory to use as tempDir base
0575: File work = null;
0576: try {
0577: File w = new File(System.getProperty("jetty.home"), "work");
0578: if (w.exists() && w.canWrite() && w.isDirectory())
0579: work = w;
0580: else if (getBaseResource() != null) {
0581: Resource web_inf = getWebInf();
0582: if (web_inf != null && web_inf.exists()) {
0583: w = new File(web_inf.getFile(), "work");
0584: if (w.exists() && w.canWrite() && w.isDirectory())
0585: work = w;
0586: }
0587: }
0588: } catch (Exception e) {
0589: Log.ignore(e);
0590: }
0591:
0592: // No tempdir set so make one!
0593: try {
0594:
0595: String temp = getCanonicalNameForWebAppTmpDir();
0596:
0597: if (work != null)
0598: _tmpDir = new File(work, temp);
0599: else {
0600: _tmpDir = new File(
0601: System.getProperty("java.io.tmpdir"), temp);
0602:
0603: if (_tmpDir.exists()) {
0604: if (Log.isDebugEnabled())
0605: Log.debug("Delete existing temp dir " + _tmpDir
0606: + " for " + this );
0607: if (!IO.delete(_tmpDir)) {
0608: if (Log.isDebugEnabled())
0609: Log.debug("Failed to delete temp dir "
0610: + _tmpDir);
0611: }
0612:
0613: if (_tmpDir.exists()) {
0614: String old = _tmpDir.toString();
0615: _tmpDir = File.createTempFile(temp + "_", "");
0616: if (_tmpDir.exists())
0617: _tmpDir.delete();
0618: Log.warn("Can't reuse " + old + ", using "
0619: + _tmpDir);
0620: }
0621: }
0622: }
0623:
0624: if (!_tmpDir.exists())
0625: _tmpDir.mkdir();
0626:
0627: //if not in a dir called "work" then we want to delete it on jvm exit
0628: if (!isTempWorkDirectory())
0629: _tmpDir.deleteOnExit();
0630: if (Log.isDebugEnabled())
0631: Log.debug("Created temp dir " + _tmpDir + " for "
0632: + this );
0633: } catch (Exception e) {
0634: _tmpDir = null;
0635: Log.ignore(e);
0636: }
0637:
0638: if (_tmpDir == null) {
0639: try {
0640: // that didn't work, so try something simpler (ish)
0641: _tmpDir = File.createTempFile("JettyContext", "");
0642: if (_tmpDir.exists())
0643: _tmpDir.delete();
0644: _tmpDir.mkdir();
0645: _tmpDir.deleteOnExit();
0646: if (Log.isDebugEnabled())
0647: Log.debug("Created temp dir " + _tmpDir + " for "
0648: + this );
0649: } catch (IOException e) {
0650: Log.warn("tmpdir", e);
0651: System.exit(1);
0652: }
0653: }
0654:
0655: setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, _tmpDir);
0656: return _tmpDir;
0657: }
0658:
0659: /**
0660: * Check if the _tmpDir itself is called "work", or if the _tmpDir
0661: * is in a directory called "work".
0662: * @return
0663: */
0664: public boolean isTempWorkDirectory() {
0665: File tmpDir = getTempDirectory();
0666: if (tmpDir == null)
0667: return false;
0668: if (tmpDir.getName().equalsIgnoreCase("work"))
0669: return true;
0670: tmpDir = tmpDir.getParentFile();
0671: if (tmpDir == null)
0672: return false;
0673: return (tmpDir.getName().equalsIgnoreCase("work"));
0674: }
0675:
0676: /* ------------------------------------------------------------ */
0677: /**
0678: * @return Returns the war as a file or URL string (Resource)
0679: */
0680: public String getWar() {
0681: if (_war == null)
0682: _war = getResourceBase();
0683: return _war;
0684: }
0685:
0686: /* ------------------------------------------------------------ */
0687: public Resource getWebInf() throws IOException {
0688: resolveWebApp();
0689:
0690: // Iw there a WEB-INF directory?
0691: Resource web_inf = super .getBaseResource().addPath("WEB-INF/");
0692: if (!web_inf.exists() || !web_inf.isDirectory())
0693: return null;
0694:
0695: return web_inf;
0696: }
0697:
0698: /* ------------------------------------------------------------ */
0699: /**
0700: * @return Returns the distributable.
0701: */
0702: public boolean isDistributable() {
0703: return _distributable;
0704: }
0705:
0706: /* ------------------------------------------------------------ */
0707: /**
0708: * @return Returns the extractWAR.
0709: */
0710: public boolean isExtractWAR() {
0711: return _extractWAR;
0712: }
0713:
0714: /* ------------------------------------------------------------ */
0715: /**
0716: * @return True if the webdir is copied (to allow hot replacement of jars)
0717: */
0718: public boolean isCopyWebDir() {
0719: return _copyDir;
0720: }
0721:
0722: /* ------------------------------------------------------------ */
0723: /**
0724: * @return Returns the java2compliant.
0725: */
0726: public boolean isParentLoaderPriority() {
0727: return _parentLoaderPriority;
0728: }
0729:
0730: /* ------------------------------------------------------------ */
0731: protected void loadConfigurations() throws Exception {
0732: if (_configurations != null)
0733: return;
0734: if (_configurationClasses == null)
0735: _configurationClasses = __dftConfigurationClasses;
0736:
0737: _configurations = new Configuration[_configurationClasses.length];
0738: for (int i = 0; i < _configurations.length; i++) {
0739: _configurations[i] = (Configuration) Loader.loadClass(
0740: this .getClass(), _configurationClasses[i])
0741: .newInstance();
0742: }
0743: }
0744:
0745: /* ------------------------------------------------------------ */
0746: protected boolean isProtectedTarget(String target) {
0747: return StringUtil.startsWithIgnoreCase(target, "/web-inf")
0748: || StringUtil.startsWithIgnoreCase(target, "/meta-inf");
0749: }
0750:
0751: /* ------------------------------------------------------------ */
0752: public String toString() {
0753: return this .getClass().getName() + "@"
0754: + Integer.toHexString(hashCode()) + "{"
0755: + getContextPath() + ","
0756: + (_war == null ? getResourceBase() : _war) + "}";
0757: }
0758:
0759: /* ------------------------------------------------------------ */
0760: /** Resolve Web App directory
0761: * If the BaseResource has not been set, use the war resource to
0762: * derive a webapp resource (expanding WAR if required).
0763: */
0764: protected void resolveWebApp() throws IOException {
0765: Resource web_app = super .getBaseResource();
0766: if (web_app == null) {
0767: if (_war == null || _war.length() == 0)
0768: _war = getResourceBase();
0769:
0770: // Set dir or WAR
0771: web_app = Resource.newResource(_war);
0772:
0773: // Accept aliases for WAR files
0774: if (web_app.getAlias() != null) {
0775: Log.debug(web_app + " anti-aliased to "
0776: + web_app.getAlias());
0777: web_app = Resource.newResource(web_app.getAlias());
0778: }
0779:
0780: if (Log.isDebugEnabled())
0781: Log.debug("Try webapp=" + web_app + ", exists="
0782: + web_app.exists() + ", directory="
0783: + web_app.isDirectory());
0784:
0785: // Is the WAR usable directly?
0786: if (web_app.exists() && !web_app.isDirectory()
0787: && !web_app.toString().startsWith("jar:")) {
0788: // No - then lets see if it can be turned into a jar URL.
0789: Resource jarWebApp = Resource.newResource("jar:"
0790: + web_app + "!/");
0791: if (jarWebApp.exists() && jarWebApp.isDirectory()) {
0792: web_app = jarWebApp;
0793: _war = web_app.toString();
0794: }
0795: }
0796:
0797: // If we should extract or the URL is still not usable
0798: if (web_app.exists()
0799: && ((_copyDir && web_app.getFile() != null && web_app
0800: .getFile().isDirectory())
0801: || (_extractWAR
0802: && web_app.getFile() != null && !web_app
0803: .getFile().isDirectory())
0804: || (_extractWAR && web_app.getFile() == null) || !web_app
0805: .isDirectory())) {
0806: // Then extract it if necessary.
0807: File extractedWebAppDir = new File(getTempDirectory(),
0808: "webapp");
0809:
0810: if (web_app.getFile() != null
0811: && web_app.getFile().isDirectory()) {
0812: // Copy directory
0813: Log.info("Copy " + web_app.getFile() + " to "
0814: + extractedWebAppDir);
0815: IO.copyDir(web_app.getFile(), extractedWebAppDir);
0816: } else {
0817: if (!extractedWebAppDir.exists()) {
0818: //it hasn't been extracted before so extract it
0819: extractedWebAppDir.mkdir();
0820: Log.info("Extract " + _war + " to "
0821: + extractedWebAppDir);
0822: JarResource.extract(web_app,
0823: extractedWebAppDir, false);
0824: } else {
0825: //only extract if the war file is newer
0826: if (web_app.lastModified() > extractedWebAppDir
0827: .lastModified()) {
0828: extractedWebAppDir.delete();
0829: extractedWebAppDir.mkdir();
0830: Log.info("Extract " + _war + " to "
0831: + extractedWebAppDir);
0832: JarResource.extract(web_app,
0833: extractedWebAppDir, false);
0834: }
0835: }
0836: }
0837:
0838: web_app = Resource.newResource(extractedWebAppDir
0839: .getCanonicalPath());
0840:
0841: }
0842:
0843: // Now do we have something usable?
0844: if (!web_app.exists() || !web_app.isDirectory()) {
0845: Log.warn("Web application not found " + _war);
0846: throw new java.io.FileNotFoundException(_war);
0847: }
0848:
0849: if (Log.isDebugEnabled())
0850: Log.debug("webapp=" + web_app);
0851:
0852: // ResourcePath
0853: super .setBaseResource(web_app);
0854: }
0855: }
0856:
0857: /* ------------------------------------------------------------ */
0858: /**
0859: * @param configurations The configuration class names. If setConfigurations is not called
0860: * these classes are used to create a configurations array.
0861: */
0862: public void setConfigurationClasses(String[] configurations) {
0863: _configurationClasses = configurations == null ? null
0864: : (String[]) configurations.clone();
0865: }
0866:
0867: /* ------------------------------------------------------------ */
0868: /**
0869: * @param configurations The configurations to set.
0870: */
0871: public void setConfigurations(Configuration[] configurations) {
0872: _configurations = configurations == null ? null
0873: : (Configuration[]) configurations.clone();
0874: }
0875:
0876: /* ------------------------------------------------------------ */
0877: /**
0878: * The default descriptor is a web.xml format file that is applied to the context before the standard WEB-INF/web.xml
0879: * @param defaultsDescriptor The defaultsDescriptor to set.
0880: */
0881: public void setDefaultsDescriptor(String defaultsDescriptor) {
0882: _defaultsDescriptor = defaultsDescriptor;
0883: }
0884:
0885: /* ------------------------------------------------------------ */
0886: /**
0887: * The override descriptor is a web.xml format file that is applied to the context after the standard WEB-INF/web.xml
0888: * @param defaultsDescriptor The overrideDescritpor to set.
0889: */
0890: public void setOverrideDescriptor(String overrideDescriptor) {
0891: _overrideDescriptor = overrideDescriptor;
0892: }
0893:
0894: /* ------------------------------------------------------------ */
0895: /**
0896: * @return the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
0897: */
0898: public String getDescriptor() {
0899: return _descriptor;
0900: }
0901:
0902: /* ------------------------------------------------------------ */
0903: /**
0904: * @param descriptor the web.xml descriptor to use. If set to null, WEB-INF/web.xml is used if it exists.
0905: */
0906: public void setDescriptor(String descriptor) {
0907: _descriptor = descriptor;
0908: }
0909:
0910: /* ------------------------------------------------------------ */
0911: /**
0912: * @param distributable The distributable to set.
0913: */
0914: public void setDistributable(boolean distributable) {
0915: this ._distributable = distributable;
0916: }
0917:
0918: /* ------------------------------------------------------------ */
0919: public void setEventListeners(EventListener[] eventListeners) {
0920: if (_sessionHandler != null)
0921: _sessionHandler.clearEventListeners();
0922:
0923: super .setEventListeners(eventListeners);
0924:
0925: for (int i = 0; eventListeners != null
0926: && i < eventListeners.length; i++) {
0927: EventListener listener = eventListeners[i];
0928:
0929: if ((listener instanceof HttpSessionActivationListener)
0930: || (listener instanceof HttpSessionAttributeListener)
0931: || (listener instanceof HttpSessionBindingListener)
0932: || (listener instanceof HttpSessionListener)) {
0933: if (_sessionHandler != null)
0934: _sessionHandler.addEventListener(listener);
0935: }
0936:
0937: }
0938: }
0939:
0940: /* ------------------------------------------------------------ */
0941: /** Add EventListener
0942: * Conveniance method that calls {@link #setEventListeners(EventListener[])}
0943: * @param listener
0944: */
0945: public void addEventListener(EventListener listener) {
0946: setEventListeners((EventListener[]) LazyList.addToArray(
0947: getEventListeners(), listener, EventListener.class));
0948: }
0949:
0950: /* ------------------------------------------------------------ */
0951: /**
0952: * @param extractWAR True if war files are extracted
0953: */
0954: public void setExtractWAR(boolean extractWAR) {
0955: _extractWAR = extractWAR;
0956: }
0957:
0958: /* ------------------------------------------------------------ */
0959: /**
0960: *
0961: * @param copy True if the webdir is copied (to allow hot replacement of jars)
0962: */
0963: public void setCopyWebDir(boolean copy) {
0964: _copyDir = copy;
0965: }
0966:
0967: /* ------------------------------------------------------------ */
0968: /**
0969: * @param java2compliant The java2compliant to set.
0970: */
0971: public void setParentLoaderPriority(boolean java2compliant) {
0972: _parentLoaderPriority = java2compliant;
0973: }
0974:
0975: /* ------------------------------------------------------------ */
0976: /**
0977: * @param permissions The permissions to set.
0978: */
0979: public void setPermissions(PermissionCollection permissions) {
0980: _permissions = permissions;
0981: }
0982:
0983: /* ------------------------------------------------------------ */
0984: /**
0985: * @param securityHandler The {@link SecurityHandler} to set on this context.
0986: */
0987: public void setSecurityHandler(SecurityHandler securityHandler) {
0988: _securityHandler = securityHandler;
0989: }
0990:
0991: /* ------------------------------------------------------------ */
0992: /**
0993: * @param serverClasses The serverClasses to set.
0994: */
0995: public void setServerClasses(String[] serverClasses) {
0996: _serverClasses = serverClasses == null ? null
0997: : (String[]) serverClasses.clone();
0998: }
0999:
1000: /* ------------------------------------------------------------ */
1001: /**
1002: * @param servletHandler The servletHandler to set.
1003: */
1004: public void setServletHandler(ServletHandler servletHandler) {
1005: _servletHandler = servletHandler;
1006: }
1007:
1008: /* ------------------------------------------------------------ */
1009: /**
1010: * @param sessionHandler The sessionHandler to set.
1011: */
1012: public void setSessionHandler(SessionHandler sessionHandler) {
1013: _sessionHandler = sessionHandler;
1014: }
1015:
1016: /* ------------------------------------------------------------ */
1017: /**
1018: * @param systemClasses The systemClasses to set.
1019: */
1020: public void setSystemClasses(String[] systemClasses) {
1021: _systemClasses = systemClasses == null ? null
1022: : (String[]) systemClasses.clone();
1023: }
1024:
1025: /* ------------------------------------------------------------ */
1026: /** Set temporary directory for context.
1027: * The javax.servlet.context.tempdir attribute is also set.
1028: * @param dir Writable temporary directory.
1029: */
1030: public void setTempDirectory(File dir) {
1031: if (isStarted())
1032: throw new IllegalStateException("Started");
1033:
1034: if (dir != null) {
1035: try {
1036: dir = new File(dir.getCanonicalPath());
1037: } catch (IOException e) {
1038: Log.warn(Log.EXCEPTION, e);
1039: }
1040: }
1041:
1042: if (dir != null && !dir.exists()) {
1043: dir.mkdir();
1044: dir.deleteOnExit();
1045: } else if (dir != null)
1046: _isExistingTmpDir = true;
1047:
1048: if (dir != null
1049: && (!dir.exists() || !dir.isDirectory() || !dir
1050: .canWrite()))
1051: throw new IllegalArgumentException("Bad temp directory: "
1052: + dir);
1053:
1054: _tmpDir = dir;
1055: setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, _tmpDir);
1056: }
1057:
1058: /* ------------------------------------------------------------ */
1059: /**
1060: * @param war The war to set as a file name or URL
1061: */
1062: public void setWar(String war) {
1063: _war = war;
1064: }
1065:
1066: /* ------------------------------------------------------------ */
1067: /**
1068: * @return Comma or semicolon separated path of filenames or URLs
1069: * pointing to directories or jar files. Directories should end
1070: * with '/'.
1071: */
1072: public String getExtraClasspath() {
1073: return _extraClasspath;
1074: }
1075:
1076: /* ------------------------------------------------------------ */
1077: /**
1078: * @param extraClasspath Comma or semicolon separated path of filenames or URLs
1079: * pointing to directories or jar files. Directories should end
1080: * with '/'.
1081: */
1082: public void setExtraClasspath(String extraClasspath) {
1083: _extraClasspath = extraClasspath;
1084: }
1085:
1086: /* ------------------------------------------------------------ */
1087: protected void startContext() throws Exception {
1088: // Configure defaults
1089: for (int i = 0; i < _configurations.length; i++)
1090: _configurations[i].configureDefaults();
1091:
1092: // Is there a WEB-INF work directory
1093: Resource web_inf = getWebInf();
1094: if (web_inf != null) {
1095: Resource work = web_inf.addPath("work");
1096: if (work.exists()
1097: && work.isDirectory()
1098: && work.getFile() != null
1099: && work.getFile().canWrite()
1100: && getAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR) == null)
1101: setAttribute(ServletHandler.__J_S_CONTEXT_TEMPDIR, work
1102: .getFile());
1103: }
1104:
1105: // Configure webapp
1106: for (int i = 0; i < _configurations.length; i++)
1107: _configurations[i].configureWebApp();
1108:
1109: super .startContext();
1110: }
1111:
1112: /**
1113: * Create a canonical name for a webapp tmp directory.
1114: * The form of the name is:
1115: * "Jetty_"+host+"_"+port+"__"+resourceBase+"_"+context+"_"+virtualhost+base36 hashcode of whole string
1116: *
1117: * host and port uniquely identify the server
1118: * context and virtual host uniquely identify the webapp
1119: * @return
1120: */
1121: private String getCanonicalNameForWebAppTmpDir() {
1122: StringBuffer canonicalName = new StringBuffer();
1123: canonicalName.append("Jetty");
1124:
1125: //get the host and the port from the first connector
1126: Connector[] connectors = getServer().getConnectors();
1127:
1128: //Get the host
1129: canonicalName.append("_");
1130: String host = (connectors == null || connectors[0] == null ? ""
1131: : connectors[0].getHost());
1132: if (host == null)
1133: host = "0.0.0.0";
1134: canonicalName.append(host.replace('.', '_'));
1135:
1136: //Get the port
1137: canonicalName.append("_");
1138: //try getting the real port being listened on
1139: int port = (connectors == null || connectors[0] == null ? 0
1140: : connectors[0].getLocalPort());
1141: //if not available (eg no connectors or connector not started),
1142: //try getting one that was configured.
1143: if (port < 0)
1144: port = connectors[0].getPort();
1145: canonicalName.append(port);
1146:
1147: //Resource base
1148: canonicalName.append("_");
1149: try {
1150: Resource resource = super .getBaseResource();
1151: if (resource == null) {
1152: if (_war == null || _war.length() == 0)
1153: resource = Resource.newResource(getResourceBase());
1154:
1155: // Set dir or WAR
1156: resource = Resource.newResource(_war);
1157: }
1158:
1159: String tmp = resource.getURL().toExternalForm();
1160: if (tmp.endsWith("/"))
1161: tmp = tmp.substring(0, tmp.length() - 1);
1162: if (tmp.endsWith("!"))
1163: tmp = tmp.substring(0, tmp.length() - 1);
1164: //get just the last part which is the filename
1165: int i = tmp.lastIndexOf("/");
1166: canonicalName.append(tmp.substring(i + 1, tmp.length()));
1167: } catch (Exception e) {
1168: Log
1169: .warn(
1170: "Can't generate resourceBase as part of webapp tmp dir name",
1171: e);
1172: }
1173:
1174: //Context name
1175: canonicalName.append("_");
1176: String contextPath = getContextPath();
1177: contextPath = contextPath.replace('/', '_');
1178: contextPath = contextPath.replace('.', '_');
1179: contextPath = contextPath.replace('\\', '_');
1180: canonicalName.append(contextPath);
1181:
1182: //Virtual host (if there is one)
1183: canonicalName.append("_");
1184: String[] vhosts = getVirtualHosts();
1185: canonicalName.append((vhosts == null || vhosts[0] == null ? ""
1186: : vhosts[0]));
1187:
1188: //base36 hash of the whole string for uniqueness
1189: String hash = Integer.toString(canonicalName.toString()
1190: .hashCode(), 36);
1191: canonicalName.append("_");
1192: canonicalName.append(hash);
1193: return canonicalName.toString();
1194: }
1195: }
|