Return-Path: Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 53856 invoked by uid 500); 4 Oct 2000 23:03:10 -0000 Delivered-To: apmail-jakarta-tomcat-4.0-cvs@apache.org Received: (qmail 53853 invoked by uid 1059); 4 Oct 2000 23:03:08 -0000 Date: 4 Oct 2000 23:03:08 -0000 Message-ID: <20001004230308.53852.qmail@locus.apache.org> From: craigmcc@locus.apache.org To: jakarta-tomcat-4.0-cvs@apache.org Subject: cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup HostConfig.java LocalStrings.properties craigmcc 00/10/04 16:03:06 Modified: catalina/src/share/org/apache/catalina/core LocalStrings.properties StandardHost.java catalina/src/share/org/apache/catalina/startup HostConfig.java LocalStrings.properties Added: catalina/src/share/org/apache/catalina Deployer.java Log: Add a new extension of Container called Deployer, which represents a Container into which web applications can be deployed and undeployed. In the current environment, StandardHost is the only container implementation that is also a deployer -- but additional such containers can be created (such as the forthcoming specialized container for the Apache connector). Implemented the deploy() and undeploy() functionality in StandardHost. Now, Tomcat 4.0 is capable of "hot" application loading and unloading, given just a context path and the URL of a WAR file. This will be of tremendous use in administrative and management applications. As before, application deployment from a WAR file in the application base directory ($CATALINA_HOME/webapps for the default configuration) will cause the WAR to be automatically expanded. However, the new undeploy functionality also deletes the directory when the application is undeployed -- the theory is that the developer will be replacing the WAR anyway in this scenario, and this is a common complaint about the way that Tomcat 3.x works (once the directory is expanded the user has to remember to delete it). Revision Changes Path 1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Deployer.java Index: Deployer.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/Deployer.java,v 1.1 2000/10/04 23:03:03 craigmcc Exp $ * $Revision: 1.1 $ * $Date: 2000/10/04 23:03:03 $ * * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina; import java.io.IOException; import java.net.URL; /** * A Deployer is a specialized Container into which web applications * can be deployed and undeployed. Such a Container will create and install * child Context instances for each deployed application. The unique key * for each web application will be the context path to which it is attached. * * @author Craig R. McClanahan * @version $Revision: 1.1 $ $Date: 2000/10/04 23:03:03 $ */ public interface Deployer extends Container { // ----------------------------------------------------- Manifest Constants /** * The ContainerEvent event type sent when a new application is * deployed by deploy(). */ public static final String DEPLOY_EVENT = "deploy"; /** * The ContainerEvent event type sent when an existing application is * undeployed by undeploy(). */ public static final String UNDEPLOY_EVENT = "undeploy"; // --------------------------------------------------------- Public Methods /** * Deploy a new web application, whose web application archive is at the * specified URL, into this container with the specified context path. * A context path of "" (the empty string) should be used for the root * application for this container. Otherwise, the context path must * start with a slash. *

* If this application is successfully deployed, a ContainerEvent of type * DEPLOY_EVENT will be sent to all registered listeners, * with the newly created Context as an argument. * * @param contextPath The context path to which this application should * be deployed (must be unique) * @param war A URL of type "jar:" that points to a WAR file, or type * "file:" that points to an unpacked directory structure containing * the web application to be deployed * * @exception IllegalArgumentException if the specified context path * is malformed (it must be "" or start with a slash) * @exception IllegalArgumentException if the specified context path * is already attached to an existing web application * @exception IOException if an input/output error was encountered * during deployment */ public void deploy(String contextPath, URL war) throws IOException; /** * Return the Context for the deployed application that is associated * with the specified context path (if any); otherwise return * null. * * @param contextPath The context path of the requested web application */ public Context findDeployedApp(String contextPath); /** * Return the context paths of all deployed web applications in this * Container. If there are no deployed applications, a zero-length * array is returned. */ public String[] findDeployedApps(); /** * Undeploy an existing web application, attached to the specified context * path. If this application is successfully undeployed, a * ContainerEvent of type UNDEPLOY_EVENT will be sent to all * registered listeners, with the undeployed Context as * an argument. * * @param contextPath The context path of the application to be undeployed * * @exception IllegalArgumentException if the specified context path * is malformed (it must be "" or start with a slash) * @exception IllegalArgumentException if the specified context path does * not identify a currently deployed web application * @exception IOException if an input/output error occurs during * undeployment */ public void undeploy(String contextPath) throws IOException; } 1.11 +14 -1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/LocalStrings.properties,v retrieving revision 1.10 retrieving revision 1.11 diff -u -r1.10 -r1.11 --- LocalStrings.properties 2000/10/03 21:30:34 1.10 +++ LocalStrings.properties 2000/10/04 23:03:03 1.11 @@ -49,14 +49,27 @@ standardEngine.notStarted=Engine has not yet been started standardEngine.unfoundHost=Virtual host {0} not found standardEngine.unknownHost=No server host specified in this request +standardHost.accessBase=Cannot access document base directory {0} standardHost.alreadyStarted=Host has already been started -standardHost.mappingError=MAPPING configuration error for requset URI {0} +standardHost.appBase=Application base directory {0} does not exist +standardHost.deploying=Deploying web application at context path {0} from URL {1} +standardHost.deployError=Error deploying application at context path {0} +standardHost.docBase=Document base directory {0} already exists +standardHost.mappingError=MAPPING configuration error for request URI {0} standardHost.noContext=No Context configured to process this request standardHost.noHost=No Host configured to process this request standardHost.notContext=Child of a Host must be a Context standardHost.notStarted=Host has not yet been started standardHost.nullName=Host name is required +standardHost.pathFormat=Invalid context path: {0} +standardHost.pathMissing=Context path {0} is not currently in use +standardHost.pathRequired=Context path is required +standardHost.pathUsed=Context path {0} is already in use +standardHost.undeploying=Undeploying web application at context path {0} +standardHost.undeployError=Error undeploying application at context path {0} standardHost.unfoundContext=Cannot find context for request URI {0} +standardHost.warRequired=URL to web application archive is required +standardHost.warURL=Invalid URL for web application archive: {0} standardServer.addContainer.ise=No connectors available to associate this container with standardServer.start.connectors=At least one connector is not associated with any container standardServer.start.started=This server has already been started 1.3 +410 -5 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardHost.java Index: StandardHost.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardHost.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- StandardHost.java 2000/08/12 18:57:29 1.2 +++ StandardHost.java 2000/10/04 23:03:04 1.3 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardHost.java,v 1.2 2000/08/12 18:57:29 craigmcc Exp $ - * $Revision: 1.2 $ - * $Date: 2000/08/12 18:57:29 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/core/StandardHost.java,v 1.3 2000/10/04 23:03:04 craigmcc Exp $ + * $Revision: 1.3 $ + * $Date: 2000/10/04 23:03:04 $ * * ==================================================================== * @@ -65,15 +65,28 @@ package org.apache.catalina.core; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.InputStream; import java.io.IOException; +import java.net.JarURLConnection; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Container; import org.apache.catalina.Context; +import org.apache.catalina.Deployer; import org.apache.catalina.HttpRequest; import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleListener; import org.apache.catalina.Request; import org.apache.catalina.Response; @@ -84,12 +97,12 @@ * requests directed to a particular web application. * * @author Craig R. McClanahan - * @version $Revision: 1.2 $ $Date: 2000/08/12 18:57:29 $ + * @version $Revision: 1.3 $ $Date: 2000/10/04 23:03:04 $ */ public final class StandardHost extends ContainerBase - implements Host { + implements Deployer, Host { // ----------------------------------------------------------- Constructors @@ -122,6 +135,31 @@ /** + * The Java class name of the default context configuration class + * for deployed web applications. + */ + private String configClass = + "org.apache.catalina.startup.ContextConfig"; + + + /** + * The Java class name of the default Context implementation class for + * deployed web applications. + */ + private String contextClass = + "org.apache.catalina.core.StandardContext"; + + + /** + * The set of absolute pathnames to directories that were expanded + * from WAR files, keyed by context path. These entries may be used + * to indicate that the expanded directory is to be removed when the + * application is undeployed. + */ + private HashMap expanded = new HashMap(); + + + /** * The descriptive information string for this implementation. */ private static final String info = @@ -165,6 +203,60 @@ /** + * Return the Java class name of the context configuration class + * for new web applications. + */ + public String getConfigClass() { + + return (this.configClass); + + } + + + /** + * Set the Java class name of the context configuration class + * for new web applications. + * + * @param configClass The new context configuration class + */ + public void setConfigClass(String configClass) { + + String oldConfigClass = this.configClass; + this.configClass = configClass; + support.firePropertyChange("configClass", + oldConfigClass, this.configClass); + + } + + + /** + * Return the Java class name of the Context implementation class + * for new web applications. + */ + public String getContextClass() { + + return (this.contextClass); + + } + + + /** + * Set the Java class name of the Context implementation class + * for new web applications. + * + * @param contextClass The new context implementation class + */ + public void setContextClass(String contextClass) { + + String oldContextClass = this.contextClass; + this.contextClass = contextClass; + support.firePropertyChange("contextClass", + oldContextClass, this.contextClass); + + } + + + /** * Return the canonical, fully qualified, name of the virtual host * this Container represents. */ @@ -372,6 +464,188 @@ } + // ------------------------------------------------------- Deployer Methods + + + /** + * Deploy a new web application, whose web application archive is at the + * specified URL, into this container with the specified context path. + * A context path of "" (the empty string) should be used for the root + * application for this container. Otherwise, the context path must + * start with a slash. + *

+ * If this application is successfully deployed, a ContainerEvent of type + * DEPLOY_EVENT will be sent to all registered listeners, + * with the newly created Context as an argument. + * + * @param contextPath The context path to which this application should + * be deployed (must be unique) + * @param war A URL of type "jar:" that points to a WAR file, or type + * "file:" that points to an unpacked directory structure containing + * the web application to be deployed + * + * @exception IllegalArgumentException if the specified context path + * is malformed (it must be "" or start with a slash) + * @exception IllegalArgumentException if the specified context path + * is already attached to an existing web application + * @exception IOException if an input/output error was encountered + * during deployment + */ + public void deploy(String contextPath, URL war) throws IOException { + + // Validate the format and state of our arguments + if (contextPath == null) + throw new IllegalArgumentException + (sm.getString("standardHost.pathRequired")); + if (!contextPath.equals("") && !contextPath.startsWith("/")) + throw new IllegalArgumentException + (sm.getString("standardHost.pathFormat", contextPath)); + if (findDeployedApp(contextPath) != null) + throw new IllegalArgumentException + (sm.getString("standardHost.pathUsed", contextPath)); + if (war == null) + throw new IllegalArgumentException + (sm.getString("standardHost.warRequired")); + + // Prepare the local variables we will require + String url = war.toString(); + String docBase = null; + log(sm.getString("standardHost.deploying", contextPath, url)); + + // Expand a WAR archive into an unpacked directory if needed + if (url.startsWith("jar:")) + docBase = expand(war); + else if (url.startsWith("file://")) + docBase = url.substring(7); + else if (url.startsWith("file:")) + docBase = url.substring(5); + else + throw new IllegalArgumentException + (sm.getString("standardHost.warURL", url)); + + // Make sure the document base directory exists and is readable + File docBaseDir = new File(docBase); + if (!docBaseDir.exists() || !docBaseDir.isDirectory() || + !docBaseDir.canRead()) + throw new IllegalArgumentException + (sm.getString("standardHost.accessBase", docBase)); + + // Deploy this new web application + try { + Class clazz = Class.forName(contextClass); + Context context = (Context) clazz.newInstance(); + context.setPath(contextPath); + context.setDocBase(docBase); + if (context instanceof Lifecycle) { + clazz = Class.forName(configClass); + LifecycleListener listener = + (LifecycleListener) clazz.newInstance(); + ((Lifecycle) context).addLifecycleListener(listener); + } + addChild(context); + fireContainerEvent(DEPLOY_EVENT, context); + if (url.startsWith("jar:")) { + synchronized (expanded) { + if (debug >= 1) + log("Recording expanded app at path " + contextPath); + expanded.put(contextPath, docBase); + } + } + } catch (Exception e) { + log(sm.getString("standardHost.deployError", contextPath), e); + throw new IOException(e.toString()); + } + + } + + + /** + * Return the Context for the deployed application that is associated + * with the specified context path (if any); otherwise return + * null. + * + * @param contextPath The context path of the requested web application + */ + public Context findDeployedApp(String contextPath) { + + if (name == null) + return (null); + synchronized (children) { + return ((Context) children.get(contextPath)); + } + + } + + + /** + * Return the context paths of all deployed web applications in this + * Container. If there are no deployed applications, a zero-length + * array is returned. + */ + public String[] findDeployedApps() { + + synchronized (children) { + String results[] = new String[children.size()]; + return ((String[]) children.keySet().toArray(results)); + + } + + } + + + /** + * Undeploy an existing web application, attached to the specified context + * path. If this application is successfully undeployed, a + * ContainerEvent of type UNDEPLOY_EVENT will be sent to all + * registered listeners, with the undeployed Context as + * an argument. + * + * @param contextPath The context path of the application to be undeployed + * + * @exception IllegalArgumentException if the specified context path + * is malformed (it must be "" or start with a slash) + * @exception IllegalArgumentException if the specified context path does + * not identify a currently deployed web application + * @exception IOException if an input/output error occurs during + * undeployment + */ + public void undeploy(String contextPath) throws IOException { + + // Validate the format and state of our arguments + if (contextPath == null) + throw new IllegalArgumentException + (sm.getString("standardHost.pathRequired")); + if (!contextPath.equals("") && !contextPath.startsWith("/")) + throw new IllegalArgumentException + (sm.getString("standardHost.pathFormat", contextPath)); + Context context = findDeployedApp(contextPath); + if (context == null) + throw new IllegalArgumentException + (sm.getString("standardHost.pathMissing", contextPath)); + + // Undeploy this web application + log(sm.getString("standardHost.undeploying", contextPath)); + try { + removeChild(context); + } catch (Exception e) { + log(sm.getString("standardHost.undeployError", contextPath), e); + throw new IOException(e.toString()); + } + + // Remove the expanded directory if we created one + synchronized (expanded) { + String docBase = (String) expanded.get(contextPath); + if (docBase != null) { + if (debug >= 1) + log("Removing expanded directory " + docBase); + expanded.remove(contextPath); + remove(new File(docBase)); + } + } + + } + + // -------------------------------------------------------- Private Methods @@ -384,6 +658,137 @@ protected void addDefaultMapper(String mapperClass) { super.addDefaultMapper(this.mapperClass); + + } + + + /** + * Expand the WAR file found at the specified URL into an unpacked + * directory structure, and return the absolute pathname to the expanded + * directory. + * + * @param war URL of the web application archive to be expanded + * (must start with "jar:") + * + * @exception IllegalArgumentException if this is not a "jar:" URL + * @exception IOException if an input/output error was encountered + * during expansion + */ + protected String expand(URL war) throws IOException { + + // Calculate the directory name of the expanded directory + if (debug >= 1) + log("expand(" + war.toString() + ")"); + String pathname = war.toString(); + if (pathname.endsWith("!/")) + pathname = pathname.substring(0, pathname.length() - 2); + int period = pathname.lastIndexOf("."); + if (period >= pathname.length() - 4) + pathname = pathname.substring(0, period); + int slash = pathname.lastIndexOf("/"); + if (slash >= 0) + pathname = pathname.substring(slash + 1); + if (debug >= 1) + log(" Proposed directory name: " + pathname); + + // Make sure that there is no such directory already existing + File appBase = new File(getAppBase()); + if (!appBase.isAbsolute()) + appBase = new File(System.getProperty("catalina.home"), + getAppBase()); + if (!appBase.exists() || !appBase.isDirectory()) + throw new IOException + (sm.getString("standardHost.appBase", + appBase.getAbsolutePath())); + File docBase = new File(appBase, pathname); + if (docBase.exists()) + throw new IOException + (sm.getString("standardHost.docBase", + docBase.getAbsolutePath())); + docBase.mkdir(); + + // Expand the WAR into the new document base directory + JarFile jarFile = + ((JarURLConnection) war.openConnection()).getJarFile(); + Enumeration jarEntries = jarFile.entries(); + while (jarEntries.hasMoreElements()) { + JarEntry jarEntry = (JarEntry) jarEntries.nextElement(); + String name = jarEntry.getName(); + int last = name.lastIndexOf("/"); + if (last >= 0) { + File parent = new File(docBase, + name.substring(0, last)); + if (debug >= 2) + log(" Creating parent directory " + parent); + parent.mkdirs(); + } + if (name.endsWith("/")) + continue; + if (debug >= 2) + log(" Creating expanded file " + name); + InputStream input = jarFile.getInputStream(jarEntry); + expand(input, docBase, name); + } + jarFile.close(); + + // Return the absolute path to our new document base directory + return (docBase.getAbsolutePath()); + + } + + + /** + * Expand the specified input stream into the specified directory, creating + * a file named from the specified relative path. + * + * @param input InputStream to be copied + * @param docBase Document base directory into which we are expanding + * @param name Relative pathname of the file to be created + * + * @exception IOException if an input/output error occurs + */ + protected void expand(InputStream input, File docBase, String name) + throws IOException { + + File file = new File(docBase, name); + BufferedOutputStream output = + new BufferedOutputStream(new FileOutputStream(file)); + byte buffer[] = new byte[2048]; + while (true) { + int n = input.read(buffer); + if (n <= 0) + break; + output.write(buffer, 0, n); + } + output.close(); + input.close(); + + } + + + /** + * Remove the specified directory and all of its contents. + * + * @param dir Directory to be removed + * + * @exception IOException if an input/output error occurs + */ + protected void remove(File dir) throws IOException { + + String list[] = dir.list(); + for (int i = 0; i < list.length; i++) { + File file = new File(dir, list[i]); + if (file.isDirectory()) { + remove(file); + } else { + if (!file.delete()) + throw new IOException("Cannot delete file " + + file.getAbsolutePath()); + } + } + if (!dir.delete()) + throw new IOException("Cannot delete directory " + + dir.getAbsolutePath()); } 1.2 +106 -153 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/HostConfig.java Index: HostConfig.java =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/HostConfig.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- HostConfig.java 2000/08/11 23:38:41 1.1 +++ HostConfig.java 2000/10/04 23:03:05 1.2 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/HostConfig.java,v 1.1 2000/08/11 23:38:41 craigmcc Exp $ - * $Revision: 1.1 $ - * $Date: 2000/08/11 23:38:41 $ + * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/HostConfig.java,v 1.2 2000/10/04 23:03:05 craigmcc Exp $ + * $Revision: 1.2 $ + * $Date: 2000/10/04 23:03:05 $ * * ==================================================================== * @@ -79,6 +79,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.apache.catalina.Context; +import org.apache.catalina.Deployer; import org.apache.catalina.Host; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleEvent; @@ -92,7 +93,7 @@ * of that Host, and the associated defined contexts. * * @author Craig R. McClanahan - * @version $Revision: 1.1 $ $Date: 2000/08/11 23:38:41 $ + * @version $Revision: 1.2 $ $Date: 2000/10/04 23:03:05 $ */ public final class HostConfig @@ -248,161 +249,87 @@ /** - * Deploy any directories found in our "application root" directory that - * look like they contain a web application. For the purposes of this - * method, the directory must contain a WEB-INF subdirectory that contains - * a web application deployment descriptor file (web.xml). + * Deploy applications for any directories or WAR files that are found + * in our "application root" directory. */ - private void deploy() { + private void deployApps() { - if (debug >= 1) - log(sm.getString("hostConfig.deploying")); - - // Discover and deploy web application directories as necessary - File appBase = appBase(); - if (!appBase.exists() || !appBase.isDirectory()) - return; - String files[] = appBase.list(); + if (!(host instanceof Deployer)) + return; + if (debug >= 1) + log(sm.getString("hostConfig.deploying")); + + File appBase = appBase(); + if (!appBase.exists() || !appBase.isDirectory()) + return; + String files[] = appBase.list(); + + for (int i = 0; i < files.length; i++) { + + if (files[i].equalsIgnoreCase("META-INF")) + continue; + if (files[i].equalsIgnoreCase("WEB-INF")) + continue; + File dir = new File(appBase, files[i]); + if (dir.isDirectory()) { + + // Make sure there is an application deployment descriptor + File webXml = new File(dir, "/WEB-INF/web.xml"); + if (!webXml.exists() || !webXml.isFile() || + !webXml.canRead()) + continue; + + // Calculate the context path and make sure it is unique + String contextPath = "/" + files[i]; + if (files[i].equals("ROOT")) + contextPath = ""; + if (host.findChild(contextPath) != null) + continue; + + // Deploy the application in this directory + if (debug >= 1) + log(sm.getString("hostConfig.deployDir", files[i])); + try { + URL url = new URL("file", null, dir.getAbsolutePath()); + ((Deployer) host).deploy(contextPath, url); + } catch (Throwable t) { + log(sm.getString("hostConfig.deployDir.error", files[i]), + t); + } + + } else if (files[i].toLowerCase().endsWith(".war")) { + + // Calculate the context path and make sure it is unique + String contextPath = "/" + files[i]; + int period = contextPath.lastIndexOf("."); + if (period >= 0) + contextPath = contextPath.substring(0, period); + if (contextPath.equals("/ROOT")) + contextPath = ""; + if (host.findChild(contextPath) != null) + continue; + + // Deploy the application in this WAR file + if (debug >= 1) + log(sm.getString("hostConfig.deployJar", files[i])); + try { + URL url = new URL("file", null, dir.getAbsolutePath()); + url = new URL("jar:" + url.toString() + "!/"); + ((Deployer) host).deploy(contextPath, url); + } catch (Throwable t) { + log(sm.getString("hostConfig.deployJar.error", files[i]), + t); + } - for (int i = 0; i < files.length; i++) { + } - File dir = new File(appBase, files[i]); - if (!dir.isDirectory()) - continue; - File webXml = new File(dir, "/WEB-INF/web.xml"); - if (!webXml.exists() || !webXml.isFile() || - !webXml.canRead()) - continue; - String contextPath = "/" + files[i]; - if (files[i].equals("ROOT")) - contextPath = ""; - if (host.findChild(contextPath) != null) - continue; - - log(sm.getString("hostConfig.deploy", files[i])); - try { - Class clazz = Class.forName(contextClass); - Context context = - (Context) clazz.newInstance(); - context.setPath(contextPath); - context.setDocBase(files[i]); - if (context instanceof Lifecycle) { - clazz = Class.forName(configClass); - LifecycleListener listener = - (LifecycleListener) clazz.newInstance(); - ((Lifecycle) context).addLifecycleListener(listener); - } - host.addChild(context); - } catch (Exception e) { - log(sm.getString("hostConfig.deploy.error", files[i]), e); - } - - } + } } - /** - * Expand any JAR files found in our "application root" directory that - * do not have a corresponding directory without the ".jar" extension. - */ - private void expand() { - - if (debug >= 1) - log(sm.getString("hostConfig.expanding")); - - // Discover and expand WAR files as necessary - File appBase = appBase(); - if (!appBase.exists() || !appBase.isDirectory()) - return; - String files[] = appBase.list(); - - for (int i = 0; i < files.length; i++) { - - // Is this file a WAR that needs to be expanded? - File file = new File(appBase, files[i]); - if (file.isDirectory()) - continue; - String filename = files[i].toLowerCase(); - if (!filename.endsWith(".war")) - continue; - - // Has this WAR been expanded previously? - File dir = new File(appBase, - files[i].substring(0, files[i].length() - 4)); - if (dir.exists()) - continue; - - log(sm.getString("hostConfig.expand", files[i])); - JarFile jarFile = null; - try { - dir.mkdirs(); - jarFile = new JarFile(new File(appBase, files[i])); - Enumeration jarEntries = jarFile.entries(); - while (jarEntries.hasMoreElements()) { - JarEntry jarEntry = (JarEntry) jarEntries.nextElement(); - String name = jarEntry.getName(); - int slash = name.lastIndexOf("/"); - if (slash >= 0) { - File parent = new File(dir, - name.substring(0, slash)); - if (debug >= 2) - log(" Creating parent directory " + parent); - parent.mkdirs(); - } - if (name.endsWith("/")) - continue; - if (debug >= 2) - log(" Creating expanded file " + name); - InputStream input = jarFile.getInputStream(jarEntry); - expand(input, dir, name); - } - } catch (Exception e) { - log(sm.getString("hostConfig.expand.error", files[i]), e); - if (jarFile != null) { - try { - jarFile.close(); - } catch (Exception f) { - ; - } - } - } - - } - - } - /** - * Expand the specified input stream into the specified directory, into - * a file named from the specified relative filename path. - * - * @param input InputStream to be copied - * @param directory Base directory into which the file is created - * @param path Relative pathname of the file to be created - * - * @exception IOException if any processing exception occurs - */ - private void expand(InputStream input, File directory, String path) - throws IOException { - - File file = new File(directory, path); - BufferedOutputStream output = - new BufferedOutputStream(new FileOutputStream(file)); - byte buffer[] = new byte[2048]; - while (true) { - int n = input.read(buffer); - if (n <= 0) - break; - output.write(buffer, 0, n); - } - output.close(); - input.close(); - - } - - - /** * Log a message on the Logger associated with our Host (if any) * * @param message Message to be logged @@ -450,11 +377,10 @@ */ private void start() { - if (debug > 0) + if (debug >= 1) log(sm.getString("hostConfig.start")); - expand(); - deploy(); + deployApps(); } @@ -464,8 +390,35 @@ */ private void stop() { - if (debug > 0) + if (debug >= 1) log(sm.getString("hostConfig.stop")); + + undeployApps(); + + } + + + /** + * Undeploy all deployed applications. + */ + private void undeployApps() { + + if (!(host instanceof Deployer)) + return; + if (debug >= 1) + log(sm.getString("hostConfig.undeploying")); + + String contextPaths[] = ((Deployer) host).findDeployedApps(); + for (int i = 0; i < contextPaths.length; i++) { + if (debug >= 1) + log(sm.getString("hostConfig.undeploy", contextPaths[i])); + try { + ((Deployer) host).undeploy(contextPaths[i]); + } catch (Throwable t) { + log(sm.getString("hostConfig.undeploy.error", + contextPaths[i]), t); + } + } } 1.8 +8 -1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/startup/LocalStrings.properties,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- LocalStrings.properties 2000/10/03 21:30:44 1.7 +++ LocalStrings.properties 2000/10/04 23:03:05 1.8 @@ -36,13 +36,20 @@ engineConfig.stop=EngineConfig: Processing STOP hostConfig.cce=Lifecycle event data object {0} is not a Host hostConfig.deploy=Deploying web application directory {0} +hostConfig.deployDir=Deploying web application directory {0} +hostConfig.deployDir.error=Error deploying web application directory {0} +hostConfig.deployJar=Deploying web application archive {0} +hostConfig.deployJar.error=Error deploying web application archive {0} hostConfig.deploy.error=Exception while deploying web application directory {0} -hostConfig.deploying=Deploying discovered web application directories +hostConfig.deploying=Deploying discovered web applications hostConfig.expand=Expanding web application archive {0} hostConfig.expand.error=Exception while expanding web application archive {0} hostConfig.expanding=Expanding discovered web application archives hostConfig.start=HostConfig: Processing START hostConfig.stop=HostConfig: Processing STOP +hostConfig.undeploy=Undeploying web application at context path {0} +hostConfig.undeploy.error=Error undeploying web application at context path {0} +hostConfig.undeploying=Undeploying deployed web applications userConfig.database=Exception loading user database userConfig.deploy=Deploying web application for user {0} userConfig.error=Error deploying web application for user {0}