| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| package org.apache.catalina.core; |
| |
| |
| import javax.management.MBeanServer; |
| import javax.management.ObjectName; |
| |
| import org.apache.catalina.Container; |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Host; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.Valve; |
| import org.apache.catalina.startup.HostConfig; |
| 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$ $Date$ |
| */ |
| |
| public class StandardHost |
| extends ContainerBase |
| implements 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 host config configuration class (deployer) |
| */ |
| private String hostConfigClass = |
| "org.apache.catalina.startup.HostConfig"; |
| |
| |
| /** |
| * The Java class name of the default Context implementation class for |
| * deployed web applications. |
| */ |
| private String contextClass = |
| "org.apache.catalina.core.StandardContext"; |
| |
| |
| /** |
| * 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 descriptive information string for this implementation. |
| */ |
| private static final String info = |
| "org.apache.catalina.core.StandardHost/1.0"; |
| |
| |
| /** |
| * Unpack WARs property. |
| */ |
| private boolean unpackWARs = true; |
| |
| |
| /** |
| * Work Directory base for applications. |
| */ |
| private String workDir = null; |
| |
| |
| /** |
| * 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); |
| |
| } |
| |
| |
| /** |
| * Return the Java class name of the host configuration class (deployer) |
| */ |
| public String getHostConfigClass() { |
| |
| return (this.hostConfigClass); |
| |
| } |
| |
| |
| /** |
| * Set the Java class name of the host config configuration class (deployer) |
| * |
| * @param hostConfigClass The new host config configuration class (deployer) |
| */ |
| public void setHostConfigClass(String hostConfigClass) { |
| |
| String oldHostConfigClass = this.hostConfigClass; |
| this.hostConfigClass = hostConfigClass; |
| support.firePropertyChange("hostConfigClass", |
| oldHostConfigClass, this.hostConfigClass); |
| } |
| |
| |
| /** |
| * 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 deployOnStartup 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 |
| |
| |
| /** |
| * 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=Realm,host=" + getName()); |
| if( mserver.isRegistered(realmName ) ) { |
| mserver.invoke(realmName, "init", |
| new Object[] {}, |
| new String[] {} |
| ); |
| } |
| } catch( Throwable t ) { |
| log.debug("No realm for this host " + realmName); |
| } |
| } |
| |
| // Set error report valve |
| if ((errorReportValveClass != null) |
| && (!errorReportValveClass.equals(""))) { |
| try { |
| boolean found = false; |
| Valve[] valves = |
| ((StandardPipeline)pipeline).getValves(); |
| for (int i=0; !found && i<valves.length; i++) |
| if(errorReportValveClass.equals( |
| valves[i].getClass().getName())) |
| found = true ; |
| if(!found) { |
| Valve valve = (Valve) Class.forName(errorReportValveClass) |
| .newInstance(); |
| addValve(valve); |
| } |
| } catch (Throwable t) { |
| log.error(sm.getString |
| ("standardHost.invalidErrorReportValveClass", |
| errorReportValveClass), t); |
| } |
| } |
| if(log.isInfoEnabled()) { |
| if (xmlValidation) |
| log.info( sm.getString("standardHost.validationEnabled")); |
| else |
| log.info( sm.getString("standardHost.validationDisabled")); |
| } |
| super.start(); |
| |
| } |
| |
| |
| // -------------------- 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"); |
| |
| HostConfig deployer = null; |
| try { |
| String hostConfigClassname = getHostConfigClass(); |
| if (hostConfigClassname != null) { |
| Class clazz = Class.forName(hostConfigClassname); |
| deployer = (HostConfig) clazz.newInstance(); |
| } else { |
| deployer = new HostConfig(); |
| } |
| } catch (Exception e) { |
| log.warn("Error creating HostConfig for host " + name, e); |
| throw e; |
| } |
| addLifecycleListener(deployer); |
| if( mserver.isRegistered( serviceName )) { |
| if(log.isDebugEnabled()) |
| log.debug("Registering "+ serviceName +" with the Engine"); |
| mserver.invoke( serviceName, "addChild", |
| new Object[] { this }, |
| new String[] { "org.apache.catalina.Container" } ); |
| } |
| } catch( Exception ex ) { |
| log.error("Host registering failed!",ex); |
| } |
| } |
| |
| if( oname==null ) { |
| // not registered in JMX yet - standalone mode |
| try { |
| StandardEngine engine=(StandardEngine)parent; |
| domain=engine.getName(); |
| if(log.isDebugEnabled()) |
| log.debug( "Register host " + getName() + " with domain "+ domain ); |
| oname=new ObjectName(domain + ":type=Host,host=" + |
| this.getName()); |
| controller = oname; |
| Registry.getRegistry(null, null) |
| .registerComponent(this, oname, null); |
| } catch( Throwable t ) { |
| log.error("Host registering failed!", t ); |
| } |
| } |
| } |
| |
| public void destroy() throws Exception { |
| // destroy our child containers, if any |
| Container children[] = findChildren(); |
| super.destroy(); |
| for (int i = 0; i < children.length; i++) { |
| if(children[i] instanceof StandardContext) |
| ((StandardContext)children[i]).destroy(); |
| } |
| |
| } |
| |
| 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()); |
| } |
| |
| } |