Java tutorial
/* * ==================================================================== * * 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. *catalina run * 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 * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.core; import java.io.IOException; import java.lang.reflect.Method; import java.net.URL; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.DefaultContext; import org.apache.catalina.Deployer; import org.apache.catalina.Host; import org.apache.catalina.LifecycleException; import org.apache.catalina.Valve; import org.apache.catalina.valves.ValveBase; import org.apache.commons.modeler.Registry; /** * Standard implementation of the <b>Host</b> interface. Each * child container must be a Context implementation to process the * requests directed to a particular web application. * * @author Craig R. McClanahan * @author Remy Maucherat * @version $Revision: 1.27 $ $Date: 2004/01/08 05:32:25 $ */ public class StandardHost extends ContainerBase implements Deployer, Host { /* Why do we implement deployer and delegate to deployer ??? */ private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory .getLog(StandardHost.class); // ----------------------------------------------------------- Constructors /** * Create a new StandardHost component with the default basic Valve. */ public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); } // ----------------------------------------------------- Instance Variables /** * The set of aliases for this Host. */ private String[] aliases = new String[0]; /** * The application root for this Host. */ private String appBase = "."; /** * The auto deploy flag for this Host. */ private boolean autoDeploy = true; /** * 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 <code>Deployer</code> to whom we delegate application * deployment requests. */ private Deployer deployer = null; /** * The deploy on startup flag for this Host. */ private boolean deployOnStartup = true; /** * deploy Context XML config files property. */ private boolean deployXML = true; /** * The Java class name of the default error reporter implementation class * for deployed web applications. */ private String errorReportValveClass = "org.apache.catalina.valves.ErrorReportValve"; /** * The object name for the errorReportValve. */ private ObjectName errorReportValveObjectName = null; /** * The descriptive information string for this implementation. */ private static final String info = "org.apache.catalina.core.StandardHost/1.0"; /** * The live deploy flag for this Host. */ private boolean liveDeploy = true; /** * Unpack WARs property. */ private boolean unpackWARs = true; /** * Work Directory base for applications. */ private String workDir = null; /** * DefaultContext config */ private DefaultContext defaultContext; /** * Attribute value used to turn on/off XML validation */ private boolean xmlValidation = false; /** * Attribute value used to turn on/off XML namespace awarenes. */ private boolean xmlNamespaceAware = false; // ------------------------------------------------------------- Properties /** * Return the application root for this Host. This can be an absolute * pathname, a relative pathname, or a URL. */ public String getAppBase() { return (this.appBase); } /** * Set the application root for this Host. This can be an absolute * pathname, a relative pathname, or a URL. * * @param appBase The new application root */ public void setAppBase(String appBase) { String oldAppBase = this.appBase; this.appBase = appBase; support.firePropertyChange("appBase", oldAppBase, this.appBase); } /** * Return the value of the auto deploy flag. If true, it indicates that * this host's child webapps will be dynamically deployed. */ public boolean getAutoDeploy() { return (this.autoDeploy); } /** * Set the auto deploy flag value for this host. * * @param autoDeploy The new auto deploy flag */ public void setAutoDeploy(boolean autoDeploy) { boolean oldAutoDeploy = this.autoDeploy; this.autoDeploy = autoDeploy; support.firePropertyChange("autoDeploy", oldAutoDeploy, this.autoDeploy); } /** * 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); } /** * Set the DefaultContext * for new web applications. * * @param defaultContext The new DefaultContext */ public void addDefaultContext(DefaultContext defaultContext) { DefaultContext oldDefaultContext = this.defaultContext; this.defaultContext = defaultContext; support.firePropertyChange("defaultContext", oldDefaultContext, this.defaultContext); } /** * Retrieve the DefaultContext for new web applications. */ public DefaultContext getDefaultContext() { return (this.defaultContext); } /** * 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 value of the deploy on startup flag. If true, it indicates * that this host's child webapps should be discovred and automatically * deployed at startup time. */ public boolean getDeployOnStartup() { return (this.deployOnStartup); } /** * Set the deploy on startup flag value for this host. * * @param autoDeploy The new deploy on startup flag */ public void setDeployOnStartup(boolean deployOnStartup) { boolean oldDeployOnStartup = this.deployOnStartup; this.deployOnStartup = deployOnStartup; support.firePropertyChange("deployOnStartup", oldDeployOnStartup, this.deployOnStartup); } /** * Deploy XML Context config files flag accessor. */ public boolean isDeployXML() { return (deployXML); } /** * Deploy XML Context config files flag mutator. */ public void setDeployXML(boolean deployXML) { this.deployXML = deployXML; } /** * Return the value of the live deploy flag. If true, it indicates that * a background thread should be started that looks for web application * context files, WAR files, or unpacked directories being dropped in to * the <code>appBase</code> directory, and deploys new ones as they are * encountered. */ public boolean getLiveDeploy() { return (this.autoDeploy); } /** * Set the live deploy flag value for this host. * * @param liveDeploy The new live deploy flag */ public void setLiveDeploy(boolean liveDeploy) { setAutoDeploy(liveDeploy); } /** * Return the Java class name of the error report valve class * for new web applications. */ public String getErrorReportValveClass() { return (this.errorReportValveClass); } /** * Set the Java class name of the error report valve class * for new web applications. * * @param errorReportValveClass The new error report valve class */ public void setErrorReportValveClass(String errorReportValveClass) { String oldErrorReportValveClassClass = this.errorReportValveClass; this.errorReportValveClass = errorReportValveClass; support.firePropertyChange("errorReportValveClass", oldErrorReportValveClassClass, this.errorReportValveClass); } /** * Return the canonical, fully qualified, name of the virtual host * this Container represents. */ public String getName() { return (name); } /** * Set the canonical, fully qualified, name of the virtual host * this Container represents. * * @param name Virtual host name * * @exception IllegalArgumentException if name is null */ public void setName(String name) { if (name == null) throw new IllegalArgumentException(sm.getString("standardHost.nullName")); name = name.toLowerCase(); // Internally all names are lower case String oldName = this.name; this.name = name; support.firePropertyChange("name", oldName, this.name); } /** * Unpack WARs flag accessor. */ public boolean isUnpackWARs() { return (unpackWARs); } /** * Unpack WARs flag mutator. */ public void setUnpackWARs(boolean unpackWARs) { this.unpackWARs = unpackWARs; } /** * Set the validation feature of the XML parser used when * parsing xml instances. * @param xmlValidation true to enable xml instance validation */ public void setXmlValidation(boolean xmlValidation) { this.xmlValidation = xmlValidation; } /** * Get the server.xml <host> attribute's xmlValidation. * @return true if validation is enabled. * */ public boolean getXmlValidation() { return xmlValidation; } /** * Get the server.xml <host> attribute's xmlNamespaceAware. * @return true if namespace awarenes is enabled. * */ public boolean getXmlNamespaceAware() { return xmlNamespaceAware; } /** * Set the namespace aware feature of the XML parser used when * parsing xml instances. * @param xmlNamespaceAware true to enable namespace awareness */ public void setXmlNamespaceAware(boolean xmlNamespaceAware) { this.xmlNamespaceAware = xmlNamespaceAware; } /** * Host work directory base. */ public String getWorkDir() { return (workDir); } /** * Host work directory base. */ public void setWorkDir(String workDir) { this.workDir = workDir; } // --------------------------------------------------------- Public Methods /** * Install the StandardContext portion of the DefaultContext * configuration into current Context. * * @param context current web application context */ public void installDefaultContext(Context context) { if (defaultContext != null && defaultContext instanceof StandardDefaultContext) { ((StandardDefaultContext) defaultContext).installDefaultContext(context); } } /** * Import the DefaultContext config into a web application context. * * @param context web application context to import default context */ public void importDefaultContext(Context context) { if (this.defaultContext != null) this.defaultContext.importDefaultContext(context); } /** * Add an alias name that should be mapped to this same Host. * * @param alias The alias to be added */ public void addAlias(String alias) { alias = alias.toLowerCase(); // Skip duplicate aliases for (int i = 0; i < aliases.length; i++) { if (aliases[i].equals(alias)) return; } // Add this alias to the list String newAliases[] = new String[aliases.length + 1]; for (int i = 0; i < aliases.length; i++) newAliases[i] = aliases[i]; newAliases[aliases.length] = alias; aliases = newAliases; // Inform interested listeners fireContainerEvent(ADD_ALIAS_EVENT, alias); } /** * Add a child Container, only if the proposed child is an implementation * of Context. * * @param child Child container to be added */ public void addChild(Container child) { if (!(child instanceof Context)) throw new IllegalArgumentException(sm.getString("standardHost.notContext")); super.addChild(child); } /** * Return the set of alias names for this Host. If none are defined, * a zero length array is returned. */ public String[] findAliases() { return (this.aliases); } /** * Return descriptive information about this Container implementation and * the corresponding version number, in the format * <code><description>/<version></code>. */ public String getInfo() { return (info); } /** * Return the Context that would be used to process the specified * host-relative request URI, if any; otherwise return <code>null</code>. * * @param uri Request URI to be mapped */ public Context map(String uri) { if (log.isDebugEnabled()) log.debug("Mapping request URI '" + uri + "'"); if (uri == null) return (null); // Match on the longest possible context path prefix if (log.isTraceEnabled()) log.trace(" Trying the longest context path prefix"); Context context = null; String mapuri = uri; while (true) { context = (Context) findChild(mapuri); if (context != null) break; int slash = mapuri.lastIndexOf('/'); if (slash < 0) break; mapuri = mapuri.substring(0, slash); } // If no Context matches, select the default Context if (context == null) { if (log.isTraceEnabled()) log.trace(" Trying the default context"); context = (Context) findChild(""); } // Complain if no Context has been selected if (context == null) { log.error(sm.getString("standardHost.mappingError", uri)); return (null); } // Return the mapped Context (if any) if (log.isDebugEnabled()) log.debug(" Mapped to context '" + context.getPath() + "'"); return (context); } /** * Remove the specified alias name from the aliases for this Host. * * @param alias Alias name to be removed */ public void removeAlias(String alias) { alias = alias.toLowerCase(); synchronized (aliases) { // Make sure this alias is currently present int n = -1; for (int i = 0; i < aliases.length; i++) { if (aliases[i].equals(alias)) { n = i; break; } } if (n < 0) return; // Remove the specified alias int j = 0; String results[] = new String[aliases.length - 1]; for (int i = 0; i < aliases.length; i++) { if (i != n) results[j++] = aliases[i]; } aliases = results; } // Inform interested listeners fireContainerEvent(REMOVE_ALIAS_EVENT, alias); } /** * Return a String representation of this component. */ public String toString() { StringBuffer sb = new StringBuffer(); if (getParent() != null) { sb.append(getParent().toString()); sb.append("."); } sb.append("StandardHost["); sb.append(getName()); sb.append("]"); return (sb.toString()); } /** * Start this host. * * @exception LifecycleException if this component detects a fatal error * that prevents it from being started */ public synchronized void start() throws LifecycleException { if (started) { return; } if (!initialized) init(); // Look for a realm - that may have been configured earlier. // If the realm is added after context - it'll set itself. if (realm == null) { ObjectName realmName = null; try { realmName = new ObjectName(domain + ":type=Host,host=" + getName()); if (mserver.isRegistered(realmName)) { mserver.invoke(realmName, "setContext", new Object[] { this }, new String[] { "org.apache.catalina.Container" }); } } catch (Throwable t) { log.debug("No realm for this host " + realmName); } } // Set error report valve if ((errorReportValveClass != null) && (!errorReportValveClass.equals(""))) { try { boolean found = false; if (errorReportValveObjectName != null) { ObjectName[] names = ((StandardPipeline) pipeline).getValveObjectNames(); for (int i = 0; !found && i < names.length; i++) if (errorReportValveObjectName.equals(names[i])) found = true; } if (!found) { Valve valve = (Valve) Class.forName(errorReportValveClass).newInstance(); addValve(valve); errorReportValveObjectName = ((ValveBase) valve).getObjectName(); } } catch (Throwable t) { log.error(sm.getString("standardHost.invalidErrorReportValveClass", errorReportValveClass)); } } if (xmlValidation) log.info(sm.getString("standardHost.validationEnabled")); else log.info(sm.getString("standardHost.validationDisabled")); super.start(); } /** * Execute a periodic task, such as reloading, etc. This method will be * invoked inside the classloading context of this container. Unexpected * throwables will be caught and logged. */ public void backgroundProcess() { lifecycle.fireLifecycleEvent("check", null); } // ------------------------------------------------------- Deployer Methods /** * Install 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. * <p> * If this application is successfully installed, a ContainerEvent of type * <code>INSTALL_EVENT</code> will be sent to all registered listeners, * with the newly created <code>Context</code> as an argument. * * @param contextPath The context path to which this application should * be installed (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 installed * * @exception IllegalArgumentException if the specified context path * is malformed (it must be "" or start with a slash) * @exception IllegalStateException if the specified context path * is already attached to an existing web application * @exception IOException if an input/output error was encountered * during install */ public void install(String contextPath, URL war) throws IOException { getDeployer().install(contextPath, war); } /** * <p>Install a new web application, whose context configuration file * (consisting of a <code><Context></code> element) and web * application archive are at the specified URLs.</p> * * <p>If this application is successfully installed, a ContainerEvent * of type <code>INSTALL_EVENT</code> will be sent to all registered * listeners, with the newly created <code>Context</code> as an argument. * </p> * * @param config A URL that points to the context configuration file to * be used for configuring the new Context * @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 installed * * @exception IllegalArgumentException if one of the specified URLs is * null * @exception IllegalStateException if the context path specified in the * context configuration file is already attached to an existing web * application * @exception IOException if an input/output error was encountered * during installation */ public synchronized void install(URL config, URL war) throws IOException { getDeployer().install(config, war); } /** * Return the Context for the deployed application that is associated * with the specified context path (if any); otherwise return * <code>null</code>. * * @param contextPath The context path of the requested web application */ public Context findDeployedApp(String contextPath) { return (getDeployer().findDeployedApp(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() { return (getDeployer().findDeployedApps()); } /** * Remove an existing web application, attached to the specified context * path. If this application is successfully removed, a * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all * registered listeners, with the removed <code>Context</code> as * an argument. * * @param contextPath The context path of the application to be removed * * @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 installed web application * @exception IOException if an input/output error occurs during * removal */ public void remove(String contextPath) throws IOException { getDeployer().remove(contextPath); } /** * Remove an existing web application, attached to the specified context * path. If this application is successfully removed, a * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all * registered listeners, with the removed <code>Context</code> as * an argument. Deletes the web application war file and/or directory * if they exist in the Host's appBase. * * @param contextPath The context path of the application to be removed * @param undeploy boolean flag to remove web application from server * * @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 installed web application * @exception IOException if an input/output error occurs during * removal */ public void remove(String contextPath, boolean undeploy) throws IOException { getDeployer().remove(contextPath, undeploy); } /** * Start an existing web application, attached to the specified context * path. Only starts a web application if it is not running. * * @param contextPath The context path of the application to be started * * @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 installed web application * @exception IOException if an input/output error occurs during * startup */ public void start(String contextPath) throws IOException { getDeployer().start(contextPath); } /** * Stop an existing web application, attached to the specified context * path. Only stops a web application if it is running. * * @param contextPath The context path of the application to be stopped * * @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 installed web application * @exception IOException if an input/output error occurs while stopping * the web application */ public void stop(String contextPath) throws IOException { getDeployer().stop(contextPath); } // ------------------------------------------------------ Protected Methods static String STANDARD_HOST_DEPLOYER = "org.apache.catalina.core.StandardHostDeployer"; public Deployer getDeployer() { if (deployer != null) return deployer; log.info("Create Host deployer for direct deployment ( non-jmx ) "); try { Class c = Class.forName(STANDARD_HOST_DEPLOYER); deployer = (Deployer) c.newInstance(); Method m = c.getMethod("setHost", new Class[] { Host.class }); m.invoke(deployer, new Object[] { this }); } catch (Throwable t) { log.error("Error creating deployer ", t); } return deployer; } public void setDeployer(Deployer d) { this.deployer = d; } // -------------------- JMX -------------------- /** * Return the MBean Names of the Valves assoicated with this Host * * @exception Exception if an MBean cannot be created or registered */ public String[] getValveNames() throws Exception { Valve[] valves = this.getValves(); String[] mbeanNames = new String[valves.length]; for (int i = 0; i < valves.length; i++) { if (valves[i] == null) continue; if (((ValveBase) valves[i]).getObjectName() == null) continue; mbeanNames[i] = ((ValveBase) valves[i]).getObjectName().toString(); } return mbeanNames; } public String[] getAliases() { return aliases; } private boolean initialized = false; public void init() { if (initialized) return; initialized = true; // already registered. if (getParent() == null) { try { // Register with the Engine ObjectName serviceName = new ObjectName(domain + ":type=Engine"); if (mserver.isRegistered(serviceName)) { log.debug("Registering with the Engine"); mserver.invoke(serviceName, "addChild", new Object[] { this }, new String[] { "org.apache.catalina.Container" }); } } catch (Exception ex) { ex.printStackTrace(); } } if (oname == null) { // not registered in JMX yet - standalone mode try { StandardEngine engine = (StandardEngine) parent; domain = engine.getName(); log.debug("Register " + domain); oname = new ObjectName(domain + ":type=Host,host=" + this.getName()); Registry.getRegistry().registerComponent(this, oname, null); } catch (Throwable t) { log.info("Error registering ", t); } } } public ObjectName preRegister(MBeanServer server, ObjectName oname) throws Exception { ObjectName res = super.preRegister(server, oname); String name = oname.getKeyProperty("host"); if (name != null) setName(name); return res; } public ObjectName createObjectName(String domain, ObjectName parent) throws Exception { if (log.isDebugEnabled()) log.debug("Create ObjectName " + domain + " " + parent); return new ObjectName(domain + ":type=Host,host=" + getName()); } }