diff --git a/java/org/apache/catalina/Container.java b/java/org/apache/catalina/Container.java index 73b265e..5ca3c09 100644 --- a/java/org/apache/catalina/Container.java +++ b/java/org/apache/catalina/Container.java @@ -1,479 +1,496 @@ -/* - * 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; - - -import java.beans.PropertyChangeListener; -import java.io.IOException; - -import javax.management.ObjectName; -import javax.naming.directory.DirContext; -import javax.servlet.ServletException; - -import org.apache.catalina.connector.Request; -import org.apache.catalina.connector.Response; -import org.apache.juli.logging.Log; - - -/** - * A Container is an object that can execute requests received from - * a client, and return responses based on those requests. A Container may - * optionally support a pipeline of Valves that process the request in an - * order configured at runtime, by implementing the Pipeline interface - * as well. - *

- * Containers will exist at several conceptual levels within Catalina. The - * following examples represent common cases: - *

- * A given deployment of Catalina need not include Containers at all of the - * levels described above. For example, an administration application - * embedded within a network device (such as a router) might only contain - * a single Context and a few Wrappers, or even a single Wrapper if the - * application is relatively small. Therefore, Container implementations - * need to be designed so that they will operate correctly in the absence - * of parent Containers in a given deployment. - *

- * A Container may also be associated with a number of support components - * that provide functionality which might be shared (by attaching it to a - * parent Container) or individually customized. The following support - * components are currently recognized: - *

- * - * @author Craig R. McClanahan - * @author Remy Maucherat - * @version $Id$ - */ - -public interface Container extends Lifecycle { - - - // ----------------------------------------------------- Manifest Constants - - - /** - * The ContainerEvent event type sent when a child container is added - * by addChild(). - */ - public static final String ADD_CHILD_EVENT = "addChild"; - - - /** - * The ContainerEvent event type sent when a Mapper is added - * by addMapper(). - */ - public static final String ADD_MAPPER_EVENT = "addMapper"; - - - /** - * The ContainerEvent event type sent when a valve is added - * by addValve(), if this Container supports pipelines. - */ - public static final String ADD_VALVE_EVENT = "addValve"; - - - /** - * The ContainerEvent event type sent when a child container is removed - * by removeChild(). - */ - public static final String REMOVE_CHILD_EVENT = "removeChild"; - - - /** - * The ContainerEvent event type sent when a Mapper is removed - * by removeMapper(). - */ - public static final String REMOVE_MAPPER_EVENT = "removeMapper"; - - - /** - * The ContainerEvent event type sent when a valve is removed - * by removeValve(), if this Container supports pipelines. - */ - public static final String REMOVE_VALVE_EVENT = "removeValve"; - - - // ------------------------------------------------------------- Properties - - - /** - * Return descriptive information about this Container implementation and - * the corresponding version number, in the format - * <description>/<version>. - */ - public String getInfo(); - - - /** - * Return the Loader with which this Container is associated. If there is - * no associated Loader, return the Loader associated with our parent - * Container (if any); otherwise, return null. - */ - public Loader getLoader(); - - - /** - * Set the Loader with which this Container is associated. - * - * @param loader The newly associated loader - */ - public void setLoader(Loader loader); - - - /** - * Return the Logger with which this Container is associated. If there is - * no associated Logger, return the Logger associated with our parent - * Container (if any); otherwise return null. - */ - public Log getLogger(); - - - /** - * Return the Manager with which this Container is associated. If there is - * no associated Manager, return the Manager associated with our parent - * Container (if any); otherwise return null. - */ - public Manager getManager(); - - - /** - * Set the Manager with which this Container is associated. - * - * @param manager The newly associated Manager - */ - public void setManager(Manager manager); - - - /** - * Return an object which may be utilized for mapping to this component. - */ - public Object getMappingObject(); - - - /** - * Return the JMX name associated with this container. - */ - public ObjectName getObjectName(); - - /** - * Return the Pipeline object that manages the Valves associated with - * this Container. - */ - public Pipeline getPipeline(); - - - /** - * Return the Cluster with which this Container is associated. If there is - * no associated Cluster, return the Cluster associated with our parent - * Container (if any); otherwise return null. - */ - public Cluster getCluster(); - - - /** - * Set the Cluster with which this Container is associated. - * - * @param cluster the Cluster with which this Container is associated. - */ - public void setCluster(Cluster cluster); - - - /** - * Get the delay between the invocation of the backgroundProcess method on - * this container and its children. Child containers will not be invoked - * if their delay value is not negative (which would mean they are using - * their own thread). Setting this to a positive value will cause - * a thread to be spawn. After waiting the specified amount of time, - * the thread will invoke the executePeriodic method on this container - * and all its children. - */ - public int getBackgroundProcessorDelay(); - - - /** - * Set the delay between the invocation of the execute method on this - * container and its children. - * - * @param delay The delay in seconds between the invocation of - * backgroundProcess methods - */ - public void setBackgroundProcessorDelay(int delay); - - - /** - * Return a name string (suitable for use by humans) that describes this - * Container. Within the set of child containers belonging to a particular - * parent, Container names must be unique. - */ - public String getName(); - - - /** - * Set a name string (suitable for use by humans) that describes this - * Container. Within the set of child containers belonging to a particular - * parent, Container names must be unique. - * - * @param name New name of this container - * - * @exception IllegalStateException if this Container has already been - * added to the children of a parent Container (after which the name - * may not be changed) - */ - public void setName(String name); - - - /** - * Return the Container for which this Container is a child, if there is - * one. If there is no defined parent, return null. - */ - public Container getParent(); - - - /** - * Set the parent Container to which this Container is being added as a - * child. This Container may refuse to become attached to the specified - * Container by throwing an exception. - * - * @param container Container to which this Container is being added - * as a child - * - * @exception IllegalArgumentException if this Container refuses to become - * attached to the specified Container - */ - public void setParent(Container container); - - - /** - * Return the parent class loader for this component. If not set, return - * {@link #getParent()} {@link #getParentClassLoader()}. If no parent has - * been set, return the system class loader. - */ - public ClassLoader getParentClassLoader(); - - - /** - * Set the parent class loader for this component. For {@link Context}s - * this call is meaningful only before a Loader has - * been configured, and the specified value (if non-null) should be - * passed as an argument to the class loader constructor. - * - * @param parent The new parent class loader - */ - public void setParentClassLoader(ClassLoader parent); - - - /** - * Return the Realm with which this Container is associated. If there is - * no associated Realm, return the Realm associated with our parent - * Container (if any); otherwise return null. - */ - public Realm getRealm(); - - - /** - * Set the Realm with which this Container is associated. - * - * @param realm The newly associated Realm - */ - public void setRealm(Realm realm); - - - /** - * Return the Resources with which this Container is associated. If there - * is no associated Resources object, return the Resources associated with - * our parent Container (if any); otherwise return null. - */ - public DirContext getResources(); - - - /** - * Set the Resources object with which this Container is associated. - * - * @param resources The newly associated Resources - */ - public void setResources(DirContext resources); - - - // --------------------------------------------------------- Public Methods - - - /** - * 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(); - - - /** - * Add a new child Container to those associated with this Container, - * if supported. Prior to adding this Container to the set of children, - * the child's setParent() method must be called, with this - * Container as an argument. This method may thrown an - * IllegalArgumentException if this Container chooses not - * to be attached to the specified Container, in which case it is not added - * - * @param child New child Container to be added - * - * @exception IllegalArgumentException if this exception is thrown by - * the setParent() method of the child Container - * @exception IllegalArgumentException if the new child does not have - * a name unique from that of existing children of this Container - * @exception IllegalStateException if this Container does not support - * child Containers - */ - public void addChild(Container child); - - - /** - * Add a container event listener to this component. - * - * @param listener The listener to add - */ - public void addContainerListener(ContainerListener listener); - - - /** - * Add a property change listener to this component. - * - * @param listener The listener to add - */ - public void addPropertyChangeListener(PropertyChangeListener listener); - - - /** - * Return the child Container, associated with this Container, with - * the specified name (if any); otherwise, return null - * - * @param name Name of the child Container to be retrieved - */ - public Container findChild(String name); - - - /** - * Return the set of children Containers associated with this Container. - * If this Container has no children, a zero-length array is returned. - */ - public Container[] findChildren(); - - - /** - * Return the set of container listeners associated with this Container. - * If this Container has no registered container listeners, a zero-length - * array is returned. - */ - public ContainerListener[] findContainerListeners(); - - - /** - * Process the specified Request, and generate the corresponding Response, - * according to the design of this particular Container. - * - * @param request Request to be processed - * @param response Response to be produced - * - * @exception IOException if an input/output error occurred while - * processing - * @exception ServletException if a ServletException was thrown - * while processing this request - */ - public void invoke(Request request, Response response) - throws IOException, ServletException; - - - /** - * Remove an existing child Container from association with this parent - * Container. - * - * @param child Existing child Container to be removed - */ - public void removeChild(Container child); - - - /** - * Remove a container event listener from this component. - * - * @param listener The listener to remove - */ - public void removeContainerListener(ContainerListener listener); - - - /** - * Remove a property change listener from this component. - * - * @param listener The listener to remove - */ - public void removePropertyChangeListener(PropertyChangeListener listener); - - - /** - * Notify all container event listeners that a particular event has - * occurred for this Container. The default implementation performs - * this notification synchronously using the calling thread. - * - * @param type Event type - * @param data Event data - */ - public void fireContainerEvent(String type, Object data); - - - /** - * Log a request/response that was destined for this container but has been - * handled earlier in the processing chain so that the request/response - * still appears in the correct access logs. - * @param request Request (associated with the response) to log - * @param response Response (associated with the request) to log - * @param time Time taken to process the request/response in - * milliseconds (use 0 if not known) - * @param useDefault Flag that indicates that the request/response should - * be logged in the engine's default access log - */ - public void logAccess(Request request, Response response, long time, - boolean useDefault); - - - /** - * Identify the AccessLog to use to log a request/response that was destined - * for this container but was handled earlier in the processing chain so - * that the request/response still appears in the correct access logs. - */ - public AccessLog getAccessLog(); -} +/* + * 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; + + +import java.beans.PropertyChangeListener; +import java.io.IOException; + +import javax.management.ObjectName; +import javax.naming.directory.DirContext; +import javax.servlet.ServletException; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.juli.logging.Log; + + +/** + * A Container is an object that can execute requests received from + * a client, and return responses based on those requests. A Container may + * optionally support a pipeline of Valves that process the request in an + * order configured at runtime, by implementing the Pipeline interface + * as well. + *

+ * Containers will exist at several conceptual levels within Catalina. The + * following examples represent common cases: + *

+ * A given deployment of Catalina need not include Containers at all of the + * levels described above. For example, an administration application + * embedded within a network device (such as a router) might only contain + * a single Context and a few Wrappers, or even a single Wrapper if the + * application is relatively small. Therefore, Container implementations + * need to be designed so that they will operate correctly in the absence + * of parent Containers in a given deployment. + *

+ * A Container may also be associated with a number of support components + * that provide functionality which might be shared (by attaching it to a + * parent Container) or individually customized. The following support + * components are currently recognized: + *

+ * + * @author Craig R. McClanahan + * @author Remy Maucherat + * @version $Id$ + */ + +public interface Container extends Lifecycle { + + + // ----------------------------------------------------- Manifest Constants + + + /** + * The ContainerEvent event type sent when a child container is added + * by addChild(). + */ + public static final String ADD_CHILD_EVENT = "addChild"; + + + /** + * The ContainerEvent event type sent when a Mapper is added + * by addMapper(). + */ + public static final String ADD_MAPPER_EVENT = "addMapper"; + + + /** + * The ContainerEvent event type sent when a valve is added + * by addValve(), if this Container supports pipelines. + */ + public static final String ADD_VALVE_EVENT = "addValve"; + + + /** + * The ContainerEvent event type sent when a child container is removed + * by removeChild(). + */ + public static final String REMOVE_CHILD_EVENT = "removeChild"; + + + /** + * The ContainerEvent event type sent when a Mapper is removed + * by removeMapper(). + */ + public static final String REMOVE_MAPPER_EVENT = "removeMapper"; + + + /** + * The ContainerEvent event type sent when a valve is removed + * by removeValve(), if this Container supports pipelines. + */ + public static final String REMOVE_VALVE_EVENT = "removeValve"; + + + // ------------------------------------------------------------- Properties + + + /** + * Return descriptive information about this Container implementation and + * the corresponding version number, in the format + * <description>/<version>. + */ + public String getInfo(); + + + /** + * Return the Loader with which this Container is associated. If there is + * no associated Loader, return the Loader associated with our parent + * Container (if any); otherwise, return null. + */ + public Loader getLoader(); + + + /** + * Set the Loader with which this Container is associated. + * + * @param loader The newly associated loader + */ + public void setLoader(Loader loader); + + + /** + * Return the Logger with which this Container is associated. If there is + * no associated Logger, return the Logger associated with our parent + * Container (if any); otherwise return null. + */ + public Log getLogger(); + + + /** + * Return the Manager with which this Container is associated. If there is + * no associated Manager, return the Manager associated with our parent + * Container (if any); otherwise return null. + */ + public Manager getManager(); + + + /** + * Set the Manager with which this Container is associated. + * + * @param manager The newly associated Manager + */ + public void setManager(Manager manager); + + + /** + * Return an object which may be utilized for mapping to this component. + */ + public Object getMappingObject(); + + + /** + * Return the JMX name associated with this container. + */ + public ObjectName getObjectName(); + + /** + * Return the Pipeline object that manages the Valves associated with + * this Container. + */ + public Pipeline getPipeline(); + + + /** + * Return the Cluster with which this Container is associated. If there is + * no associated Cluster, return the Cluster associated with our parent + * Container (if any); otherwise return null. + */ + public Cluster getCluster(); + + + /** + * Set the Cluster with which this Container is associated. + * + * @param cluster the Cluster with which this Container is associated. + */ + public void setCluster(Cluster cluster); + + + /** + * Get the delay between the invocation of the backgroundProcess method on + * this container and its children. Child containers will not be invoked + * if their delay value is not negative (which would mean they are using + * their own thread). Setting this to a positive value will cause + * a thread to be spawn. After waiting the specified amount of time, + * the thread will invoke the executePeriodic method on this container + * and all its children. + */ + public int getBackgroundProcessorDelay(); + + + /** + * Set the delay between the invocation of the execute method on this + * container and its children. + * + * @param delay The delay in seconds between the invocation of + * backgroundProcess methods + */ + public void setBackgroundProcessorDelay(int delay); + + + /** + * Return a name string (suitable for use by humans) that describes this + * Container. Within the set of child containers belonging to a particular + * parent, Container names must be unique. + */ + public String getName(); + + + /** + * Set a name string (suitable for use by humans) that describes this + * Container. Within the set of child containers belonging to a particular + * parent, Container names must be unique. + * + * @param name New name of this container + * + * @exception IllegalStateException if this Container has already been + * added to the children of a parent Container (after which the name + * may not be changed) + */ + public void setName(String name); + + + /** + * Return the Container for which this Container is a child, if there is + * one. If there is no defined parent, return null. + */ + public Container getParent(); + + + /** + * Set the parent Container to which this Container is being added as a + * child. This Container may refuse to become attached to the specified + * Container by throwing an exception. + * + * @param container Container to which this Container is being added + * as a child + * + * @exception IllegalArgumentException if this Container refuses to become + * attached to the specified Container + */ + public void setParent(Container container); + + + /** + * Return the parent class loader for this component. If not set, return + * {@link #getParent()} {@link #getParentClassLoader()}. If no parent has + * been set, return the system class loader. + */ + public ClassLoader getParentClassLoader(); + + + /** + * Set the parent class loader for this component. For {@link Context}s + * this call is meaningful only before a Loader has + * been configured, and the specified value (if non-null) should be + * passed as an argument to the class loader constructor. + * + * @param parent The new parent class loader + */ + public void setParentClassLoader(ClassLoader parent); + + + /** + * Return the Realm with which this Container is associated. If there is + * no associated Realm, return the Realm associated with our parent + * Container (if any); otherwise return null. + */ + public Realm getRealm(); + + + /** + * Set the Realm with which this Container is associated. + * + * @param realm The newly associated Realm + */ + public void setRealm(Realm realm); + + + /** + * Return the Resources with which this Container is associated. If there + * is no associated Resources object, return the Resources associated with + * our parent Container (if any); otherwise return null. + */ + public DirContext getResources(); + + + /** + * Set the Resources object with which this Container is associated. + * + * @param resources The newly associated Resources + */ + public void setResources(DirContext resources); + + + // --------------------------------------------------------- Public Methods + + + /** + * 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(); + + + /** + * Add a new child Container to those associated with this Container, + * if supported. Prior to adding this Container to the set of children, + * the child's setParent() method must be called, with this + * Container as an argument. This method may thrown an + * IllegalArgumentException if this Container chooses not + * to be attached to the specified Container, in which case it is not added + * + * @param child New child Container to be added + * + * @exception IllegalArgumentException if this exception is thrown by + * the setParent() method of the child Container + * @exception IllegalArgumentException if the new child does not have + * a name unique from that of existing children of this Container + * @exception IllegalStateException if this Container does not support + * child Containers + */ + public void addChild(Container child); + + + /** + * Add a container event listener to this component. + * + * @param listener The listener to add + */ + public void addContainerListener(ContainerListener listener); + + + /** + * Add a property change listener to this component. + * + * @param listener The listener to add + */ + public void addPropertyChangeListener(PropertyChangeListener listener); + + + /** + * Return the child Container, associated with this Container, with + * the specified name (if any); otherwise, return null + * + * @param name Name of the child Container to be retrieved + */ + public Container findChild(String name); + + + /** + * Return the set of children Containers associated with this Container. + * If this Container has no children, a zero-length array is returned. + */ + public Container[] findChildren(); + + + /** + * Return the set of container listeners associated with this Container. + * If this Container has no registered container listeners, a zero-length + * array is returned. + */ + public ContainerListener[] findContainerListeners(); + + + /** + * Process the specified Request, and generate the corresponding Response, + * according to the design of this particular Container. + * + * @param request Request to be processed + * @param response Response to be produced + * + * @exception IOException if an input/output error occurred while + * processing + * @exception ServletException if a ServletException was thrown + * while processing this request + */ + public void invoke(Request request, Response response) + throws IOException, ServletException; + + + /** + * Remove an existing child Container from association with this parent + * Container. + * + * @param child Existing child Container to be removed + */ + public void removeChild(Container child); + + + /** + * Remove a container event listener from this component. + * + * @param listener The listener to remove + */ + public void removeContainerListener(ContainerListener listener); + + + /** + * Remove a property change listener from this component. + * + * @param listener The listener to remove + */ + public void removePropertyChangeListener(PropertyChangeListener listener); + + + /** + * Notify all container event listeners that a particular event has + * occurred for this Container. The default implementation performs + * this notification synchronously using the calling thread. + * + * @param type Event type + * @param data Event data + */ + public void fireContainerEvent(String type, Object data); + + + /** + * Log a request/response that was destined for this container but has been + * handled earlier in the processing chain so that the request/response + * still appears in the correct access logs. + * @param request Request (associated with the response) to log + * @param response Response (associated with the request) to log + * @param time Time taken to process the request/response in + * milliseconds (use 0 if not known) + * @param useDefault Flag that indicates that the request/response should + * be logged in the engine's default access log + */ + public void logAccess(Request request, Response response, long time, + boolean useDefault); + + + /** + * Identify the AccessLog to use to log a request/response that was destined + * for this container but was handled earlier in the processing chain so + * that the request/response still appears in the correct access logs. + */ + public AccessLog getAccessLog(); + + + /** + * Returns the number of threads available for starting and stopping any + * children associated with this container. This allows start/stop calls to + * children to be processed in parallel. + */ + public int getStartStopThreads(); + + + /** + * Sets the number of threads available for starting and stopping any + * children associated with this container. This allows start/stop calls to + * children to be processed in parallel. + * @param startStopThreads The new number of threads to be used + */ + public void setStartStopThreads(int startStopThreads); +} diff --git a/java/org/apache/catalina/Host.java b/java/org/apache/catalina/Host.java index 72f5dc2..9d27191 100644 --- a/java/org/apache/catalina/Host.java +++ b/java/org/apache/catalina/Host.java @@ -1,220 +1,228 @@ -/* - * 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; - -import java.io.File; -import java.util.regex.Pattern; - - -/** - * A Host is a Container that represents a virtual host in the - * Catalina servlet engine. It is useful in the following types of scenarios: - * - * In general, you would not use a Host when deploying Catalina connected - * to a web server (such as Apache), because the Connector will have - * utilized the web server's facilities to determine which Context (or - * perhaps even which Wrapper) should be utilized to process this request. - *

- * The parent Container attached to a Host is generally an Engine, but may - * be some other implementation, or may be omitted if it is not necessary. - *

- * The child containers attached to a Host are generally implementations - * of Context (representing an individual servlet context). - * - * @author Craig R. McClanahan - * @version $Id$ - */ - -public interface Host extends Container { - - - // ----------------------------------------------------- Manifest Constants - - - /** - * The ContainerEvent event type sent when a new alias is added - * by addAlias(). - */ - public static final String ADD_ALIAS_EVENT = "addAlias"; - - - /** - * The ContainerEvent event type sent when an old alias is removed - * by removeAlias(). - */ - public static final String REMOVE_ALIAS_EVENT = "removeAlias"; - - - // ------------------------------------------------------------- Properties - - - /** - * Return the XML root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - * If null, defaults to - * ${catalina.base}/conf/<engine name>/<host name> directory - */ - public String getXmlBase(); - - /** - * Set the Xml root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - * If null, defaults to - * ${catalina.base}/conf/<engine name>/<host name> directory - * @param xmlBase The new XML root - */ - public void setXmlBase(String xmlBase); - - /** - * Return the application root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - */ - public String getAppBase(); - - - /** - * Return an absolute {@link File} for the appBase of this Host. The file - * will be canonical if possible. There is no guarantee that that the - * appBase exists. - */ - public File getAppBaseFile(); - - - /** - * 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); - - - /** - * Return the value of the auto deploy flag. If true, it indicates that - * this host's child webapps should be discovered and automatically - * deployed dynamically. - */ - public boolean getAutoDeploy(); - - - /** - * Set the auto deploy flag value for this host. - * - * @param autoDeploy The new auto deploy flag - */ - public void setAutoDeploy(boolean autoDeploy); - - - /** - * Return the Java class name of the context configuration class - * for new web applications. - */ - public String getConfigClass(); - - - /** - * 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); - - - /** - * Return the value of the deploy on startup flag. If true, it indicates - * that this host's child webapps should be discovered and automatically - * deployed. - */ - public boolean getDeployOnStartup(); - - - /** - * Set the deploy on startup flag value for this host. - * - * @param deployOnStartup The new deploy on startup flag - */ - public void setDeployOnStartup(boolean deployOnStartup); - - - /** - * Return the regular expression that defines the files and directories in - * the host's appBase that will be ignored by the automatic deployment - * process. - */ - public String getDeployIgnore(); - - - /** - * Return the compiled regular expression that defines the files and - * directories in the host's appBase that will be ignored by the automatic - * deployment process. - */ - public Pattern getDeployIgnorePattern(); - - - /** - * Set the regular expression that defines the files and directories in - * the host's appBase that will be ignored by the automatic deployment - * process. - */ - public void setDeployIgnore(String deployIgnore); - - - // --------------------------------------------------------- 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); - - - /** - * Return the set of alias names for this Host. If none are defined, - * a zero length array is returned. - */ - public String[] findAliases(); - - - /** - * Remove the specified alias name from the aliases for this Host. - * - * @param alias Alias name to be removed - */ - public void removeAlias(String alias); - - /** - * Returns true if the Host will attempt to create directories for appBase and xmlBase - * unless they already exist. - * @return true if the Host will attempt to create directories - */ - public boolean getCreateDirs(); - /** - * Set to true if the Host should attempt to create directories for xmlBase and appBase upon startup - * @param createDirs - */ - public void setCreateDirs(boolean createDirs); - -} +/* + * 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; + +import java.io.File; +import java.util.concurrent.ExecutorService; +import java.util.regex.Pattern; + + +/** + * A Host is a Container that represents a virtual host in the + * Catalina servlet engine. It is useful in the following types of scenarios: + *

+ * In general, you would not use a Host when deploying Catalina connected + * to a web server (such as Apache), because the Connector will have + * utilized the web server's facilities to determine which Context (or + * perhaps even which Wrapper) should be utilized to process this request. + *

+ * The parent Container attached to a Host is generally an Engine, but may + * be some other implementation, or may be omitted if it is not necessary. + *

+ * The child containers attached to a Host are generally implementations + * of Context (representing an individual servlet context). + * + * @author Craig R. McClanahan + * @version $Id$ + */ + +public interface Host extends Container { + + + // ----------------------------------------------------- Manifest Constants + + + /** + * The ContainerEvent event type sent when a new alias is added + * by addAlias(). + */ + public static final String ADD_ALIAS_EVENT = "addAlias"; + + + /** + * The ContainerEvent event type sent when an old alias is removed + * by removeAlias(). + */ + public static final String REMOVE_ALIAS_EVENT = "removeAlias"; + + + // ------------------------------------------------------------- Properties + + + /** + * Return the XML root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + * If null, defaults to + * ${catalina.base}/conf/<engine name>/<host name> directory + */ + public String getXmlBase(); + + /** + * Set the Xml root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + * If null, defaults to + * ${catalina.base}/conf/<engine name>/<host name> directory + * @param xmlBase The new XML root + */ + public void setXmlBase(String xmlBase); + + /** + * Return the application root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + */ + public String getAppBase(); + + + /** + * Return an absolute {@link File} for the appBase of this Host. The file + * will be canonical if possible. There is no guarantee that that the + * appBase exists. + */ + public File getAppBaseFile(); + + + /** + * 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); + + + /** + * Return the value of the auto deploy flag. If true, it indicates that + * this host's child webapps should be discovered and automatically + * deployed dynamically. + */ + public boolean getAutoDeploy(); + + + /** + * Set the auto deploy flag value for this host. + * + * @param autoDeploy The new auto deploy flag + */ + public void setAutoDeploy(boolean autoDeploy); + + + /** + * Return the Java class name of the context configuration class + * for new web applications. + */ + public String getConfigClass(); + + + /** + * 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); + + + /** + * Return the value of the deploy on startup flag. If true, it indicates + * that this host's child webapps should be discovered and automatically + * deployed. + */ + public boolean getDeployOnStartup(); + + + /** + * Set the deploy on startup flag value for this host. + * + * @param deployOnStartup The new deploy on startup flag + */ + public void setDeployOnStartup(boolean deployOnStartup); + + + /** + * Return the regular expression that defines the files and directories in + * the host's appBase that will be ignored by the automatic deployment + * process. + */ + public String getDeployIgnore(); + + + /** + * Return the compiled regular expression that defines the files and + * directories in the host's appBase that will be ignored by the automatic + * deployment process. + */ + public Pattern getDeployIgnorePattern(); + + + /** + * Set the regular expression that defines the files and directories in + * the host's appBase that will be ignored by the automatic deployment + * process. + */ + public void setDeployIgnore(String deployIgnore); + + + /** + * Return the executor that is used for starting and stopping contexts. This + * is primarily for use by components deploying contexts that want to do + * this in a multi-threaded manner. + */ + public ExecutorService getStartStopExecutor(); + + + // --------------------------------------------------------- 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); + + + /** + * Return the set of alias names for this Host. If none are defined, + * a zero length array is returned. + */ + public String[] findAliases(); + + + /** + * Remove the specified alias name from the aliases for this Host. + * + * @param alias Alias name to be removed + */ + public void removeAlias(String alias); + + /** + * Returns true if the Host will attempt to create directories for appBase and xmlBase + * unless they already exist. + * @return true if the Host will attempt to create directories + */ + public boolean getCreateDirs(); + /** + * Set to true if the Host should attempt to create directories for xmlBase and appBase upon startup + * @param createDirs + */ + public void setCreateDirs(boolean createDirs); + +} diff --git a/java/org/apache/catalina/core/ContainerBase.java b/java/org/apache/catalina/core/ContainerBase.java index 4a736a6..0a8edfc 100644 --- a/java/org/apache/catalina/core/ContainerBase.java +++ b/java/org/apache/catalina/core/ContainerBase.java @@ -1,1415 +1,1546 @@ -/* - * 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 java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; -import java.io.IOException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; - -import javax.management.ObjectName; -import javax.naming.directory.DirContext; -import javax.servlet.ServletException; - -import org.apache.catalina.AccessLog; -import org.apache.catalina.CatalinaFactory; -import org.apache.catalina.Cluster; -import org.apache.catalina.Container; -import org.apache.catalina.ContainerEvent; -import org.apache.catalina.ContainerListener; -import org.apache.catalina.Globals; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Loader; -import org.apache.catalina.Manager; -import org.apache.catalina.Pipeline; -import org.apache.catalina.Realm; -import org.apache.catalina.Valve; -import org.apache.catalina.connector.Request; -import org.apache.catalina.connector.Response; -import org.apache.catalina.mbeans.MBeanUtils; -import org.apache.catalina.util.LifecycleMBeanBase; -import org.apache.juli.logging.Log; -import org.apache.juli.logging.LogFactory; -import org.apache.naming.resources.ProxyDirContext; -import org.apache.tomcat.util.ExceptionUtils; -import org.apache.tomcat.util.res.StringManager; - - -/** - * Abstract implementation of the Container interface, providing common - * functionality required by nearly every implementation. Classes extending - * this base class must implement getInfo(), and may implement - * a replacement for invoke(). - *

- * All subclasses of this abstract base class will include support for a - * Pipeline object that defines the processing to be performed for each request - * received by the invoke() method of this class, utilizing the - * "Chain of Responsibility" design pattern. A subclass should encapsulate its - * own processing functionality as a Valve, and configure this - * Valve into the pipeline by calling setBasic(). - *

- * This implementation fires property change events, per the JavaBeans design - * pattern, for changes in singleton properties. In addition, it fires the - * following ContainerEvent events to listeners who register - * themselves with addContainerListener(): - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
TypeDataDescription
addChildContainerChild container added to this Container.
addValveValveValve added to this Container.
removeChildContainerChild container removed from this Container.
removeValveValveValve removed from this Container.
startnullContainer was started.
stopnullContainer was stopped.
- * Subclasses that fire additional events should document them in the - * class comments of the implementation class. - * - * TODO: Review synchronisation around background processing. See bug 47024. - * - * @author Craig R. McClanahan - */ -public abstract class ContainerBase extends LifecycleMBeanBase - implements Container { - - private static final org.apache.juli.logging.Log log= - org.apache.juli.logging.LogFactory.getLog( ContainerBase.class ); - - /** - * Perform addChild with the permissions of this class. - * addChild can be called with the XML parser on the stack, - * this allows the XML parser to have fewer privileges than - * Tomcat. - */ - protected class PrivilegedAddChild - implements PrivilegedAction { - - private Container child; - - PrivilegedAddChild(Container child) { - this.child = child; - } - - @Override - public Void run() { - addChildInternal(child); - return null; - } - - } - - - // ----------------------------------------------------- Instance Variables - - - /** - * The child Containers belonging to this Container, keyed by name. - */ - protected HashMap children = - new HashMap(); - - - /** - * The processor delay for this component. - */ - protected int backgroundProcessorDelay = -1; - - - /** - * The container event listeners for this Container. - */ - protected ArrayList listeners = new ArrayList(); - - - /** - * The Loader implementation with which this Container is associated. - */ - protected Loader loader = null; - - - /** - * The Logger implementation with which this Container is associated. - */ - protected Log logger = null; - - - /** - * Associated logger name. - */ - protected String logName = null; - - - /** - * The Manager implementation with which this Container is associated. - */ - protected Manager manager = null; - - - /** - * The cluster with which this Container is associated. - */ - protected Cluster cluster = null; - - - /** - * The human-readable name of this Container. - */ - protected String name = null; - - - /** - * The parent Container to which this Container is a child. - */ - protected Container parent = null; - - - /** - * The parent class loader to be configured when we install a Loader. - */ - protected ClassLoader parentClassLoader = null; - - - /** - * The Pipeline object with which this Container is associated. - */ - protected Pipeline pipeline = - CatalinaFactory.getFactory().createPipeline(this); - - - /** - * The Realm with which this Container is associated. - */ - protected Realm realm = null; - - - /** - * The resources DirContext object with which this Container is associated. - */ - protected DirContext resources = null; - - - /** - * The string manager for this package. - */ - protected static final StringManager sm = - StringManager.getManager(Constants.Package); - - - /** - * Will children be started automatically when they are added. - */ - protected boolean startChildren = true; - - /** - * The property change support for this component. - */ - protected PropertyChangeSupport support = new PropertyChangeSupport(this); - - - /** - * The background thread. - */ - private Thread thread = null; - - - /** - * The background thread completion semaphore. - */ - private volatile boolean threadDone = false; - - - /** - * The access log to use for requests normally handled by this container - * that have been handled earlier in the processing chain. - */ - protected volatile AccessLog accessLog = null; - private volatile boolean accessLogScanComplete = false; - - // ------------------------------------------------------------- Properties - - - /** - * Get the delay between the invocation of the backgroundProcess method on - * this container and its children. Child containers will not be invoked - * if their delay value is not negative (which would mean they are using - * their own thread). Setting this to a positive value will cause - * a thread to be spawn. After waiting the specified amount of time, - * the thread will invoke the executePeriodic method on this container - * and all its children. - */ - @Override - public int getBackgroundProcessorDelay() { - return backgroundProcessorDelay; - } - - - /** - * Set the delay between the invocation of the execute method on this - * container and its children. - * - * @param delay The delay in seconds between the invocation of - * backgroundProcess methods - */ - @Override - public void setBackgroundProcessorDelay(int delay) { - backgroundProcessorDelay = delay; - } - - - /** - * Return descriptive information about this Container implementation and - * the corresponding version number, in the format - * <description>/<version>. - */ - @Override - public String getInfo() { - return this.getClass().getName(); - } - - - /** - * Return the Loader with which this Container is associated. If there is - * no associated Loader, return the Loader associated with our parent - * Container (if any); otherwise, return null. - */ - @Override - public Loader getLoader() { - - if (loader != null) - return (loader); - if (parent != null) - return (parent.getLoader()); - return (null); - - } - - - /** - * Set the Loader with which this Container is associated. - * - * @param loader The newly associated loader - */ - @Override - public synchronized void setLoader(Loader loader) { - - // Change components if necessary - Loader oldLoader = this.loader; - if (oldLoader == loader) - return; - this.loader = loader; - - // Stop the old component if necessary - if (getState().isAvailable() && (oldLoader != null) && - (oldLoader instanceof Lifecycle)) { - try { - ((Lifecycle) oldLoader).stop(); - } catch (LifecycleException e) { - log.error("ContainerBase.setLoader: stop: ", e); - } - } - - // Start the new component if necessary - if (loader != null) - loader.setContainer(this); - if (getState().isAvailable() && (loader != null) && - (loader instanceof Lifecycle)) { - try { - ((Lifecycle) loader).start(); - } catch (LifecycleException e) { - log.error("ContainerBase.setLoader: start: ", e); - } - } - - // Report this property change to interested listeners - support.firePropertyChange("loader", oldLoader, this.loader); - - } - - - /** - * Return the Logger for this Container. - */ - @Override - public Log getLogger() { - - if (logger != null) - return (logger); - logger = LogFactory.getLog(logName()); - return (logger); - - } - - - /** - * Return the Manager with which this Container is associated. If there is - * no associated Manager, return the Manager associated with our parent - * Container (if any); otherwise return null. - */ - @Override - public Manager getManager() { - - if (manager != null) - return (manager); - if (parent != null) - return (parent.getManager()); - return (null); - - } - - - /** - * Set the Manager with which this Container is associated. - * - * @param manager The newly associated Manager - */ - @Override - public synchronized void setManager(Manager manager) { - - // Change components if necessary - Manager oldManager = this.manager; - if (oldManager == manager) - return; - this.manager = manager; - - // Stop the old component if necessary - if (getState().isAvailable() && (oldManager != null) && - (oldManager instanceof Lifecycle)) { - try { - ((Lifecycle) oldManager).stop(); - } catch (LifecycleException e) { - log.error("ContainerBase.setManager: stop: ", e); - } - } - - // Start the new component if necessary - if (manager != null) - manager.setContainer(this); - if (getState().isAvailable() && (manager != null) && - (manager instanceof Lifecycle)) { - try { - ((Lifecycle) manager).start(); - } catch (LifecycleException e) { - log.error("ContainerBase.setManager: start: ", e); - } - } - - // Report this property change to interested listeners - support.firePropertyChange("manager", oldManager, this.manager); - - } - - - /** - * Return an object which may be utilized for mapping to this component. - */ - @Override - public Object getMappingObject() { - return this; - } - - - /** - * Return the Cluster with which this Container is associated. If there is - * no associated Cluster, return the Cluster associated with our parent - * Container (if any); otherwise return null. - */ - @Override - public Cluster getCluster() { - if (cluster != null) - return (cluster); - - if (parent != null) - return (parent.getCluster()); - - return (null); - } - - - /** - * Set the Cluster with which this Container is associated. - * - * @param cluster The newly associated Cluster - */ - @Override - public synchronized void setCluster(Cluster cluster) { - // Change components if necessary - Cluster oldCluster = this.cluster; - if (oldCluster == cluster) - return; - this.cluster = cluster; - - // Stop the old component if necessary - if (getState().isAvailable() && (oldCluster != null) && - (oldCluster instanceof Lifecycle)) { - try { - ((Lifecycle) oldCluster).stop(); - } catch (LifecycleException e) { - log.error("ContainerBase.setCluster: stop: ", e); - } - } - - // Start the new component if necessary - if (cluster != null) - cluster.setContainer(this); - - if (getState().isAvailable() && (cluster != null) && - (cluster instanceof Lifecycle)) { - try { - ((Lifecycle) cluster).start(); - } catch (LifecycleException e) { - log.error("ContainerBase.setCluster: start: ", e); - } - } - - // Report this property change to interested listeners - support.firePropertyChange("cluster", oldCluster, this.cluster); - } - - - /** - * Return a name string (suitable for use by humans) that describes this - * Container. Within the set of child containers belonging to a particular - * parent, Container names must be unique. - */ - @Override - public String getName() { - - return (name); - - } - - - /** - * Set a name string (suitable for use by humans) that describes this - * Container. Within the set of child containers belonging to a particular - * parent, Container names must be unique. - * - * @param name New name of this container - * - * @exception IllegalStateException if this Container has already been - * added to the children of a parent Container (after which the name - * may not be changed) - */ - @Override - public void setName(String name) { - - String oldName = this.name; - this.name = name; - support.firePropertyChange("name", oldName, this.name); - } - - - /** - * Return if children of this container will be started automatically when - * they are added to this container. - */ - public boolean getStartChildren() { - - return (startChildren); - - } - - - /** - * Set if children of this container will be started automatically when - * they are added to this container. - * - * @param startChildren New value of the startChildren flag - */ - public void setStartChildren(boolean startChildren) { - - boolean oldStartChildren = this.startChildren; - this.startChildren = startChildren; - support.firePropertyChange("startChildren", oldStartChildren, this.startChildren); - } - - - /** - * Return the Container for which this Container is a child, if there is - * one. If there is no defined parent, return null. - */ - @Override - public Container getParent() { - - return (parent); - - } - - - /** - * Set the parent Container to which this Container is being added as a - * child. This Container may refuse to become attached to the specified - * Container by throwing an exception. - * - * @param container Container to which this Container is being added - * as a child - * - * @exception IllegalArgumentException if this Container refuses to become - * attached to the specified Container - */ - @Override - public void setParent(Container container) { - - Container oldParent = this.parent; - this.parent = container; - support.firePropertyChange("parent", oldParent, this.parent); - - } - - - /** - * Return the parent class loader (if any) for this web application. - * This call is meaningful only after a Loader has - * been configured. - */ - @Override - public ClassLoader getParentClassLoader() { - if (parentClassLoader != null) - return (parentClassLoader); - if (parent != null) { - return (parent.getParentClassLoader()); - } - return (ClassLoader.getSystemClassLoader()); - - } - - - /** - * Set the parent class loader (if any) for this web application. - * This call is meaningful only before a Loader has - * been configured, and the specified value (if non-null) should be - * passed as an argument to the class loader constructor. - * - * - * @param parent The new parent class loader - */ - @Override - public void setParentClassLoader(ClassLoader parent) { - ClassLoader oldParentClassLoader = this.parentClassLoader; - this.parentClassLoader = parent; - support.firePropertyChange("parentClassLoader", oldParentClassLoader, - this.parentClassLoader); - - } - - - /** - * Return the Pipeline object that manages the Valves associated with - * this Container. - */ - @Override - public Pipeline getPipeline() { - - return (this.pipeline); - - } - - - /** - * Return the Realm with which this Container is associated. If there is - * no associated Realm, return the Realm associated with our parent - * Container (if any); otherwise return null. - */ - @Override - public Realm getRealm() { - - if (realm != null) - return (realm); - if (parent != null) - return (parent.getRealm()); - return (null); - - } - - - /** - * Set the Realm with which this Container is associated. - * - * @param realm The newly associated Realm - */ - @Override - public synchronized void setRealm(Realm realm) { - - // Change components if necessary - Realm oldRealm = this.realm; - if (oldRealm == realm) - return; - this.realm = realm; - - // Stop the old component if necessary - if (getState().isAvailable() && (oldRealm != null) && - (oldRealm instanceof Lifecycle)) { - try { - ((Lifecycle) oldRealm).stop(); - } catch (LifecycleException e) { - log.error("ContainerBase.setRealm: stop: ", e); - } - } - - // Start the new component if necessary - if (realm != null) - realm.setContainer(this); - if (getState().isAvailable() && (realm != null) && - (realm instanceof Lifecycle)) { - try { - ((Lifecycle) realm).start(); - } catch (LifecycleException e) { - log.error("ContainerBase.setRealm: start: ", e); - } - } - - // Report this property change to interested listeners - support.firePropertyChange("realm", oldRealm, this.realm); - - } - - - /** - * Return the resources DirContext object with which this Container is - * associated. If there is no associated resources object, return the - * resources associated with our parent Container (if any); otherwise - * return null. - */ - @Override - public DirContext getResources() { - if (resources != null) - return (resources); - if (parent != null) - return (parent.getResources()); - return (null); - - } - - - /** - * Set the resources DirContext object with which this Container is - * associated. - * - * @param resources The newly associated DirContext - */ - @Override - public synchronized void setResources(DirContext resources) { - // Called from StandardContext.setResources() - // <- StandardContext.start() - // <- ContainerBase.addChildInternal() - - // Change components if necessary - DirContext oldResources = this.resources; - if (oldResources == resources) - return; - Hashtable env = new Hashtable(); - if (getParent() != null) - env.put(ProxyDirContext.HOST, getParent().getName()); - env.put(ProxyDirContext.CONTEXT, getName()); - this.resources = new ProxyDirContext(env, resources); - // Report this property change to interested listeners - support.firePropertyChange("resources", oldResources, this.resources); - - } - - - // ------------------------------------------------------ Container Methods - - - /** - * Add a new child Container to those associated with this Container, - * if supported. Prior to adding this Container to the set of children, - * the child's setParent() method must be called, with this - * Container as an argument. This method may thrown an - * IllegalArgumentException if this Container chooses not - * to be attached to the specified Container, in which case it is not added - * - * @param child New child Container to be added - * - * @exception IllegalArgumentException if this exception is thrown by - * the setParent() method of the child Container - * @exception IllegalArgumentException if the new child does not have - * a name unique from that of existing children of this Container - * @exception IllegalStateException if this Container does not support - * child Containers - */ - @Override - public void addChild(Container child) { - if (Globals.IS_SECURITY_ENABLED) { - PrivilegedAction dp = - new PrivilegedAddChild(child); - AccessController.doPrivileged(dp); - } else { - addChildInternal(child); - } - } - - private void addChildInternal(Container child) { - - if( log.isDebugEnabled() ) - log.debug("Add child " + child + " " + this); - synchronized(children) { - if (children.get(child.getName()) != null) - throw new IllegalArgumentException("addChild: Child name '" + - child.getName() + - "' is not unique"); - child.setParent(this); // May throw IAE - children.put(child.getName(), child); - } - - // Start child - // Don't do this inside sync block - start can be a slow process and - // locking the children object can cause problems elsewhere - if ((getState().isAvailable() || - LifecycleState.STARTING_PREP.equals(getState())) && - startChildren) { - boolean success = false; - try { - child.start(); - success = true; - } catch (LifecycleException e) { - log.error("ContainerBase.addChild: start: ", e); - throw new IllegalStateException - ("ContainerBase.addChild: start: " + e); - } finally { - if (!success) { - synchronized (children) { - children.remove(child.getName()); - } - } - } - } - - fireContainerEvent(ADD_CHILD_EVENT, child); - } - - - /** - * Add a container event listener to this component. - * - * @param listener The listener to add - */ - @Override - public void addContainerListener(ContainerListener listener) { - - synchronized (listeners) { - listeners.add(listener); - } - - } - - - /** - * Add a property change listener to this component. - * - * @param listener The listener to add - */ - @Override - public void addPropertyChangeListener(PropertyChangeListener listener) { - - support.addPropertyChangeListener(listener); - - } - - - /** - * Return the child Container, associated with this Container, with - * the specified name (if any); otherwise, return null - * - * @param name Name of the child Container to be retrieved - */ - @Override - public Container findChild(String name) { - - if (name == null) - return (null); - synchronized (children) { - return children.get(name); - } - - } - - - /** - * Return the set of children Containers associated with this Container. - * If this Container has no children, a zero-length array is returned. - */ - @Override - public Container[] findChildren() { - - synchronized (children) { - Container results[] = new Container[children.size()]; - return children.values().toArray(results); - } - - } - - - /** - * Return the set of container listeners associated with this Container. - * If this Container has no registered container listeners, a zero-length - * array is returned. - */ - @Override - public ContainerListener[] findContainerListeners() { - - synchronized (listeners) { - ContainerListener[] results = - new ContainerListener[listeners.size()]; - return listeners.toArray(results); - } - - } - - - /** - * Process the specified Request, to produce the corresponding Response, - * by invoking the first Valve in our pipeline (if any), or the basic - * Valve otherwise. - * - * @param request Request to be processed - * @param response Response to be produced - * - * @exception IllegalStateException if neither a pipeline or a basic - * Valve have been configured for this Container - * @exception IOException if an input/output error occurred while - * processing - * @exception ServletException if a ServletException was thrown - * while processing this request - */ - @Override - public void invoke(Request request, Response response) - throws IOException, ServletException { - - pipeline.getFirst().invoke(request, response); - - } - - - /** - * Remove an existing child Container from association with this parent - * Container. - * - * @param child Existing child Container to be removed - */ - @Override - public void removeChild(Container child) { - - if (child == null) { - return; - } - - synchronized(children) { - if (children.get(child.getName()) == null) - return; - children.remove(child.getName()); - } - - try { - if (child.getState().isAvailable()) { - child.stop(); - } - } catch (LifecycleException e) { - log.error("ContainerBase.removeChild: stop: ", e); - } - - fireContainerEvent(REMOVE_CHILD_EVENT, child); - - try { - // child.destroy() may have already been called which would have - // triggered this call. If that is the case, no need to destroy the - // child again. - if (!LifecycleState.DESTROYING.equals(child.getState())) { - child.destroy(); - } - } catch (LifecycleException e) { - log.error("ContainerBase.removeChild: destroy: ", e); - } - - } - - - /** - * Remove a container event listener from this component. - * - * @param listener The listener to remove - */ - @Override - public void removeContainerListener(ContainerListener listener) { - - synchronized (listeners) { - listeners.remove(listener); - } - - } - - - /** - * Remove a property change listener from this component. - * - * @param listener The listener to remove - */ - @Override - public void removePropertyChangeListener(PropertyChangeListener listener) { - - support.removePropertyChangeListener(listener); - - } - - - /** - * Start this component and implement the requirements - * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - @Override - protected synchronized void startInternal() throws LifecycleException { - - // Start our subordinate components, if any - if ((loader != null) && (loader instanceof Lifecycle)) - ((Lifecycle) loader).start(); - logger = null; - getLogger(); - if ((logger != null) && (logger instanceof Lifecycle)) - ((Lifecycle) logger).start(); - if ((manager != null) && (manager instanceof Lifecycle)) - ((Lifecycle) manager).start(); - if ((cluster != null) && (cluster instanceof Lifecycle)) - ((Lifecycle) cluster).start(); - if ((realm != null) && (realm instanceof Lifecycle)) - ((Lifecycle) realm).start(); - if ((resources != null) && (resources instanceof Lifecycle)) - ((Lifecycle) resources).start(); - - // Start our child containers, if any - Container children[] = findChildren(); - for (int i = 0; i < children.length; i++) { - children[i].start(); - } - - // Start the Valves in our pipeline (including the basic), if any - if (pipeline instanceof Lifecycle) - ((Lifecycle) pipeline).start(); - - - setState(LifecycleState.STARTING); - - // Start our thread - threadStart(); - - } - - - /** - * Stop this component and implement the requirements - * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - @Override - protected synchronized void stopInternal() throws LifecycleException { - - // Stop our thread - threadStop(); - - setState(LifecycleState.STOPPING); - - // Stop the Valves in our pipeline (including the basic), if any - if (pipeline instanceof Lifecycle && - ((Lifecycle) pipeline).getState().isAvailable()) { - ((Lifecycle) pipeline).stop(); - } - - // Stop our child containers, if any - Container children[] = findChildren(); - for (int i = 0; i < children.length; i++) { - children[i].stop(); - } - - // Stop our subordinate components, if any - if ((resources != null) && (resources instanceof Lifecycle)) { - ((Lifecycle) resources).stop(); - } - if ((realm != null) && (realm instanceof Lifecycle)) { - ((Lifecycle) realm).stop(); - } - if ((cluster != null) && (cluster instanceof Lifecycle)) { - ((Lifecycle) cluster).stop(); - } - if ((manager != null) && (manager instanceof Lifecycle) && - ((Lifecycle) manager).getState().isAvailable() ) { - ((Lifecycle) manager).stop(); - } - if ((logger != null) && (logger instanceof Lifecycle)) { - ((Lifecycle) logger).stop(); - } - if ((loader != null) && (loader instanceof Lifecycle)) { - ((Lifecycle) loader).stop(); - } - } - - @Override - protected void destroyInternal() throws LifecycleException { - - // Stop the Valves in our pipeline (including the basic), if any - if (pipeline instanceof Lifecycle) { - ((Lifecycle) pipeline).destroy(); - } - - // Remove children now this container is being destroyed - for (Container child : findChildren()) { - removeChild(child); - } - - // Required if the child is destroyed directly. - if (parent != null) { - parent.removeChild(this); - } - - super.destroyInternal(); - } - - - /** - * Check this container for an access log and if none is found, look to the - * parent. If there is no parent and still none is found, use the NoOp - * access log. - */ - @Override - public void logAccess(Request request, Response response, long time, - boolean useDefault) { - - boolean logged = false; - - if (getAccessLog() != null) { - getAccessLog().log(request, response, time); - logged = true; - } - - if (getParent() != null) { - // No need to use default logger once request/response has been logged - // once - getParent().logAccess(request, response, time, (useDefault && !logged)); - } - } - - @Override - public AccessLog getAccessLog() { - - if (accessLogScanComplete) { - return accessLog; - } - - AccessLogAdapter adapter = null; - Valve valves[] = getPipeline().getValves(); - for (Valve valve : valves) { - if (valve instanceof AccessLog) { - if (adapter == null) { - adapter = new AccessLogAdapter((AccessLog) valve); - } else { - adapter.add((AccessLog) valve); - } - } - } - if (adapter != null) { - accessLog = adapter; - } - accessLogScanComplete = true; - return accessLog; - } - - // ------------------------------------------------------- Pipeline Methods - - - /** - * Convenience method, intended for use by the digester to simplify the - * process of adding Valves to containers. See - * {@link Pipeline#addValve(Valve)} for full details. Components other than - * the digester should use {@link #getPipeline()}.{@link #addValve(Valve)} in case a - * future implementation provides an alternative method for the digester to - * use. - * - * @param valve Valve to be added - * - * @exception IllegalArgumentException if this Container refused to - * accept the specified Valve - * @exception IllegalArgumentException if the specified Valve refuses to be - * associated with this Container - * @exception IllegalStateException if the specified Valve is already - * associated with a different Container - */ - public synchronized void addValve(Valve valve) { - - pipeline.addValve(valve); - } - - - /** - * 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. - */ - @Override - public void backgroundProcess() { - - if (!getState().isAvailable()) - return; - - if (cluster != null) { - try { - cluster.backgroundProcess(); - } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); - } - } - if (loader != null) { - try { - loader.backgroundProcess(); - } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); - } - } - if (manager != null) { - try { - manager.backgroundProcess(); - } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); - } - } - if (realm != null) { - try { - realm.backgroundProcess(); - } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); - } - } - Valve current = pipeline.getFirst(); - while (current != null) { - try { - current.backgroundProcess(); - } catch (Exception e) { - log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); - } - current = current.getNext(); - } - fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null); - } - - - // ------------------------------------------------------ Protected Methods - - - /** - * Notify all container event listeners that a particular event has - * occurred for this Container. The default implementation performs - * this notification synchronously using the calling thread. - * - * @param type Event type - * @param data Event data - */ - @Override - public void fireContainerEvent(String type, Object data) { - - if (listeners.size() < 1) - return; - ContainerEvent event = new ContainerEvent(this, type, data); - ContainerListener list[] = new ContainerListener[0]; - synchronized (listeners) { - list = listeners.toArray(list); - } - for (int i = 0; i < list.length; i++) - list[i].containerEvent(event); - - } - - - /** - * Return the abbreviated name of this container for logging messages - */ - protected String logName() { - - if (logName != null) { - return logName; - } - String loggerName = null; - Container current = this; - while (current != null) { - String name = current.getName(); - if ((name == null) || (name.equals(""))) { - name = "/"; - } else if (name.startsWith("##")) { - name = "/" + name; - } - loggerName = "[" + name + "]" - + ((loggerName != null) ? ("." + loggerName) : ""); - current = current.getParent(); - } - logName = ContainerBase.class.getName() + "." + loggerName; - return logName; - - } - - - // -------------------- JMX and Registration -------------------- - - @Override - protected String getDomainInternal() { - return MBeanUtils.getDomain(this); - } - - public ObjectName[] getChildren() { - ObjectName result[]=new ObjectName[children.size()]; - Iterator it=children.values().iterator(); - int i=0; - while( it.hasNext() ) { - Object next=it.next(); - if( next instanceof ContainerBase ) { - result[i++]=((ContainerBase)next).getObjectName(); - } - } - return result; - } - - - // -------------------- Background Thread -------------------- - - /** - * Start the background thread that will periodically check for - * session timeouts. - */ - protected void threadStart() { - - if (thread != null) - return; - if (backgroundProcessorDelay <= 0) - return; - - threadDone = false; - String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; - thread = new Thread(new ContainerBackgroundProcessor(), threadName); - thread.setDaemon(true); - thread.start(); - - } - - - /** - * Stop the background thread that is periodically checking for - * session timeouts. - */ - protected void threadStop() { - - if (thread == null) - return; - - threadDone = true; - thread.interrupt(); - try { - thread.join(); - } catch (InterruptedException e) { - // Ignore - } - - thread = null; - - } - - - // -------------------------------------- ContainerExecuteDelay Inner Class - - - /** - * Private thread class to invoke the backgroundProcess method - * of this container and its children after a fixed delay. - */ - protected class ContainerBackgroundProcessor implements Runnable { - - @Override - public void run() { - while (!threadDone) { - try { - Thread.sleep(backgroundProcessorDelay * 1000L); - } catch (InterruptedException e) { - // Ignore - } - if (!threadDone) { - Container parent = (Container) getMappingObject(); - ClassLoader cl = - Thread.currentThread().getContextClassLoader(); - if (parent.getLoader() != null) { - cl = parent.getLoader().getClassLoader(); - } - processChildren(parent, cl); - } - } - } - - protected void processChildren(Container container, ClassLoader cl) { - try { - if (container.getLoader() != null) { - Thread.currentThread().setContextClassLoader - (container.getLoader().getClassLoader()); - } - container.backgroundProcess(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error("Exception invoking periodic operation: ", t); - } finally { - Thread.currentThread().setContextClassLoader(cl); - } - Container[] children = container.findChildren(); - for (int i = 0; i < children.length; i++) { - if (children[i].getBackgroundProcessorDelay() <= 0) { - processChildren(children[i], cl); - } - } - } - } -} +/* + * 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 java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.management.ObjectName; +import javax.naming.directory.DirContext; +import javax.servlet.ServletException; + +import org.apache.catalina.AccessLog; +import org.apache.catalina.CatalinaFactory; +import org.apache.catalina.Cluster; +import org.apache.catalina.Container; +import org.apache.catalina.ContainerEvent; +import org.apache.catalina.ContainerListener; +import org.apache.catalina.Globals; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.Loader; +import org.apache.catalina.Manager; +import org.apache.catalina.Pipeline; +import org.apache.catalina.Realm; +import org.apache.catalina.Valve; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.mbeans.MBeanUtils; +import org.apache.catalina.util.LifecycleMBeanBase; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.naming.resources.ProxyDirContext; +import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.res.StringManager; + + +/** + * Abstract implementation of the Container interface, providing common + * functionality required by nearly every implementation. Classes extending + * this base class must implement getInfo(), and may implement + * a replacement for invoke(). + *

+ * All subclasses of this abstract base class will include support for a + * Pipeline object that defines the processing to be performed for each request + * received by the invoke() method of this class, utilizing the + * "Chain of Responsibility" design pattern. A subclass should encapsulate its + * own processing functionality as a Valve, and configure this + * Valve into the pipeline by calling setBasic(). + *

+ * This implementation fires property change events, per the JavaBeans design + * pattern, for changes in singleton properties. In addition, it fires the + * following ContainerEvent events to listeners who register + * themselves with addContainerListener(): + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
TypeDataDescription
addChildContainerChild container added to this Container.
addValveValveValve added to this Container.
removeChildContainerChild container removed from this Container.
removeValveValveValve removed from this Container.
startnullContainer was started.
stopnullContainer was stopped.
+ * Subclasses that fire additional events should document them in the + * class comments of the implementation class. + * + * TODO: Review synchronisation around background processing. See bug 47024. + * + * @author Craig R. McClanahan + */ +public abstract class ContainerBase extends LifecycleMBeanBase + implements Container { + + private static final org.apache.juli.logging.Log log= + org.apache.juli.logging.LogFactory.getLog( ContainerBase.class ); + + /** + * Perform addChild with the permissions of this class. + * addChild can be called with the XML parser on the stack, + * this allows the XML parser to have fewer privileges than + * Tomcat. + */ + protected class PrivilegedAddChild + implements PrivilegedAction { + + private Container child; + + PrivilegedAddChild(Container child) { + this.child = child; + } + + @Override + public Void run() { + addChildInternal(child); + return null; + } + + } + + + // ----------------------------------------------------- Instance Variables + + + /** + * The child Containers belonging to this Container, keyed by name. + */ + protected HashMap children = + new HashMap(); + + + /** + * The processor delay for this component. + */ + protected int backgroundProcessorDelay = -1; + + + /** + * The container event listeners for this Container. + */ + protected ArrayList listeners = new ArrayList(); + + + /** + * The Loader implementation with which this Container is associated. + */ + protected Loader loader = null; + + + /** + * The Logger implementation with which this Container is associated. + */ + protected Log logger = null; + + + /** + * Associated logger name. + */ + protected String logName = null; + + + /** + * The Manager implementation with which this Container is associated. + */ + protected Manager manager = null; + + + /** + * The cluster with which this Container is associated. + */ + protected Cluster cluster = null; + + + /** + * The human-readable name of this Container. + */ + protected String name = null; + + + /** + * The parent Container to which this Container is a child. + */ + protected Container parent = null; + + + /** + * The parent class loader to be configured when we install a Loader. + */ + protected ClassLoader parentClassLoader = null; + + + /** + * The Pipeline object with which this Container is associated. + */ + protected Pipeline pipeline = + CatalinaFactory.getFactory().createPipeline(this); + + + /** + * The Realm with which this Container is associated. + */ + protected Realm realm = null; + + + /** + * The resources DirContext object with which this Container is associated. + */ + protected DirContext resources = null; + + + /** + * The string manager for this package. + */ + protected static final StringManager sm = + StringManager.getManager(Constants.Package); + + + /** + * Will children be started automatically when they are added. + */ + protected boolean startChildren = true; + + /** + * The property change support for this component. + */ + protected PropertyChangeSupport support = new PropertyChangeSupport(this); + + + /** + * The background thread. + */ + private Thread thread = null; + + + /** + * The background thread completion semaphore. + */ + private volatile boolean threadDone = false; + + + /** + * The access log to use for requests normally handled by this container + * that have been handled earlier in the processing chain. + */ + protected volatile AccessLog accessLog = null; + private volatile boolean accessLogScanComplete = false; + + + /** + * The number of threads available to process start and stop events for any + * children associated with this container. + */ + private int startStopThreads = 1; + protected ThreadPoolExecutor startStopExecutor; + + // ------------------------------------------------------------- Properties + + @Override + public int getStartStopThreads() { + return startStopThreads; + } + + /** + * Handles the special values. + */ + private int getStartStopThreadsInternal() { + int result = getStartStopThreads(); + + // Positive values are unchanged + if (result > 0) { + return result; + } + + // Zero == Runtime.getRuntime().availableProcessors() + // -ve == Runtime.getRuntime().availableProcessors() + value + // These two are the same + result = Runtime.getRuntime().availableProcessors() + result; + if (result < 1) { + result = 1; + } + return result; + } + + @Override + public void setStartStopThreads(int startStopThreads) { + this.startStopThreads = startStopThreads; + + // Use local copies to ensure thread safety + ThreadPoolExecutor executor = startStopExecutor; + if (executor != null) { + int newThreads = getStartStopThreadsInternal(); + executor.setMaximumPoolSize(newThreads); + executor.setCorePoolSize(newThreads); + } + } + + + /** + * Get the delay between the invocation of the backgroundProcess method on + * this container and its children. Child containers will not be invoked + * if their delay value is not negative (which would mean they are using + * their own thread). Setting this to a positive value will cause + * a thread to be spawn. After waiting the specified amount of time, + * the thread will invoke the executePeriodic method on this container + * and all its children. + */ + @Override + public int getBackgroundProcessorDelay() { + return backgroundProcessorDelay; + } + + + /** + * Set the delay between the invocation of the execute method on this + * container and its children. + * + * @param delay The delay in seconds between the invocation of + * backgroundProcess methods + */ + @Override + public void setBackgroundProcessorDelay(int delay) { + backgroundProcessorDelay = delay; + } + + + /** + * Return descriptive information about this Container implementation and + * the corresponding version number, in the format + * <description>/<version>. + */ + @Override + public String getInfo() { + return this.getClass().getName(); + } + + + /** + * Return the Loader with which this Container is associated. If there is + * no associated Loader, return the Loader associated with our parent + * Container (if any); otherwise, return null. + */ + @Override + public Loader getLoader() { + + if (loader != null) + return (loader); + if (parent != null) + return (parent.getLoader()); + return (null); + + } + + + /** + * Set the Loader with which this Container is associated. + * + * @param loader The newly associated loader + */ + @Override + public synchronized void setLoader(Loader loader) { + + // Change components if necessary + Loader oldLoader = this.loader; + if (oldLoader == loader) + return; + this.loader = loader; + + // Stop the old component if necessary + if (getState().isAvailable() && (oldLoader != null) && + (oldLoader instanceof Lifecycle)) { + try { + ((Lifecycle) oldLoader).stop(); + } catch (LifecycleException e) { + log.error("ContainerBase.setLoader: stop: ", e); + } + } + + // Start the new component if necessary + if (loader != null) + loader.setContainer(this); + if (getState().isAvailable() && (loader != null) && + (loader instanceof Lifecycle)) { + try { + ((Lifecycle) loader).start(); + } catch (LifecycleException e) { + log.error("ContainerBase.setLoader: start: ", e); + } + } + + // Report this property change to interested listeners + support.firePropertyChange("loader", oldLoader, this.loader); + + } + + + /** + * Return the Logger for this Container. + */ + @Override + public Log getLogger() { + + if (logger != null) + return (logger); + logger = LogFactory.getLog(logName()); + return (logger); + + } + + + /** + * Return the Manager with which this Container is associated. If there is + * no associated Manager, return the Manager associated with our parent + * Container (if any); otherwise return null. + */ + @Override + public Manager getManager() { + + if (manager != null) + return (manager); + if (parent != null) + return (parent.getManager()); + return (null); + + } + + + /** + * Set the Manager with which this Container is associated. + * + * @param manager The newly associated Manager + */ + @Override + public synchronized void setManager(Manager manager) { + + // Change components if necessary + Manager oldManager = this.manager; + if (oldManager == manager) + return; + this.manager = manager; + + // Stop the old component if necessary + if (getState().isAvailable() && (oldManager != null) && + (oldManager instanceof Lifecycle)) { + try { + ((Lifecycle) oldManager).stop(); + } catch (LifecycleException e) { + log.error("ContainerBase.setManager: stop: ", e); + } + } + + // Start the new component if necessary + if (manager != null) + manager.setContainer(this); + if (getState().isAvailable() && (manager != null) && + (manager instanceof Lifecycle)) { + try { + ((Lifecycle) manager).start(); + } catch (LifecycleException e) { + log.error("ContainerBase.setManager: start: ", e); + } + } + + // Report this property change to interested listeners + support.firePropertyChange("manager", oldManager, this.manager); + + } + + + /** + * Return an object which may be utilized for mapping to this component. + */ + @Override + public Object getMappingObject() { + return this; + } + + + /** + * Return the Cluster with which this Container is associated. If there is + * no associated Cluster, return the Cluster associated with our parent + * Container (if any); otherwise return null. + */ + @Override + public Cluster getCluster() { + if (cluster != null) + return (cluster); + + if (parent != null) + return (parent.getCluster()); + + return (null); + } + + + /** + * Set the Cluster with which this Container is associated. + * + * @param cluster The newly associated Cluster + */ + @Override + public synchronized void setCluster(Cluster cluster) { + // Change components if necessary + Cluster oldCluster = this.cluster; + if (oldCluster == cluster) + return; + this.cluster = cluster; + + // Stop the old component if necessary + if (getState().isAvailable() && (oldCluster != null) && + (oldCluster instanceof Lifecycle)) { + try { + ((Lifecycle) oldCluster).stop(); + } catch (LifecycleException e) { + log.error("ContainerBase.setCluster: stop: ", e); + } + } + + // Start the new component if necessary + if (cluster != null) + cluster.setContainer(this); + + if (getState().isAvailable() && (cluster != null) && + (cluster instanceof Lifecycle)) { + try { + ((Lifecycle) cluster).start(); + } catch (LifecycleException e) { + log.error("ContainerBase.setCluster: start: ", e); + } + } + + // Report this property change to interested listeners + support.firePropertyChange("cluster", oldCluster, this.cluster); + } + + + /** + * Return a name string (suitable for use by humans) that describes this + * Container. Within the set of child containers belonging to a particular + * parent, Container names must be unique. + */ + @Override + public String getName() { + + return (name); + + } + + + /** + * Set a name string (suitable for use by humans) that describes this + * Container. Within the set of child containers belonging to a particular + * parent, Container names must be unique. + * + * @param name New name of this container + * + * @exception IllegalStateException if this Container has already been + * added to the children of a parent Container (after which the name + * may not be changed) + */ + @Override + public void setName(String name) { + + String oldName = this.name; + this.name = name; + support.firePropertyChange("name", oldName, this.name); + } + + + /** + * Return if children of this container will be started automatically when + * they are added to this container. + */ + public boolean getStartChildren() { + + return (startChildren); + + } + + + /** + * Set if children of this container will be started automatically when + * they are added to this container. + * + * @param startChildren New value of the startChildren flag + */ + public void setStartChildren(boolean startChildren) { + + boolean oldStartChildren = this.startChildren; + this.startChildren = startChildren; + support.firePropertyChange("startChildren", oldStartChildren, this.startChildren); + } + + + /** + * Return the Container for which this Container is a child, if there is + * one. If there is no defined parent, return null. + */ + @Override + public Container getParent() { + + return (parent); + + } + + + /** + * Set the parent Container to which this Container is being added as a + * child. This Container may refuse to become attached to the specified + * Container by throwing an exception. + * + * @param container Container to which this Container is being added + * as a child + * + * @exception IllegalArgumentException if this Container refuses to become + * attached to the specified Container + */ + @Override + public void setParent(Container container) { + + Container oldParent = this.parent; + this.parent = container; + support.firePropertyChange("parent", oldParent, this.parent); + + } + + + /** + * Return the parent class loader (if any) for this web application. + * This call is meaningful only after a Loader has + * been configured. + */ + @Override + public ClassLoader getParentClassLoader() { + if (parentClassLoader != null) + return (parentClassLoader); + if (parent != null) { + return (parent.getParentClassLoader()); + } + return (ClassLoader.getSystemClassLoader()); + + } + + + /** + * Set the parent class loader (if any) for this web application. + * This call is meaningful only before a Loader has + * been configured, and the specified value (if non-null) should be + * passed as an argument to the class loader constructor. + * + * + * @param parent The new parent class loader + */ + @Override + public void setParentClassLoader(ClassLoader parent) { + ClassLoader oldParentClassLoader = this.parentClassLoader; + this.parentClassLoader = parent; + support.firePropertyChange("parentClassLoader", oldParentClassLoader, + this.parentClassLoader); + + } + + + /** + * Return the Pipeline object that manages the Valves associated with + * this Container. + */ + @Override + public Pipeline getPipeline() { + + return (this.pipeline); + + } + + + /** + * Return the Realm with which this Container is associated. If there is + * no associated Realm, return the Realm associated with our parent + * Container (if any); otherwise return null. + */ + @Override + public Realm getRealm() { + + if (realm != null) + return (realm); + if (parent != null) + return (parent.getRealm()); + return (null); + + } + + + /** + * Set the Realm with which this Container is associated. + * + * @param realm The newly associated Realm + */ + @Override + public synchronized void setRealm(Realm realm) { + + // Change components if necessary + Realm oldRealm = this.realm; + if (oldRealm == realm) + return; + this.realm = realm; + + // Stop the old component if necessary + if (getState().isAvailable() && (oldRealm != null) && + (oldRealm instanceof Lifecycle)) { + try { + ((Lifecycle) oldRealm).stop(); + } catch (LifecycleException e) { + log.error("ContainerBase.setRealm: stop: ", e); + } + } + + // Start the new component if necessary + if (realm != null) + realm.setContainer(this); + if (getState().isAvailable() && (realm != null) && + (realm instanceof Lifecycle)) { + try { + ((Lifecycle) realm).start(); + } catch (LifecycleException e) { + log.error("ContainerBase.setRealm: start: ", e); + } + } + + // Report this property change to interested listeners + support.firePropertyChange("realm", oldRealm, this.realm); + + } + + + /** + * Return the resources DirContext object with which this Container is + * associated. If there is no associated resources object, return the + * resources associated with our parent Container (if any); otherwise + * return null. + */ + @Override + public DirContext getResources() { + if (resources != null) + return (resources); + if (parent != null) + return (parent.getResources()); + return (null); + + } + + + /** + * Set the resources DirContext object with which this Container is + * associated. + * + * @param resources The newly associated DirContext + */ + @Override + public synchronized void setResources(DirContext resources) { + // Called from StandardContext.setResources() + // <- StandardContext.start() + // <- ContainerBase.addChildInternal() + + // Change components if necessary + DirContext oldResources = this.resources; + if (oldResources == resources) + return; + Hashtable env = new Hashtable(); + if (getParent() != null) + env.put(ProxyDirContext.HOST, getParent().getName()); + env.put(ProxyDirContext.CONTEXT, getName()); + this.resources = new ProxyDirContext(env, resources); + // Report this property change to interested listeners + support.firePropertyChange("resources", oldResources, this.resources); + + } + + + // ------------------------------------------------------ Container Methods + + + /** + * Add a new child Container to those associated with this Container, + * if supported. Prior to adding this Container to the set of children, + * the child's setParent() method must be called, with this + * Container as an argument. This method may thrown an + * IllegalArgumentException if this Container chooses not + * to be attached to the specified Container, in which case it is not added + * + * @param child New child Container to be added + * + * @exception IllegalArgumentException if this exception is thrown by + * the setParent() method of the child Container + * @exception IllegalArgumentException if the new child does not have + * a name unique from that of existing children of this Container + * @exception IllegalStateException if this Container does not support + * child Containers + */ + @Override + public void addChild(Container child) { + if (Globals.IS_SECURITY_ENABLED) { + PrivilegedAction dp = + new PrivilegedAddChild(child); + AccessController.doPrivileged(dp); + } else { + addChildInternal(child); + } + } + + private void addChildInternal(Container child) { + + if( log.isDebugEnabled() ) + log.debug("Add child " + child + " " + this); + synchronized(children) { + if (children.get(child.getName()) != null) + throw new IllegalArgumentException("addChild: Child name '" + + child.getName() + + "' is not unique"); + child.setParent(this); // May throw IAE + children.put(child.getName(), child); + } + + // Start child + // Don't do this inside sync block - start can be a slow process and + // locking the children object can cause problems elsewhere + if ((getState().isAvailable() || + LifecycleState.STARTING_PREP.equals(getState())) && + startChildren) { + boolean success = false; + try { + child.start(); + success = true; + } catch (LifecycleException e) { + log.error("ContainerBase.addChild: start: ", e); + throw new IllegalStateException + ("ContainerBase.addChild: start: " + e); + } finally { + if (!success) { + synchronized (children) { + children.remove(child.getName()); + } + } + } + } + + fireContainerEvent(ADD_CHILD_EVENT, child); + } + + + /** + * Add a container event listener to this component. + * + * @param listener The listener to add + */ + @Override + public void addContainerListener(ContainerListener listener) { + + synchronized (listeners) { + listeners.add(listener); + } + + } + + + /** + * Add a property change listener to this component. + * + * @param listener The listener to add + */ + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + + support.addPropertyChangeListener(listener); + + } + + + /** + * Return the child Container, associated with this Container, with + * the specified name (if any); otherwise, return null + * + * @param name Name of the child Container to be retrieved + */ + @Override + public Container findChild(String name) { + + if (name == null) + return (null); + synchronized (children) { + return children.get(name); + } + + } + + + /** + * Return the set of children Containers associated with this Container. + * If this Container has no children, a zero-length array is returned. + */ + @Override + public Container[] findChildren() { + + synchronized (children) { + Container results[] = new Container[children.size()]; + return children.values().toArray(results); + } + + } + + + /** + * Return the set of container listeners associated with this Container. + * If this Container has no registered container listeners, a zero-length + * array is returned. + */ + @Override + public ContainerListener[] findContainerListeners() { + + synchronized (listeners) { + ContainerListener[] results = + new ContainerListener[listeners.size()]; + return listeners.toArray(results); + } + + } + + + /** + * Process the specified Request, to produce the corresponding Response, + * by invoking the first Valve in our pipeline (if any), or the basic + * Valve otherwise. + * + * @param request Request to be processed + * @param response Response to be produced + * + * @exception IllegalStateException if neither a pipeline or a basic + * Valve have been configured for this Container + * @exception IOException if an input/output error occurred while + * processing + * @exception ServletException if a ServletException was thrown + * while processing this request + */ + @Override + public void invoke(Request request, Response response) + throws IOException, ServletException { + + pipeline.getFirst().invoke(request, response); + + } + + + /** + * Remove an existing child Container from association with this parent + * Container. + * + * @param child Existing child Container to be removed + */ + @Override + public void removeChild(Container child) { + + if (child == null) { + return; + } + + synchronized(children) { + if (children.get(child.getName()) == null) + return; + children.remove(child.getName()); + } + + try { + if (child.getState().isAvailable()) { + child.stop(); + } + } catch (LifecycleException e) { + log.error("ContainerBase.removeChild: stop: ", e); + } + + fireContainerEvent(REMOVE_CHILD_EVENT, child); + + try { + // child.destroy() may have already been called which would have + // triggered this call. If that is the case, no need to destroy the + // child again. + if (!LifecycleState.DESTROYING.equals(child.getState())) { + child.destroy(); + } + } catch (LifecycleException e) { + log.error("ContainerBase.removeChild: destroy: ", e); + } + + } + + + /** + * Remove a container event listener from this component. + * + * @param listener The listener to remove + */ + @Override + public void removeContainerListener(ContainerListener listener) { + + synchronized (listeners) { + listeners.remove(listener); + } + + } + + + /** + * Remove a property change listener from this component. + * + * @param listener The listener to remove + */ + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + + support.removePropertyChangeListener(listener); + + } + + + @Override + protected void initInternal() throws LifecycleException { + BlockingQueue startStopQueue = + new LinkedBlockingQueue(); + startStopExecutor = new ThreadPoolExecutor(0, + getStartStopThreadsInternal(), 10, TimeUnit.SECONDS, + startStopQueue); + super.initInternal(); + } + + + /** + * Start this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void startInternal() throws LifecycleException { + + // Start our subordinate components, if any + if ((loader != null) && (loader instanceof Lifecycle)) + ((Lifecycle) loader).start(); + logger = null; + getLogger(); + if ((logger != null) && (logger instanceof Lifecycle)) + ((Lifecycle) logger).start(); + if ((manager != null) && (manager instanceof Lifecycle)) + ((Lifecycle) manager).start(); + if ((cluster != null) && (cluster instanceof Lifecycle)) + ((Lifecycle) cluster).start(); + if ((realm != null) && (realm instanceof Lifecycle)) + ((Lifecycle) realm).start(); + if ((resources != null) && (resources instanceof Lifecycle)) + ((Lifecycle) resources).start(); + + // Start our child containers, if any + Container children[] = findChildren(); + List> results = new ArrayList>(); + for (int i = 0; i < children.length; i++) { + results.add(startStopExecutor.submit(new StartChild(children[i]))); + } + + boolean fail = false; + for (Future result : results) { + try { + result.get(); + } catch (Exception e) { + log.error(sm.getString("containerBase.threadedStartFailed"), e); + fail = true; + } + + } + if (fail) { + throw new LifecycleException( + sm.getString("containerBase.threadedStartFailed")); + } + + // Start the Valves in our pipeline (including the basic), if any + if (pipeline instanceof Lifecycle) + ((Lifecycle) pipeline).start(); + + + setState(LifecycleState.STARTING); + + // Start our thread + threadStart(); + + } + + + /** + * Stop this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void stopInternal() throws LifecycleException { + + // Stop our thread + threadStop(); + + setState(LifecycleState.STOPPING); + + // Stop the Valves in our pipeline (including the basic), if any + if (pipeline instanceof Lifecycle && + ((Lifecycle) pipeline).getState().isAvailable()) { + ((Lifecycle) pipeline).stop(); + } + + // Stop our child containers, if any + Container children[] = findChildren(); + List> results = new ArrayList>(); + for (int i = 0; i < children.length; i++) { + results.add(startStopExecutor.submit(new StopChild(children[i]))); + } + + boolean fail = false; + for (Future result : results) { + try { + result.get(); + } catch (Exception e) { + log.error(sm.getString("containerBase.threadedStopFailed"), e); + fail = true; + } + } + if (fail) { + throw new LifecycleException( + sm.getString("containerBase.threadedStopFailed")); + } + + // Stop our subordinate components, if any + if ((resources != null) && (resources instanceof Lifecycle)) { + ((Lifecycle) resources).stop(); + } + if ((realm != null) && (realm instanceof Lifecycle)) { + ((Lifecycle) realm).stop(); + } + if ((cluster != null) && (cluster instanceof Lifecycle)) { + ((Lifecycle) cluster).stop(); + } + if ((manager != null) && (manager instanceof Lifecycle) && + ((Lifecycle) manager).getState().isAvailable() ) { + ((Lifecycle) manager).stop(); + } + if ((logger != null) && (logger instanceof Lifecycle)) { + ((Lifecycle) logger).stop(); + } + if ((loader != null) && (loader instanceof Lifecycle)) { + ((Lifecycle) loader).stop(); + } + } + + @Override + protected void destroyInternal() throws LifecycleException { + + // Stop the Valves in our pipeline (including the basic), if any + if (pipeline instanceof Lifecycle) { + ((Lifecycle) pipeline).destroy(); + } + + // Remove children now this container is being destroyed + for (Container child : findChildren()) { + removeChild(child); + } + + // Required if the child is destroyed directly. + if (parent != null) { + parent.removeChild(this); + } + + startStopExecutor.shutdownNow(); + + super.destroyInternal(); + } + + + /** + * Check this container for an access log and if none is found, look to the + * parent. If there is no parent and still none is found, use the NoOp + * access log. + */ + @Override + public void logAccess(Request request, Response response, long time, + boolean useDefault) { + + boolean logged = false; + + if (getAccessLog() != null) { + getAccessLog().log(request, response, time); + logged = true; + } + + if (getParent() != null) { + // No need to use default logger once request/response has been logged + // once + getParent().logAccess(request, response, time, (useDefault && !logged)); + } + } + + @Override + public AccessLog getAccessLog() { + + if (accessLogScanComplete) { + return accessLog; + } + + AccessLogAdapter adapter = null; + Valve valves[] = getPipeline().getValves(); + for (Valve valve : valves) { + if (valve instanceof AccessLog) { + if (adapter == null) { + adapter = new AccessLogAdapter((AccessLog) valve); + } else { + adapter.add((AccessLog) valve); + } + } + } + if (adapter != null) { + accessLog = adapter; + } + accessLogScanComplete = true; + return accessLog; + } + + // ------------------------------------------------------- Pipeline Methods + + + /** + * Convenience method, intended for use by the digester to simplify the + * process of adding Valves to containers. See + * {@link Pipeline#addValve(Valve)} for full details. Components other than + * the digester should use {@link #getPipeline()}.{@link #addValve(Valve)} in case a + * future implementation provides an alternative method for the digester to + * use. + * + * @param valve Valve to be added + * + * @exception IllegalArgumentException if this Container refused to + * accept the specified Valve + * @exception IllegalArgumentException if the specified Valve refuses to be + * associated with this Container + * @exception IllegalStateException if the specified Valve is already + * associated with a different Container + */ + public synchronized void addValve(Valve valve) { + + pipeline.addValve(valve); + } + + + /** + * 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. + */ + @Override + public void backgroundProcess() { + + if (!getState().isAvailable()) + return; + + if (cluster != null) { + try { + cluster.backgroundProcess(); + } catch (Exception e) { + log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e); + } + } + if (loader != null) { + try { + loader.backgroundProcess(); + } catch (Exception e) { + log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e); + } + } + if (manager != null) { + try { + manager.backgroundProcess(); + } catch (Exception e) { + log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e); + } + } + if (realm != null) { + try { + realm.backgroundProcess(); + } catch (Exception e) { + log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e); + } + } + Valve current = pipeline.getFirst(); + while (current != null) { + try { + current.backgroundProcess(); + } catch (Exception e) { + log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e); + } + current = current.getNext(); + } + fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null); + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Notify all container event listeners that a particular event has + * occurred for this Container. The default implementation performs + * this notification synchronously using the calling thread. + * + * @param type Event type + * @param data Event data + */ + @Override + public void fireContainerEvent(String type, Object data) { + + if (listeners.size() < 1) + return; + ContainerEvent event = new ContainerEvent(this, type, data); + ContainerListener list[] = new ContainerListener[0]; + synchronized (listeners) { + list = listeners.toArray(list); + } + for (int i = 0; i < list.length; i++) + list[i].containerEvent(event); + + } + + + /** + * Return the abbreviated name of this container for logging messages + */ + protected String logName() { + + if (logName != null) { + return logName; + } + String loggerName = null; + Container current = this; + while (current != null) { + String name = current.getName(); + if ((name == null) || (name.equals(""))) { + name = "/"; + } else if (name.startsWith("##")) { + name = "/" + name; + } + loggerName = "[" + name + "]" + + ((loggerName != null) ? ("." + loggerName) : ""); + current = current.getParent(); + } + logName = ContainerBase.class.getName() + "." + loggerName; + return logName; + + } + + + // -------------------- JMX and Registration -------------------- + + @Override + protected String getDomainInternal() { + return MBeanUtils.getDomain(this); + } + + public ObjectName[] getChildren() { + ObjectName result[]=new ObjectName[children.size()]; + Iterator it=children.values().iterator(); + int i=0; + while( it.hasNext() ) { + Object next=it.next(); + if( next instanceof ContainerBase ) { + result[i++]=((ContainerBase)next).getObjectName(); + } + } + return result; + } + + + // -------------------- Background Thread -------------------- + + /** + * Start the background thread that will periodically check for + * session timeouts. + */ + protected void threadStart() { + + if (thread != null) + return; + if (backgroundProcessorDelay <= 0) + return; + + threadDone = false; + String threadName = "ContainerBackgroundProcessor[" + toString() + "]"; + thread = new Thread(new ContainerBackgroundProcessor(), threadName); + thread.setDaemon(true); + thread.start(); + + } + + + /** + * Stop the background thread that is periodically checking for + * session timeouts. + */ + protected void threadStop() { + + if (thread == null) + return; + + threadDone = true; + thread.interrupt(); + try { + thread.join(); + } catch (InterruptedException e) { + // Ignore + } + + thread = null; + + } + + + // -------------------------------------- ContainerExecuteDelay Inner Class + + + /** + * Private thread class to invoke the backgroundProcess method + * of this container and its children after a fixed delay. + */ + protected class ContainerBackgroundProcessor implements Runnable { + + @Override + public void run() { + while (!threadDone) { + try { + Thread.sleep(backgroundProcessorDelay * 1000L); + } catch (InterruptedException e) { + // Ignore + } + if (!threadDone) { + Container parent = (Container) getMappingObject(); + ClassLoader cl = + Thread.currentThread().getContextClassLoader(); + if (parent.getLoader() != null) { + cl = parent.getLoader().getClassLoader(); + } + processChildren(parent, cl); + } + } + } + + protected void processChildren(Container container, ClassLoader cl) { + try { + if (container.getLoader() != null) { + Thread.currentThread().setContextClassLoader + (container.getLoader().getClassLoader()); + } + container.backgroundProcess(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error("Exception invoking periodic operation: ", t); + } finally { + Thread.currentThread().setContextClassLoader(cl); + } + Container[] children = container.findChildren(); + for (int i = 0; i < children.length; i++) { + if (children[i].getBackgroundProcessorDelay() <= 0) { + processChildren(children[i], cl); + } + } + } + } + + + // ----------------------------- Inner classes used with start/stop Executor + + private static class StartChild implements Callable { + + private Container child; + + public StartChild(Container child) { + this.child = child; + } + + @Override + public Void call() throws LifecycleException { + child.start(); + return null; + } + } + + private static class StopChild implements Callable { + + private Container child; + + public StopChild(Container child) { + this.child = child; + } + + @Override + public Void call() throws LifecycleException { + child.stop(); + return null; + } + } +} diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties index 839005c..f51c19e 100644 --- a/java/org/apache/catalina/core/LocalStrings.properties +++ b/java/org/apache/catalina/core/LocalStrings.properties @@ -1,255 +1,257 @@ -# 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. - -applicationContext.addFilter.ise=Filters can not be added to context {0} as the context has been initialised -applicationContext.addListener.iae.cnfe=Unable to create an instance of type [{0}] -applicationContext.addListener.iae.wrongType=The type specified [{0}] is not one of the expected listener types -applicationContext.addListener.iae.sclNotAllowed=Once the first ServletContextListener has been called, no more ServletContextListeners may be added. -applicationContext.addListener.ise=Listeners can not be added to context {0} as the context has been initialised -applicationContext.addRole.ise=Roles can not be added to context {0} as the context has been initialised -applicationContext.addServlet.ise=Servlets can not be added to context {0} as the context has been initialised -applicationContext.attributeEvent=Exception thrown by attributes event listener -applicationContext.mapping.error=Error during mapping -applicationContext.requestDispatcher.iae=Path {0} does not start with a "/" character -applicationContext.resourcePaths.iae=Path {0} does not start with a "/" character -applicationContext.role.iae=An individual role to declare for context [{0}] may not be null nor the empty string -applicationContext.roles.iae=Array of roles to declare for context [{0}] cannot be null -applicationContext.setAttribute.namenull=Name cannot be null -applicationContext.addSessionCookieConfig.ise=Session Cookie configuration cannot be set for context {0} as the context has been initialised -applicationContext.setSessionTracking.ise=The session tracking modes for context {0} cannot be set whilst the context is running -applicationContext.setSessionTracking.iae.invalid=The session tracking mode {0} requested for context {1} is not supported by that context -applicationContext.setSessionTracking.iae.ssl=The session tracking modes requested for context {1} included SSL and at least one other mode. SSL may not be configured with other modes. -applicationContext.lookup.error=Failed to locate resource [{0}] in context [{1}] -applicationDispatcher.allocateException=Allocate exception for servlet {0} -applicationDispatcher.deallocateException=Deallocate exception for servlet {0} -applicationDispatcher.forward.ise=Cannot forward after response has been committed -applicationDispatcher.forward.throw=Forwarded resource threw an exception -applicationDispatcher.include.throw=Included resource threw an exception -applicationDispatcher.isUnavailable=Servlet {0} is currently unavailable -applicationDispatcher.serviceException=Servlet.service() for servlet {0} threw exception -applicationDispatcher.specViolation.request=Original SevletRequest or wrapped original ServletRequest not passed to RequestDispatcher in violation of SRV.8.2 and SRV.14.2.5.1 -applicationDispatcher.specViolation.response=Original SevletResponse or wrapped original ServletResponse not passed to RequestDispatcher in violation of SRV.8.2 and SRV.14.2.5.1 -applicationFilterConfig.jmxRegisterFail=JMX registration failed for filter of type [{0}] and name [{1}] -applicationFilterConfig.jmxUnregister=JMX de-registration complete for filter of type [{0}] and name [{1}] -applicationFilterConfig.jmxUnregisterFail=JMX de-registration failed for filter of type [{0}] and name [{1}] -applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}] -applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}] -applicationRequest.badParent=Cannot locate parent Request implementation -applicationRequest.badRequest=Request is not a javax.servlet.ServletRequestWrapper -applicationResponse.badParent=Cannot locate parent Response implementation -applicationResponse.badResponse=Response is not a javax.servlet.ServletResponseWrapper -applicationServletRegistration.setServletSecurity.iae=Null constraint specified for servlet [{0}] deployed to context with name [{1}] -applicationServletRegistration.setServletSecurity.ise=Security constraints can't be added to servlet [{0}] deployed to context with name [{1}] as the context has already been initialised -aprListener.aprInit=The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: {0} -aprListener.tcnInvalid=An incompatible version {0} of the APR based Apache Tomcat Native library is installed, while Tomcat requires version {1} -aprListener.tcnVersion=An older version {0} of the APR based Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of {1} -aprListener.aprDestroy=Failed shutdown of APR based Apache Tomcat Native library -aprListener.sslInit=Failed to initialize the SSLEngine. -aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0}. -aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}]. -asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing. -containerBase.alreadyStarted=Container {0} has already been started -containerBase.notConfigured=No basic Valve has been configured -containerBase.notStarted=Container {0} has not been started -containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process -containerBase.backgroundProcess.loader=Exception processing loader {0} background process -containerBase.backgroundProcess.manager=Exception processing manager {0} background process -containerBase.backgroundProcess.realm=Exception processing realm {0} background process -containerBase.backgroundProcess.valve=Exception processing valve {0} background process -fastEngineMapper.alreadyStarted=FastEngineMapper {0} has already been started -fastEngineMapper.notStarted=FastEngineMapper {0} has not yet been started -filterChain.filter=Filter execution threw an exception -filterChain.servlet=Servlet execution threw an exception -httpContextMapper.container=This container is not a StandardContext -httpEngineMapper.container=This container is not a StandardEngine -httpHostMapper.container=This container is not a StandardHost -interceptorValve.alreadyStarted=InterceptorValve has already been started -interceptorValve.notStarted=InterceptorValve has not yet been started -jreLeakListener.keepAliveFail=Failed to trigger creation of the sun.net.www.http.HttpClient class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. -jreLeakListener.gcDaemonFail=Failed to trigger creation of the GC Daemon thread during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. -jreLeakListener.jarUrlConnCacheFail=Failed to disable Jar URL connection caching by default -jreLeakListener.xmlParseFail=Error whilst attempting to prevent memory leaks during XML parsing -jreLeakListener.authPolicyFail=Error whilst attempting to prevent memory leak in javax.security.auth.Policy class -jreLeakListener.ldapPoolManagerFail=Failed to trigger creation of the com.sun.jndi.ldap.LdapPoolManager class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. -jreLeakListener.classToInitializeFail=Failed to load class {0} during Tomcat start to prevent possible memory leaks. -naming.wsdlFailed=Failed to find wsdl file: {0} -naming.bindFailed=Failed to bind object: {0} -naming.jmxRegistrationFailed=Failed to register in JMX: {0} -naming.unbindFailed=Failed to unbind object: {0} -naming.invalidEnvEntryType=Environment entry {0} has an invalid type -naming.invalidEnvEntryValue=Environment entry {0} has an invalid value -naming.namingContextCreationFailed=Creation of the naming context failed: {0} -standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper -standardContext.alreadyStarted=Context has already been started -standardContext.applicationListener=Error configuring application listener of class {0} -standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s) -standardContext.badRequest=Invalid request path ({0}). -standardContext.cluster.noManager=No manager found. Checking if cluster manager should be used. Cluster configured: [{0}], Application distributable: [{1}] -standardContext.crlfinurl=The URL pattern "{0}" contains a CR or LF and so can never be matched. -standardContext.duplicateListener=The listener "{0}" is already configured for this context. The duplicate definition has been ignored. -standardContext.errorPage.error=Error page location {0} must start with a ''/'' -standardContext.errorPage.required=ErrorPage cannot be null -standardContext.errorPage.warning=WARNING: Error page location {0} must start with a ''/'' in Servlet 2.4 -standardContext.filterMap.either=Filter mapping must specify either a or a -standardContext.filterMap.name=Filter mapping specifies an unknown filter name {0} -standardContext.filterMap.pattern=Invalid {0} in filter mapping -standardContext.filterStart=Exception starting filter {0} -standardContext.filterStartFailed=Failed to start application Filters successfully -standardContext.requestListener.requestInit=Exception sending request initialized lifecycle event to listener instance of class {0} -standardContext.requestListener.requestDestroy=Exception sending request destroyed lifecycle event to listener instance of class {0} -standardContext.requestListenerStartFailed=Failed to start request listener valve successfully -standardContext.requestListenerConfig.added=Added request listener Valve -standardContext.requestListenerConfig.error=Exception adding request listener Valve: {0} -standardContext.isUnavailable=This application is not currently available -standardContext.listenerStart=Exception sending context initialized event to listener instance of class {0} -standardContext.listenerStartFailed=Failed to start application Listeners successfully -standardContext.listenerStop=Exception sending context destroyed event to listener instance of class {0} -standardContext.loginConfig.errorPage=Form error page {0} must start with a ''/' -standardContext.loginConfig.errorWarning=WARNING: Form error page {0} must start with a ''/'' in Servlet 2.4 -standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/' -standardContext.loginConfig.loginWarning=WARNING: Form login page {0} must start with a ''/'' in Servlet 2.4 -standardContext.loginConfig.required=LoginConfig cannot be null -standardContext.manager=Configured a manager of class [{0}] -standardContext.mappingError=MAPPING configuration error for relative URI {0} -standardContext.namingResource.init.fail=Failed to init new naming resources -standardContext.namingResource.destroy.fail=Failed to destroy old naming resources -standardContext.noResourceJar=Resource JARs are not supported. The JAR found at [{0}] will not be used to provide static content for context with name [{1}] -standardContext.notFound=The requested resource ({0}) is not available. -standardContext.notReloadable=Reloading is disabled on this Context -standardContext.notStarted=Context with name [{0}] has not yet been started -standardContext.notWrapper=Child of a Context must be a Wrapper -standardContext.parameter.duplicate=Duplicate context initialization parameter {0} -standardContext.parameter.required=Both parameter name and parameter value are required -standardContext.pathInvalid=A context path must either be an empty string or start with a ''/''. The path [{0}] does not meet these criteria and has been changed to [{1}] -standardContext.reloadingCompleted=Reloading Context with name [{0}] is completed -standardContext.reloadingFailed=Reloading this Context failed due to previous errors -standardContext.reloadingStarted=Reloading Context with name [{0}] has started -standardContext.resourcesStart=Error starting static Resources -standardContext.securityConstraint.mixHttpMethod=It is not permitted to mix and in the same web resource collection -standardContext.securityConstraint.pattern=Invalid {0} in security constraint -standardContext.servletMap.name=Servlet mapping specifies an unknown servlet name {0} -standardContext.servletMap.pattern=Invalid {0} in servlet mapping -standardContext.startCleanup=Exception during cleanup after start failed -standardContext.startFailed=Context [{0}] startup failed due to previous errors -standardContext.startingContext=Exception starting Context with name [{0}] -standardContext.startingLoader=Exception starting Loader -standardContext.startingManager=Exception starting Manager -standardContext.startingWrapper=Exception starting Wrapper for servlet {0} -standardContext.stoppingContext=Exception stopping Context with name [{0}] -standardContext.stoppingLoader=Exception stopping Loader -standardContext.stoppingManager=Exception stopping Manager -standardContext.stoppingWrapper=Exception stopping Wrapper for servlet {0} -standardContext.urlDecode=Cannot URL decode request path {0} -standardContext.urlPattern.patternWarning=WARNING: URL pattern {0} must start with a ''/'' in Servlet 2.4 -standardContext.urlValidate=Cannot validate URL decoded request path {0} -standardContext.workPath=Exception obtaining work path for context [{0}] -standardContext.workCreateException=Failed to determine absolute work directory from directory [{0}] and CATALINA_HOME [{1}] for context [{2}] -standardContext.workCreateFail=Failed to create work directory [{0}] for context [{1}] -standardContextValve.acknowledgeException=Failed to acknowledge request with a 100 (Continue) response -standardEngine.alreadyStarted=Engine has already been started -standardEngine.jvmRouteFail=Failed to set Engine's jvmRoute attribute from system property -standardEngine.mappingError=MAPPING configuration error for server name {0} -standardEngine.noHost=No Host matches server name {0} -standardEngine.noHostHeader=HTTP/1.1 request with no Host: header -standardEngine.notHost=Child of an Engine must be a Host -standardEngine.notParent=Engine cannot have a parent Container -standardEngine.notStarted=Engine has not yet been started -standardEngine.unfoundHost=Virtual host {0} not found -standardEngine.unknownHost=No server host specified in this request -standardEngine.unregister.mbeans.failed=Error in destroy() for mbean file {0} -standardHost.accessBase=Cannot access document base directory {0} -standardHost.alreadyStarted=Host has already been started -standardHost.appBase=Application base directory {0} does not exist -standardHost.clientAbort=Remote Client Aborted Request, IOException: {0} -standardHost.configRequired=URL to configuration file is required -standardHost.configNotAllowed=Use of configuration file is not allowed -standardHost.installBase=Only web applications in the Host web application directory can be installed -standardHost.installing=Installing web application at context path {0} from URL {1} -standardHost.installingWAR=Installing web application from URL {0} -standardHost.installingXML=Processing Context configuration file URL {0} -standardHost.installError=Error deploying application at context path {0} -standardHost.invalidErrorReportValveClass=Couldn''t load specified error report valve class: {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.pathMatch=Context path {0} must match the directory or WAR file name: {1} -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.removing=Removing web application at context path {0} -standardHost.removeError=Error removing application at context path {0} -standardHost.start=Starting web application at context path {0} -standardHost.stop=Stopping web 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.onameFail=MBean name specified for Server [{0}] is not valid -standardServer.shutdownViaPort=A valid shutdown command was received via the shutdown port. Stopping the Server instance. -standardService.connector.initFailed=Failed to initialize connector [{0}] -standardService.connector.destroyFailed=Failed to destroy connector [{0}] -standardService.connector.pauseFailed=Failed to pause connector [{0}] -standardService.connector.startFailed=Failed to start connector [{0}] -standardService.connector.stopFailed=Failed to stop connector [{0}] -standardService.initialize.failed=Service initializing at {0} failed -standardService.onameFail=MBean name specified for Service [{0}] is not valid -standardService.register.failed=Error registering Service at domain {0} -standardService.start.name=Starting service {0} -standardService.stop.name=Stopping service {0} -standardThreadExecutor.onameFail=MBean name specified for Thread Executor [{0}] is not valid -standardWrapper.allocate=Error allocating a servlet instance -standardWrapper.allocateException=Allocate exception for servlet {0} -standardWrapper.containerServlet=Loading container servlet {0} -standardWrapper.createFilters=Create filters exception for servlet {0} -standardWrapper.deallocateException=Deallocate exception for servlet {0} -standardWrapper.destroyException=Servlet.destroy() for servlet {0} threw exception -standardWrapper.exception0=Tomcat Exception Report -standardWrapper.exception1=A Servlet Exception Has Occurred -standardWrapper.exception2=Exception Report: -standardWrapper.exception3=Root Cause: -standardWrapper.initException=Servlet.init() for servlet {0} threw exception -standardWrapper.instantiate=Error instantiating servlet class {0} -standardWrapper.isUnavailable=Servlet {0} is currently unavailable -standardWrapper.jasperLoader=Using Jasper classloader for servlet {0} -standardWrapper.jspFile.format=JSP file {0} does not start with a ''/'' character -standardWrapper.loadException=Servlet {0} threw load() exception -standardWrapper.missingClass=Wrapper cannot find servlet class {0} or a class it depends on -standardWrapper.missingLoader=Wrapper cannot find Loader for servlet {0} -standardWrapper.notChild=Wrapper container may not have child containers -standardWrapper.notClass=No servlet class has been specified for servlet {0} -standardWrapper.notContext=Parent container of a Wrapper must be a Context -standardWrapper.notFound=Servlet {0} is not available -standardWrapper.notServlet=Class {0} is not a Servlet -standardWrapper.releaseFilters=Release filters exception for servlet {0} -standardWrapper.serviceException=Servlet.service() for servlet [{0}] in context with path [{1}] threw exception -standardWrapper.serviceExceptionRoot=Servlet.service() for servlet [{0}] in context with path [{1}] threw exception [{2}] with root cause -standardWrapper.statusHeader=HTTP Status {0} - {1} -standardWrapper.statusTitle=Tomcat Error Report -standardWrapper.unavailable=Marking servlet {0} as unavailable -standardWrapper.unloadException=Servlet {0} threw unload() exception -standardWrapper.unloading=Cannot allocate servlet {0} because it is being unloaded -standardWrapper.waiting=Waiting for {0} instance(s) to be deallocated -threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing lifecycle event {0} -threadLocalLeakPreventionListener.containerEvent.error=Exception processing container event {0} - -defaultInstanceManager.restrictedServletsResource=Restricted servlets property file not found -defaultInstanceManager.privilegedServlet=Servlet of class {0} is privileged and cannot be loaded by this web application -defaultInstanceManager.restrictedFiltersResource=Restricted filters property file not found -defaultInstanceManager.privilegedFilter=Filter of class {0} is privileged and cannot be loaded by this web application -defaultInstanceManager.restrictedListenersResources="Restricted listeners property file not found +# 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. + +applicationContext.addFilter.ise=Filters can not be added to context {0} as the context has been initialised +applicationContext.addListener.iae.cnfe=Unable to create an instance of type [{0}] +applicationContext.addListener.iae.wrongType=The type specified [{0}] is not one of the expected listener types +applicationContext.addListener.iae.sclNotAllowed=Once the first ServletContextListener has been called, no more ServletContextListeners may be added. +applicationContext.addListener.ise=Listeners can not be added to context {0} as the context has been initialised +applicationContext.addRole.ise=Roles can not be added to context {0} as the context has been initialised +applicationContext.addServlet.ise=Servlets can not be added to context {0} as the context has been initialised +applicationContext.attributeEvent=Exception thrown by attributes event listener +applicationContext.mapping.error=Error during mapping +applicationContext.requestDispatcher.iae=Path {0} does not start with a "/" character +applicationContext.resourcePaths.iae=Path {0} does not start with a "/" character +applicationContext.role.iae=An individual role to declare for context [{0}] may not be null nor the empty string +applicationContext.roles.iae=Array of roles to declare for context [{0}] cannot be null +applicationContext.setAttribute.namenull=Name cannot be null +applicationContext.addSessionCookieConfig.ise=Session Cookie configuration cannot be set for context {0} as the context has been initialised +applicationContext.setSessionTracking.ise=The session tracking modes for context {0} cannot be set whilst the context is running +applicationContext.setSessionTracking.iae.invalid=The session tracking mode {0} requested for context {1} is not supported by that context +applicationContext.setSessionTracking.iae.ssl=The session tracking modes requested for context {1} included SSL and at least one other mode. SSL may not be configured with other modes. +applicationContext.lookup.error=Failed to locate resource [{0}] in context [{1}] +applicationDispatcher.allocateException=Allocate exception for servlet {0} +applicationDispatcher.deallocateException=Deallocate exception for servlet {0} +applicationDispatcher.forward.ise=Cannot forward after response has been committed +applicationDispatcher.forward.throw=Forwarded resource threw an exception +applicationDispatcher.include.throw=Included resource threw an exception +applicationDispatcher.isUnavailable=Servlet {0} is currently unavailable +applicationDispatcher.serviceException=Servlet.service() for servlet {0} threw exception +applicationDispatcher.specViolation.request=Original SevletRequest or wrapped original ServletRequest not passed to RequestDispatcher in violation of SRV.8.2 and SRV.14.2.5.1 +applicationDispatcher.specViolation.response=Original SevletResponse or wrapped original ServletResponse not passed to RequestDispatcher in violation of SRV.8.2 and SRV.14.2.5.1 +applicationFilterConfig.jmxRegisterFail=JMX registration failed for filter of type [{0}] and name [{1}] +applicationFilterConfig.jmxUnregister=JMX de-registration complete for filter of type [{0}] and name [{1}] +applicationFilterConfig.jmxUnregisterFail=JMX de-registration failed for filter of type [{0}] and name [{1}] +applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}] +applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}] +applicationRequest.badParent=Cannot locate parent Request implementation +applicationRequest.badRequest=Request is not a javax.servlet.ServletRequestWrapper +applicationResponse.badParent=Cannot locate parent Response implementation +applicationResponse.badResponse=Response is not a javax.servlet.ServletResponseWrapper +applicationServletRegistration.setServletSecurity.iae=Null constraint specified for servlet [{0}] deployed to context with name [{1}] +applicationServletRegistration.setServletSecurity.ise=Security constraints can't be added to servlet [{0}] deployed to context with name [{1}] as the context has already been initialised +aprListener.aprInit=The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: {0} +aprListener.tcnInvalid=An incompatible version {0} of the APR based Apache Tomcat Native library is installed, while Tomcat requires version {1} +aprListener.tcnVersion=An older version {0} of the APR based Apache Tomcat Native library is installed, while Tomcat recommends a minimum version of {1} +aprListener.aprDestroy=Failed shutdown of APR based Apache Tomcat Native library +aprListener.sslInit=Failed to initialize the SSLEngine. +aprListener.tcnValid=Loaded APR based Apache Tomcat Native library {0}. +aprListener.flags=APR capabilities: IPv6 [{0}], sendfile [{1}], accept filters [{2}], random [{3}]. +asyncContextImpl.requestEnded=The request associated with the AsyncContext has already completed processing. +containerBase.alreadyStarted=Container {0} has already been started +containerBase.notConfigured=No basic Valve has been configured +containerBase.notStarted=Container {0} has not been started +containerBase.threadedStartFailed=A child container failed during start +containerBase.threadedStopFailed=A child container failed during stop +containerBase.backgroundProcess.cluster=Exception processing cluster {0} background process +containerBase.backgroundProcess.loader=Exception processing loader {0} background process +containerBase.backgroundProcess.manager=Exception processing manager {0} background process +containerBase.backgroundProcess.realm=Exception processing realm {0} background process +containerBase.backgroundProcess.valve=Exception processing valve {0} background process +fastEngineMapper.alreadyStarted=FastEngineMapper {0} has already been started +fastEngineMapper.notStarted=FastEngineMapper {0} has not yet been started +filterChain.filter=Filter execution threw an exception +filterChain.servlet=Servlet execution threw an exception +httpContextMapper.container=This container is not a StandardContext +httpEngineMapper.container=This container is not a StandardEngine +httpHostMapper.container=This container is not a StandardHost +interceptorValve.alreadyStarted=InterceptorValve has already been started +interceptorValve.notStarted=InterceptorValve has not yet been started +jreLeakListener.keepAliveFail=Failed to trigger creation of the sun.net.www.http.HttpClient class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. +jreLeakListener.gcDaemonFail=Failed to trigger creation of the GC Daemon thread during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. +jreLeakListener.jarUrlConnCacheFail=Failed to disable Jar URL connection caching by default +jreLeakListener.xmlParseFail=Error whilst attempting to prevent memory leaks during XML parsing +jreLeakListener.authPolicyFail=Error whilst attempting to prevent memory leak in javax.security.auth.Policy class +jreLeakListener.ldapPoolManagerFail=Failed to trigger creation of the com.sun.jndi.ldap.LdapPoolManager class during Tomcat start to prevent possible memory leaks. This is expected on non-Sun JVMs. +jreLeakListener.classToInitializeFail=Failed to load class {0} during Tomcat start to prevent possible memory leaks. +naming.wsdlFailed=Failed to find wsdl file: {0} +naming.bindFailed=Failed to bind object: {0} +naming.jmxRegistrationFailed=Failed to register in JMX: {0} +naming.unbindFailed=Failed to unbind object: {0} +naming.invalidEnvEntryType=Environment entry {0} has an invalid type +naming.invalidEnvEntryValue=Environment entry {0} has an invalid value +naming.namingContextCreationFailed=Creation of the naming context failed: {0} +standardContext.invalidWrapperClass={0} is not a subclass of StandardWrapper +standardContext.alreadyStarted=Context has already been started +standardContext.applicationListener=Error configuring application listener of class {0} +standardContext.applicationSkipped=Skipped installing application listeners due to previous error(s) +standardContext.badRequest=Invalid request path ({0}). +standardContext.cluster.noManager=No manager found. Checking if cluster manager should be used. Cluster configured: [{0}], Application distributable: [{1}] +standardContext.crlfinurl=The URL pattern "{0}" contains a CR or LF and so can never be matched. +standardContext.duplicateListener=The listener "{0}" is already configured for this context. The duplicate definition has been ignored. +standardContext.errorPage.error=Error page location {0} must start with a ''/'' +standardContext.errorPage.required=ErrorPage cannot be null +standardContext.errorPage.warning=WARNING: Error page location {0} must start with a ''/'' in Servlet 2.4 +standardContext.filterMap.either=Filter mapping must specify either a or a +standardContext.filterMap.name=Filter mapping specifies an unknown filter name {0} +standardContext.filterMap.pattern=Invalid {0} in filter mapping +standardContext.filterStart=Exception starting filter {0} +standardContext.filterStartFailed=Failed to start application Filters successfully +standardContext.requestListener.requestInit=Exception sending request initialized lifecycle event to listener instance of class {0} +standardContext.requestListener.requestDestroy=Exception sending request destroyed lifecycle event to listener instance of class {0} +standardContext.requestListenerStartFailed=Failed to start request listener valve successfully +standardContext.requestListenerConfig.added=Added request listener Valve +standardContext.requestListenerConfig.error=Exception adding request listener Valve: {0} +standardContext.isUnavailable=This application is not currently available +standardContext.listenerStart=Exception sending context initialized event to listener instance of class {0} +standardContext.listenerStartFailed=Failed to start application Listeners successfully +standardContext.listenerStop=Exception sending context destroyed event to listener instance of class {0} +standardContext.loginConfig.errorPage=Form error page {0} must start with a ''/' +standardContext.loginConfig.errorWarning=WARNING: Form error page {0} must start with a ''/'' in Servlet 2.4 +standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/' +standardContext.loginConfig.loginWarning=WARNING: Form login page {0} must start with a ''/'' in Servlet 2.4 +standardContext.loginConfig.required=LoginConfig cannot be null +standardContext.manager=Configured a manager of class [{0}] +standardContext.mappingError=MAPPING configuration error for relative URI {0} +standardContext.namingResource.init.fail=Failed to init new naming resources +standardContext.namingResource.destroy.fail=Failed to destroy old naming resources +standardContext.noResourceJar=Resource JARs are not supported. The JAR found at [{0}] will not be used to provide static content for context with name [{1}] +standardContext.notFound=The requested resource ({0}) is not available. +standardContext.notReloadable=Reloading is disabled on this Context +standardContext.notStarted=Context with name [{0}] has not yet been started +standardContext.notWrapper=Child of a Context must be a Wrapper +standardContext.parameter.duplicate=Duplicate context initialization parameter {0} +standardContext.parameter.required=Both parameter name and parameter value are required +standardContext.pathInvalid=A context path must either be an empty string or start with a ''/''. The path [{0}] does not meet these criteria and has been changed to [{1}] +standardContext.reloadingCompleted=Reloading Context with name [{0}] is completed +standardContext.reloadingFailed=Reloading this Context failed due to previous errors +standardContext.reloadingStarted=Reloading Context with name [{0}] has started +standardContext.resourcesStart=Error starting static Resources +standardContext.securityConstraint.mixHttpMethod=It is not permitted to mix and in the same web resource collection +standardContext.securityConstraint.pattern=Invalid {0} in security constraint +standardContext.servletMap.name=Servlet mapping specifies an unknown servlet name {0} +standardContext.servletMap.pattern=Invalid {0} in servlet mapping +standardContext.startCleanup=Exception during cleanup after start failed +standardContext.startFailed=Context [{0}] startup failed due to previous errors +standardContext.startingContext=Exception starting Context with name [{0}] +standardContext.startingLoader=Exception starting Loader +standardContext.startingManager=Exception starting Manager +standardContext.startingWrapper=Exception starting Wrapper for servlet {0} +standardContext.stoppingContext=Exception stopping Context with name [{0}] +standardContext.stoppingLoader=Exception stopping Loader +standardContext.stoppingManager=Exception stopping Manager +standardContext.stoppingWrapper=Exception stopping Wrapper for servlet {0} +standardContext.urlDecode=Cannot URL decode request path {0} +standardContext.urlPattern.patternWarning=WARNING: URL pattern {0} must start with a ''/'' in Servlet 2.4 +standardContext.urlValidate=Cannot validate URL decoded request path {0} +standardContext.workPath=Exception obtaining work path for context [{0}] +standardContext.workCreateException=Failed to determine absolute work directory from directory [{0}] and CATALINA_HOME [{1}] for context [{2}] +standardContext.workCreateFail=Failed to create work directory [{0}] for context [{1}] +standardContextValve.acknowledgeException=Failed to acknowledge request with a 100 (Continue) response +standardEngine.alreadyStarted=Engine has already been started +standardEngine.jvmRouteFail=Failed to set Engine's jvmRoute attribute from system property +standardEngine.mappingError=MAPPING configuration error for server name {0} +standardEngine.noHost=No Host matches server name {0} +standardEngine.noHostHeader=HTTP/1.1 request with no Host: header +standardEngine.notHost=Child of an Engine must be a Host +standardEngine.notParent=Engine cannot have a parent Container +standardEngine.notStarted=Engine has not yet been started +standardEngine.unfoundHost=Virtual host {0} not found +standardEngine.unknownHost=No server host specified in this request +standardEngine.unregister.mbeans.failed=Error in destroy() for mbean file {0} +standardHost.accessBase=Cannot access document base directory {0} +standardHost.alreadyStarted=Host has already been started +standardHost.appBase=Application base directory {0} does not exist +standardHost.clientAbort=Remote Client Aborted Request, IOException: {0} +standardHost.configRequired=URL to configuration file is required +standardHost.configNotAllowed=Use of configuration file is not allowed +standardHost.installBase=Only web applications in the Host web application directory can be installed +standardHost.installing=Installing web application at context path {0} from URL {1} +standardHost.installingWAR=Installing web application from URL {0} +standardHost.installingXML=Processing Context configuration file URL {0} +standardHost.installError=Error deploying application at context path {0} +standardHost.invalidErrorReportValveClass=Couldn''t load specified error report valve class: {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.pathMatch=Context path {0} must match the directory or WAR file name: {1} +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.removing=Removing web application at context path {0} +standardHost.removeError=Error removing application at context path {0} +standardHost.start=Starting web application at context path {0} +standardHost.stop=Stopping web 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.onameFail=MBean name specified for Server [{0}] is not valid +standardServer.shutdownViaPort=A valid shutdown command was received via the shutdown port. Stopping the Server instance. +standardService.connector.initFailed=Failed to initialize connector [{0}] +standardService.connector.destroyFailed=Failed to destroy connector [{0}] +standardService.connector.pauseFailed=Failed to pause connector [{0}] +standardService.connector.startFailed=Failed to start connector [{0}] +standardService.connector.stopFailed=Failed to stop connector [{0}] +standardService.initialize.failed=Service initializing at {0} failed +standardService.onameFail=MBean name specified for Service [{0}] is not valid +standardService.register.failed=Error registering Service at domain {0} +standardService.start.name=Starting service {0} +standardService.stop.name=Stopping service {0} +standardThreadExecutor.onameFail=MBean name specified for Thread Executor [{0}] is not valid +standardWrapper.allocate=Error allocating a servlet instance +standardWrapper.allocateException=Allocate exception for servlet {0} +standardWrapper.containerServlet=Loading container servlet {0} +standardWrapper.createFilters=Create filters exception for servlet {0} +standardWrapper.deallocateException=Deallocate exception for servlet {0} +standardWrapper.destroyException=Servlet.destroy() for servlet {0} threw exception +standardWrapper.exception0=Tomcat Exception Report +standardWrapper.exception1=A Servlet Exception Has Occurred +standardWrapper.exception2=Exception Report: +standardWrapper.exception3=Root Cause: +standardWrapper.initException=Servlet.init() for servlet {0} threw exception +standardWrapper.instantiate=Error instantiating servlet class {0} +standardWrapper.isUnavailable=Servlet {0} is currently unavailable +standardWrapper.jasperLoader=Using Jasper classloader for servlet {0} +standardWrapper.jspFile.format=JSP file {0} does not start with a ''/'' character +standardWrapper.loadException=Servlet {0} threw load() exception +standardWrapper.missingClass=Wrapper cannot find servlet class {0} or a class it depends on +standardWrapper.missingLoader=Wrapper cannot find Loader for servlet {0} +standardWrapper.notChild=Wrapper container may not have child containers +standardWrapper.notClass=No servlet class has been specified for servlet {0} +standardWrapper.notContext=Parent container of a Wrapper must be a Context +standardWrapper.notFound=Servlet {0} is not available +standardWrapper.notServlet=Class {0} is not a Servlet +standardWrapper.releaseFilters=Release filters exception for servlet {0} +standardWrapper.serviceException=Servlet.service() for servlet [{0}] in context with path [{1}] threw exception +standardWrapper.serviceExceptionRoot=Servlet.service() for servlet [{0}] in context with path [{1}] threw exception [{2}] with root cause +standardWrapper.statusHeader=HTTP Status {0} - {1} +standardWrapper.statusTitle=Tomcat Error Report +standardWrapper.unavailable=Marking servlet {0} as unavailable +standardWrapper.unloadException=Servlet {0} threw unload() exception +standardWrapper.unloading=Cannot allocate servlet {0} because it is being unloaded +standardWrapper.waiting=Waiting for {0} instance(s) to be deallocated +threadLocalLeakPreventionListener.lifecycleEvent.error=Exception processing lifecycle event {0} +threadLocalLeakPreventionListener.containerEvent.error=Exception processing container event {0} + +defaultInstanceManager.restrictedServletsResource=Restricted servlets property file not found +defaultInstanceManager.privilegedServlet=Servlet of class {0} is privileged and cannot be loaded by this web application +defaultInstanceManager.restrictedFiltersResource=Restricted filters property file not found +defaultInstanceManager.privilegedFilter=Filter of class {0} is privileged and cannot be loaded by this web application +defaultInstanceManager.restrictedListenersResources="Restricted listeners property file not found diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java index a408da0..8e7b971 100644 --- a/java/org/apache/catalina/core/StandardContext.java +++ b/java/org/apache/catalina/core/StandardContext.java @@ -1,6544 +1,6462 @@ -/* - * 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 java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Stack; -import java.util.TreeMap; -import java.util.concurrent.Callable; -import java.util.concurrent.atomic.AtomicLong; - -import javax.management.ListenerNotFoundException; -import javax.management.MBeanNotificationInfo; -import javax.management.Notification; -import javax.management.NotificationBroadcasterSupport; -import javax.management.NotificationEmitter; -import javax.management.NotificationFilter; -import javax.management.NotificationListener; -import javax.management.ObjectName; -import javax.naming.NamingException; -import javax.naming.directory.DirContext; -import javax.servlet.FilterConfig; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletContainerInitializer; -import javax.servlet.ServletContext; -import javax.servlet.ServletContextAttributeListener; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.ServletException; -import javax.servlet.ServletRegistration; -import javax.servlet.ServletRequest; -import javax.servlet.ServletRequestAttributeListener; -import javax.servlet.ServletRequestEvent; -import javax.servlet.ServletRequestListener; -import javax.servlet.ServletSecurityElement; -import javax.servlet.descriptor.JspConfigDescriptor; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionListener; - -import org.apache.catalina.Authenticator; -import org.apache.catalina.Container; -import org.apache.catalina.ContainerListener; -import org.apache.catalina.Context; -import org.apache.catalina.Globals; -import org.apache.catalina.Host; -import org.apache.catalina.InstanceListener; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.Loader; -import org.apache.catalina.Manager; -import org.apache.catalina.Pipeline; -import org.apache.catalina.Valve; -import org.apache.catalina.Wrapper; -import org.apache.catalina.deploy.ApplicationParameter; -import org.apache.catalina.deploy.ErrorPage; -import org.apache.catalina.deploy.FilterDef; -import org.apache.catalina.deploy.FilterMap; -import org.apache.catalina.deploy.Injectable; -import org.apache.catalina.deploy.InjectionTarget; -import org.apache.catalina.deploy.LoginConfig; -import org.apache.catalina.deploy.MessageDestination; -import org.apache.catalina.deploy.MessageDestinationRef; -import org.apache.catalina.deploy.NamingResources; -import org.apache.catalina.deploy.SecurityCollection; -import org.apache.catalina.deploy.SecurityConstraint; -import org.apache.catalina.loader.WebappLoader; -import org.apache.catalina.session.StandardManager; -import org.apache.catalina.startup.TldConfig; -import org.apache.catalina.util.CharsetMapper; -import org.apache.catalina.util.ContextName; -import org.apache.catalina.util.ExtensionValidator; -import org.apache.catalina.util.RequestUtil; -import org.apache.catalina.util.URLEncoder; -import org.apache.juli.logging.Log; -import org.apache.juli.logging.LogFactory; -import org.apache.naming.ContextBindings; -import org.apache.naming.resources.BaseDirContext; -import org.apache.naming.resources.DirContextURLStreamHandler; -import org.apache.naming.resources.FileDirContext; -import org.apache.naming.resources.ProxyDirContext; -import org.apache.naming.resources.WARDirContext; -import org.apache.tomcat.InstanceManager; -import org.apache.tomcat.JarScanner; -import org.apache.tomcat.util.ExceptionUtils; -import org.apache.tomcat.util.modeler.Registry; -import org.apache.tomcat.util.scan.StandardJarScanner; -import org.apache.tomcat.util.threads.DedicatedThreadExecutor; - -/** - * Standard implementation of the Context interface. Each - * child container must be a Wrapper implementation to process the - * requests directed to a particular servlet. - * - * @author Craig R. McClanahan - * @author Remy Maucherat - * @version $Id$ - */ - -public class StandardContext extends ContainerBase - implements Context, NotificationEmitter { - - private static final Log log = LogFactory.getLog(StandardContext.class); - - - // ----------------------------------------------------------- Constructors - - - /** - * Create a new StandardContext component with the default basic Valve. - */ - public StandardContext() { - - super(); - pipeline.setBasic(new StandardContextValve()); - broadcaster = new NotificationBroadcasterSupport(); - // Set defaults - if (!Globals.STRICT_SERVLET_COMPLIANCE) { - // Strict servlet compliance requires all extension mapped servlets - // to be checked against welcome files - resourceOnlyServlets.add("jsp"); - } - } - - - // ----------------------------------------------------- Class Variables - - - /** - * The descriptive information string for this implementation. - */ - private static final String info = - "org.apache.catalina.core.StandardContext/1.0"; - - - /** - * Array containing the safe characters set. - */ - protected static URLEncoder urlEncoder; - - - /** - * GMT timezone - all HTTP dates are on GMT - */ - static { - urlEncoder = new URLEncoder(); - urlEncoder.addSafeCharacter('~'); - urlEncoder.addSafeCharacter('-'); - urlEncoder.addSafeCharacter('_'); - urlEncoder.addSafeCharacter('.'); - urlEncoder.addSafeCharacter('*'); - urlEncoder.addSafeCharacter('/'); - } - - - // ----------------------------------------------------- Instance Variables - - - /** - * Allow multipart/form-data requests to be parsed even when the - * target servlet doesn't specify @MultipartConfig or have a - * <multipart-config> element. - */ - protected boolean allowCasualMultipartParsing = false; - - /** - * Control whether remaining request data will be read - * (swallowed) even if the request violates a data size constraint. - */ - private boolean swallowAbortedUploads = true; - - /** - * The alternate deployment descriptor name. - */ - private String altDDName = null; - - - /** - * Lifecycle provider. - */ - private InstanceManager instanceManager = null; - - - /** - * Associated host name. - */ - private String hostName; - - - /** - * The antiJARLocking flag for this Context. - */ - private boolean antiJARLocking = false; - - - /** - * The antiResourceLocking flag for this Context. - */ - private boolean antiResourceLocking = false; - - - /** - * The set of application listener class names configured for this - * application, in the order they were encountered in the web.xml file. - */ - private String applicationListeners[] = new String[0]; - - private final Object applicationListenersLock = new Object(); - - - /** - * The set of instantiated application event listener objects. - */ - private Object applicationEventListenersObjects[] = - new Object[0]; - - - /** - * The set of instantiated application lifecycle listener objects. - */ - private Object applicationLifecycleListenersObjects[] = - new Object[0]; - - - /** - * The ordered set of ServletContainerInitializers for this web application. - */ - private Map>> initializers = - new LinkedHashMap>>(); - - - /** - * The set of application parameters defined for this application. - */ - private ApplicationParameter applicationParameters[] = - new ApplicationParameter[0]; - - private final Object applicationParametersLock = new Object(); - - - /** - * The broadcaster that sends j2ee notifications. - */ - private NotificationBroadcasterSupport broadcaster = null; - - /** - * The Locale to character set mapper for this application. - */ - private CharsetMapper charsetMapper = null; - - - /** - * The Java class name of the CharsetMapper class to be created. - */ - private String charsetMapperClass = - "org.apache.catalina.util.CharsetMapper"; - - - /** - * The URL of the XML descriptor for this context. - */ - private URL configFile = null; - - - /** - * The "correctly configured" flag for this Context. - */ - private boolean configured = false; - - - /** - * The security constraints for this web application. - */ - private volatile SecurityConstraint constraints[] = - new SecurityConstraint[0]; - - private final Object constraintsLock = new Object(); - - - /** - * The ServletContext implementation associated with this Context. - */ - protected ApplicationContext context = null; - - - /** - * Compiler classpath to use. - */ - private String compilerClasspath = null; - - - /** - * Should we attempt to use cookies for session id communication? - */ - private boolean cookies = true; - - - /** - * Should we allow the ServletContext.getContext() method - * to access the context of other web applications in this server? - */ - private boolean crossContext = false; - - - /** - * Encoded path. - */ - private String encodedPath = null; - - - /** - * Unencoded path for this web application. - */ - private String path = null; - - - /** - * The "follow standard delegation model" flag that will be used to - * configure our ClassLoader. - */ - private boolean delegate = false; - - - /** - * The display name of this web application. - */ - private String displayName = null; - - - /** - * Override the default context xml location. - */ - private String defaultContextXml; - - - /** - * Override the default web xml location. - */ - private String defaultWebXml; - - - /** - * The distributable flag for this web application. - */ - private boolean distributable = false; - - - /** - * The document root for this web application. - */ - private String docBase = null; - - - /** - * The exception pages for this web application, keyed by fully qualified - * class name of the Java exception. - */ - private HashMap exceptionPages = - new HashMap(); - - - /** - * The set of filter configurations (and associated filter instances) we - * have initialized, keyed by filter name. - */ - private HashMap filterConfigs = - new HashMap(); - - - /** - * The set of filter definitions for this application, keyed by - * filter name. - */ - private HashMap filterDefs = - new HashMap(); - - - /** - * The set of filter mappings for this application, in the order - * they were defined in the deployment descriptor with additional mappings - * added via the {@link ServletContext} possibly both before and after those - * defined in the deployment descriptor. - */ - private final ContextFilterMaps filterMaps = new ContextFilterMaps(); - - /** - * Ignore annotations. - */ - private boolean ignoreAnnotations = false; - - - /** - * The set of classnames of InstanceListeners that will be added - * to each newly created Wrapper by createWrapper(). - */ - private String instanceListeners[] = new String[0]; - - private final Object instanceListenersLock = new Object(); - - - /** - * The login configuration descriptor for this web application. - */ - private LoginConfig loginConfig = null; - - - /** - * The mapper associated with this context. - */ - private org.apache.tomcat.util.http.mapper.Mapper mapper = - new org.apache.tomcat.util.http.mapper.Mapper(); - - - /** - * The naming context listener for this web application. - */ - private NamingContextListener namingContextListener = null; - - - /** - * The naming resources for this web application. - */ - private NamingResources namingResources = null; - - /** - * The message destinations for this web application. - */ - private HashMap messageDestinations = - new HashMap(); - - - /** - * The MIME mappings for this web application, keyed by extension. - */ - private HashMap mimeMappings = - new HashMap(); - - - /** - * Special case: error page for status 200. - */ - private ErrorPage okErrorPage = null; - - - /** - * The context initialization parameters for this web application, - * keyed by name. - */ - private HashMap parameters = new HashMap(); - - - /** - * The request processing pause flag (while reloading occurs) - */ - private boolean paused = false; - - - /** - * The public identifier of the DTD for the web application deployment - * descriptor version we are currently parsing. This is used to support - * relaxed validation rules when processing version 2.2 web.xml files. - */ - private String publicId = null; - - - /** - * The reloadable flag for this web application. - */ - private boolean reloadable = false; - - - /** - * Unpack WAR property. - */ - private boolean unpackWAR = true; - - - /** - * The default context override flag for this web application. - */ - private boolean override = false; - - - /** - * The original document root for this web application. - */ - private String originalDocBase = null; - - - /** - * The privileged flag for this web application. - */ - private boolean privileged = false; - - - /** - * Should the next call to addWelcomeFile() cause replacement - * of any existing welcome files? This will be set before processing the - * web application's deployment descriptor, so that application specified - * choices replace, rather than append to, those defined - * in the global descriptor. - */ - private boolean replaceWelcomeFiles = false; - - - /** - * The security role mappings for this application, keyed by role - * name (as used within the application). - */ - private HashMap roleMappings = - new HashMap(); - - - /** - * The security roles for this application, keyed by role name. - */ - private String securityRoles[] = new String[0]; - - private final Object securityRolesLock = new Object(); - - - /** - * The servlet mappings for this web application, keyed by - * matching pattern. - */ - private HashMap servletMappings = - new HashMap(); - - private final Object servletMappingsLock = new Object(); - - - /** - * The session timeout (in minutes) for this web application. - */ - private int sessionTimeout = 30; - - /** - * The notification sequence number. - */ - private AtomicLong sequenceNumber = new AtomicLong(0); - - /** - * The status code error pages for this web application, keyed by - * HTTP status code (as an Integer). - */ - private HashMap statusPages = - new HashMap(); - - - /** - * Set flag to true to cause the system.out and system.err to be redirected - * to the logger when executing a servlet. - */ - private boolean swallowOutput = false; - - - /** - * Amount of ms that the container will wait for servlets to unload. - */ - private long unloadDelay = 2000; - - - /** - * The watched resources for this application. - */ - private String watchedResources[] = new String[0]; - - private final Object watchedResourcesLock = new Object(); - - - /** - * The welcome files for this application. - */ - private String welcomeFiles[] = new String[0]; - - private final Object welcomeFilesLock = new Object(); - - - /** - * The set of classnames of LifecycleListeners that will be added - * to each newly created Wrapper by createWrapper(). - */ - private String wrapperLifecycles[] = new String[0]; - - private final Object wrapperLifecyclesLock = new Object(); - - /** - * The set of classnames of ContainerListeners that will be added - * to each newly created Wrapper by createWrapper(). - */ - private String wrapperListeners[] = new String[0]; - - private final Object wrapperListenersLock = new Object(); - - /** - * The pathname to the work directory for this context (relative to - * the server's home if not absolute). - */ - private String workDir = null; - - - /** - * Java class name of the Wrapper class implementation we use. - */ - private String wrapperClassName = StandardWrapper.class.getName(); - private Class wrapperClass = null; - - - /** - * JNDI use flag. - */ - private boolean useNaming = true; - - - /** - * Filesystem based flag. - */ - private boolean filesystemBased = false; - - - /** - * Name of the associated naming context. - */ - private String namingContextName = null; - - - /** - * Caching allowed flag. - */ - private boolean cachingAllowed = true; - - - /** - * Allow linking. - */ - protected boolean allowLinking = false; - - - /** - * Cache max size in KB. - */ - protected int cacheMaxSize = 10240; // 10 MB - - - /** - * Cache object max size in KB. - */ - protected int cacheObjectMaxSize = 512; // 512K - - - /** - * Cache TTL in ms. - */ - protected int cacheTTL = 5000; - - - /** - * List of resource aliases. - */ - private String aliases = null; - - - /** - * Non proxied resources. - */ - private DirContext webappResources = null; - - private long startupTime; - private long startTime; - private long tldScanTime; - - /** - * Name of the engine. If null, the domain is used. - */ - private String j2EEApplication="none"; - private String j2EEServer="none"; - - - /** - * Attribute value used to turn on/off XML validation - */ - private boolean webXmlValidation = Globals.STRICT_SERVLET_COMPLIANCE; - - - /** - * Attribute value used to turn on/off XML namespace validation - */ - private boolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE; - - /** - * Attribute value used to turn on/off TLD processing - */ - private boolean processTlds = true; - - /** - * Attribute value used to turn on/off XML validation - */ - private boolean tldValidation = Globals.STRICT_SERVLET_COMPLIANCE; - - - /** - * Attribute value used to turn on/off TLD XML namespace validation - */ - private boolean tldNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE; - - - /** - * Should we save the configuration. - */ - private boolean saveConfig = true; - - - /** - * The name to use for session cookies. null indicates that - * the name is controlled by the application. - */ - private String sessionCookieName; - - - /** - * The flag that indicates that session cookies should use HttpOnly - */ - private boolean useHttpOnly = true; - - - /** - * The domain to use for session cookies. null indicates that - * the domain is controlled by the application. - */ - private String sessionCookieDomain; - - - /** - * The path to use for session cookies. null indicates that - * the path is controlled by the application. - */ - private String sessionCookiePath; - - - /** - * Is a / added to the end of the session cookie path to ensure browsers, - * particularly IE, don't send a session cookie for context /foo with - * requests intended for context /foobar. - */ - private boolean sessionCookiePathUsesTrailingSlash = true; - - - /** - * The Jar scanner to use to search for Jars that might contain - * configuration information such as TLDs or web-fragment.xml files. - */ - private JarScanner jarScanner = null; - - /** - * Should Tomcat attempt to null out any static or final fields from loaded - * classes when a web application is stopped as a work around for apparent - * garbage collection bugs and application coding errors? There have been - * some issues reported with log4j when this option is true. Applications - * without memory leaks using recent JVMs should operate correctly with this - * option set to false. If not specified, the default value of - * false will be used. - */ - private boolean clearReferencesStatic = false; - - /** - * Should Tomcat attempt to terminate threads that have been started by the - * web application? Stopping threads is performed via the deprecated (for - * good reason) Thread.stop() method and is likely to result in - * instability. As such, enabling this should be viewed as an option of last - * resort in a development environment and is not recommended in a - * production environment. If not specified, the default value of - * false will be used. - */ - private boolean clearReferencesStopThreads = false; - - /** - * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s - * that have been started by the web application? If not specified, the - * default value of false will be used. - */ - private boolean clearReferencesStopTimerThreads = false; - - /** - * If an HttpClient keep-alive timer thread has been started by this web - * application and is still running, should Tomcat change the context class - * loader from the current {@link WebappClassLoader} to - * {@link WebappClassLoader#parent} to prevent a memory leak? Note that the - * keep-alive timer thread will stop on its own once the keep-alives all - * expire however, on a busy system that might not happen for some time. - */ - private boolean clearReferencesHttpClientKeepAliveThread = true; - - /** - * Should Tomcat renew the threads of the thread pool when the application - * is stopped to avoid memory leaks because of uncleaned ThreadLocal - * variables. This also requires that the threadRenewalDelay property of the - * StandardThreadExecutor of ThreadPoolExecutor be set to a positive value. - */ - private boolean renewThreadsWhenStoppingContext = true; - - /** - * Should the effective web.xml be logged when the context starts? - */ - private boolean logEffectiveWebXml = false; - - private int effectiveMajorVersion = 3; - - private int effectiveMinorVersion = 0; - - private JspConfigDescriptor jspConfigDescriptor = - new ApplicationJspConfigDescriptor(); - - private Set resourceOnlyServlets = new HashSet(); - - private String webappVersion = ""; - - private boolean addWebinfClassesResources = false; - - private boolean fireRequestListenersOnForwards = false; - - /** - * Servlets created via {@link ApplicationContext#createServlet(Class)} for - * tracking purposes. - */ - private Set createdServlets = new HashSet(); - - private boolean preemptiveAuthentication = false; - - private boolean sendRedirectBody = false; - - - // ----------------------------------------------------- Context Properties - - @Override - public boolean getSendRedirectBody() { - return sendRedirectBody; - } - - - @Override - public void setSendRedirectBody(boolean sendRedirectBody) { - this.sendRedirectBody = sendRedirectBody; - } - - - @Override - public boolean getPreemptiveAuthentication() { - return preemptiveAuthentication; - } - - - @Override - public void setPreemptiveAuthentication(boolean preemptiveAuthentication) { - this.preemptiveAuthentication = preemptiveAuthentication; - } - - - @Override - public void setFireRequestListenersOnForwards(boolean enable) { - fireRequestListenersOnForwards = enable; - } - - - @Override - public boolean getFireRequestListenersOnForwards() { - return fireRequestListenersOnForwards; - } - - - public void setAddWebinfClassesResources( - boolean addWebinfClassesResources) { - this.addWebinfClassesResources = addWebinfClassesResources; - } - - - public boolean getAddWebinfClassesResources() { - return addWebinfClassesResources; - } - - - @Override - public void setWebappVersion(String webappVersion) { - if (null == webappVersion) { - this.webappVersion = ""; - } else { - this.webappVersion = webappVersion; - } - } - - - @Override - public String getWebappVersion() { - return webappVersion; - } - - - @Override - public String getBaseName() { - return new ContextName(path, webappVersion).getBaseName(); - } - - - @Override - public String getResourceOnlyServlets() { - StringBuilder result = new StringBuilder(); - boolean first = true; - for (String servletName : resourceOnlyServlets) { - if (!first) { - result.append(','); - } - result.append(servletName); - } - return result.toString(); - } - - - @Override - public void setResourceOnlyServlets(String resourceOnlyServlets) { - this.resourceOnlyServlets.clear(); - if (resourceOnlyServlets == null) { - return; - } - for (String servletName : resourceOnlyServlets.split(",")) { - servletName = servletName.trim(); - if (servletName.length()>0) { - this.resourceOnlyServlets.add(servletName); - } - } - } - - - @Override - public boolean isResourceOnlyServlet(String servletName) { - return resourceOnlyServlets.contains(servletName); - } - - - @Override - public int getEffectiveMajorVersion() { - return effectiveMajorVersion; - } - - @Override - public void setEffectiveMajorVersion(int effectiveMajorVersion) { - this.effectiveMajorVersion = effectiveMajorVersion; - } - - @Override - public int getEffectiveMinorVersion() { - return effectiveMinorVersion; - } - - @Override - public void setEffectiveMinorVersion(int effectiveMinorVersion) { - this.effectiveMinorVersion = effectiveMinorVersion; - } - - @Override - public void setLogEffectiveWebXml(boolean logEffectiveWebXml) { - this.logEffectiveWebXml = logEffectiveWebXml; - } - - @Override - public boolean getLogEffectiveWebXml() { - return logEffectiveWebXml; - } - - @Override - public Authenticator getAuthenticator() { - if (this instanceof Authenticator) - return (Authenticator) this; - - Pipeline pipeline = getPipeline(); - if (pipeline != null) { - Valve basic = pipeline.getBasic(); - if ((basic != null) && (basic instanceof Authenticator)) - return (Authenticator) basic; - Valve valves[] = pipeline.getValves(); - for (int i = 0; i < valves.length; i++) { - if (valves[i] instanceof Authenticator) - return (Authenticator) valves[i]; - } - } - return null; - } - - @Override - public JarScanner getJarScanner() { - if (jarScanner == null) { - jarScanner = new StandardJarScanner(); - } - return jarScanner; - } - - - @Override - public void setJarScanner(JarScanner jarScanner) { - this.jarScanner = jarScanner; - } - - - public InstanceManager getInstanceManager() { - return instanceManager; - } - - - public void setInstanceManager(InstanceManager instanceManager) { - this.instanceManager = instanceManager; - } - - - @Override - public String getEncodedPath() { - return encodedPath; - } - - - /** - * Is caching allowed ? - */ - public boolean isCachingAllowed() { - return cachingAllowed; - } - - - /** - * Set caching allowed flag. - */ - public void setCachingAllowed(boolean cachingAllowed) { - this.cachingAllowed = cachingAllowed; - } - - - /** - * Set allow linking. - */ - public void setAllowLinking(boolean allowLinking) { - this.allowLinking = allowLinking; - } - - - /** - * Is linking allowed. - */ - public boolean isAllowLinking() { - return allowLinking; - } - - /** - * Set to true to allow requests mapped to servlets that - * do not explicitly declare @MultipartConfig or have - * <multipart-config> specified in web.xml to parse - * multipart/form-data requests. - * - * @param allowCasualMultipartParsing true to allow such - * casual parsing, false otherwise. - */ - @Override - public void setAllowCasualMultipartParsing( - boolean allowCasualMultipartParsing) { - this.allowCasualMultipartParsing = allowCasualMultipartParsing; - } - - /** - * Returns true if requests mapped to servlets without - * "multipart config" to parse multipart/form-data requests anyway. - * - * @return true if requests mapped to servlets without - * "multipart config" to parse multipart/form-data requests, - * false otherwise. - */ - @Override - public boolean getAllowCasualMultipartParsing() { - return this.allowCasualMultipartParsing; - } - - /** - * Set to false to disable request data swallowing - * after an upload was aborted due to size constraints. - * - * @param swallowAbortedUploads false to disable - * swallowing, true otherwise (default). - */ - @Override - public void setSwallowAbortedUploads(boolean swallowAbortedUploads) { - this.swallowAbortedUploads = swallowAbortedUploads; - } - - /** - * Returns true if remaining request data will be read - * (swallowed) even the request violates a data size constraint. - * - * @return true if data will be swallowed (default), - * false otherwise. - */ - @Override - public boolean getSwallowAbortedUploads() { - return this.swallowAbortedUploads; - } - - /** - * Set cache TTL. - */ - public void setCacheTTL(int cacheTTL) { - this.cacheTTL = cacheTTL; - } - - - /** - * Get cache TTL. - */ - public int getCacheTTL() { - return cacheTTL; - } - - - /** - * Return the maximum size of the cache in KB. - */ - public int getCacheMaxSize() { - return cacheMaxSize; - } - - - /** - * Set the maximum size of the cache in KB. - */ - public void setCacheMaxSize(int cacheMaxSize) { - this.cacheMaxSize = cacheMaxSize; - } - - - /** - * Return the maximum size of objects to be cached in KB. - */ - public int getCacheObjectMaxSize() { - return cacheObjectMaxSize; - } - - - /** - * Set the maximum size of objects to be placed the cache in KB. - */ - public void setCacheObjectMaxSize(int cacheObjectMaxSize) { - this.cacheObjectMaxSize = cacheObjectMaxSize; - } - - - /** - * Return the list of resource aliases. - */ - public String getAliases() { - return this.aliases; - } - - - /** - * Add a URL for a JAR that contains static resources in a - * META-INF/resources directory that should be included in the static - * resources for this context. - */ - @Override - public void addResourceJarUrl(URL url) { - if (webappResources instanceof BaseDirContext) { - ((BaseDirContext) webappResources).addResourcesJar(url); - } else { - log.error(sm.getString("standardContext.noResourceJar", url, - getName())); - } - } - - - /** - * Set the current alias configuration. The list of aliases should be of the - * form "/aliasPath1=docBase1,/aliasPath2=docBase2" where aliasPathN must - * include a leading '/' and docBaseN must be an absolute path to either a - * .war file or a directory. - */ - public void setAliases(String aliases) { - this.aliases = aliases; - } - - - /** - * Add a ServletContainerInitializer instance to this web application. - * - * @param sci The instance to add - * @param classes The classes in which the initializer expressed an - * interest - */ - @Override - public void addServletContainerInitializer( - ServletContainerInitializer sci, Set> classes) { - initializers.put(sci, classes); - } - - - /** - * Return the "follow standard delegation model" flag used to configure - * our ClassLoader. - */ - public boolean getDelegate() { - - return (this.delegate); - - } - - - /** - * Set the "follow standard delegation model" flag used to configure - * our ClassLoader. - * - * @param delegate The new flag - */ - public void setDelegate(boolean delegate) { - - boolean oldDelegate = this.delegate; - this.delegate = delegate; - support.firePropertyChange("delegate", oldDelegate, - this.delegate); - - } - - - /** - * Returns true if the internal naming support is used. - */ - public boolean isUseNaming() { - - return (useNaming); - - } - - - /** - * Enables or disables naming. - */ - public void setUseNaming(boolean useNaming) { - this.useNaming = useNaming; - } - - - /** - * Returns true if the resources associated with this context are - * filesystem based. - */ - public boolean isFilesystemBased() { - - return (filesystemBased); - - } - - - /** - * Return the set of initialized application event listener objects, - * in the order they were specified in the web application deployment - * descriptor, for this application. - * - * @exception IllegalStateException if this method is called before - * this application has started, or after it has been stopped - */ - @Override - public Object[] getApplicationEventListeners() { - return (applicationEventListenersObjects); - } - - - /** - * Store the set of initialized application event listener objects, - * in the order they were specified in the web application deployment - * descriptor, for this application. - * - * @param listeners The set of instantiated listener objects. - */ - @Override - public void setApplicationEventListeners(Object listeners[]) { - applicationEventListenersObjects = listeners; - } - - - /** - * Add a listener to the end of the list of initialized application event - * listeners. - */ - public void addApplicationEventListener(Object listener) { - int len = applicationEventListenersObjects.length; - Object[] newListeners = Arrays.copyOf(applicationEventListenersObjects, - len + 1); - newListeners[len] = listener; - applicationEventListenersObjects = newListeners; - } - - - /** - * Return the set of initialized application lifecycle listener objects, - * in the order they were specified in the web application deployment - * descriptor, for this application. - * - * @exception IllegalStateException if this method is called before - * this application has started, or after it has been stopped - */ - @Override - public Object[] getApplicationLifecycleListeners() { - return (applicationLifecycleListenersObjects); - } - - - /** - * Store the set of initialized application lifecycle listener objects, - * in the order they were specified in the web application deployment - * descriptor, for this application. - * - * @param listeners The set of instantiated listener objects. - */ - @Override - public void setApplicationLifecycleListeners(Object listeners[]) { - applicationLifecycleListenersObjects = listeners; - } - - - /** - * Add a listener to the end of the list of initialized application - * lifecycle listeners. - */ - public void addApplicationLifecycleListener(Object listener) { - int len = applicationLifecycleListenersObjects.length; - Object[] newListeners = Arrays.copyOf( - applicationLifecycleListenersObjects, len + 1); - newListeners[len] = listener; - applicationLifecycleListenersObjects = newListeners; - } - - - /** - * Return the antiJARLocking flag for this Context. - */ - public boolean getAntiJARLocking() { - - return (this.antiJARLocking); - - } - - - /** - * Return the antiResourceLocking flag for this Context. - */ - public boolean getAntiResourceLocking() { - - return (this.antiResourceLocking); - - } - - - /** - * Set the antiJARLocking feature for this Context. - * - * @param antiJARLocking The new flag value - */ - public void setAntiJARLocking(boolean antiJARLocking) { - - boolean oldAntiJARLocking = this.antiJARLocking; - this.antiJARLocking = antiJARLocking; - support.firePropertyChange("antiJARLocking", - oldAntiJARLocking, - this.antiJARLocking); - - } - - - /** - * Set the antiResourceLocking feature for this Context. - * - * @param antiResourceLocking The new flag value - */ - public void setAntiResourceLocking(boolean antiResourceLocking) { - - boolean oldAntiResourceLocking = this.antiResourceLocking; - this.antiResourceLocking = antiResourceLocking; - support.firePropertyChange("antiResourceLocking", - oldAntiResourceLocking, - this.antiResourceLocking); - - } - - - /** - * Return the application available flag for this Context. - */ - @Override - public boolean getAvailable() { - - // TODO Remove this method entirely - return getState().isAvailable(); - - } - - - /** - * Return the Locale to character set mapper for this Context. - */ - @Override - public CharsetMapper getCharsetMapper() { - - // Create a mapper the first time it is requested - if (this.charsetMapper == null) { - try { - Class clazz = Class.forName(charsetMapperClass); - this.charsetMapper = (CharsetMapper) clazz.newInstance(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - this.charsetMapper = new CharsetMapper(); - } - } - - return (this.charsetMapper); - - } - - - /** - * Set the Locale to character set mapper for this Context. - * - * @param mapper The new mapper - */ - @Override - public void setCharsetMapper(CharsetMapper mapper) { - - CharsetMapper oldCharsetMapper = this.charsetMapper; - this.charsetMapper = mapper; - if( mapper != null ) - this.charsetMapperClass= mapper.getClass().getName(); - support.firePropertyChange("charsetMapper", oldCharsetMapper, - this.charsetMapper); - - } - - /** - * Return the URL of the XML descriptor for this context. - */ - @Override - public URL getConfigFile() { - - return (this.configFile); - - } - - - /** - * Set the URL of the XML descriptor for this context. - * - * @param configFile The URL of the XML descriptor for this context. - */ - @Override - public void setConfigFile(URL configFile) { - - this.configFile = configFile; - } - - - /** - * Return the "correctly configured" flag for this Context. - */ - @Override - public boolean getConfigured() { - - return (this.configured); - - } - - - /** - * Set the "correctly configured" flag for this Context. This can be - * set to false by startup listeners that detect a fatal configuration - * error to avoid the application from being made available. - * - * @param configured The new correctly configured flag - */ - @Override - public void setConfigured(boolean configured) { - - boolean oldConfigured = this.configured; - this.configured = configured; - support.firePropertyChange("configured", - oldConfigured, - this.configured); - - } - - - /** - * Return the "use cookies for session ids" flag. - */ - @Override - public boolean getCookies() { - - return (this.cookies); - - } - - - /** - * Set the "use cookies for session ids" flag. - * - * @param cookies The new flag - */ - @Override - public void setCookies(boolean cookies) { - - boolean oldCookies = this.cookies; - this.cookies = cookies; - support.firePropertyChange("cookies", - oldCookies, - this.cookies); - - } - - - /** - * Gets the name to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @return The value of the default session cookie name or null if not - * specified - */ - @Override - public String getSessionCookieName() { - return sessionCookieName; - } - - - /** - * Sets the name to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @param sessionCookieName The name to use - */ - @Override - public void setSessionCookieName(String sessionCookieName) { - String oldSessionCookieName = this.sessionCookieName; - this.sessionCookieName = sessionCookieName; - support.firePropertyChange("sessionCookieName", - oldSessionCookieName, sessionCookieName); - } - - - /** - * Gets the value of the use HttpOnly cookies for session cookies flag. - * - * @return true if the HttpOnly flag should be set on session - * cookies - */ - @Override - public boolean getUseHttpOnly() { - return useHttpOnly; - } - - - /** - * Sets the use HttpOnly cookies for session cookies flag. - * - * @param useHttpOnly Set to true to use HttpOnly cookies - * for session cookies - */ - @Override - public void setUseHttpOnly(boolean useHttpOnly) { - boolean oldUseHttpOnly = this.useHttpOnly; - this.useHttpOnly = useHttpOnly; - support.firePropertyChange("useHttpOnly", - oldUseHttpOnly, - this.useHttpOnly); - } - - - /** - * Gets the domain to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @return The value of the default session cookie domain or null if not - * specified - */ - @Override - public String getSessionCookieDomain() { - return sessionCookieDomain; - } - - - /** - * Sets the domain to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @param sessionCookieDomain The domain to use - */ - @Override - public void setSessionCookieDomain(String sessionCookieDomain) { - String oldSessionCookieDomain = this.sessionCookieDomain; - this.sessionCookieDomain = sessionCookieDomain; - support.firePropertyChange("sessionCookieDomain", - oldSessionCookieDomain, sessionCookieDomain); - } - - - /** - * Gets the path to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @return The value of the default session cookie path or null if not - * specified - */ - @Override - public String getSessionCookiePath() { - return sessionCookiePath; - } - - - /** - * Sets the path to use for session cookies. Overrides any setting that - * may be specified by the application. - * - * @param sessionCookiePath The path to use - */ - @Override - public void setSessionCookiePath(String sessionCookiePath) { - String oldSessionCookiePath = this.sessionCookiePath; - this.sessionCookiePath = sessionCookiePath; - support.firePropertyChange("sessionCookiePath", - oldSessionCookiePath, sessionCookiePath); - } - - - @Override - public boolean getSessionCookiePathUsesTrailingSlash() { - return sessionCookiePathUsesTrailingSlash; - } - - - @Override - public void setSessionCookiePathUsesTrailingSlash( - boolean sessionCookiePathUsesTrailingSlash) { - this.sessionCookiePathUsesTrailingSlash = - sessionCookiePathUsesTrailingSlash; - } - - - /** - * Return the "allow crossing servlet contexts" flag. - */ - @Override - public boolean getCrossContext() { - - return (this.crossContext); - - } - - - /** - * Set the "allow crossing servlet contexts" flag. - * - * @param crossContext The new cross contexts flag - */ - @Override - public void setCrossContext(boolean crossContext) { - - boolean oldCrossContext = this.crossContext; - this.crossContext = crossContext; - support.firePropertyChange("crossContext", - oldCrossContext, - this.crossContext); - - } - - public String getDefaultContextXml() { - return defaultContextXml; - } - - /** - * Set the location of the default context xml that will be used. - * If not absolute, it'll be made relative to the engine's base dir - * ( which defaults to catalina.base system property ). - * - * @param defaultContextXml The default web xml - */ - public void setDefaultContextXml(String defaultContextXml) { - this.defaultContextXml = defaultContextXml; - } - - public String getDefaultWebXml() { - return defaultWebXml; - } - - /** - * Set the location of the default web xml that will be used. - * If not absolute, it'll be made relative to the engine's base dir - * ( which defaults to catalina.base system property ). - * - * @param defaultWebXml The default web xml - */ - public void setDefaultWebXml(String defaultWebXml) { - this.defaultWebXml = defaultWebXml; - } - - /** - * Gets the time (in milliseconds) it took to start this context. - * - * @return Time (in milliseconds) it took to start this context. - */ - public long getStartupTime() { - return startupTime; - } - - public void setStartupTime(long startupTime) { - this.startupTime = startupTime; - } - - public long getTldScanTime() { - return tldScanTime; - } - - public void setTldScanTime(long tldScanTime) { - this.tldScanTime = tldScanTime; - } - - /** - * Return the display name of this web application. - */ - @Override - public String getDisplayName() { - - return (this.displayName); - - } - - - /** - * Return the alternate Deployment Descriptor name. - */ - @Override - public String getAltDDName(){ - return altDDName; - } - - - /** - * Set an alternate Deployment Descriptor name. - */ - @Override - public void setAltDDName(String altDDName) { - this.altDDName = altDDName; - if (context != null) { - context.setAttribute(Globals.ALT_DD_ATTR,altDDName); - } - } - - - /** - * Return the compiler classpath. - */ - public String getCompilerClasspath(){ - return compilerClasspath; - } - - - /** - * Set the compiler classpath. - */ - public void setCompilerClasspath(String compilerClasspath) { - this.compilerClasspath = compilerClasspath; - } - - - /** - * Set the display name of this web application. - * - * @param displayName The new display name - */ - @Override - public void setDisplayName(String displayName) { - - String oldDisplayName = this.displayName; - this.displayName = displayName; - support.firePropertyChange("displayName", oldDisplayName, - this.displayName); - } - - - /** - * Return the distributable flag for this web application. - */ - @Override - public boolean getDistributable() { - - return (this.distributable); - - } - - /** - * Set the distributable flag for this web application. - * - * @param distributable The new distributable flag - */ - @Override - public void setDistributable(boolean distributable) { - boolean oldDistributable = this.distributable; - this.distributable = distributable; - support.firePropertyChange("distributable", - oldDistributable, - this.distributable); - - // Bugzilla 32866 - if(getManager() != null) { - if(log.isDebugEnabled()) { - log.debug("Propagating distributable=" + distributable - + " to manager"); - } - getManager().setDistributable(distributable); - } - } - - - /** - * Return the document root for this Context. This can be an absolute - * pathname, a relative pathname, or a URL. - */ - @Override - public String getDocBase() { - - return (this.docBase); - - } - - - /** - * Set the document root for this Context. This can be an absolute - * pathname, a relative pathname, or a URL. - * - * @param docBase The new document root - */ - @Override - public void setDocBase(String docBase) { - - this.docBase = docBase; - - } - - /** - * Return descriptive information about this Container implementation and - * the corresponding version number, in the format - * <description>/<version>. - */ - @Override - public String getInfo() { - - return (info); - - } - - public String getJ2EEApplication() { - return j2EEApplication; - } - - public void setJ2EEApplication(String j2EEApplication) { - this.j2EEApplication = j2EEApplication; - } - - public String getJ2EEServer() { - return j2EEServer; - } - - public void setJ2EEServer(String j2EEServer) { - this.j2EEServer = j2EEServer; - } - - - /** - * Set the Loader with which this Context is associated. - * - * @param loader The newly associated loader - */ - @Override - public synchronized void setLoader(Loader loader) { - - super.setLoader(loader); - - } - - - /** - * Return the boolean on the annotations parsing. - */ - @Override - public boolean getIgnoreAnnotations() { - return this.ignoreAnnotations; - } - - - /** - * Set the boolean on the annotations parsing for this web - * application. - * - * @param ignoreAnnotations The boolean on the annotations parsing - */ - @Override - public void setIgnoreAnnotations(boolean ignoreAnnotations) { - boolean oldIgnoreAnnotations = this.ignoreAnnotations; - this.ignoreAnnotations = ignoreAnnotations; - support.firePropertyChange("ignoreAnnotations", oldIgnoreAnnotations, - this.ignoreAnnotations); - } - - - /** - * Return the login configuration descriptor for this web application. - */ - @Override - public LoginConfig getLoginConfig() { - - return (this.loginConfig); - - } - - - /** - * Set the login configuration descriptor for this web application. - * - * @param config The new login configuration - */ - @Override - public void setLoginConfig(LoginConfig config) { - - // Validate the incoming property value - if (config == null) - throw new IllegalArgumentException - (sm.getString("standardContext.loginConfig.required")); - String loginPage = config.getLoginPage(); - if ((loginPage != null) && !loginPage.startsWith("/")) { - if (isServlet22()) { - if(log.isDebugEnabled()) - log.debug(sm.getString("standardContext.loginConfig.loginWarning", - loginPage)); - config.setLoginPage("/" + loginPage); - } else { - throw new IllegalArgumentException - (sm.getString("standardContext.loginConfig.loginPage", - loginPage)); - } - } - String errorPage = config.getErrorPage(); - if ((errorPage != null) && !errorPage.startsWith("/")) { - if (isServlet22()) { - if(log.isDebugEnabled()) - log.debug(sm.getString("standardContext.loginConfig.errorWarning", - errorPage)); - config.setErrorPage("/" + errorPage); - } else { - throw new IllegalArgumentException - (sm.getString("standardContext.loginConfig.errorPage", - errorPage)); - } - } - - // Process the property setting change - LoginConfig oldLoginConfig = this.loginConfig; - this.loginConfig = config; - support.firePropertyChange("loginConfig", - oldLoginConfig, this.loginConfig); - - } - - - /** - * Get the mapper associated with the context. - */ - @Override - public org.apache.tomcat.util.http.mapper.Mapper getMapper() { - return (mapper); - } - - - /** - * Return the naming resources associated with this web application. - */ - @Override - public NamingResources getNamingResources() { - - if (namingResources == null) { - setNamingResources(new NamingResources()); - } - return (namingResources); - - } - - - /** - * Set the naming resources for this web application. - * - * @param namingResources The new naming resources - */ - @Override - public void setNamingResources(NamingResources namingResources) { - - // Process the property setting change - NamingResources oldNamingResources = this.namingResources; - this.namingResources = namingResources; - if (namingResources != null) { - namingResources.setContainer(this); - } - support.firePropertyChange("namingResources", - oldNamingResources, this.namingResources); - - if (getState() == LifecycleState.NEW || - getState() == LifecycleState.INITIALIZING || - getState() == LifecycleState.INITIALIZED) { - // NEW will occur if Context is defined in server.xml - // At this point getObjectKeyPropertiesNameOnly() will trigger an - // NPE. - // INITIALIZED will occur if the Context is defined in a context.xml - // file - // If started now, a second start will be attempted when the context - // starts - - // In both cases, return and let context init the namingResources - // when it starts - return; - } - - if (oldNamingResources != null) { - try { - oldNamingResources.stop(); - oldNamingResources.destroy(); - } catch (LifecycleException e) { - log.warn("standardContext.namingResource.destroy.fail", e); - } - } - if (namingResources != null) { - try { - namingResources.init(); - namingResources.start(); - } catch (LifecycleException e) { - log.warn("standardContext.namingResource.init.fail", e); - } - } - } - - - /** - * Return the context path for this Context. - */ - @Override - public String getPath() { - return (path); - } - - - /** - * Set the context path for this Context. - * - * @param path The new context path - */ - @Override - public void setPath(String path) { - if (path == null || (!path.equals("") && !path.startsWith("/"))) { - this.path = "/" + path; - log.warn(sm.getString( - "standardContext.pathInvalid", path, this.path)); - } else { - this.path = path; - } - encodedPath = urlEncoder.encode(this.path); - if (getName() == null) { - setName(this.path); - } - } - - - /** - * Return the public identifier of the deployment descriptor DTD that is - * currently being parsed. - */ - @Override - public String getPublicId() { - - return (this.publicId); - - } - - - /** - * Set the public identifier of the deployment descriptor DTD that is - * currently being parsed. - * - * @param publicId The public identifier - */ - @Override - public void setPublicId(String publicId) { - - if (log.isDebugEnabled()) - log.debug("Setting deployment descriptor public ID to '" + - publicId + "'"); - - String oldPublicId = this.publicId; - this.publicId = publicId; - support.firePropertyChange("publicId", oldPublicId, publicId); - - } - - - /** - * Return the reloadable flag for this web application. - */ - @Override - public boolean getReloadable() { - - return (this.reloadable); - - } - - - /** - * Return the default context override flag for this web application. - */ - @Override - public boolean getOverride() { - - return (this.override); - - } - - - /** - * Return the original document root for this Context. This can be an absolute - * pathname, a relative pathname, or a URL. - * Is only set as deployment has change docRoot! - */ - public String getOriginalDocBase() { - - return (this.originalDocBase); - - } - - /** - * Set the original document root for this Context. This can be an absolute - * pathname, a relative pathname, or a URL. - * - * @param docBase The original document root - */ - public void setOriginalDocBase(String docBase) { - - this.originalDocBase = docBase; - } - - - /** - * Return the parent class loader (if any) for this web application. - * This call is meaningful only after a Loader has - * been configured. - */ - @Override - public ClassLoader getParentClassLoader() { - if (parentClassLoader != null) - return (parentClassLoader); - if (getPrivileged()) { - return this.getClass().getClassLoader(); - } else if (parent != null) { - return (parent.getParentClassLoader()); - } - return (ClassLoader.getSystemClassLoader()); - } - - - /** - * Return the privileged flag for this web application. - */ - @Override - public boolean getPrivileged() { - - return (this.privileged); - - } - - - /** - * Set the privileged flag for this web application. - * - * @param privileged The new privileged flag - */ - @Override - public void setPrivileged(boolean privileged) { - - boolean oldPrivileged = this.privileged; - this.privileged = privileged; - support.firePropertyChange("privileged", - oldPrivileged, - this.privileged); - - } - - - /** - * Set the reloadable flag for this web application. - * - * @param reloadable The new reloadable flag - */ - @Override - public void setReloadable(boolean reloadable) { - - boolean oldReloadable = this.reloadable; - this.reloadable = reloadable; - support.firePropertyChange("reloadable", - oldReloadable, - this.reloadable); - - } - - - /** - * Set the default context override flag for this web application. - * - * @param override The new override flag - */ - @Override - public void setOverride(boolean override) { - - boolean oldOverride = this.override; - this.override = override; - support.firePropertyChange("override", - oldOverride, - this.override); - - } - - - /** - * Return the "replace welcome files" property. - */ - public boolean isReplaceWelcomeFiles() { - - return (this.replaceWelcomeFiles); - - } - - - /** - * Set the "replace welcome files" property. - * - * @param replaceWelcomeFiles The new property value - */ - public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) { - - boolean oldReplaceWelcomeFiles = this.replaceWelcomeFiles; - this.replaceWelcomeFiles = replaceWelcomeFiles; - support.firePropertyChange("replaceWelcomeFiles", - oldReplaceWelcomeFiles, - this.replaceWelcomeFiles); - - } - - - /** - * Return the servlet context for which this Context is a facade. - */ - @Override - public ServletContext getServletContext() { - - if (context == null) { - context = new ApplicationContext(this); - if (altDDName != null) - context.setAttribute(Globals.ALT_DD_ATTR,altDDName); - } - return (context.getFacade()); - - } - - - /** - * Return the default session timeout (in minutes) for this - * web application. - */ - @Override - public int getSessionTimeout() { - - return (this.sessionTimeout); - - } - - - /** - * Set the default session timeout (in minutes) for this - * web application. - * - * @param timeout The new default session timeout - */ - @Override - public void setSessionTimeout(int timeout) { - - int oldSessionTimeout = this.sessionTimeout; - /* - * SRV.13.4 ("Deployment Descriptor"): - * If the timeout is 0 or less, the container ensures the default - * behaviour of sessions is never to time out. - */ - this.sessionTimeout = (timeout == 0) ? -1 : timeout; - support.firePropertyChange("sessionTimeout", - oldSessionTimeout, - this.sessionTimeout); - - } - - - /** - * Return the value of the swallowOutput flag. - */ - @Override - public boolean getSwallowOutput() { - - return (this.swallowOutput); - - } - - - /** - * Set the value of the swallowOutput flag. If set to true, the system.out - * and system.err will be redirected to the logger during a servlet - * execution. - * - * @param swallowOutput The new value - */ - @Override - public void setSwallowOutput(boolean swallowOutput) { - - boolean oldSwallowOutput = this.swallowOutput; - this.swallowOutput = swallowOutput; - support.firePropertyChange("swallowOutput", - oldSwallowOutput, - this.swallowOutput); - - } - - - /** - * Return the value of the unloadDelay flag. - */ - public long getUnloadDelay() { - - return (this.unloadDelay); - - } - - - /** - * Set the value of the unloadDelay flag, which represents the amount - * of ms that the container will wait when unloading servlets. - * Setting this to a small value may cause more requests to fail - * to complete when stopping a web application. - * - * @param unloadDelay The new value - */ - public void setUnloadDelay(long unloadDelay) { - - long oldUnloadDelay = this.unloadDelay; - this.unloadDelay = unloadDelay; - support.firePropertyChange("unloadDelay", - Long.valueOf(oldUnloadDelay), - Long.valueOf(this.unloadDelay)); - - } - - - /** - * Unpack WAR flag accessor. - */ - public boolean getUnpackWAR() { - - return (unpackWAR); - - } - - - /** - * Unpack WAR flag mutator. - */ - public void setUnpackWAR(boolean unpackWAR) { - - this.unpackWAR = unpackWAR; - - } - - /** - * Return the Java class name of the Wrapper implementation used - * for servlets registered in this Context. - */ - @Override - public String getWrapperClass() { - - return (this.wrapperClassName); - - } - - - /** - * Set the Java class name of the Wrapper implementation used - * for servlets registered in this Context. - * - * @param wrapperClassName The new wrapper class name - * - * @throws IllegalArgumentException if the specified wrapper class - * cannot be found or is not a subclass of StandardWrapper - */ - @Override - public void setWrapperClass(String wrapperClassName) { - - this.wrapperClassName = wrapperClassName; - - try { - wrapperClass = Class.forName(wrapperClassName); - if (!StandardWrapper.class.isAssignableFrom(wrapperClass)) { - throw new IllegalArgumentException( - sm.getString("standardContext.invalidWrapperClass", - wrapperClassName)); - } - } catch (ClassNotFoundException cnfe) { - throw new IllegalArgumentException(cnfe.getMessage()); - } - } - - - /** - * Set the resources DirContext object with which this Container is - * associated. - * - * @param resources The newly associated DirContext - */ - @Override - public synchronized void setResources(DirContext resources) { - - if (getState().isAvailable()) { - throw new IllegalStateException - (sm.getString("standardContext.resources.started")); - } - - DirContext oldResources = this.webappResources; - if (oldResources == resources) - return; - - if (resources instanceof BaseDirContext) { - // Caching - ((BaseDirContext) resources).setCached(isCachingAllowed()); - ((BaseDirContext) resources).setCacheTTL(getCacheTTL()); - ((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize()); - ((BaseDirContext) resources).setCacheObjectMaxSize( - getCacheObjectMaxSize()); - // Alias support - ((BaseDirContext) resources).setAliases(getAliases()); - } - if (resources instanceof FileDirContext) { - filesystemBased = true; - ((FileDirContext) resources).setAllowLinking(isAllowLinking()); - } - this.webappResources = resources; - - // The proxied resources will be refreshed on start - this.resources = null; - - support.firePropertyChange("resources", oldResources, - this.webappResources); - - } - - - @Override - public JspConfigDescriptor getJspConfigDescriptor() { - return jspConfigDescriptor; - } - - - // ------------------------------------------------------ Public Properties - - - /** - * Return the Locale to character set mapper class for this Context. - */ - public String getCharsetMapperClass() { - - return (this.charsetMapperClass); - - } - - - /** - * Set the Locale to character set mapper class for this Context. - * - * @param mapper The new mapper class - */ - public void setCharsetMapperClass(String mapper) { - - String oldCharsetMapperClass = this.charsetMapperClass; - this.charsetMapperClass = mapper; - support.firePropertyChange("charsetMapperClass", - oldCharsetMapperClass, - this.charsetMapperClass); - - } - - - /** Get the absolute path to the work dir. - * To avoid duplication. - * - * @return The work path - */ - public String getWorkPath() { - if (getWorkDir() == null) { - return null; - } - File workDir = new File(getWorkDir()); - if (!workDir.isAbsolute()) { - File catalinaHome = engineBase(); - String catalinaHomePath = null; - try { - catalinaHomePath = catalinaHome.getCanonicalPath(); - workDir = new File(catalinaHomePath, - getWorkDir()); - } catch (IOException e) { - log.warn(sm.getString("standardContext.workPath", getName()), - e); - } - } - return workDir.getAbsolutePath(); - } - - /** - * Return the work directory for this Context. - */ - public String getWorkDir() { - - return (this.workDir); - - } - - - /** - * Set the work directory for this Context. - * - * @param workDir The new work directory - */ - public void setWorkDir(String workDir) { - - this.workDir = workDir; - - if (getState().isAvailable()) { - postWorkDirectory(); - } - } - - - /** - * Save config ? - */ - public boolean isSaveConfig() { - return saveConfig; - } - - - /** - * Set save config flag. - */ - public void setSaveConfig(boolean saveConfig) { - this.saveConfig = saveConfig; - } - - - /** - * Return the clearReferencesStatic flag for this Context. - */ - public boolean getClearReferencesStatic() { - - return (this.clearReferencesStatic); - - } - - - /** - * Set the clearReferencesStatic feature for this Context. - * - * @param clearReferencesStatic The new flag value - */ - public void setClearReferencesStatic(boolean clearReferencesStatic) { - - boolean oldClearReferencesStatic = this.clearReferencesStatic; - this.clearReferencesStatic = clearReferencesStatic; - support.firePropertyChange("clearReferencesStatic", - oldClearReferencesStatic, - this.clearReferencesStatic); - - } - - - /** - * Return the clearReferencesStopThreads flag for this Context. - */ - public boolean getClearReferencesStopThreads() { - - return (this.clearReferencesStopThreads); - - } - - - /** - * Set the clearReferencesStopThreads feature for this Context. - * - * @param clearReferencesStopThreads The new flag value - */ - public void setClearReferencesStopThreads( - boolean clearReferencesStopThreads) { - - boolean oldClearReferencesStopThreads = this.clearReferencesStopThreads; - this.clearReferencesStopThreads = clearReferencesStopThreads; - support.firePropertyChange("clearReferencesStopThreads", - oldClearReferencesStopThreads, - this.clearReferencesStopThreads); - - } - - - /** - * Return the clearReferencesStopTimerThreads flag for this Context. - */ - public boolean getClearReferencesStopTimerThreads() { - return (this.clearReferencesStopTimerThreads); - } - - - /** - * Set the clearReferencesStopTimerThreads feature for this Context. - * - * @param clearReferencesStopTimerThreads The new flag value - */ - public void setClearReferencesStopTimerThreads( - boolean clearReferencesStopTimerThreads) { - - boolean oldClearReferencesStopTimerThreads = - this.clearReferencesStopTimerThreads; - this.clearReferencesStopTimerThreads = clearReferencesStopTimerThreads; - support.firePropertyChange("clearReferencesStopTimerThreads", - oldClearReferencesStopTimerThreads, - this.clearReferencesStopTimerThreads); - } - - - /** - * Return the clearReferencesHttpClientKeepAliveThread flag for this - * Context. - */ - public boolean getClearReferencesHttpClientKeepAliveThread() { - return (this.clearReferencesHttpClientKeepAliveThread); - } - - - /** - * Set the clearReferencesHttpClientKeepAliveThread feature for this - * Context. - * - * @param clearReferencesHttpClientKeepAliveThread The new flag value - */ - public void setClearReferencesHttpClientKeepAliveThread( - boolean clearReferencesHttpClientKeepAliveThread) { - this.clearReferencesHttpClientKeepAliveThread = - clearReferencesHttpClientKeepAliveThread; - } - - - public boolean getRenewThreadsWhenStoppingContext() { - return this.renewThreadsWhenStoppingContext; - } - - public void setRenewThreadsWhenStoppingContext( - boolean renewThreadsWhenStoppingContext) { - boolean oldRenewThreadsWhenStoppingContext = - this.renewThreadsWhenStoppingContext; - this.renewThreadsWhenStoppingContext = renewThreadsWhenStoppingContext; - support.firePropertyChange("renewThreadsWhenStoppingContext", - oldRenewThreadsWhenStoppingContext, - this.renewThreadsWhenStoppingContext); - } - - // -------------------------------------------------------- Context Methods - - - /** - * Add a new Listener class name to the set of Listeners - * configured for this application. - * - * @param listener Java class name of a listener class - */ - @Override - public void addApplicationListener(String listener) { - - synchronized (applicationListenersLock) { - String results[] =new String[applicationListeners.length + 1]; - for (int i = 0; i < applicationListeners.length; i++) { - if (listener.equals(applicationListeners[i])) { - log.info(sm.getString( - "standardContext.duplicateListener",listener)); - return; - } - results[i] = applicationListeners[i]; - } - results[applicationListeners.length] = listener; - applicationListeners = results; - } - fireContainerEvent("addApplicationListener", listener); - - // FIXME - add instance if already started? - - } - - - /** - * Add a new application parameter for this application. - * - * @param parameter The new application parameter - */ - @Override - public void addApplicationParameter(ApplicationParameter parameter) { - - synchronized (applicationParametersLock) { - String newName = parameter.getName(); - for (ApplicationParameter p : applicationParameters) { - if (newName.equals(p.getName()) && !p.getOverride()) - return; - } - ApplicationParameter results[] = Arrays.copyOf( - applicationParameters, applicationParameters.length + 1); - results[applicationParameters.length] = parameter; - applicationParameters = results; - } - fireContainerEvent("addApplicationParameter", parameter); - - } - - - /** - * Add a child Container, only if the proposed child is an implementation - * of Wrapper. - * - * @param child Child container to be added - * - * @exception IllegalArgumentException if the proposed container is - * not an implementation of Wrapper - */ - @Override - public void addChild(Container child) { - - // Global JspServlet - Wrapper oldJspServlet = null; - - if (!(child instanceof Wrapper)) { - throw new IllegalArgumentException - (sm.getString("standardContext.notWrapper")); - } - - boolean isJspServlet = "jsp".equals(child.getName()); - - // Allow webapp to override JspServlet inherited from global web.xml. - if (isJspServlet) { - oldJspServlet = (Wrapper) findChild("jsp"); - if (oldJspServlet != null) { - removeChild(oldJspServlet); - } - } - - super.addChild(child); - - if (isJspServlet && oldJspServlet != null) { - /* - * The webapp-specific JspServlet inherits all the mappings - * specified in the global web.xml, and may add additional ones. - */ - String[] jspMappings = oldJspServlet.findMappings(); - for (int i=0; jspMappings!=null && i 0 && - collections[i].findOmittedMethods().length > 0) { - throw new IllegalArgumentException(sm.getString( - "standardContext.securityConstraint.mixHttpMethod")); - } - } - - // Add this constraint to the set for our web application - synchronized (constraintsLock) { - SecurityConstraint results[] = - new SecurityConstraint[constraints.length + 1]; - for (int i = 0; i < constraints.length; i++) - results[i] = constraints[i]; - results[constraints.length] = constraint; - constraints = results; - } - - } - - - - /** - * Add an error page for the specified error or Java exception. - * - * @param errorPage The error page definition to be added - */ - @Override - public void addErrorPage(ErrorPage errorPage) { - // Validate the input parameters - if (errorPage == null) - throw new IllegalArgumentException - (sm.getString("standardContext.errorPage.required")); - String location = errorPage.getLocation(); - if ((location != null) && !location.startsWith("/")) { - if (isServlet22()) { - if(log.isDebugEnabled()) - log.debug(sm.getString("standardContext.errorPage.warning", - location)); - errorPage.setLocation("/" + location); - } else { - throw new IllegalArgumentException - (sm.getString("standardContext.errorPage.error", - location)); - } - } - - // Add the specified error page to our internal collections - String exceptionType = errorPage.getExceptionType(); - if (exceptionType != null) { - synchronized (exceptionPages) { - exceptionPages.put(exceptionType, errorPage); - } - } else { - synchronized (statusPages) { - if (errorPage.getErrorCode() == 200) { - this.okErrorPage = errorPage; - } - statusPages.put(Integer.valueOf(errorPage.getErrorCode()), - errorPage); - } - } - fireContainerEvent("addErrorPage", errorPage); - - } - - - /** - * Add a filter definition to this Context. - * - * @param filterDef The filter definition to be added - */ - @Override - public void addFilterDef(FilterDef filterDef) { - - synchronized (filterDefs) { - filterDefs.put(filterDef.getFilterName(), filterDef); - } - fireContainerEvent("addFilterDef", filterDef); - - } - - - /** - * Add a filter mapping to this Context at the end of the current set - * of filter mappings. - * - * @param filterMap The filter mapping to be added - * - * @exception IllegalArgumentException if the specified filter name - * does not match an existing filter definition, or the filter mapping - * is malformed - */ - @Override - public void addFilterMap(FilterMap filterMap) { - validateFilterMap(filterMap); - // Add this filter mapping to our registered set - filterMaps.add(filterMap); - fireContainerEvent("addFilterMap", filterMap); - } - - - /** - * Add a filter mapping to this Context before the mappings defined in the - * deployment descriptor but after any other mappings added via this method. - * - * @param filterMap The filter mapping to be added - * - * @exception IllegalArgumentException if the specified filter name - * does not match an existing filter definition, or the filter mapping - * is malformed - */ - @Override - public void addFilterMapBefore(FilterMap filterMap) { - validateFilterMap(filterMap); - // Add this filter mapping to our registered set - filterMaps.addBefore(filterMap); - fireContainerEvent("addFilterMap", filterMap); - } - - - /** - * Validate the supplied FilterMap. - */ - private void validateFilterMap(FilterMap filterMap) { - // Validate the proposed filter mapping - String filterName = filterMap.getFilterName(); - String[] servletNames = filterMap.getServletNames(); - String[] urlPatterns = filterMap.getURLPatterns(); - if (findFilterDef(filterName) == null) - throw new IllegalArgumentException - (sm.getString("standardContext.filterMap.name", filterName)); - - if (!filterMap.getMatchAllServletNames() && - !filterMap.getMatchAllUrlPatterns() && - (servletNames.length == 0) && (urlPatterns.length == 0)) - throw new IllegalArgumentException - (sm.getString("standardContext.filterMap.either")); - // FIXME: Older spec revisions may still check this - /* - if ((servletNames.length != 0) && (urlPatterns.length != 0)) - throw new IllegalArgumentException - (sm.getString("standardContext.filterMap.either")); - */ - for (int i = 0; i < urlPatterns.length; i++) { - if (!validateURLPattern(urlPatterns[i])) { - throw new IllegalArgumentException - (sm.getString("standardContext.filterMap.pattern", - urlPatterns[i])); - } - } - } - - /** - * Add the classname of an InstanceListener to be added to each - * Wrapper appended to this Context. - * - * @param listener Java class name of an InstanceListener class - */ - @Override - public void addInstanceListener(String listener) { - - synchronized (instanceListenersLock) { - String results[] =new String[instanceListeners.length + 1]; - for (int i = 0; i < instanceListeners.length; i++) - results[i] = instanceListeners[i]; - results[instanceListeners.length] = listener; - instanceListeners = results; - } - fireContainerEvent("addInstanceListener", listener); - - } - - /** - * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4) - * - * @param locale locale to map an encoding for - * @param encoding encoding to be used for a give locale - */ - @Override - public void addLocaleEncodingMappingParameter(String locale, String encoding){ - getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding); - } - - - /** - * Add a message destination for this web application. - * - * @param md New message destination - */ - public void addMessageDestination(MessageDestination md) { - - synchronized (messageDestinations) { - messageDestinations.put(md.getName(), md); - } - fireContainerEvent("addMessageDestination", md.getName()); - - } - - - /** - * Add a message destination reference for this web application. - * - * @param mdr New message destination reference - */ - public void addMessageDestinationRef - (MessageDestinationRef mdr) { - - namingResources.addMessageDestinationRef(mdr); - fireContainerEvent("addMessageDestinationRef", mdr.getName()); - - } - - - /** - * Add a new MIME mapping, replacing any existing mapping for - * the specified extension. - * - * @param extension Filename extension being mapped - * @param mimeType Corresponding MIME type - */ - @Override - public void addMimeMapping(String extension, String mimeType) { - - synchronized (mimeMappings) { - mimeMappings.put(extension, mimeType); - } - fireContainerEvent("addMimeMapping", extension); - - } - - - /** - * Add a new context initialization parameter. - * - * @param name Name of the new parameter - * @param value Value of the new parameter - * - * @exception IllegalArgumentException if the name or value is missing, - * or if this context initialization parameter has already been - * registered - */ - @Override - public void addParameter(String name, String value) { - // Validate the proposed context initialization parameter - if ((name == null) || (value == null)) - throw new IllegalArgumentException - (sm.getString("standardContext.parameter.required")); - if (parameters.get(name) != null) - throw new IllegalArgumentException - (sm.getString("standardContext.parameter.duplicate", name)); - - // Add this parameter to our defined set - synchronized (parameters) { - parameters.put(name, value); - } - fireContainerEvent("addParameter", name); - - } - - - /** - * Add a security role reference for this web application. - * - * @param role Security role used in the application - * @param link Actual security role to check for - */ - @Override - public void addRoleMapping(String role, String link) { - - synchronized (roleMappings) { - roleMappings.put(role, link); - } - fireContainerEvent("addRoleMapping", role); - - } - - - /** - * Add a new security role for this web application. - * - * @param role New security role - */ - @Override - public void addSecurityRole(String role) { - - synchronized (securityRolesLock) { - String results[] =new String[securityRoles.length + 1]; - for (int i = 0; i < securityRoles.length; i++) - results[i] = securityRoles[i]; - results[securityRoles.length] = role; - securityRoles = results; - } - fireContainerEvent("addSecurityRole", role); - - } - - - /** - * Add a new servlet mapping, replacing any existing mapping for - * the specified pattern. - * - * @param pattern URL pattern to be mapped - * @param name Name of the corresponding servlet to execute - * - * @exception IllegalArgumentException if the specified servlet name - * is not known to this Context - */ - @Override - public void addServletMapping(String pattern, String name) { - addServletMapping(pattern, name, false); - } - - - /** - * Add a new servlet mapping, replacing any existing mapping for - * the specified pattern. - * - * @param pattern URL pattern to be mapped - * @param name Name of the corresponding servlet to execute - * @param jspWildCard true if name identifies the JspServlet - * and pattern contains a wildcard; false otherwise - * - * @exception IllegalArgumentException if the specified servlet name - * is not known to this Context - */ - @Override - public void addServletMapping(String pattern, String name, - boolean jspWildCard) { - // Validate the proposed mapping - if (findChild(name) == null) - throw new IllegalArgumentException - (sm.getString("standardContext.servletMap.name", name)); - String decodedPattern = adjustURLPattern(RequestUtil.URLDecode(pattern)); - if (!validateURLPattern(decodedPattern)) - throw new IllegalArgumentException - (sm.getString("standardContext.servletMap.pattern", decodedPattern)); - - // Add this mapping to our registered set - synchronized (servletMappingsLock) { - String name2 = servletMappings.get(decodedPattern); - if (name2 != null) { - // Don't allow more than one servlet on the same pattern - Wrapper wrapper = (Wrapper) findChild(name2); - wrapper.removeMapping(decodedPattern); - mapper.removeWrapper(decodedPattern); - } - servletMappings.put(decodedPattern, name); - } - Wrapper wrapper = (Wrapper) findChild(name); - wrapper.addMapping(decodedPattern); - - // Update context mapper - mapper.addWrapper(decodedPattern, wrapper, jspWildCard, - resourceOnlyServlets.contains(name)); - - fireContainerEvent("addServletMapping", decodedPattern); - - } - - - /** - * Add a new watched resource to the set recognized by this Context. - * - * @param name New watched resource file name - */ - @Override - public void addWatchedResource(String name) { - - synchronized (watchedResourcesLock) { - String results[] = new String[watchedResources.length + 1]; - for (int i = 0; i < watchedResources.length; i++) - results[i] = watchedResources[i]; - results[watchedResources.length] = name; - watchedResources = results; - } - fireContainerEvent("addWatchedResource", name); - - } - - - /** - * Add a new welcome file to the set recognized by this Context. - * - * @param name New welcome file name - */ - @Override - public void addWelcomeFile(String name) { - - synchronized (welcomeFilesLock) { - // Welcome files from the application deployment descriptor - // completely replace those from the default conf/web.xml file - if (replaceWelcomeFiles) { - fireContainerEvent(CLEAR_WELCOME_FILES_EVENT, null); - welcomeFiles = new String[0]; - setReplaceWelcomeFiles(false); - } - String results[] =new String[welcomeFiles.length + 1]; - for (int i = 0; i < welcomeFiles.length; i++) - results[i] = welcomeFiles[i]; - results[welcomeFiles.length] = name; - welcomeFiles = results; - } - if(this.getState().equals(LifecycleState.STARTED)) - fireContainerEvent(ADD_WELCOME_FILE_EVENT, name); - } - - - /** - * Add the classname of a LifecycleListener to be added to each - * Wrapper appended to this Context. - * - * @param listener Java class name of a LifecycleListener class - */ - @Override - public void addWrapperLifecycle(String listener) { - - synchronized (wrapperLifecyclesLock) { - String results[] =new String[wrapperLifecycles.length + 1]; - for (int i = 0; i < wrapperLifecycles.length; i++) - results[i] = wrapperLifecycles[i]; - results[wrapperLifecycles.length] = listener; - wrapperLifecycles = results; - } - fireContainerEvent("addWrapperLifecycle", listener); - - } - - - /** - * Add the classname of a ContainerListener to be added to each - * Wrapper appended to this Context. - * - * @param listener Java class name of a ContainerListener class - */ - @Override - public void addWrapperListener(String listener) { - - synchronized (wrapperListenersLock) { - String results[] =new String[wrapperListeners.length + 1]; - for (int i = 0; i < wrapperListeners.length; i++) - results[i] = wrapperListeners[i]; - results[wrapperListeners.length] = listener; - wrapperListeners = results; - } - fireContainerEvent("addWrapperListener", listener); - - } - - - /** - * Factory method to create and return a new Wrapper instance, of - * the Java implementation class appropriate for this Context - * implementation. The constructor of the instantiated Wrapper - * will have been called, but no properties will have been set. - */ - @Override - public Wrapper createWrapper() { - - Wrapper wrapper = null; - if (wrapperClass != null) { - try { - wrapper = (Wrapper) wrapperClass.newInstance(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error("createWrapper", t); - return (null); - } - } else { - wrapper = new StandardWrapper(); - } - - synchronized (instanceListenersLock) { - for (int i = 0; i < instanceListeners.length; i++) { - try { - Class clazz = Class.forName(instanceListeners[i]); - InstanceListener listener = - (InstanceListener) clazz.newInstance(); - wrapper.addInstanceListener(listener); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error("createWrapper", t); - return (null); - } - } - } - - synchronized (wrapperLifecyclesLock) { - for (int i = 0; i < wrapperLifecycles.length; i++) { - try { - Class clazz = Class.forName(wrapperLifecycles[i]); - LifecycleListener listener = - (LifecycleListener) clazz.newInstance(); - wrapper.addLifecycleListener(listener); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error("createWrapper", t); - return (null); - } - } - } - - synchronized (wrapperListenersLock) { - for (int i = 0; i < wrapperListeners.length; i++) { - try { - Class clazz = Class.forName(wrapperListeners[i]); - ContainerListener listener = - (ContainerListener) clazz.newInstance(); - wrapper.addContainerListener(listener); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error("createWrapper", t); - return (null); - } - } - } - - return (wrapper); - - } - - - /** - * Return the set of application listener class names configured - * for this application. - */ - @Override - public String[] findApplicationListeners() { - - return (applicationListeners); - - } - - - /** - * Return the set of application parameters for this application. - */ - @Override - public ApplicationParameter[] findApplicationParameters() { - - synchronized (applicationParametersLock) { - return (applicationParameters); - } - - } - - - /** - * Return the security constraints for this web application. - * If there are none, a zero-length array is returned. - */ - @Override - public SecurityConstraint[] findConstraints() { - - return (constraints); - - } - - - /** - * Return the error page entry for the specified HTTP error code, - * if any; otherwise return null. - * - * @param errorCode Error code to look up - */ - @Override - public ErrorPage findErrorPage(int errorCode) { - if (errorCode == 200) { - return (okErrorPage); - } else { - return (statusPages.get(Integer.valueOf(errorCode))); - } - - } - - - /** - * Return the error page entry for the specified Java exception type, - * if any; otherwise return null. - * - * @param exceptionType Exception type to look up - */ - @Override - public ErrorPage findErrorPage(String exceptionType) { - - synchronized (exceptionPages) { - return (exceptionPages.get(exceptionType)); - } - - } - - - /** - * Return the set of defined error pages for all specified error codes - * and exception types. - */ - @Override - public ErrorPage[] findErrorPages() { - - synchronized(exceptionPages) { - synchronized(statusPages) { - ErrorPage results1[] = new ErrorPage[exceptionPages.size()]; - results1 = exceptionPages.values().toArray(results1); - ErrorPage results2[] = new ErrorPage[statusPages.size()]; - results2 = statusPages.values().toArray(results2); - ErrorPage results[] = - new ErrorPage[results1.length + results2.length]; - for (int i = 0; i < results1.length; i++) - results[i] = results1[i]; - for (int i = results1.length; i < results.length; i++) - results[i] = results2[i - results1.length]; - return (results); - } - } - - } - - - /** - * Return the filter definition for the specified filter name, if any; - * otherwise return null. - * - * @param filterName Filter name to look up - */ - @Override - public FilterDef findFilterDef(String filterName) { - - synchronized (filterDefs) { - return (filterDefs.get(filterName)); - } - - } - - - /** - * Return the set of defined filters for this Context. - */ - @Override - public FilterDef[] findFilterDefs() { - - synchronized (filterDefs) { - FilterDef results[] = new FilterDef[filterDefs.size()]; - return (filterDefs.values().toArray(results)); - } - - } - - - /** - * Return the set of filter mappings for this Context. - */ - @Override - public FilterMap[] findFilterMaps() { - return filterMaps.asArray(); - } - - - /** - * Return the set of InstanceListener classes that will be added to - * newly created Wrappers automatically. - */ - @Override - public String[] findInstanceListeners() { - - synchronized (instanceListenersLock) { - return (instanceListeners); - } - - } - - - /** - * FIXME: Fooling introspection ... - */ - public Context findMappingObject() { - return (Context) getMappingObject(); - } - - - /** - * Return the message destination with the specified name, if any; - * otherwise, return null. - * - * @param name Name of the desired message destination - */ - public MessageDestination findMessageDestination(String name) { - - synchronized (messageDestinations) { - return (messageDestinations.get(name)); - } - - } - - - /** - * Return the set of defined message destinations for this web - * application. If none have been defined, a zero-length array - * is returned. - */ - public MessageDestination[] findMessageDestinations() { - - synchronized (messageDestinations) { - MessageDestination results[] = - new MessageDestination[messageDestinations.size()]; - return (messageDestinations.values().toArray(results)); - } - - } - - - /** - * Return the message destination ref with the specified name, if any; - * otherwise, return null. - * - * @param name Name of the desired message destination ref - */ - public MessageDestinationRef - findMessageDestinationRef(String name) { - - return namingResources.findMessageDestinationRef(name); - - } - - - /** - * Return the set of defined message destination refs for this web - * application. If none have been defined, a zero-length array - * is returned. - */ - public MessageDestinationRef[] - findMessageDestinationRefs() { - - return namingResources.findMessageDestinationRefs(); - - } - - - /** - * Return the MIME type to which the specified extension is mapped, - * if any; otherwise return null. - * - * @param extension Extension to map to a MIME type - */ - @Override - public String findMimeMapping(String extension) { - - return (mimeMappings.get(extension)); - - } - - - /** - * Return the extensions for which MIME mappings are defined. If there - * are none, a zero-length array is returned. - */ - @Override - public String[] findMimeMappings() { - - synchronized (mimeMappings) { - String results[] = new String[mimeMappings.size()]; - return - (mimeMappings.keySet().toArray(results)); - } - - } - - - /** - * Return the value for the specified context initialization - * parameter name, if any; otherwise return null. - * - * @param name Name of the parameter to return - */ - @Override - public String findParameter(String name) { - - synchronized (parameters) { - return (parameters.get(name)); - } - - } - - - /** - * Return the names of all defined context initialization parameters - * for this Context. If no parameters are defined, a zero-length - * array is returned. - */ - @Override - public String[] findParameters() { - - synchronized (parameters) { - String results[] = new String[parameters.size()]; - return (parameters.keySet().toArray(results)); - } - - } - - - /** - * For the given security role (as used by an application), return the - * corresponding role name (as defined by the underlying Realm) if there - * is one. Otherwise, return the specified role unchanged. - * - * @param role Security role to map - */ - @Override - public String findRoleMapping(String role) { - - String realRole = null; - synchronized (roleMappings) { - realRole = roleMappings.get(role); - } - if (realRole != null) - return (realRole); - else - return (role); - - } - - - /** - * Return true if the specified security role is defined - * for this application; otherwise return false. - * - * @param role Security role to verify - */ - @Override - public boolean findSecurityRole(String role) { - - synchronized (securityRolesLock) { - for (int i = 0; i < securityRoles.length; i++) { - if (role.equals(securityRoles[i])) - return (true); - } - } - return (false); - - } - - - /** - * Return the security roles defined for this application. If none - * have been defined, a zero-length array is returned. - */ - @Override - public String[] findSecurityRoles() { - - synchronized (securityRolesLock) { - return (securityRoles); - } - - } - - - /** - * Return the servlet name mapped by the specified pattern (if any); - * otherwise return null. - * - * @param pattern Pattern for which a mapping is requested - */ - @Override - public String findServletMapping(String pattern) { - - synchronized (servletMappingsLock) { - return (servletMappings.get(pattern)); - } - - } - - - /** - * Return the patterns of all defined servlet mappings for this - * Context. If no mappings are defined, a zero-length array is returned. - */ - @Override - public String[] findServletMappings() { - - synchronized (servletMappingsLock) { - String results[] = new String[servletMappings.size()]; - return - (servletMappings.keySet().toArray(results)); - } - - } - - - /** - * Return the context-relative URI of the error page for the specified - * HTTP status code, if any; otherwise return null. - * - * @param status HTTP status code to look up - */ - @Override - public String findStatusPage(int status) { - - ErrorPage errorPage = statusPages.get(Integer.valueOf(status)); - if (errorPage!=null) { - return errorPage.getLocation(); - } - return null; - - } - - - /** - * Return the set of HTTP status codes for which error pages have - * been specified. If none are specified, a zero-length array - * is returned. - */ - @Override - public int[] findStatusPages() { - - synchronized (statusPages) { - int results[] = new int[statusPages.size()]; - Iterator elements = statusPages.keySet().iterator(); - int i = 0; - while (elements.hasNext()) - results[i++] = elements.next().intValue(); - return (results); - } - - } - - - /** - * Return true if the specified welcome file is defined - * for this Context; otherwise return false. - * - * @param name Welcome file to verify - */ - @Override - public boolean findWelcomeFile(String name) { - - synchronized (welcomeFilesLock) { - for (int i = 0; i < welcomeFiles.length; i++) { - if (name.equals(welcomeFiles[i])) - return (true); - } - } - return (false); - - } - - - /** - * Return the set of watched resources for this Context. If none are - * defined, a zero length array will be returned. - */ - @Override - public String[] findWatchedResources() { - synchronized (watchedResourcesLock) { - return watchedResources; - } - } - - - /** - * Return the set of welcome files defined for this Context. If none are - * defined, a zero-length array is returned. - */ - @Override - public String[] findWelcomeFiles() { - - synchronized (welcomeFilesLock) { - return (welcomeFiles); - } - - } - - - /** - * Return the set of LifecycleListener classes that will be added to - * newly created Wrappers automatically. - */ - @Override - public String[] findWrapperLifecycles() { - - synchronized (wrapperLifecyclesLock) { - return (wrapperLifecycles); - } - - } - - - /** - * Return the set of ContainerListener classes that will be added to - * newly created Wrappers automatically. - */ - @Override - public String[] findWrapperListeners() { - - synchronized (wrapperListenersLock) { - return (wrapperListeners); - } - - } - - - /** - * Reload this web application, if reloading is supported. - *

- * IMPLEMENTATION NOTE: This method is designed to deal with - * reloads required by changes to classes in the underlying repositories - * of our class loader. It does not handle changes to the web application - * deployment descriptor. If that has occurred, you should stop this - * Context and create (and start) a new Context instance instead. - * - * @exception IllegalStateException if the reloadable - * property is set to false. - */ - @Override - public synchronized void reload() { - - // Validate our current component state - if (!getState().isAvailable()) - throw new IllegalStateException - (sm.getString("standardContext.notStarted", getName())); - - if(log.isInfoEnabled()) - log.info(sm.getString("standardContext.reloadingStarted", - getName())); - - // Stop accepting requests temporarily - setPaused(true); - - try { - stop(); - } catch (LifecycleException e) { - log.error( - sm.getString("standardContext.stoppingContext", getName()), e); - } - - try { - start(); - } catch (LifecycleException e) { - log.error( - sm.getString("standardContext.startingContext", getName()), e); - } - - setPaused(false); - - if(log.isInfoEnabled()) - log.info(sm.getString("standardContext.reloadingCompleted", - getName())); - - } - - - /** - * Remove the specified application listener class from the set of - * listeners for this application. - * - * @param listener Java class name of the listener to be removed - */ - @Override - public void removeApplicationListener(String listener) { - - synchronized (applicationListenersLock) { - - // Make sure this welcome file is currently present - int n = -1; - for (int i = 0; i < applicationListeners.length; i++) { - if (applicationListeners[i].equals(listener)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - String results[] = new String[applicationListeners.length - 1]; - for (int i = 0; i < applicationListeners.length; i++) { - if (i != n) - results[j++] = applicationListeners[i]; - } - applicationListeners = results; - - } - - // Inform interested listeners - fireContainerEvent("removeApplicationListener", listener); - - // FIXME - behavior if already started? - - } - - - /** - * Remove the application parameter with the specified name from - * the set for this application. - * - * @param name Name of the application parameter to remove - */ - @Override - public void removeApplicationParameter(String name) { - - synchronized (applicationParametersLock) { - - // Make sure this parameter is currently present - int n = -1; - for (int i = 0; i < applicationParameters.length; i++) { - if (name.equals(applicationParameters[i].getName())) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified parameter - int j = 0; - ApplicationParameter results[] = - new ApplicationParameter[applicationParameters.length - 1]; - for (int i = 0; i < applicationParameters.length; i++) { - if (i != n) - results[j++] = applicationParameters[i]; - } - applicationParameters = results; - - } - - // Inform interested listeners - fireContainerEvent("removeApplicationParameter", name); - - } - - - /** - * Add a child Container, only if the proposed child is an implementation - * of Wrapper. - * - * @param child Child container to be added - * - * @exception IllegalArgumentException if the proposed container is - * not an implementation of Wrapper - */ - @Override - public void removeChild(Container child) { - - if (!(child instanceof Wrapper)) { - throw new IllegalArgumentException - (sm.getString("standardContext.notWrapper")); - } - - super.removeChild(child); - - } - - - /** - * Remove the specified security constraint from this web application. - * - * @param constraint Constraint to be removed - */ - @Override - public void removeConstraint(SecurityConstraint constraint) { - - synchronized (constraintsLock) { - - // Make sure this constraint is currently present - int n = -1; - for (int i = 0; i < constraints.length; i++) { - if (constraints[i].equals(constraint)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - SecurityConstraint results[] = - new SecurityConstraint[constraints.length - 1]; - for (int i = 0; i < constraints.length; i++) { - if (i != n) - results[j++] = constraints[i]; - } - constraints = results; - - } - - // Inform interested listeners - fireContainerEvent("removeConstraint", constraint); - - } - - - /** - * Remove the error page for the specified error code or - * Java language exception, if it exists; otherwise, no action is taken. - * - * @param errorPage The error page definition to be removed - */ - @Override - public void removeErrorPage(ErrorPage errorPage) { - - String exceptionType = errorPage.getExceptionType(); - if (exceptionType != null) { - synchronized (exceptionPages) { - exceptionPages.remove(exceptionType); - } - } else { - synchronized (statusPages) { - if (errorPage.getErrorCode() == 200) { - this.okErrorPage = null; - } - statusPages.remove(Integer.valueOf(errorPage.getErrorCode())); - } - } - fireContainerEvent("removeErrorPage", errorPage); - - } - - - /** - * Remove the specified filter definition from this Context, if it exists; - * otherwise, no action is taken. - * - * @param filterDef Filter definition to be removed - */ - @Override - public void removeFilterDef(FilterDef filterDef) { - - synchronized (filterDefs) { - filterDefs.remove(filterDef.getFilterName()); - } - fireContainerEvent("removeFilterDef", filterDef); - - } - - - /** - * Remove a filter mapping from this Context. - * - * @param filterMap The filter mapping to be removed - */ - @Override - public void removeFilterMap(FilterMap filterMap) { - filterMaps.remove(filterMap); - // Inform interested listeners - fireContainerEvent("removeFilterMap", filterMap); - } - - - /** - * Remove a class name from the set of InstanceListener classes that - * will be added to newly created Wrappers. - * - * @param listener Class name of an InstanceListener class to be removed - */ - @Override - public void removeInstanceListener(String listener) { - - synchronized (instanceListenersLock) { - - // Make sure this welcome file is currently present - int n = -1; - for (int i = 0; i < instanceListeners.length; i++) { - if (instanceListeners[i].equals(listener)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - String results[] = new String[instanceListeners.length - 1]; - for (int i = 0; i < instanceListeners.length; i++) { - if (i != n) - results[j++] = instanceListeners[i]; - } - instanceListeners = results; - - } - - // Inform interested listeners - fireContainerEvent("removeInstanceListener", listener); - - } - - - /** - * Remove any message destination with the specified name. - * - * @param name Name of the message destination to remove - */ - public void removeMessageDestination(String name) { - - synchronized (messageDestinations) { - messageDestinations.remove(name); - } - fireContainerEvent("removeMessageDestination", name); - - } - - - /** - * Remove any message destination ref with the specified name. - * - * @param name Name of the message destination ref to remove - */ - public void removeMessageDestinationRef(String name) { - - namingResources.removeMessageDestinationRef(name); - fireContainerEvent("removeMessageDestinationRef", name); - - } - - - /** - * Remove the MIME mapping for the specified extension, if it exists; - * otherwise, no action is taken. - * - * @param extension Extension to remove the mapping for - */ - @Override - public void removeMimeMapping(String extension) { - - synchronized (mimeMappings) { - mimeMappings.remove(extension); - } - fireContainerEvent("removeMimeMapping", extension); - - } - - - /** - * Remove the context initialization parameter with the specified - * name, if it exists; otherwise, no action is taken. - * - * @param name Name of the parameter to remove - */ - @Override - public void removeParameter(String name) { - - synchronized (parameters) { - parameters.remove(name); - } - fireContainerEvent("removeParameter", name); - - } - - - /** - * Remove any security role reference for the specified name - * - * @param role Security role (as used in the application) to remove - */ - @Override - public void removeRoleMapping(String role) { - - synchronized (roleMappings) { - roleMappings.remove(role); - } - fireContainerEvent("removeRoleMapping", role); - - } - - - /** - * Remove any security role with the specified name. - * - * @param role Security role to remove - */ - @Override - public void removeSecurityRole(String role) { - - synchronized (securityRolesLock) { - - // Make sure this security role is currently present - int n = -1; - for (int i = 0; i < securityRoles.length; i++) { - if (role.equals(securityRoles[i])) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified security role - int j = 0; - String results[] = new String[securityRoles.length - 1]; - for (int i = 0; i < securityRoles.length; i++) { - if (i != n) - results[j++] = securityRoles[i]; - } - securityRoles = results; - - } - - // Inform interested listeners - fireContainerEvent("removeSecurityRole", role); - - } - - - /** - * Remove any servlet mapping for the specified pattern, if it exists; - * otherwise, no action is taken. - * - * @param pattern URL pattern of the mapping to remove - */ - @Override - public void removeServletMapping(String pattern) { - - String name = null; - synchronized (servletMappingsLock) { - name = servletMappings.remove(pattern); - } - Wrapper wrapper = (Wrapper) findChild(name); - if( wrapper != null ) { - wrapper.removeMapping(pattern); - } - mapper.removeWrapper(pattern); - fireContainerEvent("removeServletMapping", pattern); - - } - - - /** - * Remove the specified watched resource name from the list associated - * with this Context. - * - * @param name Name of the watched resource to be removed - */ - @Override - public void removeWatchedResource(String name) { - - synchronized (watchedResourcesLock) { - - // Make sure this watched resource is currently present - int n = -1; - for (int i = 0; i < watchedResources.length; i++) { - if (watchedResources[i].equals(name)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified watched resource - int j = 0; - String results[] = new String[watchedResources.length - 1]; - for (int i = 0; i < watchedResources.length; i++) { - if (i != n) - results[j++] = watchedResources[i]; - } - watchedResources = results; - - } - - fireContainerEvent("removeWatchedResource", name); - - } - - - /** - * Remove the specified welcome file name from the list recognized - * by this Context. - * - * @param name Name of the welcome file to be removed - */ - @Override - public void removeWelcomeFile(String name) { - - synchronized (welcomeFilesLock) { - - // Make sure this welcome file is currently present - int n = -1; - for (int i = 0; i < welcomeFiles.length; i++) { - if (welcomeFiles[i].equals(name)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - String results[] = new String[welcomeFiles.length - 1]; - for (int i = 0; i < welcomeFiles.length; i++) { - if (i != n) - results[j++] = welcomeFiles[i]; - } - welcomeFiles = results; - - } - - // Inform interested listeners - if(this.getState().equals(LifecycleState.STARTED)) - fireContainerEvent(REMOVE_WELCOME_FILE_EVENT, name); - - } - - - /** - * Remove a class name from the set of LifecycleListener classes that - * will be added to newly created Wrappers. - * - * @param listener Class name of a LifecycleListener class to be removed - */ - @Override - public void removeWrapperLifecycle(String listener) { - - - synchronized (wrapperLifecyclesLock) { - - // Make sure this welcome file is currently present - int n = -1; - for (int i = 0; i < wrapperLifecycles.length; i++) { - if (wrapperLifecycles[i].equals(listener)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - String results[] = new String[wrapperLifecycles.length - 1]; - for (int i = 0; i < wrapperLifecycles.length; i++) { - if (i != n) - results[j++] = wrapperLifecycles[i]; - } - wrapperLifecycles = results; - - } - - // Inform interested listeners - fireContainerEvent("removeWrapperLifecycle", listener); - - } - - - /** - * Remove a class name from the set of ContainerListener classes that - * will be added to newly created Wrappers. - * - * @param listener Class name of a ContainerListener class to be removed - */ - @Override - public void removeWrapperListener(String listener) { - - - synchronized (wrapperListenersLock) { - - // Make sure this welcome file is currently present - int n = -1; - for (int i = 0; i < wrapperListeners.length; i++) { - if (wrapperListeners[i].equals(listener)) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified constraint - int j = 0; - String results[] = new String[wrapperListeners.length - 1]; - for (int i = 0; i < wrapperListeners.length; i++) { - if (i != n) - results[j++] = wrapperListeners[i]; - } - wrapperListeners = results; - - } - - // Inform interested listeners - fireContainerEvent("removeWrapperListener", listener); - - } - - - /** - * Gets the cumulative processing times of all servlets in this - * StandardContext. - * - * @return Cumulative processing times of all servlets in this - * StandardContext - */ - public long getProcessingTime() { - - long result = 0; - - Container[] children = findChildren(); - if (children != null) { - for( int i=0; i< children.length; i++ ) { - result += ((StandardWrapper)children[i]).getProcessingTime(); - } - } - - return result; - } - - - /** - * Return the real path for a given virtual path, if possible; otherwise - * return null. - * - * @param path The path to the desired resource - */ - @Override - public String getRealPath(String path) { - if (webappResources instanceof BaseDirContext) { - return ((BaseDirContext) webappResources).getRealPath(path); - } - return null; - } - - /** - * hook to register that we need to scan for security annotations. - * @param wrapper The wrapper for the Servlet that was added - */ - public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) { - Servlet s = wrapper.getServlet(); - if (s != null && createdServlets.contains(s)) { - // Mark the wrapper to indicate annotations need to be scanned - wrapper.setServletSecurityAnnotationScanRequired(true); - } - return new ApplicationServletRegistration(wrapper, this); - } - - /** - * hook to track which registrations need annotation scanning - * @param servlet - */ - public void dynamicServletCreated(Servlet servlet) { - createdServlets.add(servlet); - } - - - /** - * A helper class to manage the filter mappings in a Context. - */ - private static final class ContextFilterMaps { - private final Object lock = new Object(); - - /** - * The set of filter mappings for this application, in the order they - * were defined in the deployment descriptor with additional mappings - * added via the {@link ServletContext} possibly both before and after - * those defined in the deployment descriptor. - */ - private FilterMap[] array = new FilterMap[0]; - - /** - * Filter mappings added via {@link ServletContext} may have to be - * inserted before the mappings in the deployment descriptor but must be - * inserted in the order the {@link ServletContext} methods are called. - * This isn't an issue for the mappings added after the deployment - * descriptor - they are just added to the end - but correctly the - * adding mappings before the deployment descriptor mappings requires - * knowing where the last 'before' mapping was added. - */ - private int insertPoint = 0; - - /** - * Return the set of filter mappings. - */ - public FilterMap[] asArray() { - synchronized (lock) { - return array; - } - } - - /** - * Add a filter mapping at the end of the current set of filter - * mappings. - * - * @param filterMap - * The filter mapping to be added - */ - public void add(FilterMap filterMap) { - synchronized (lock) { - FilterMap results[] = Arrays.copyOf(array, array.length + 1); - results[array.length] = filterMap; - array = results; - } - } - - /** - * Add a filter mapping before the mappings defined in the deployment - * descriptor but after any other mappings added via this method. - * - * @param filterMap - * The filter mapping to be added - */ - public void addBefore(FilterMap filterMap) { - synchronized (lock) { - FilterMap results[] = new FilterMap[array.length + 1]; - System.arraycopy(array, 0, results, 0, insertPoint); - System.arraycopy(array, insertPoint, results, insertPoint + 1, - array.length - insertPoint); - results[insertPoint] = filterMap; - array = results; - insertPoint++; - } - } - - /** - * Remove a filter mapping. - * - * @param filterMap The filter mapping to be removed - */ - public void remove(FilterMap filterMap) { - synchronized (lock) { - // Make sure this filter mapping is currently present - int n = -1; - for (int i = 0; i < array.length; i++) { - if (array[i] == filterMap) { - n = i; - break; - } - } - if (n < 0) - return; - - // Remove the specified filter mapping - FilterMap results[] = new FilterMap[array.length - 1]; - System.arraycopy(array, 0, results, 0, n); - System.arraycopy(array, n + 1, results, n, (array.length - 1) - - n); - array = results; - if (n < insertPoint) { - insertPoint--; - } - } - } - } - - // --------------------------------------------------------- Public Methods - - - /** - * Configure and initialize the set of filters for this Context. - * Return true if all filter initialization completed - * successfully, or false otherwise. - */ - public boolean filterStart() { - - if (getLogger().isDebugEnabled()) - getLogger().debug("Starting filters"); - // Instantiate and record a FilterConfig for each defined filter - boolean ok = true; - synchronized (filterConfigs) { - filterConfigs.clear(); - Iterator names = filterDefs.keySet().iterator(); - while (names.hasNext()) { - String name = names.next(); - if (getLogger().isDebugEnabled()) - getLogger().debug(" Starting filter '" + name + "'"); - ApplicationFilterConfig filterConfig = null; - try { - filterConfig = - new ApplicationFilterConfig(this, filterDefs.get(name)); - filterConfigs.put(name, filterConfig); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error - (sm.getString("standardContext.filterStart", name), t); - ok = false; - } - } - } - - return (ok); - - } - - - /** - * Finalize and release the set of filters for this Context. - * Return true if all filter finalization completed - * successfully, or false otherwise. - */ - public boolean filterStop() { - - if (getLogger().isDebugEnabled()) - getLogger().debug("Stopping filters"); - - // Release all Filter and FilterConfig instances - synchronized (filterConfigs) { - Iterator names = filterConfigs.keySet().iterator(); - while (names.hasNext()) { - String name = names.next(); - if (getLogger().isDebugEnabled()) - getLogger().debug(" Stopping filter '" + name + "'"); - ApplicationFilterConfig filterConfig = filterConfigs.get(name); - filterConfig.release(); - } - filterConfigs.clear(); - } - return (true); - - } - - - /** - * Find and return the initialized FilterConfig for the - * specified filter name, if any; otherwise return null. - * - * @param name Name of the desired filter - */ - public FilterConfig findFilterConfig(String name) { - - return (filterConfigs.get(name)); - - } - - - /** - * Configure the set of instantiated application event listeners - * for this Context. Return true if all listeners wre - * initialized successfully, or false otherwise. - */ - public boolean listenerStart() { - - if (log.isDebugEnabled()) - log.debug("Configuring application event listeners"); - - // Instantiate the required listeners - String listeners[] = findApplicationListeners(); - Object results[] = new Object[listeners.length]; - boolean ok = true; - for (int i = 0; i < results.length; i++) { - if (getLogger().isDebugEnabled()) - getLogger().debug(" Configuring event listener class '" + - listeners[i] + "'"); - try { - results[i] = instanceManager.newInstance(listeners[i]); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error - (sm.getString("standardContext.applicationListener", - listeners[i]), t); - ok = false; - } - } - if (!ok) { - getLogger().error(sm.getString("standardContext.applicationSkipped")); - return (false); - } - - // Sort listeners in two arrays - ArrayList eventListeners = new ArrayList(); - ArrayList lifecycleListeners = new ArrayList(); - for (int i = 0; i < results.length; i++) { - if ((results[i] instanceof ServletContextAttributeListener) - || (results[i] instanceof ServletRequestAttributeListener) - || (results[i] instanceof ServletRequestListener) - || (results[i] instanceof HttpSessionAttributeListener)) { - eventListeners.add(results[i]); - } - if ((results[i] instanceof ServletContextListener) - || (results[i] instanceof HttpSessionListener)) { - lifecycleListeners.add(results[i]); - } - } - - //Listeners may have been added by ServletContextInitializers. Put them after the ones we know about. - for (Object eventListener: getApplicationEventListeners()) { - eventListeners.add(eventListener); - } - setApplicationEventListeners(eventListeners.toArray()); - for (Object lifecycleListener: getApplicationLifecycleListeners()) { - lifecycleListeners.add(lifecycleListener); - } - setApplicationLifecycleListeners(lifecycleListeners.toArray()); - - // Send application start events - - if (getLogger().isDebugEnabled()) - getLogger().debug("Sending application start events"); - - // Ensure context is not null - getServletContext(); - context.setNewServletContextListenerAllowed(false); - - Object instances[] = getApplicationLifecycleListeners(); - if (instances == null) - return (ok); - ServletContextEvent event = - new ServletContextEvent(getServletContext()); - for (int i = 0; i < instances.length; i++) { - if (instances[i] == null) - continue; - if (!(instances[i] instanceof ServletContextListener)) - continue; - ServletContextListener listener = - (ServletContextListener) instances[i]; - try { - fireContainerEvent("beforeContextInitialized", listener); - listener.contextInitialized(event); - fireContainerEvent("afterContextInitialized", listener); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - fireContainerEvent("afterContextInitialized", listener); - getLogger().error - (sm.getString("standardContext.listenerStart", - instances[i].getClass().getName()), t); - ok = false; - } - } - return (ok); - - } - - - /** - * Send an application stop event to all interested listeners. - * Return true if all events were sent successfully, - * or false otherwise. - */ - public boolean listenerStop() { - - if (log.isDebugEnabled()) - log.debug("Sending application stop events"); - - boolean ok = true; - Object listeners[] = getApplicationLifecycleListeners(); - if (listeners != null) { - ServletContextEvent event = - new ServletContextEvent(getServletContext()); - for (int i = 0; i < listeners.length; i++) { - int j = (listeners.length - 1) - i; - if (listeners[j] == null) - continue; - if (listeners[j] instanceof ServletContextListener) { - ServletContextListener listener = - (ServletContextListener) listeners[j]; - try { - fireContainerEvent("beforeContextDestroyed", listener); - listener.contextDestroyed(event); - fireContainerEvent("afterContextDestroyed", listener); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - fireContainerEvent("afterContextDestroyed", listener); - getLogger().error - (sm.getString("standardContext.listenerStop", - listeners[j].getClass().getName()), t); - ok = false; - } - } - try { - getInstanceManager().destroyInstance(listeners[j]); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error - (sm.getString("standardContext.listenerStop", - listeners[j].getClass().getName()), t); - ok = false; - } - } - } - - // Annotation processing - listeners = getApplicationEventListeners(); - if (listeners != null) { - for (int i = 0; i < listeners.length; i++) { - int j = (listeners.length - 1) - i; - if (listeners[j] == null) - continue; - try { - getInstanceManager().destroyInstance(listeners[j]); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error - (sm.getString("standardContext.listenerStop", - listeners[j].getClass().getName()), t); - ok = false; - } - } - } - - setApplicationEventListeners(null); - setApplicationLifecycleListeners(null); - - return (ok); - - } - - - /** - * Allocate resources, including proxy. - * Return true if initialization was successfull, - * or false otherwise. - */ - public boolean resourcesStart() { - - boolean ok = true; - - Hashtable env = new Hashtable(); - if (getParent() != null) - env.put(ProxyDirContext.HOST, getParent().getName()); - env.put(ProxyDirContext.CONTEXT, getName()); - - try { - ProxyDirContext proxyDirContext = - new ProxyDirContext(env, webappResources); - if (webappResources instanceof FileDirContext) { - filesystemBased = true; - ((FileDirContext) webappResources).setAllowLinking - (isAllowLinking()); - } - if (webappResources instanceof BaseDirContext) { - ((BaseDirContext) webappResources).setDocBase(getBasePath()); - ((BaseDirContext) webappResources).setCached - (isCachingAllowed()); - ((BaseDirContext) webappResources).setCacheTTL(getCacheTTL()); - ((BaseDirContext) webappResources).setCacheMaxSize - (getCacheMaxSize()); - ((BaseDirContext) webappResources).allocate(); - // Alias support - ((BaseDirContext) webappResources).setAliases(getAliases()); - - if (effectiveMajorVersion >=3 && addWebinfClassesResources) { - try { - DirContext webInfCtx = - (DirContext) webappResources.lookup( - "/WEB-INF/classes"); - // Do the lookup to make sure it exists - webInfCtx.lookup("META-INF/resources"); - ((BaseDirContext) webappResources).addAltDirContext( - webInfCtx); - } catch (NamingException e) { - // Doesn't exist - ignore and carry on - } - } - } - // Register the cache in JMX - if (isCachingAllowed()) { - String contextName = getName(); - if (!contextName.startsWith("/")) { - contextName = "/" + contextName; - } - ObjectName resourcesName = - new ObjectName(this.getDomain() + ":type=Cache,host=" - + getHostname() + ",context=" + contextName); - Registry.getRegistry(null, null).registerComponent - (proxyDirContext.getCache(), resourcesName, null); - } - this.resources = proxyDirContext; - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("standardContext.resourcesStart"), t); - ok = false; - } - - return (ok); - - } - - - /** - * Deallocate resources and destroy proxy. - */ - public boolean resourcesStop() { - - boolean ok = true; - - try { - if (resources != null) { - if (resources instanceof Lifecycle) { - ((Lifecycle) resources).stop(); - } - if (webappResources instanceof BaseDirContext) { - ((BaseDirContext) webappResources).release(); - } - // Unregister the cache in JMX - if (isCachingAllowed()) { - String contextName = getName(); - if (!contextName.startsWith("/")) { - contextName = "/" + contextName; - } - ObjectName resourcesName = - new ObjectName(this.getDomain() - + ":type=Cache,host=" - + getHostname() + ",context=" - + contextName); - Registry.getRegistry(null, null) - .unregisterComponent(resourcesName); - } - } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("standardContext.resourcesStop"), t); - ok = false; - } - - this.resources = null; - - return (ok); - - } - - - /** - * Load and initialize all servlets marked "load on startup" in the - * web application deployment descriptor. - * - * @param children Array of wrappers for all currently defined - * servlets (including those not declared load on startup) - */ - public void loadOnStartup(Container children[]) { - - // Collect "load on startup" servlets that need to be initialized - TreeMap> map = - new TreeMap>(); - for (int i = 0; i < children.length; i++) { - Wrapper wrapper = (Wrapper) children[i]; - int loadOnStartup = wrapper.getLoadOnStartup(); - if (loadOnStartup < 0) - continue; - Integer key = Integer.valueOf(loadOnStartup); - ArrayList list = map.get(key); - if (list == null) { - list = new ArrayList(); - map.put(key, list); - } - list.add(wrapper); - } - - // Load the collected "load on startup" servlets - for (ArrayList list : map.values()) { - for (Wrapper wrapper : list) { - try { - wrapper.load(); - } catch (ServletException e) { - getLogger().error(sm.getString("standardWrapper.loadException", - getName()), StandardWrapper.getRootCause(e)); - // NOTE: load errors (including a servlet that throws - // UnavailableException from tht init() method) are NOT - // fatal to application startup - } - } - } - - } - - - /** - * Start this component and implement the requirements - * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - @Override - protected synchronized void startInternal() throws LifecycleException { - - if(log.isDebugEnabled()) - log.debug("Starting " + getBaseName()); - - // Send j2ee.state.starting notification - if (this.getObjectName() != null) { - Notification notification = new Notification("j2ee.state.starting", - this.getObjectName(), sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - - setConfigured(false); - boolean ok = true; - - // Currently this is effectively a NO-OP but needs to be called to - // ensure the NamingResources follows the correct lifecycle - if (namingResources != null) { - namingResources.start(); - } - - // Add missing components as necessary - if (webappResources == null) { // (1) Required by Loader - if (log.isDebugEnabled()) - log.debug("Configuring default Resources"); - try { - if ((getDocBase() != null) && (getDocBase().endsWith(".war")) && - (!(new File(getBasePath())).isDirectory())) - setResources(new WARDirContext()); - else - setResources(new FileDirContext()); - } catch (IllegalArgumentException e) { - log.error("Error initializing resources: " + e.getMessage()); - ok = false; - } - } - if (ok) { - if (!resourcesStart()) { - log.error( "Error in resourceStart()"); - ok = false; - } - } - - if (getLoader() == null) { - WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); - webappLoader.setDelegate(getDelegate()); - setLoader(webappLoader); - } - - // Initialize character set mapper - getCharsetMapper(); - - // Post work directory - postWorkDirectory(); - - // Validate required extensions - boolean dependencyCheck = true; - try { - dependencyCheck = ExtensionValidator.validateApplication - (getResources(), this); - } catch (IOException ioe) { - log.error("Error in dependencyCheck", ioe); - dependencyCheck = false; - } - - if (!dependencyCheck) { - // do not make application available if depency check fails - ok = false; - } - - // Reading the "catalina.useNaming" environment variable - String useNamingProperty = System.getProperty("catalina.useNaming"); - if ((useNamingProperty != null) - && (useNamingProperty.equals("false"))) { - useNaming = false; - } - - if (ok && isUseNaming()) { - if (getNamingContextListener() == null) { - NamingContextListener ncl = new NamingContextListener(); - ncl.setName(getNamingContextName()); - addLifecycleListener(ncl); - setNamingContextListener(ncl); - } - } - - // Standard container startup - if (log.isDebugEnabled()) - log.debug("Processing standard container startup"); - - - // Binding thread - ClassLoader oldCCL = bindThread(); - - try { - - if (ok) { - - // Start our subordinate components, if any - if ((loader != null) && (loader instanceof Lifecycle)) - ((Lifecycle) loader).start(); - - // since the loader just started, the webapp classloader is now - // created. - // By calling unbindThread and bindThread in a row, we setup the - // current Thread CCL to be the webapp classloader - unbindThread(oldCCL); - oldCCL = bindThread(); - - // Initialize logger again. Other components might have used it too early, - // so it should be reset. - logger = null; - getLogger(); - if ((logger != null) && (logger instanceof Lifecycle)) - ((Lifecycle) logger).start(); - - if ((cluster != null) && (cluster instanceof Lifecycle)) - ((Lifecycle) cluster).start(); - if ((realm != null) && (realm instanceof Lifecycle)) - ((Lifecycle) realm).start(); - if ((resources != null) && (resources instanceof Lifecycle)) - ((Lifecycle) resources).start(); - - // Notify our interested LifecycleListeners - fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); - - // Start our child containers, if not already started - for (Container child : findChildren()) { - if (!child.getState().isAvailable()) { - child.start(); - } - } - - // Start the Valves in our pipeline (including the basic), - // if any - if (pipeline instanceof Lifecycle) { - ((Lifecycle) pipeline).start(); - } - - // Acquire clustered manager - Manager contextManager = null; - if (manager == null) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("standardContext.cluster.noManager", - Boolean.valueOf((getCluster() != null)), - Boolean.valueOf(distributable))); - } - if ( (getCluster() != null) && distributable) { - try { - contextManager = getCluster().createManager(getName()); - } catch (Exception ex) { - log.error("standardContext.clusterFail", ex); - ok = false; - } - } else { - contextManager = new StandardManager(); - } - } - - // Configure default manager if none was specified - if (contextManager != null) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("standardContext.manager", - contextManager.getClass().getName())); - } - setManager(contextManager); - } - - if (manager!=null && (getCluster() != null) && distributable) { - //let the cluster know that there is a context that is distributable - //and that it has its own manager - getCluster().registerManager(manager); - } - } - - } finally { - // Unbinding thread - unbindThread(oldCCL); - } - - if (!getConfigured()) { - log.error( "Error getConfigured"); - ok = false; - } - - // We put the resources into the servlet context - if (ok) - getServletContext().setAttribute - (Globals.RESOURCES_ATTR, getResources()); - - // Initialize associated mapper - mapper.setContext(getPath(), welcomeFiles, resources); - - // Binding thread - oldCCL = bindThread(); - - if (ok ) { - if (getInstanceManager() == null) { - javax.naming.Context context = null; - if (isUseNaming() && getNamingContextListener() != null) { - context = getNamingContextListener().getEnvContext(); - } - Map> injectionMap = buildInjectionMap( - getIgnoreAnnotations() ? new NamingResources(): getNamingResources()); - setInstanceManager(new DefaultInstanceManager(context, - injectionMap, this, this.getClass().getClassLoader())); - getServletContext().setAttribute( - InstanceManager.class.getName(), getInstanceManager()); - } - } - - DedicatedThreadExecutor temporaryExecutor = new DedicatedThreadExecutor(); - try { - - // Create context attributes that will be required - if (ok) { - getServletContext().setAttribute( - JarScanner.class.getName(), getJarScanner()); - } - - // Set up the context init params - mergeParameters(); - - // Call ServletContainerInitializers - for (Map.Entry>> entry : - initializers.entrySet()) { - try { - entry.getKey().onStartup(entry.getValue(), - getServletContext()); - } catch (ServletException e) { - // TODO: Log error - ok = false; - break; - } - } - - // Configure and call application event listeners - if (ok) { - // we do it in a dedicated thread for memory leak protection, in - // case the Listeners registers some ThreadLocals that they - // forget to cleanup - Boolean listenerStarted = - temporaryExecutor.execute(new Callable() { - @Override - public Boolean call() throws Exception { - ClassLoader old = bindThread(); - try { - return Boolean.valueOf(listenerStart()); - } finally { - unbindThread(old); - } - } - }); - if (!listenerStarted.booleanValue()) { - log.error( "Error listenerStart"); - ok = false; - } - } - - try { - // Start manager - if ((manager != null) && (manager instanceof Lifecycle)) { - ((Lifecycle) getManager()).start(); - } - - // Start ContainerBackgroundProcessor thread - super.threadStart(); - } catch(Exception e) { - log.error("Error manager.start()", e); - ok = false; - } - - // Configure and call application filters - if (ok) { - // we do it in a dedicated thread for memory leak protection, in - // case the Filters register some ThreadLocals that they forget - // to cleanup - Boolean filterStarted = - temporaryExecutor.execute(new Callable() { - @Override - public Boolean call() throws Exception { - ClassLoader old = bindThread(); - try { - return Boolean.valueOf(filterStart()); - } finally { - unbindThread(old); - } - } - }); - if (!filterStarted.booleanValue()) { - log.error("Error filterStart"); - ok = false; - } - } - - // Load and initialize all "load on startup" servlets - if (ok) { - // we do it in a dedicated thread for memory leak protection, in - // case the Servlets register some ThreadLocals that they forget - // to cleanup - temporaryExecutor.execute(new Callable() { - @Override - public Void call() throws Exception { - ClassLoader old = bindThread(); - try { - loadOnStartup(findChildren()); - return null; - } finally { - unbindThread(old); - } - } - }); - } - - } finally { - // Unbinding thread - unbindThread(oldCCL); - temporaryExecutor.shutdown(); - } - - // Set available status depending upon startup success - if (ok) { - if (log.isDebugEnabled()) - log.debug("Starting completed"); - } else { - log.error(sm.getString("standardContext.startFailed", getName())); - } - - startTime=System.currentTimeMillis(); - - // Send j2ee.state.running notification - if (ok && (this.getObjectName() != null)) { - Notification notification = - new Notification("j2ee.state.running", this.getObjectName(), - sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - - // Close all JARs right away to avoid always opening a peak number - // of files on startup - if (getLoader() instanceof WebappLoader) { - ((WebappLoader) getLoader()).closeJARs(true); - } - - // Reinitializing if something went wrong - if (!ok) { - setState(LifecycleState.FAILED); - } else { - setState(LifecycleState.STARTING); - } - } - - private Map> buildInjectionMap(NamingResources namingResources) { - Map> injectionMap = new HashMap>(); - for (Injectable resource: namingResources.findLocalEjbs()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findEjbs()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findEnvironments()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findMessageDestinationRefs()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findResourceEnvRefs()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findResources()) { - addInjectionTarget(resource, injectionMap); - } - for (Injectable resource: namingResources.findServices()) { - addInjectionTarget(resource, injectionMap); - } - return injectionMap; - } - - private void addInjectionTarget(Injectable resource, Map> injectionMap) { - List injectionTargets = resource.getInjectionTargets(); - if (injectionTargets != null && injectionTargets.size() > 0) { - String jndiName = resource.getName(); - for (InjectionTarget injectionTarget: injectionTargets) { - String clazz = injectionTarget.getTargetClass(); - Map injections = injectionMap.get(clazz); - if (injections == null) { - injections = new HashMap(); - injectionMap.put(clazz, injections); - } - injections.put(injectionTarget.getTargetName(), jndiName); - } - } - } - - - - /** - * Merge the context initialization parameters specified in the application - * deployment descriptor with the application parameters described in the - * server configuration, respecting the override property of - * the application parameters appropriately. - */ - private void mergeParameters() { - Map mergedParams = new HashMap(); - - String names[] = findParameters(); - for (int i = 0; i < names.length; i++) { - mergedParams.put(names[i], findParameter(names[i])); - } - - ApplicationParameter params[] = findApplicationParameters(); - for (int i = 0; i < params.length; i++) { - if (params[i].getOverride()) { - if (mergedParams.get(params[i].getName()) == null) { - mergedParams.put(params[i].getName(), - params[i].getValue()); - } - } else { - mergedParams.put(params[i].getName(), params[i].getValue()); - } - } - - ServletContext sc = getServletContext(); - for (Map.Entry entry : mergedParams.entrySet()) { - sc.setInitParameter(entry.getKey(), entry.getValue()); - } - - } - - - /** - * Stop this component and implement the requirements - * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - @Override - protected synchronized void stopInternal() throws LifecycleException { - - // Send j2ee.state.stopping notification - if (this.getObjectName() != null) { - Notification notification = - new Notification("j2ee.state.stopping", this.getObjectName(), - sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - - setState(LifecycleState.STOPPING); - - // Binding thread - ClassLoader oldCCL = bindThread(); - - try { - - // Stop our child containers, if any - final Container[] children = findChildren(); - // we do it in a dedicated thread for memory leak protection, in - // case some webapp code registers some ThreadLocals that they - // forget to cleanup - // TODO Figure out why DedicatedThreadExecutor hangs randomly in the - // unit tests if used here - RunnableWithLifecycleException stop = - new RunnableWithLifecycleException() { - @Override - public void run() { - ClassLoader old = bindThread(); - try { - for (int i = 0; i < children.length; i++) { - try { - children[i].stop(); - } catch (LifecycleException e) { - le = e; - return; - } - } - - // Stop our filters - filterStop(); - - // Stop ContainerBackgroundProcessor thread - threadStop(); - - if (manager != null && manager instanceof Lifecycle && - ((Lifecycle) manager).getState().isAvailable()) { - try { - ((Lifecycle) manager).stop(); - } catch (LifecycleException e) { - le = e; - return; - } - } - - // Stop our application listeners - listenerStop(); - }finally{ - unbindThread(old); - } - } - }; - - Thread t = new Thread(stop); - t.setName("stop children - " + getObjectName().toString()); - t.start(); - try { - t.join(); - } catch (InterruptedException e) { - // Shouldn't happen - throw new LifecycleException(e); - } - if (stop.getLifecycleException() != null) { - throw stop.getLifecycleException(); - } - - // Finalize our character set mapper - setCharsetMapper(null); - - // Normal container shutdown processing - if (log.isDebugEnabled()) - log.debug("Processing standard container shutdown"); - - // JNDI resources are unbound in CONFIGURE_STOP_EVENT so stop - // naming resoucres before they are unbound since NamingResoucres - // does a JNDI lookup to retrieve the resource. This needs to be - // after the application has finished with the resource - if (namingResources != null) { - namingResources.stop(); - } - - fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null); - - // Stop the Valves in our pipeline (including the basic), if any - if (pipeline instanceof Lifecycle && - ((Lifecycle) pipeline).getState().isAvailable()) { - ((Lifecycle) pipeline).stop(); - } - - // Clear all application-originated servlet context attributes - if (context != null) - context.clearAttributes(); - - // Stop resources - resourcesStop(); - - if ((realm != null) && (realm instanceof Lifecycle)) { - ((Lifecycle) realm).stop(); - } - if ((cluster != null) && (cluster instanceof Lifecycle)) { - ((Lifecycle) cluster).stop(); - } - if ((logger != null) && (logger instanceof Lifecycle)) { - ((Lifecycle) logger).stop(); - } - if ((loader != null) && (loader instanceof Lifecycle)) { - ((Lifecycle) loader).stop(); - } - - } finally { - - // Unbinding thread - unbindThread(oldCCL); - - } - - // Send j2ee.state.stopped notification - if (this.getObjectName() != null) { - Notification notification = - new Notification("j2ee.state.stopped", this.getObjectName(), - sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - - // Reset application context - context = null; - - // This object will no longer be visible or used. - try { - resetContext(); - } catch( Exception ex ) { - log.error( "Error reseting context " + this + " " + ex, ex ); - } - - //reset the instance manager - instanceManager = null; - - if (log.isDebugEnabled()) - log.debug("Stopping complete"); - - } - - /** Destroy needs to clean up the context completely. - * - * The problem is that undoing all the config in start() and restoring - * a 'fresh' state is impossible. After stop()/destroy()/init()/start() - * we should have the same state as if a fresh start was done - i.e - * read modified web.xml, etc. This can only be done by completely - * removing the context object and remapping a new one, or by cleaning - * up everything. - * - * XXX Should this be done in stop() ? - * - */ - @Override - protected void destroyInternal() throws LifecycleException { - - if ((manager != null) && (manager instanceof Lifecycle)) { - ((Lifecycle) manager).destroy(); - } - if ((realm != null) && (realm instanceof Lifecycle)) { - ((Lifecycle) realm).destroy(); - } - if ((cluster != null) && (cluster instanceof Lifecycle)) { - ((Lifecycle) cluster).destroy(); - } - if ((logger != null) && (logger instanceof Lifecycle)) { - ((Lifecycle) logger).destroy(); - } - if ((loader != null) && (loader instanceof Lifecycle)) { - ((Lifecycle) loader).destroy(); - } - - // If in state NEW when destroy is called, the object name will never - // have been set so the notification can't be created - if (getObjectName() != null) { - // Send j2ee.object.deleted notification - Notification notification = - new Notification("j2ee.object.deleted", this.getObjectName(), - sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - - if (namingResources != null) { - namingResources.destroy(); - } - - synchronized (instanceListenersLock) { - instanceListeners = new String[0]; - } - - super.destroyInternal(); - } - - private void resetContext() throws Exception { - // Restore the original state ( pre reading web.xml in start ) - // If you extend this - override this method and make sure to clean up - - // Don't reset anything that is read from a element since - // elements are read at initialisation will not be read - // again for this object - children = new HashMap(); - startupTime = 0; - startTime = 0; - tldScanTime = 0; - - // Bugzilla 32867 - distributable = false; - - applicationListeners = new String[0]; - applicationEventListenersObjects = new Object[0]; - applicationLifecycleListenersObjects = new Object[0]; - jspConfigDescriptor = new ApplicationJspConfigDescriptor(); - - initializers.clear(); - - createdServlets.clear(); - - if(log.isDebugEnabled()) - log.debug("resetContext " + getObjectName()); - } - - /** - * Return a String representation of this component. - */ - @Override - public String toString() { - - StringBuilder sb = new StringBuilder(); - if (getParent() != null) { - sb.append(getParent().toString()); - sb.append("."); - } - sb.append("StandardContext["); - sb.append(getName()); - sb.append("]"); - return (sb.toString()); - - } - - - // ------------------------------------------------------ Protected Methods - - - /** - * Adjust the URL pattern to begin with a leading slash, if appropriate - * (i.e. we are running a servlet 2.2 application). Otherwise, return - * the specified URL pattern unchanged. - * - * @param urlPattern The URL pattern to be adjusted (if needed) - * and returned - */ - protected String adjustURLPattern(String urlPattern) { - - if (urlPattern == null) - return (urlPattern); - if (urlPattern.startsWith("/") || urlPattern.startsWith("*.")) - return (urlPattern); - if (!isServlet22()) - return (urlPattern); - if(log.isDebugEnabled()) - log.debug(sm.getString("standardContext.urlPattern.patternWarning", - urlPattern)); - return ("/" + urlPattern); - - } - - - /** - * Are we processing a version 2.2 deployment descriptor? - */ - @Override - public boolean isServlet22() { - - if (this.publicId == null) - return (false); - if (this.publicId.equals - (org.apache.catalina.startup.Constants.WebDtdPublicId_22)) - return (true); - else - return (false); - - } - - @Override - public Set addServletSecurity( - ApplicationServletRegistration registration, - ServletSecurityElement servletSecurityElement) { - - Set conflicts = new HashSet(); - - Collection urlPatterns = registration.getMappings(); - for (String urlPattern : urlPatterns) { - boolean foundConflict = false; - - SecurityConstraint[] securityConstraints = - findConstraints(); - for (SecurityConstraint securityConstraint : securityConstraints) { - - SecurityCollection[] collections = - securityConstraint.findCollections(); - for (SecurityCollection collection : collections) { - if (collection.findPattern(urlPattern)) { - // First pattern found will indicate if there is a - // conflict since for any given pattern all matching - // constraints will be from either the descriptor or - // not. It is not permitted to have a mixture - if (collection.isFromDescriptor()) { - // Skip this pattern - foundConflict = true; - conflicts.add(urlPattern); - } else { - // Need to overwrite constraint for this pattern - // so remove every pattern found - - // TODO spec 13.4.2 appears to say only the - // conflicting pattern is overwritten, not the - // entire security constraint. - removeConstraint(securityConstraint); - } - } - if (foundConflict) { - break; - } - } - if (foundConflict) { - break; - } - } - // TODO spec 13.4.2 appears to say that non-conflicting patterns are - // still used. - // TODO you can't calculate the eventual security constraint now, - // you have to wait until the context is started, since application - // code can add url patterns after calling setSecurity. - if (!foundConflict) { - SecurityConstraint[] newSecurityConstraints = - SecurityConstraint.createConstraints( - servletSecurityElement, - urlPattern); - for (SecurityConstraint securityConstraint : - newSecurityConstraints) { - addConstraint(securityConstraint); - } - } - } - - return conflicts; - - } - - - /** - * Return a File object representing the base directory for the - * entire servlet container (i.e. the Engine container if present). - */ - protected File engineBase() { - String base=System.getProperty(Globals.CATALINA_BASE_PROP); - if( base == null ) { - StandardEngine eng=(StandardEngine)this.getParent().getParent(); - base=eng.getBaseDir(); - } - return (new File(base)); - } - - - /** - * Bind current thread, both for CL purposes and for JNDI ENC support - * during : startup, shutdown and realoading of the context. - * - * @return the previous context class loader - */ - protected ClassLoader bindThread() { - - ClassLoader oldContextClassLoader = - Thread.currentThread().getContextClassLoader(); - - if (getResources() == null) - return oldContextClassLoader; - - if (getLoader().getClassLoader() != null) { - Thread.currentThread().setContextClassLoader - (getLoader().getClassLoader()); - } - - DirContextURLStreamHandler.bindThread(getResources()); - - if (isUseNaming()) { - try { - ContextBindings.bindThread(this, this); - } catch (NamingException e) { - // Silent catch, as this is a normal case during the early - // startup stages - } - } - - return oldContextClassLoader; - - } - - - /** - * Unbind thread. - */ - protected void unbindThread(ClassLoader oldContextClassLoader) { - - if (isUseNaming()) { - ContextBindings.unbindThread(this, this); - } - - DirContextURLStreamHandler.unbindThread(); - - Thread.currentThread().setContextClassLoader(oldContextClassLoader); - } - - - - /** - * Get base path. - */ - protected String getBasePath() { - String docBase = null; - Container container = this; - while (container != null) { - if (container instanceof Host) - break; - container = container.getParent(); - } - File file = new File(getDocBase()); - if (!file.isAbsolute()) { - if (container == null) { - docBase = (new File(engineBase(), getDocBase())).getPath(); - } else { - // Use the "appBase" property of this container - file = ((Host) container).getAppBaseFile(); - docBase = (new File(file, getDocBase())).getPath(); - } - } else { - docBase = file.getPath(); - } - return docBase; - } - - - /** - * Get naming context full name. - */ - private String getNamingContextName() { - if (namingContextName == null) { - Container parent = getParent(); - if (parent == null) { - namingContextName = getName(); - } else { - Stack stk = new Stack(); - StringBuilder buff = new StringBuilder(); - while (parent != null) { - stk.push(parent.getName()); - parent = parent.getParent(); - } - while (!stk.empty()) { - buff.append("/" + stk.pop()); - } - buff.append(getName()); - namingContextName = buff.toString(); - } - } - return namingContextName; - } - - - /** - * Naming context listener accessor. - */ - public NamingContextListener getNamingContextListener() { - return namingContextListener; - } - - - /** - * Naming context listener setter. - */ - public void setNamingContextListener(NamingContextListener namingContextListener) { - this.namingContextListener = namingContextListener; - } - - - /** - * Return the request processing paused flag for this Context. - */ - @Override - public boolean getPaused() { - - return (this.paused); - - } - - - public String getHostname() { - Container parentHost = getParent(); - if (parentHost != null) { - hostName = parentHost.getName(); - } - if ((hostName == null) || (hostName.length() < 1)) - hostName = "_"; - return hostName; - } - - - @Override - public boolean fireRequestInitEvent(ServletRequest request) { - - Object instances[] = getApplicationEventListeners(); - - if ((instances != null) && (instances.length > 0)) { - - ServletRequestEvent event = - new ServletRequestEvent(getServletContext(), request); - - for (int i = 0; i < instances.length; i++) { - if (instances[i] == null) - continue; - if (!(instances[i] instanceof ServletRequestListener)) - continue; - ServletRequestListener listener = - (ServletRequestListener) instances[i]; - - try { - listener.requestInitialized(event); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString( - "standardContext.requestListener.requestInit", - instances[i].getClass().getName()), t); - request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); - return false; - } - } - } - return true; - } - - - @Override - public boolean fireRequestDestroyEvent(ServletRequest request) { - Object instances[] = getApplicationEventListeners(); - - if ((instances != null) && (instances.length > 0)) { - - ServletRequestEvent event = - new ServletRequestEvent(getServletContext(), request); - - for (int i = 0; i < instances.length; i++) { - int j = (instances.length -1) -i; - if (instances[j] == null) - continue; - if (!(instances[j] instanceof ServletRequestListener)) - continue; - ServletRequestListener listener = - (ServletRequestListener) instances[j]; - - try { - listener.requestDestroyed(event); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString( - "standardContext.requestListener.requestInit", - instances[j].getClass().getName()), t); - request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); - return false; - } - } - } - return true; - } - - - /** - * Set the appropriate context attribute for our work directory. - */ - private void postWorkDirectory() { - - // Acquire (or calculate) the work directory path - String workDir = getWorkDir(); - if (workDir == null || workDir.length() == 0) { - - // Retrieve our parent (normally a host) name - String hostName = null; - String engineName = null; - String hostWorkDir = null; - Container parentHost = getParent(); - if (parentHost != null) { - hostName = parentHost.getName(); - if (parentHost instanceof StandardHost) { - hostWorkDir = ((StandardHost)parentHost).getWorkDir(); - } - Container parentEngine = parentHost.getParent(); - if (parentEngine != null) { - engineName = parentEngine.getName(); - } - } - if ((hostName == null) || (hostName.length() < 1)) - hostName = "_"; - if ((engineName == null) || (engineName.length() < 1)) - engineName = "_"; - - String temp = getName(); - if (temp.startsWith("/")) - temp = temp.substring(1); - temp = temp.replace('/', '_'); - temp = temp.replace('\\', '_'); - if (temp.length() < 1) - temp = "_"; - if (hostWorkDir != null ) { - workDir = hostWorkDir + File.separator + temp; - } else { - workDir = "work" + File.separator + engineName + - File.separator + hostName + File.separator + temp; - } - setWorkDir(workDir); - } - - // Create this directory if necessary - File dir = new File(workDir); - if (!dir.isAbsolute()) { - File catalinaHome = engineBase(); - String catalinaHomePath = null; - try { - catalinaHomePath = catalinaHome.getCanonicalPath(); - dir = new File(catalinaHomePath, workDir); - } catch (IOException e) { - log.warn(sm.getString("standardContext.workCreateException", - workDir, catalinaHomePath, getName()), e); - } - } - if (!dir.mkdirs() && !dir.isDirectory()) { - log.warn(sm.getString("standardContext.workCreateFail", dir, - getName())); - } - - // Set the appropriate servlet context attribute - if (context == null) { - getServletContext(); - } - context.setAttribute(ServletContext.TEMPDIR, dir); - context.setAttributeReadOnly(ServletContext.TEMPDIR); - } - - - /** - * Set the request processing paused flag for this Context. - * - * @param paused The new request processing paused flag - */ - private void setPaused(boolean paused) { - - this.paused = paused; - - } - - - /** - * Validate the syntax of a proposed <url-pattern> - * for conformance with specification requirements. - * - * @param urlPattern URL pattern to be validated - */ - private boolean validateURLPattern(String urlPattern) { - - if (urlPattern == null) - return (false); - if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) { - return (false); - } - if (urlPattern.startsWith("*.")) { - if (urlPattern.indexOf('/') < 0) { - checkUnusualURLPattern(urlPattern); - return (true); - } else - return (false); - } - if ( (urlPattern.startsWith("/")) && - (urlPattern.indexOf("*.") < 0)) { - checkUnusualURLPattern(urlPattern); - return (true); - } else - return (false); - - } - - - /** - * Check for unusual but valid <url-pattern>s. - * See Bugzilla 34805, 43079 & 43080 - */ - private void checkUnusualURLPattern(String urlPattern) { - if (log.isInfoEnabled()) { - if(urlPattern.endsWith("*") && (urlPattern.length() < 2 || - urlPattern.charAt(urlPattern.length()-2) != '/')) { - log.info("Suspicious url pattern: \"" + urlPattern + "\"" + - " in context [" + getName() + "] - see" + - " section SRV.11.2 of the Servlet specification" ); - } - } - } - - - // ------------------------------------------------------------- Operations - - - /** - * JSR77 deploymentDescriptor attribute - * - * @return string deployment descriptor - */ - public String getDeploymentDescriptor() { - - InputStream stream = null; - ServletContext servletContext = getServletContext(); - if (servletContext != null) { - stream = servletContext.getResourceAsStream( - org.apache.catalina.startup.Constants.ApplicationWebXml); - } - if (stream == null) { - return ""; - } - StringBuilder sb = new StringBuilder(); - BufferedReader br = null; - try { - br = new BufferedReader(new InputStreamReader(stream)); - String strRead = ""; - while (strRead != null) { - sb.append(strRead); - strRead = br.readLine(); - } - } catch (IOException e) { - return ""; - } finally { - if (br != null) { - try { - br.close(); - } catch (IOException ioe) {/*Ignore*/} - } - } - - return sb.toString(); - } - - - /** - * JSR77 servlets attribute - * - * @return list of all servlets ( we know about ) - */ - public String[] getServlets() { - - String[] result = null; - - Container[] children = findChildren(); - if (children != null) { - result = new String[children.length]; - for( int i=0; i< children.length; i++ ) { - result[i] = children[i].getObjectName().toString(); - } - } - - return result; - } - - - @Override - protected String getObjectNameKeyProperties() { - - StringBuilder keyProperties = - new StringBuilder("j2eeType=WebModule,"); - keyProperties.append(getObjectKeyPropertiesNameOnly()); - keyProperties.append(",J2EEApplication="); - keyProperties.append(getJ2EEApplication()); - keyProperties.append(",J2EEServer="); - keyProperties.append(getJ2EEServer()); - - return keyProperties.toString(); - } - - private String getObjectKeyPropertiesNameOnly() { - StringBuilder result = new StringBuilder("name=//"); - String hostname = getParent().getName(); - if (hostname == null) { - result.append("DEFAULT"); - } else { - result.append(hostname); - } - - String contextName = getName(); - if (!contextName.startsWith("/")) { - result.append('/'); - } - result.append(contextName); - - return result.toString(); - } - - @Override - protected void initInternal() throws LifecycleException { - super.initInternal(); - - if (processTlds) { - this.addLifecycleListener(new TldConfig()); - } - - // Register the naming resources - if (namingResources != null) { - namingResources.init(); - } - - // Send j2ee.object.created notification - if (this.getObjectName() != null) { - Notification notification = new Notification("j2ee.object.created", - this.getObjectName(), sequenceNumber.getAndIncrement()); - broadcaster.sendNotification(notification); - } - } - - - /* Remove a JMX notficationListener - * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) - */ - @Override - public void removeNotificationListener(NotificationListener listener, - NotificationFilter filter, Object object) throws ListenerNotFoundException { - broadcaster.removeNotificationListener(listener,filter,object); - } - - private MBeanNotificationInfo[] notificationInfo; - - /* Get JMX Broadcaster Info - * @TODO use StringManager for international support! - * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed! - * @see javax.management.NotificationBroadcaster#getNotificationInfo() - */ - @Override - public MBeanNotificationInfo[] getNotificationInfo() { - // FIXME: i18n - if(notificationInfo == null) { - notificationInfo = new MBeanNotificationInfo[]{ - new MBeanNotificationInfo(new String[] { - "j2ee.object.created"}, - Notification.class.getName(), - "web application is created" - ), - new MBeanNotificationInfo(new String[] { - "j2ee.state.starting"}, - Notification.class.getName(), - "change web application is starting" - ), - new MBeanNotificationInfo(new String[] { - "j2ee.state.running"}, - Notification.class.getName(), - "web application is running" - ), - new MBeanNotificationInfo(new String[] { - "j2ee.state.stopping"}, - Notification.class.getName(), - "web application start to stopped" - ), - new MBeanNotificationInfo(new String[] { - "j2ee.object.stopped"}, - Notification.class.getName(), - "web application is stopped" - ), - new MBeanNotificationInfo(new String[] { - "j2ee.object.deleted"}, - Notification.class.getName(), - "web application is deleted" - ) - }; - - } - - return notificationInfo; - } - - - /* Add a JMX-NotificationListener - * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) - */ - @Override - public void addNotificationListener(NotificationListener listener, - NotificationFilter filter, Object object) throws IllegalArgumentException { - broadcaster.addNotificationListener(listener,filter,object); - } - - - /** - * Remove a JMX-NotificationListener - * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) - */ - @Override - public void removeNotificationListener(NotificationListener listener) - throws ListenerNotFoundException { - broadcaster.removeNotificationListener(listener); - } - - - // ------------------------------------------------------------- Attributes - - - /** - * Return the naming resources associated with this web application. - */ - public javax.naming.directory.DirContext getStaticResources() { - - return getResources(); - - } - - - /** - * Return the naming resources associated with this web application. - * FIXME: Fooling introspection ... - */ - public javax.naming.directory.DirContext findStaticResources() { - - return getResources(); - - } - - - /** - * Return the naming resources associated with this web application. - */ - public String[] getWelcomeFiles() { - - return findWelcomeFiles(); - - } - - /** - * Set the validation feature of the XML parser used when - * parsing xml instances. - * @param webXmlValidation true to enable xml instance validation - */ - @Override - public void setXmlValidation(boolean webXmlValidation){ - - this.webXmlValidation = webXmlValidation; - - } - - /** - * Get the server.xml attribute's xmlValidation. - * @return true if validation is enabled. - * - */ - @Override - public boolean getXmlValidation(){ - return webXmlValidation; - } - - - /** - * Get the server.xml attribute's xmlNamespaceAware. - * @return true if namespace awarenes is enabled. - */ - @Override - public boolean getXmlNamespaceAware(){ - return webXmlNamespaceAware; - } - - - /** - * Set the namespace aware feature of the XML parser used when - * parsing xml instances. - * @param webXmlNamespaceAware true to enable namespace awareness - */ - @Override - public void setXmlNamespaceAware(boolean webXmlNamespaceAware){ - this.webXmlNamespaceAware= webXmlNamespaceAware; - } - - - /** - * Set the validation feature of the XML parser used when - * parsing tlds files. - * @param tldValidation true to enable xml instance validation - */ - @Override - public void setTldValidation(boolean tldValidation){ - - this.tldValidation = tldValidation; - - } - - /** - * Get the server.xml attribute's webXmlValidation. - * @return true if validation is enabled. - * - */ - @Override - public boolean getTldValidation(){ - return tldValidation; - } - - /** - * Sets the process TLDs attribute. - * - * @param newProcessTlds The new value - */ - public void setProcessTlds(boolean newProcessTlds) { - processTlds = newProcessTlds; - } - - /** - * Returns the processTlds attribute value. - */ - public boolean getProcessTlds() { - return processTlds; - } - - /** - * Get the server.xml <host> attribute's xmlNamespaceAware. - * @return true if namespace awarenes is enabled. - */ - @Override - public boolean getTldNamespaceAware(){ - return tldNamespaceAware; - } - - - /** - * Set the namespace aware feature of the XML parser used when - * parsing xml instances. - * @param tldNamespaceAware true to enable namespace awareness - */ - @Override - public void setTldNamespaceAware(boolean tldNamespaceAware){ - this.tldNamespaceAware= tldNamespaceAware; - } - - - /** - * Support for "stateManageable" JSR77 - */ - public boolean isStateManageable() { - return true; - } - - public void startRecursive() throws LifecycleException { - // nothing to start recursive, the servlets will be started by load-on-startup - start(); - } - - /** - * The J2EE Server ObjectName this module is deployed on. - */ - private String server = null; - - /** - * The Java virtual machines on which this module is running. - */ - private String[] javaVMs = null; - - public String getServer() { - return server; - } - - public String setServer(String server) { - return this.server=server; - } - - public String[] getJavaVMs() { - return javaVMs; - } - - public String[] setJavaVMs(String[] javaVMs) { - return this.javaVMs = javaVMs; - } - - /** - * Gets the time this context was started. - * - * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this - * context was started - */ - public long getStartTime() { - return startTime; - } - - public boolean isEventProvider() { - return false; - } - - public boolean isStatisticsProvider() { - return false; - } - - private abstract static class RunnableWithLifecycleException - implements Runnable { - - protected LifecycleException le = null; - - public LifecycleException getLifecycleException() { - return le; - } - } -} +/* + * 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 java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicLong; + +import javax.management.ListenerNotFoundException; +import javax.management.MBeanNotificationInfo; +import javax.management.Notification; +import javax.management.NotificationBroadcasterSupport; +import javax.management.NotificationEmitter; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.naming.NamingException; +import javax.naming.directory.DirContext; +import javax.servlet.FilterConfig; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContainerInitializer; +import javax.servlet.ServletContext; +import javax.servlet.ServletContextAttributeListener; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.ServletRequest; +import javax.servlet.ServletRequestAttributeListener; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; +import javax.servlet.ServletSecurityElement; +import javax.servlet.descriptor.JspConfigDescriptor; +import javax.servlet.http.HttpSessionAttributeListener; +import javax.servlet.http.HttpSessionListener; + +import org.apache.catalina.Authenticator; +import org.apache.catalina.Container; +import org.apache.catalina.ContainerListener; +import org.apache.catalina.Context; +import org.apache.catalina.Globals; +import org.apache.catalina.Host; +import org.apache.catalina.InstanceListener; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.LifecycleState; +import org.apache.catalina.Loader; +import org.apache.catalina.Manager; +import org.apache.catalina.Pipeline; +import org.apache.catalina.Valve; +import org.apache.catalina.Wrapper; +import org.apache.catalina.deploy.ApplicationParameter; +import org.apache.catalina.deploy.ErrorPage; +import org.apache.catalina.deploy.FilterDef; +import org.apache.catalina.deploy.FilterMap; +import org.apache.catalina.deploy.Injectable; +import org.apache.catalina.deploy.InjectionTarget; +import org.apache.catalina.deploy.LoginConfig; +import org.apache.catalina.deploy.MessageDestination; +import org.apache.catalina.deploy.MessageDestinationRef; +import org.apache.catalina.deploy.NamingResources; +import org.apache.catalina.deploy.SecurityCollection; +import org.apache.catalina.deploy.SecurityConstraint; +import org.apache.catalina.loader.WebappLoader; +import org.apache.catalina.session.StandardManager; +import org.apache.catalina.startup.TldConfig; +import org.apache.catalina.util.CharsetMapper; +import org.apache.catalina.util.ContextName; +import org.apache.catalina.util.ExtensionValidator; +import org.apache.catalina.util.RequestUtil; +import org.apache.catalina.util.URLEncoder; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.naming.ContextBindings; +import org.apache.naming.resources.BaseDirContext; +import org.apache.naming.resources.DirContextURLStreamHandler; +import org.apache.naming.resources.FileDirContext; +import org.apache.naming.resources.ProxyDirContext; +import org.apache.naming.resources.WARDirContext; +import org.apache.tomcat.InstanceManager; +import org.apache.tomcat.JarScanner; +import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.modeler.Registry; +import org.apache.tomcat.util.scan.StandardJarScanner; + +/** + * Standard implementation of the Context interface. Each + * child container must be a Wrapper implementation to process the + * requests directed to a particular servlet. + * + * @author Craig R. McClanahan + * @author Remy Maucherat + * @version $Id$ + */ + +public class StandardContext extends ContainerBase + implements Context, NotificationEmitter { + + private static final Log log = LogFactory.getLog(StandardContext.class); + + + // ----------------------------------------------------------- Constructors + + + /** + * Create a new StandardContext component with the default basic Valve. + */ + public StandardContext() { + + super(); + pipeline.setBasic(new StandardContextValve()); + broadcaster = new NotificationBroadcasterSupport(); + // Set defaults + if (!Globals.STRICT_SERVLET_COMPLIANCE) { + // Strict servlet compliance requires all extension mapped servlets + // to be checked against welcome files + resourceOnlyServlets.add("jsp"); + } + } + + + // ----------------------------------------------------- Class Variables + + + /** + * The descriptive information string for this implementation. + */ + private static final String info = + "org.apache.catalina.core.StandardContext/1.0"; + + + /** + * Array containing the safe characters set. + */ + protected static URLEncoder urlEncoder; + + + /** + * GMT timezone - all HTTP dates are on GMT + */ + static { + urlEncoder = new URLEncoder(); + urlEncoder.addSafeCharacter('~'); + urlEncoder.addSafeCharacter('-'); + urlEncoder.addSafeCharacter('_'); + urlEncoder.addSafeCharacter('.'); + urlEncoder.addSafeCharacter('*'); + urlEncoder.addSafeCharacter('/'); + } + + + // ----------------------------------------------------- Instance Variables + + + /** + * Allow multipart/form-data requests to be parsed even when the + * target servlet doesn't specify @MultipartConfig or have a + * <multipart-config> element. + */ + protected boolean allowCasualMultipartParsing = false; + + /** + * Control whether remaining request data will be read + * (swallowed) even if the request violates a data size constraint. + */ + private boolean swallowAbortedUploads = true; + + /** + * The alternate deployment descriptor name. + */ + private String altDDName = null; + + + /** + * Lifecycle provider. + */ + private InstanceManager instanceManager = null; + + + /** + * Associated host name. + */ + private String hostName; + + + /** + * The antiJARLocking flag for this Context. + */ + private boolean antiJARLocking = false; + + + /** + * The antiResourceLocking flag for this Context. + */ + private boolean antiResourceLocking = false; + + + /** + * The set of application listener class names configured for this + * application, in the order they were encountered in the web.xml file. + */ + private String applicationListeners[] = new String[0]; + + private final Object applicationListenersLock = new Object(); + + + /** + * The set of instantiated application event listener objects. + */ + private Object applicationEventListenersObjects[] = + new Object[0]; + + + /** + * The set of instantiated application lifecycle listener objects. + */ + private Object applicationLifecycleListenersObjects[] = + new Object[0]; + + + /** + * The ordered set of ServletContainerInitializers for this web application. + */ + private Map>> initializers = + new LinkedHashMap>>(); + + + /** + * The set of application parameters defined for this application. + */ + private ApplicationParameter applicationParameters[] = + new ApplicationParameter[0]; + + private final Object applicationParametersLock = new Object(); + + + /** + * The broadcaster that sends j2ee notifications. + */ + private NotificationBroadcasterSupport broadcaster = null; + + /** + * The Locale to character set mapper for this application. + */ + private CharsetMapper charsetMapper = null; + + + /** + * The Java class name of the CharsetMapper class to be created. + */ + private String charsetMapperClass = + "org.apache.catalina.util.CharsetMapper"; + + + /** + * The URL of the XML descriptor for this context. + */ + private URL configFile = null; + + + /** + * The "correctly configured" flag for this Context. + */ + private boolean configured = false; + + + /** + * The security constraints for this web application. + */ + private volatile SecurityConstraint constraints[] = + new SecurityConstraint[0]; + + private final Object constraintsLock = new Object(); + + + /** + * The ServletContext implementation associated with this Context. + */ + protected ApplicationContext context = null; + + + /** + * Compiler classpath to use. + */ + private String compilerClasspath = null; + + + /** + * Should we attempt to use cookies for session id communication? + */ + private boolean cookies = true; + + + /** + * Should we allow the ServletContext.getContext() method + * to access the context of other web applications in this server? + */ + private boolean crossContext = false; + + + /** + * Encoded path. + */ + private String encodedPath = null; + + + /** + * Unencoded path for this web application. + */ + private String path = null; + + + /** + * The "follow standard delegation model" flag that will be used to + * configure our ClassLoader. + */ + private boolean delegate = false; + + + /** + * The display name of this web application. + */ + private String displayName = null; + + + /** + * Override the default context xml location. + */ + private String defaultContextXml; + + + /** + * Override the default web xml location. + */ + private String defaultWebXml; + + + /** + * The distributable flag for this web application. + */ + private boolean distributable = false; + + + /** + * The document root for this web application. + */ + private String docBase = null; + + + /** + * The exception pages for this web application, keyed by fully qualified + * class name of the Java exception. + */ + private HashMap exceptionPages = + new HashMap(); + + + /** + * The set of filter configurations (and associated filter instances) we + * have initialized, keyed by filter name. + */ + private HashMap filterConfigs = + new HashMap(); + + + /** + * The set of filter definitions for this application, keyed by + * filter name. + */ + private HashMap filterDefs = + new HashMap(); + + + /** + * The set of filter mappings for this application, in the order + * they were defined in the deployment descriptor with additional mappings + * added via the {@link ServletContext} possibly both before and after those + * defined in the deployment descriptor. + */ + private final ContextFilterMaps filterMaps = new ContextFilterMaps(); + + /** + * Ignore annotations. + */ + private boolean ignoreAnnotations = false; + + + /** + * The set of classnames of InstanceListeners that will be added + * to each newly created Wrapper by createWrapper(). + */ + private String instanceListeners[] = new String[0]; + + private final Object instanceListenersLock = new Object(); + + + /** + * The login configuration descriptor for this web application. + */ + private LoginConfig loginConfig = null; + + + /** + * The mapper associated with this context. + */ + private org.apache.tomcat.util.http.mapper.Mapper mapper = + new org.apache.tomcat.util.http.mapper.Mapper(); + + + /** + * The naming context listener for this web application. + */ + private NamingContextListener namingContextListener = null; + + + /** + * The naming resources for this web application. + */ + private NamingResources namingResources = null; + + /** + * The message destinations for this web application. + */ + private HashMap messageDestinations = + new HashMap(); + + + /** + * The MIME mappings for this web application, keyed by extension. + */ + private HashMap mimeMappings = + new HashMap(); + + + /** + * Special case: error page for status 200. + */ + private ErrorPage okErrorPage = null; + + + /** + * The context initialization parameters for this web application, + * keyed by name. + */ + private HashMap parameters = new HashMap(); + + + /** + * The request processing pause flag (while reloading occurs) + */ + private boolean paused = false; + + + /** + * The public identifier of the DTD for the web application deployment + * descriptor version we are currently parsing. This is used to support + * relaxed validation rules when processing version 2.2 web.xml files. + */ + private String publicId = null; + + + /** + * The reloadable flag for this web application. + */ + private boolean reloadable = false; + + + /** + * Unpack WAR property. + */ + private boolean unpackWAR = true; + + + /** + * The default context override flag for this web application. + */ + private boolean override = false; + + + /** + * The original document root for this web application. + */ + private String originalDocBase = null; + + + /** + * The privileged flag for this web application. + */ + private boolean privileged = false; + + + /** + * Should the next call to addWelcomeFile() cause replacement + * of any existing welcome files? This will be set before processing the + * web application's deployment descriptor, so that application specified + * choices replace, rather than append to, those defined + * in the global descriptor. + */ + private boolean replaceWelcomeFiles = false; + + + /** + * The security role mappings for this application, keyed by role + * name (as used within the application). + */ + private HashMap roleMappings = + new HashMap(); + + + /** + * The security roles for this application, keyed by role name. + */ + private String securityRoles[] = new String[0]; + + private final Object securityRolesLock = new Object(); + + + /** + * The servlet mappings for this web application, keyed by + * matching pattern. + */ + private HashMap servletMappings = + new HashMap(); + + private final Object servletMappingsLock = new Object(); + + + /** + * The session timeout (in minutes) for this web application. + */ + private int sessionTimeout = 30; + + /** + * The notification sequence number. + */ + private AtomicLong sequenceNumber = new AtomicLong(0); + + /** + * The status code error pages for this web application, keyed by + * HTTP status code (as an Integer). + */ + private HashMap statusPages = + new HashMap(); + + + /** + * Set flag to true to cause the system.out and system.err to be redirected + * to the logger when executing a servlet. + */ + private boolean swallowOutput = false; + + + /** + * Amount of ms that the container will wait for servlets to unload. + */ + private long unloadDelay = 2000; + + + /** + * The watched resources for this application. + */ + private String watchedResources[] = new String[0]; + + private final Object watchedResourcesLock = new Object(); + + + /** + * The welcome files for this application. + */ + private String welcomeFiles[] = new String[0]; + + private final Object welcomeFilesLock = new Object(); + + + /** + * The set of classnames of LifecycleListeners that will be added + * to each newly created Wrapper by createWrapper(). + */ + private String wrapperLifecycles[] = new String[0]; + + private final Object wrapperLifecyclesLock = new Object(); + + /** + * The set of classnames of ContainerListeners that will be added + * to each newly created Wrapper by createWrapper(). + */ + private String wrapperListeners[] = new String[0]; + + private final Object wrapperListenersLock = new Object(); + + /** + * The pathname to the work directory for this context (relative to + * the server's home if not absolute). + */ + private String workDir = null; + + + /** + * Java class name of the Wrapper class implementation we use. + */ + private String wrapperClassName = StandardWrapper.class.getName(); + private Class wrapperClass = null; + + + /** + * JNDI use flag. + */ + private boolean useNaming = true; + + + /** + * Filesystem based flag. + */ + private boolean filesystemBased = false; + + + /** + * Name of the associated naming context. + */ + private String namingContextName = null; + + + /** + * Caching allowed flag. + */ + private boolean cachingAllowed = true; + + + /** + * Allow linking. + */ + protected boolean allowLinking = false; + + + /** + * Cache max size in KB. + */ + protected int cacheMaxSize = 10240; // 10 MB + + + /** + * Cache object max size in KB. + */ + protected int cacheObjectMaxSize = 512; // 512K + + + /** + * Cache TTL in ms. + */ + protected int cacheTTL = 5000; + + + /** + * List of resource aliases. + */ + private String aliases = null; + + + /** + * Non proxied resources. + */ + private DirContext webappResources = null; + + private long startupTime; + private long startTime; + private long tldScanTime; + + /** + * Name of the engine. If null, the domain is used. + */ + private String j2EEApplication="none"; + private String j2EEServer="none"; + + + /** + * Attribute value used to turn on/off XML validation + */ + private boolean webXmlValidation = Globals.STRICT_SERVLET_COMPLIANCE; + + + /** + * Attribute value used to turn on/off XML namespace validation + */ + private boolean webXmlNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE; + + /** + * Attribute value used to turn on/off TLD processing + */ + private boolean processTlds = true; + + /** + * Attribute value used to turn on/off XML validation + */ + private boolean tldValidation = Globals.STRICT_SERVLET_COMPLIANCE; + + + /** + * Attribute value used to turn on/off TLD XML namespace validation + */ + private boolean tldNamespaceAware = Globals.STRICT_SERVLET_COMPLIANCE; + + + /** + * Should we save the configuration. + */ + private boolean saveConfig = true; + + + /** + * The name to use for session cookies. null indicates that + * the name is controlled by the application. + */ + private String sessionCookieName; + + + /** + * The flag that indicates that session cookies should use HttpOnly + */ + private boolean useHttpOnly = true; + + + /** + * The domain to use for session cookies. null indicates that + * the domain is controlled by the application. + */ + private String sessionCookieDomain; + + + /** + * The path to use for session cookies. null indicates that + * the path is controlled by the application. + */ + private String sessionCookiePath; + + + /** + * Is a / added to the end of the session cookie path to ensure browsers, + * particularly IE, don't send a session cookie for context /foo with + * requests intended for context /foobar. + */ + private boolean sessionCookiePathUsesTrailingSlash = true; + + + /** + * The Jar scanner to use to search for Jars that might contain + * configuration information such as TLDs or web-fragment.xml files. + */ + private JarScanner jarScanner = null; + + /** + * Should Tomcat attempt to null out any static or final fields from loaded + * classes when a web application is stopped as a work around for apparent + * garbage collection bugs and application coding errors? There have been + * some issues reported with log4j when this option is true. Applications + * without memory leaks using recent JVMs should operate correctly with this + * option set to false. If not specified, the default value of + * false will be used. + */ + private boolean clearReferencesStatic = false; + + /** + * Should Tomcat attempt to terminate threads that have been started by the + * web application? Stopping threads is performed via the deprecated (for + * good reason) Thread.stop() method and is likely to result in + * instability. As such, enabling this should be viewed as an option of last + * resort in a development environment and is not recommended in a + * production environment. If not specified, the default value of + * false will be used. + */ + private boolean clearReferencesStopThreads = false; + + /** + * Should Tomcat attempt to terminate any {@link java.util.TimerThread}s + * that have been started by the web application? If not specified, the + * default value of false will be used. + */ + private boolean clearReferencesStopTimerThreads = false; + + /** + * If an HttpClient keep-alive timer thread has been started by this web + * application and is still running, should Tomcat change the context class + * loader from the current {@link WebappClassLoader} to + * {@link WebappClassLoader#parent} to prevent a memory leak? Note that the + * keep-alive timer thread will stop on its own once the keep-alives all + * expire however, on a busy system that might not happen for some time. + */ + private boolean clearReferencesHttpClientKeepAliveThread = true; + + /** + * Should Tomcat renew the threads of the thread pool when the application + * is stopped to avoid memory leaks because of uncleaned ThreadLocal + * variables. This also requires that the threadRenewalDelay property of the + * StandardThreadExecutor of ThreadPoolExecutor be set to a positive value. + */ + private boolean renewThreadsWhenStoppingContext = true; + + /** + * Should the effective web.xml be logged when the context starts? + */ + private boolean logEffectiveWebXml = false; + + private int effectiveMajorVersion = 3; + + private int effectiveMinorVersion = 0; + + private JspConfigDescriptor jspConfigDescriptor = + new ApplicationJspConfigDescriptor(); + + private Set resourceOnlyServlets = new HashSet(); + + private String webappVersion = ""; + + private boolean addWebinfClassesResources = false; + + private boolean fireRequestListenersOnForwards = false; + + /** + * Servlets created via {@link ApplicationContext#createServlet(Class)} for + * tracking purposes. + */ + private Set createdServlets = new HashSet(); + + private boolean preemptiveAuthentication = false; + + private boolean sendRedirectBody = false; + + + // ----------------------------------------------------- Context Properties + + @Override + public boolean getSendRedirectBody() { + return sendRedirectBody; + } + + + @Override + public void setSendRedirectBody(boolean sendRedirectBody) { + this.sendRedirectBody = sendRedirectBody; + } + + + @Override + public boolean getPreemptiveAuthentication() { + return preemptiveAuthentication; + } + + + @Override + public void setPreemptiveAuthentication(boolean preemptiveAuthentication) { + this.preemptiveAuthentication = preemptiveAuthentication; + } + + + @Override + public void setFireRequestListenersOnForwards(boolean enable) { + fireRequestListenersOnForwards = enable; + } + + + @Override + public boolean getFireRequestListenersOnForwards() { + return fireRequestListenersOnForwards; + } + + + public void setAddWebinfClassesResources( + boolean addWebinfClassesResources) { + this.addWebinfClassesResources = addWebinfClassesResources; + } + + + public boolean getAddWebinfClassesResources() { + return addWebinfClassesResources; + } + + + @Override + public void setWebappVersion(String webappVersion) { + if (null == webappVersion) { + this.webappVersion = ""; + } else { + this.webappVersion = webappVersion; + } + } + + + @Override + public String getWebappVersion() { + return webappVersion; + } + + + @Override + public String getBaseName() { + return new ContextName(path, webappVersion).getBaseName(); + } + + + @Override + public String getResourceOnlyServlets() { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String servletName : resourceOnlyServlets) { + if (!first) { + result.append(','); + } + result.append(servletName); + } + return result.toString(); + } + + + @Override + public void setResourceOnlyServlets(String resourceOnlyServlets) { + this.resourceOnlyServlets.clear(); + if (resourceOnlyServlets == null) { + return; + } + for (String servletName : resourceOnlyServlets.split(",")) { + servletName = servletName.trim(); + if (servletName.length()>0) { + this.resourceOnlyServlets.add(servletName); + } + } + } + + + @Override + public boolean isResourceOnlyServlet(String servletName) { + return resourceOnlyServlets.contains(servletName); + } + + + @Override + public int getEffectiveMajorVersion() { + return effectiveMajorVersion; + } + + @Override + public void setEffectiveMajorVersion(int effectiveMajorVersion) { + this.effectiveMajorVersion = effectiveMajorVersion; + } + + @Override + public int getEffectiveMinorVersion() { + return effectiveMinorVersion; + } + + @Override + public void setEffectiveMinorVersion(int effectiveMinorVersion) { + this.effectiveMinorVersion = effectiveMinorVersion; + } + + @Override + public void setLogEffectiveWebXml(boolean logEffectiveWebXml) { + this.logEffectiveWebXml = logEffectiveWebXml; + } + + @Override + public boolean getLogEffectiveWebXml() { + return logEffectiveWebXml; + } + + @Override + public Authenticator getAuthenticator() { + if (this instanceof Authenticator) + return (Authenticator) this; + + Pipeline pipeline = getPipeline(); + if (pipeline != null) { + Valve basic = pipeline.getBasic(); + if ((basic != null) && (basic instanceof Authenticator)) + return (Authenticator) basic; + Valve valves[] = pipeline.getValves(); + for (int i = 0; i < valves.length; i++) { + if (valves[i] instanceof Authenticator) + return (Authenticator) valves[i]; + } + } + return null; + } + + @Override + public JarScanner getJarScanner() { + if (jarScanner == null) { + jarScanner = new StandardJarScanner(); + } + return jarScanner; + } + + + @Override + public void setJarScanner(JarScanner jarScanner) { + this.jarScanner = jarScanner; + } + + + public InstanceManager getInstanceManager() { + return instanceManager; + } + + + public void setInstanceManager(InstanceManager instanceManager) { + this.instanceManager = instanceManager; + } + + + @Override + public String getEncodedPath() { + return encodedPath; + } + + + /** + * Is caching allowed ? + */ + public boolean isCachingAllowed() { + return cachingAllowed; + } + + + /** + * Set caching allowed flag. + */ + public void setCachingAllowed(boolean cachingAllowed) { + this.cachingAllowed = cachingAllowed; + } + + + /** + * Set allow linking. + */ + public void setAllowLinking(boolean allowLinking) { + this.allowLinking = allowLinking; + } + + + /** + * Is linking allowed. + */ + public boolean isAllowLinking() { + return allowLinking; + } + + /** + * Set to true to allow requests mapped to servlets that + * do not explicitly declare @MultipartConfig or have + * <multipart-config> specified in web.xml to parse + * multipart/form-data requests. + * + * @param allowCasualMultipartParsing true to allow such + * casual parsing, false otherwise. + */ + @Override + public void setAllowCasualMultipartParsing( + boolean allowCasualMultipartParsing) { + this.allowCasualMultipartParsing = allowCasualMultipartParsing; + } + + /** + * Returns true if requests mapped to servlets without + * "multipart config" to parse multipart/form-data requests anyway. + * + * @return true if requests mapped to servlets without + * "multipart config" to parse multipart/form-data requests, + * false otherwise. + */ + @Override + public boolean getAllowCasualMultipartParsing() { + return this.allowCasualMultipartParsing; + } + + /** + * Set to false to disable request data swallowing + * after an upload was aborted due to size constraints. + * + * @param swallowAbortedUploads false to disable + * swallowing, true otherwise (default). + */ + @Override + public void setSwallowAbortedUploads(boolean swallowAbortedUploads) { + this.swallowAbortedUploads = swallowAbortedUploads; + } + + /** + * Returns true if remaining request data will be read + * (swallowed) even the request violates a data size constraint. + * + * @return true if data will be swallowed (default), + * false otherwise. + */ + @Override + public boolean getSwallowAbortedUploads() { + return this.swallowAbortedUploads; + } + + /** + * Set cache TTL. + */ + public void setCacheTTL(int cacheTTL) { + this.cacheTTL = cacheTTL; + } + + + /** + * Get cache TTL. + */ + public int getCacheTTL() { + return cacheTTL; + } + + + /** + * Return the maximum size of the cache in KB. + */ + public int getCacheMaxSize() { + return cacheMaxSize; + } + + + /** + * Set the maximum size of the cache in KB. + */ + public void setCacheMaxSize(int cacheMaxSize) { + this.cacheMaxSize = cacheMaxSize; + } + + + /** + * Return the maximum size of objects to be cached in KB. + */ + public int getCacheObjectMaxSize() { + return cacheObjectMaxSize; + } + + + /** + * Set the maximum size of objects to be placed the cache in KB. + */ + public void setCacheObjectMaxSize(int cacheObjectMaxSize) { + this.cacheObjectMaxSize = cacheObjectMaxSize; + } + + + /** + * Return the list of resource aliases. + */ + public String getAliases() { + return this.aliases; + } + + + /** + * Add a URL for a JAR that contains static resources in a + * META-INF/resources directory that should be included in the static + * resources for this context. + */ + @Override + public void addResourceJarUrl(URL url) { + if (webappResources instanceof BaseDirContext) { + ((BaseDirContext) webappResources).addResourcesJar(url); + } else { + log.error(sm.getString("standardContext.noResourceJar", url, + getName())); + } + } + + + /** + * Set the current alias configuration. The list of aliases should be of the + * form "/aliasPath1=docBase1,/aliasPath2=docBase2" where aliasPathN must + * include a leading '/' and docBaseN must be an absolute path to either a + * .war file or a directory. + */ + public void setAliases(String aliases) { + this.aliases = aliases; + } + + + /** + * Add a ServletContainerInitializer instance to this web application. + * + * @param sci The instance to add + * @param classes The classes in which the initializer expressed an + * interest + */ + @Override + public void addServletContainerInitializer( + ServletContainerInitializer sci, Set> classes) { + initializers.put(sci, classes); + } + + + /** + * Return the "follow standard delegation model" flag used to configure + * our ClassLoader. + */ + public boolean getDelegate() { + + return (this.delegate); + + } + + + /** + * Set the "follow standard delegation model" flag used to configure + * our ClassLoader. + * + * @param delegate The new flag + */ + public void setDelegate(boolean delegate) { + + boolean oldDelegate = this.delegate; + this.delegate = delegate; + support.firePropertyChange("delegate", oldDelegate, + this.delegate); + + } + + + /** + * Returns true if the internal naming support is used. + */ + public boolean isUseNaming() { + + return (useNaming); + + } + + + /** + * Enables or disables naming. + */ + public void setUseNaming(boolean useNaming) { + this.useNaming = useNaming; + } + + + /** + * Returns true if the resources associated with this context are + * filesystem based. + */ + public boolean isFilesystemBased() { + + return (filesystemBased); + + } + + + /** + * Return the set of initialized application event listener objects, + * in the order they were specified in the web application deployment + * descriptor, for this application. + * + * @exception IllegalStateException if this method is called before + * this application has started, or after it has been stopped + */ + @Override + public Object[] getApplicationEventListeners() { + return (applicationEventListenersObjects); + } + + + /** + * Store the set of initialized application event listener objects, + * in the order they were specified in the web application deployment + * descriptor, for this application. + * + * @param listeners The set of instantiated listener objects. + */ + @Override + public void setApplicationEventListeners(Object listeners[]) { + applicationEventListenersObjects = listeners; + } + + + /** + * Add a listener to the end of the list of initialized application event + * listeners. + */ + public void addApplicationEventListener(Object listener) { + int len = applicationEventListenersObjects.length; + Object[] newListeners = Arrays.copyOf(applicationEventListenersObjects, + len + 1); + newListeners[len] = listener; + applicationEventListenersObjects = newListeners; + } + + + /** + * Return the set of initialized application lifecycle listener objects, + * in the order they were specified in the web application deployment + * descriptor, for this application. + * + * @exception IllegalStateException if this method is called before + * this application has started, or after it has been stopped + */ + @Override + public Object[] getApplicationLifecycleListeners() { + return (applicationLifecycleListenersObjects); + } + + + /** + * Store the set of initialized application lifecycle listener objects, + * in the order they were specified in the web application deployment + * descriptor, for this application. + * + * @param listeners The set of instantiated listener objects. + */ + @Override + public void setApplicationLifecycleListeners(Object listeners[]) { + applicationLifecycleListenersObjects = listeners; + } + + + /** + * Add a listener to the end of the list of initialized application + * lifecycle listeners. + */ + public void addApplicationLifecycleListener(Object listener) { + int len = applicationLifecycleListenersObjects.length; + Object[] newListeners = Arrays.copyOf( + applicationLifecycleListenersObjects, len + 1); + newListeners[len] = listener; + applicationLifecycleListenersObjects = newListeners; + } + + + /** + * Return the antiJARLocking flag for this Context. + */ + public boolean getAntiJARLocking() { + + return (this.antiJARLocking); + + } + + + /** + * Return the antiResourceLocking flag for this Context. + */ + public boolean getAntiResourceLocking() { + + return (this.antiResourceLocking); + + } + + + /** + * Set the antiJARLocking feature for this Context. + * + * @param antiJARLocking The new flag value + */ + public void setAntiJARLocking(boolean antiJARLocking) { + + boolean oldAntiJARLocking = this.antiJARLocking; + this.antiJARLocking = antiJARLocking; + support.firePropertyChange("antiJARLocking", + oldAntiJARLocking, + this.antiJARLocking); + + } + + + /** + * Set the antiResourceLocking feature for this Context. + * + * @param antiResourceLocking The new flag value + */ + public void setAntiResourceLocking(boolean antiResourceLocking) { + + boolean oldAntiResourceLocking = this.antiResourceLocking; + this.antiResourceLocking = antiResourceLocking; + support.firePropertyChange("antiResourceLocking", + oldAntiResourceLocking, + this.antiResourceLocking); + + } + + + /** + * Return the application available flag for this Context. + */ + @Override + public boolean getAvailable() { + + // TODO Remove this method entirely + return getState().isAvailable(); + + } + + + /** + * Return the Locale to character set mapper for this Context. + */ + @Override + public CharsetMapper getCharsetMapper() { + + // Create a mapper the first time it is requested + if (this.charsetMapper == null) { + try { + Class clazz = Class.forName(charsetMapperClass); + this.charsetMapper = (CharsetMapper) clazz.newInstance(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + this.charsetMapper = new CharsetMapper(); + } + } + + return (this.charsetMapper); + + } + + + /** + * Set the Locale to character set mapper for this Context. + * + * @param mapper The new mapper + */ + @Override + public void setCharsetMapper(CharsetMapper mapper) { + + CharsetMapper oldCharsetMapper = this.charsetMapper; + this.charsetMapper = mapper; + if( mapper != null ) + this.charsetMapperClass= mapper.getClass().getName(); + support.firePropertyChange("charsetMapper", oldCharsetMapper, + this.charsetMapper); + + } + + /** + * Return the URL of the XML descriptor for this context. + */ + @Override + public URL getConfigFile() { + + return (this.configFile); + + } + + + /** + * Set the URL of the XML descriptor for this context. + * + * @param configFile The URL of the XML descriptor for this context. + */ + @Override + public void setConfigFile(URL configFile) { + + this.configFile = configFile; + } + + + /** + * Return the "correctly configured" flag for this Context. + */ + @Override + public boolean getConfigured() { + + return (this.configured); + + } + + + /** + * Set the "correctly configured" flag for this Context. This can be + * set to false by startup listeners that detect a fatal configuration + * error to avoid the application from being made available. + * + * @param configured The new correctly configured flag + */ + @Override + public void setConfigured(boolean configured) { + + boolean oldConfigured = this.configured; + this.configured = configured; + support.firePropertyChange("configured", + oldConfigured, + this.configured); + + } + + + /** + * Return the "use cookies for session ids" flag. + */ + @Override + public boolean getCookies() { + + return (this.cookies); + + } + + + /** + * Set the "use cookies for session ids" flag. + * + * @param cookies The new flag + */ + @Override + public void setCookies(boolean cookies) { + + boolean oldCookies = this.cookies; + this.cookies = cookies; + support.firePropertyChange("cookies", + oldCookies, + this.cookies); + + } + + + /** + * Gets the name to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @return The value of the default session cookie name or null if not + * specified + */ + @Override + public String getSessionCookieName() { + return sessionCookieName; + } + + + /** + * Sets the name to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @param sessionCookieName The name to use + */ + @Override + public void setSessionCookieName(String sessionCookieName) { + String oldSessionCookieName = this.sessionCookieName; + this.sessionCookieName = sessionCookieName; + support.firePropertyChange("sessionCookieName", + oldSessionCookieName, sessionCookieName); + } + + + /** + * Gets the value of the use HttpOnly cookies for session cookies flag. + * + * @return true if the HttpOnly flag should be set on session + * cookies + */ + @Override + public boolean getUseHttpOnly() { + return useHttpOnly; + } + + + /** + * Sets the use HttpOnly cookies for session cookies flag. + * + * @param useHttpOnly Set to true to use HttpOnly cookies + * for session cookies + */ + @Override + public void setUseHttpOnly(boolean useHttpOnly) { + boolean oldUseHttpOnly = this.useHttpOnly; + this.useHttpOnly = useHttpOnly; + support.firePropertyChange("useHttpOnly", + oldUseHttpOnly, + this.useHttpOnly); + } + + + /** + * Gets the domain to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @return The value of the default session cookie domain or null if not + * specified + */ + @Override + public String getSessionCookieDomain() { + return sessionCookieDomain; + } + + + /** + * Sets the domain to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @param sessionCookieDomain The domain to use + */ + @Override + public void setSessionCookieDomain(String sessionCookieDomain) { + String oldSessionCookieDomain = this.sessionCookieDomain; + this.sessionCookieDomain = sessionCookieDomain; + support.firePropertyChange("sessionCookieDomain", + oldSessionCookieDomain, sessionCookieDomain); + } + + + /** + * Gets the path to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @return The value of the default session cookie path or null if not + * specified + */ + @Override + public String getSessionCookiePath() { + return sessionCookiePath; + } + + + /** + * Sets the path to use for session cookies. Overrides any setting that + * may be specified by the application. + * + * @param sessionCookiePath The path to use + */ + @Override + public void setSessionCookiePath(String sessionCookiePath) { + String oldSessionCookiePath = this.sessionCookiePath; + this.sessionCookiePath = sessionCookiePath; + support.firePropertyChange("sessionCookiePath", + oldSessionCookiePath, sessionCookiePath); + } + + + @Override + public boolean getSessionCookiePathUsesTrailingSlash() { + return sessionCookiePathUsesTrailingSlash; + } + + + @Override + public void setSessionCookiePathUsesTrailingSlash( + boolean sessionCookiePathUsesTrailingSlash) { + this.sessionCookiePathUsesTrailingSlash = + sessionCookiePathUsesTrailingSlash; + } + + + /** + * Return the "allow crossing servlet contexts" flag. + */ + @Override + public boolean getCrossContext() { + + return (this.crossContext); + + } + + + /** + * Set the "allow crossing servlet contexts" flag. + * + * @param crossContext The new cross contexts flag + */ + @Override + public void setCrossContext(boolean crossContext) { + + boolean oldCrossContext = this.crossContext; + this.crossContext = crossContext; + support.firePropertyChange("crossContext", + oldCrossContext, + this.crossContext); + + } + + public String getDefaultContextXml() { + return defaultContextXml; + } + + /** + * Set the location of the default context xml that will be used. + * If not absolute, it'll be made relative to the engine's base dir + * ( which defaults to catalina.base system property ). + * + * @param defaultContextXml The default web xml + */ + public void setDefaultContextXml(String defaultContextXml) { + this.defaultContextXml = defaultContextXml; + } + + public String getDefaultWebXml() { + return defaultWebXml; + } + + /** + * Set the location of the default web xml that will be used. + * If not absolute, it'll be made relative to the engine's base dir + * ( which defaults to catalina.base system property ). + * + * @param defaultWebXml The default web xml + */ + public void setDefaultWebXml(String defaultWebXml) { + this.defaultWebXml = defaultWebXml; + } + + /** + * Gets the time (in milliseconds) it took to start this context. + * + * @return Time (in milliseconds) it took to start this context. + */ + public long getStartupTime() { + return startupTime; + } + + public void setStartupTime(long startupTime) { + this.startupTime = startupTime; + } + + public long getTldScanTime() { + return tldScanTime; + } + + public void setTldScanTime(long tldScanTime) { + this.tldScanTime = tldScanTime; + } + + /** + * Return the display name of this web application. + */ + @Override + public String getDisplayName() { + + return (this.displayName); + + } + + + /** + * Return the alternate Deployment Descriptor name. + */ + @Override + public String getAltDDName(){ + return altDDName; + } + + + /** + * Set an alternate Deployment Descriptor name. + */ + @Override + public void setAltDDName(String altDDName) { + this.altDDName = altDDName; + if (context != null) { + context.setAttribute(Globals.ALT_DD_ATTR,altDDName); + } + } + + + /** + * Return the compiler classpath. + */ + public String getCompilerClasspath(){ + return compilerClasspath; + } + + + /** + * Set the compiler classpath. + */ + public void setCompilerClasspath(String compilerClasspath) { + this.compilerClasspath = compilerClasspath; + } + + + /** + * Set the display name of this web application. + * + * @param displayName The new display name + */ + @Override + public void setDisplayName(String displayName) { + + String oldDisplayName = this.displayName; + this.displayName = displayName; + support.firePropertyChange("displayName", oldDisplayName, + this.displayName); + } + + + /** + * Return the distributable flag for this web application. + */ + @Override + public boolean getDistributable() { + + return (this.distributable); + + } + + /** + * Set the distributable flag for this web application. + * + * @param distributable The new distributable flag + */ + @Override + public void setDistributable(boolean distributable) { + boolean oldDistributable = this.distributable; + this.distributable = distributable; + support.firePropertyChange("distributable", + oldDistributable, + this.distributable); + + // Bugzilla 32866 + if(getManager() != null) { + if(log.isDebugEnabled()) { + log.debug("Propagating distributable=" + distributable + + " to manager"); + } + getManager().setDistributable(distributable); + } + } + + + /** + * Return the document root for this Context. This can be an absolute + * pathname, a relative pathname, or a URL. + */ + @Override + public String getDocBase() { + + return (this.docBase); + + } + + + /** + * Set the document root for this Context. This can be an absolute + * pathname, a relative pathname, or a URL. + * + * @param docBase The new document root + */ + @Override + public void setDocBase(String docBase) { + + this.docBase = docBase; + + } + + /** + * Return descriptive information about this Container implementation and + * the corresponding version number, in the format + * <description>/<version>. + */ + @Override + public String getInfo() { + + return (info); + + } + + public String getJ2EEApplication() { + return j2EEApplication; + } + + public void setJ2EEApplication(String j2EEApplication) { + this.j2EEApplication = j2EEApplication; + } + + public String getJ2EEServer() { + return j2EEServer; + } + + public void setJ2EEServer(String j2EEServer) { + this.j2EEServer = j2EEServer; + } + + + /** + * Set the Loader with which this Context is associated. + * + * @param loader The newly associated loader + */ + @Override + public synchronized void setLoader(Loader loader) { + + super.setLoader(loader); + + } + + + /** + * Return the boolean on the annotations parsing. + */ + @Override + public boolean getIgnoreAnnotations() { + return this.ignoreAnnotations; + } + + + /** + * Set the boolean on the annotations parsing for this web + * application. + * + * @param ignoreAnnotations The boolean on the annotations parsing + */ + @Override + public void setIgnoreAnnotations(boolean ignoreAnnotations) { + boolean oldIgnoreAnnotations = this.ignoreAnnotations; + this.ignoreAnnotations = ignoreAnnotations; + support.firePropertyChange("ignoreAnnotations", oldIgnoreAnnotations, + this.ignoreAnnotations); + } + + + /** + * Return the login configuration descriptor for this web application. + */ + @Override + public LoginConfig getLoginConfig() { + + return (this.loginConfig); + + } + + + /** + * Set the login configuration descriptor for this web application. + * + * @param config The new login configuration + */ + @Override + public void setLoginConfig(LoginConfig config) { + + // Validate the incoming property value + if (config == null) + throw new IllegalArgumentException + (sm.getString("standardContext.loginConfig.required")); + String loginPage = config.getLoginPage(); + if ((loginPage != null) && !loginPage.startsWith("/")) { + if (isServlet22()) { + if(log.isDebugEnabled()) + log.debug(sm.getString("standardContext.loginConfig.loginWarning", + loginPage)); + config.setLoginPage("/" + loginPage); + } else { + throw new IllegalArgumentException + (sm.getString("standardContext.loginConfig.loginPage", + loginPage)); + } + } + String errorPage = config.getErrorPage(); + if ((errorPage != null) && !errorPage.startsWith("/")) { + if (isServlet22()) { + if(log.isDebugEnabled()) + log.debug(sm.getString("standardContext.loginConfig.errorWarning", + errorPage)); + config.setErrorPage("/" + errorPage); + } else { + throw new IllegalArgumentException + (sm.getString("standardContext.loginConfig.errorPage", + errorPage)); + } + } + + // Process the property setting change + LoginConfig oldLoginConfig = this.loginConfig; + this.loginConfig = config; + support.firePropertyChange("loginConfig", + oldLoginConfig, this.loginConfig); + + } + + + /** + * Get the mapper associated with the context. + */ + @Override + public org.apache.tomcat.util.http.mapper.Mapper getMapper() { + return (mapper); + } + + + /** + * Return the naming resources associated with this web application. + */ + @Override + public NamingResources getNamingResources() { + + if (namingResources == null) { + setNamingResources(new NamingResources()); + } + return (namingResources); + + } + + + /** + * Set the naming resources for this web application. + * + * @param namingResources The new naming resources + */ + @Override + public void setNamingResources(NamingResources namingResources) { + + // Process the property setting change + NamingResources oldNamingResources = this.namingResources; + this.namingResources = namingResources; + if (namingResources != null) { + namingResources.setContainer(this); + } + support.firePropertyChange("namingResources", + oldNamingResources, this.namingResources); + + if (getState() == LifecycleState.NEW || + getState() == LifecycleState.INITIALIZING || + getState() == LifecycleState.INITIALIZED) { + // NEW will occur if Context is defined in server.xml + // At this point getObjectKeyPropertiesNameOnly() will trigger an + // NPE. + // INITIALIZED will occur if the Context is defined in a context.xml + // file + // If started now, a second start will be attempted when the context + // starts + + // In both cases, return and let context init the namingResources + // when it starts + return; + } + + if (oldNamingResources != null) { + try { + oldNamingResources.stop(); + oldNamingResources.destroy(); + } catch (LifecycleException e) { + log.warn("standardContext.namingResource.destroy.fail", e); + } + } + if (namingResources != null) { + try { + namingResources.init(); + namingResources.start(); + } catch (LifecycleException e) { + log.warn("standardContext.namingResource.init.fail", e); + } + } + } + + + /** + * Return the context path for this Context. + */ + @Override + public String getPath() { + return (path); + } + + + /** + * Set the context path for this Context. + * + * @param path The new context path + */ + @Override + public void setPath(String path) { + if (path == null || (!path.equals("") && !path.startsWith("/"))) { + this.path = "/" + path; + log.warn(sm.getString( + "standardContext.pathInvalid", path, this.path)); + } else { + this.path = path; + } + encodedPath = urlEncoder.encode(this.path); + if (getName() == null) { + setName(this.path); + } + } + + + /** + * Return the public identifier of the deployment descriptor DTD that is + * currently being parsed. + */ + @Override + public String getPublicId() { + + return (this.publicId); + + } + + + /** + * Set the public identifier of the deployment descriptor DTD that is + * currently being parsed. + * + * @param publicId The public identifier + */ + @Override + public void setPublicId(String publicId) { + + if (log.isDebugEnabled()) + log.debug("Setting deployment descriptor public ID to '" + + publicId + "'"); + + String oldPublicId = this.publicId; + this.publicId = publicId; + support.firePropertyChange("publicId", oldPublicId, publicId); + + } + + + /** + * Return the reloadable flag for this web application. + */ + @Override + public boolean getReloadable() { + + return (this.reloadable); + + } + + + /** + * Return the default context override flag for this web application. + */ + @Override + public boolean getOverride() { + + return (this.override); + + } + + + /** + * Return the original document root for this Context. This can be an absolute + * pathname, a relative pathname, or a URL. + * Is only set as deployment has change docRoot! + */ + public String getOriginalDocBase() { + + return (this.originalDocBase); + + } + + /** + * Set the original document root for this Context. This can be an absolute + * pathname, a relative pathname, or a URL. + * + * @param docBase The original document root + */ + public void setOriginalDocBase(String docBase) { + + this.originalDocBase = docBase; + } + + + /** + * Return the parent class loader (if any) for this web application. + * This call is meaningful only after a Loader has + * been configured. + */ + @Override + public ClassLoader getParentClassLoader() { + if (parentClassLoader != null) + return (parentClassLoader); + if (getPrivileged()) { + return this.getClass().getClassLoader(); + } else if (parent != null) { + return (parent.getParentClassLoader()); + } + return (ClassLoader.getSystemClassLoader()); + } + + + /** + * Return the privileged flag for this web application. + */ + @Override + public boolean getPrivileged() { + + return (this.privileged); + + } + + + /** + * Set the privileged flag for this web application. + * + * @param privileged The new privileged flag + */ + @Override + public void setPrivileged(boolean privileged) { + + boolean oldPrivileged = this.privileged; + this.privileged = privileged; + support.firePropertyChange("privileged", + oldPrivileged, + this.privileged); + + } + + + /** + * Set the reloadable flag for this web application. + * + * @param reloadable The new reloadable flag + */ + @Override + public void setReloadable(boolean reloadable) { + + boolean oldReloadable = this.reloadable; + this.reloadable = reloadable; + support.firePropertyChange("reloadable", + oldReloadable, + this.reloadable); + + } + + + /** + * Set the default context override flag for this web application. + * + * @param override The new override flag + */ + @Override + public void setOverride(boolean override) { + + boolean oldOverride = this.override; + this.override = override; + support.firePropertyChange("override", + oldOverride, + this.override); + + } + + + /** + * Return the "replace welcome files" property. + */ + public boolean isReplaceWelcomeFiles() { + + return (this.replaceWelcomeFiles); + + } + + + /** + * Set the "replace welcome files" property. + * + * @param replaceWelcomeFiles The new property value + */ + public void setReplaceWelcomeFiles(boolean replaceWelcomeFiles) { + + boolean oldReplaceWelcomeFiles = this.replaceWelcomeFiles; + this.replaceWelcomeFiles = replaceWelcomeFiles; + support.firePropertyChange("replaceWelcomeFiles", + oldReplaceWelcomeFiles, + this.replaceWelcomeFiles); + + } + + + /** + * Return the servlet context for which this Context is a facade. + */ + @Override + public ServletContext getServletContext() { + + if (context == null) { + context = new ApplicationContext(this); + if (altDDName != null) + context.setAttribute(Globals.ALT_DD_ATTR,altDDName); + } + return (context.getFacade()); + + } + + + /** + * Return the default session timeout (in minutes) for this + * web application. + */ + @Override + public int getSessionTimeout() { + + return (this.sessionTimeout); + + } + + + /** + * Set the default session timeout (in minutes) for this + * web application. + * + * @param timeout The new default session timeout + */ + @Override + public void setSessionTimeout(int timeout) { + + int oldSessionTimeout = this.sessionTimeout; + /* + * SRV.13.4 ("Deployment Descriptor"): + * If the timeout is 0 or less, the container ensures the default + * behaviour of sessions is never to time out. + */ + this.sessionTimeout = (timeout == 0) ? -1 : timeout; + support.firePropertyChange("sessionTimeout", + oldSessionTimeout, + this.sessionTimeout); + + } + + + /** + * Return the value of the swallowOutput flag. + */ + @Override + public boolean getSwallowOutput() { + + return (this.swallowOutput); + + } + + + /** + * Set the value of the swallowOutput flag. If set to true, the system.out + * and system.err will be redirected to the logger during a servlet + * execution. + * + * @param swallowOutput The new value + */ + @Override + public void setSwallowOutput(boolean swallowOutput) { + + boolean oldSwallowOutput = this.swallowOutput; + this.swallowOutput = swallowOutput; + support.firePropertyChange("swallowOutput", + oldSwallowOutput, + this.swallowOutput); + + } + + + /** + * Return the value of the unloadDelay flag. + */ + public long getUnloadDelay() { + + return (this.unloadDelay); + + } + + + /** + * Set the value of the unloadDelay flag, which represents the amount + * of ms that the container will wait when unloading servlets. + * Setting this to a small value may cause more requests to fail + * to complete when stopping a web application. + * + * @param unloadDelay The new value + */ + public void setUnloadDelay(long unloadDelay) { + + long oldUnloadDelay = this.unloadDelay; + this.unloadDelay = unloadDelay; + support.firePropertyChange("unloadDelay", + Long.valueOf(oldUnloadDelay), + Long.valueOf(this.unloadDelay)); + + } + + + /** + * Unpack WAR flag accessor. + */ + public boolean getUnpackWAR() { + + return (unpackWAR); + + } + + + /** + * Unpack WAR flag mutator. + */ + public void setUnpackWAR(boolean unpackWAR) { + + this.unpackWAR = unpackWAR; + + } + + /** + * Return the Java class name of the Wrapper implementation used + * for servlets registered in this Context. + */ + @Override + public String getWrapperClass() { + + return (this.wrapperClassName); + + } + + + /** + * Set the Java class name of the Wrapper implementation used + * for servlets registered in this Context. + * + * @param wrapperClassName The new wrapper class name + * + * @throws IllegalArgumentException if the specified wrapper class + * cannot be found or is not a subclass of StandardWrapper + */ + @Override + public void setWrapperClass(String wrapperClassName) { + + this.wrapperClassName = wrapperClassName; + + try { + wrapperClass = Class.forName(wrapperClassName); + if (!StandardWrapper.class.isAssignableFrom(wrapperClass)) { + throw new IllegalArgumentException( + sm.getString("standardContext.invalidWrapperClass", + wrapperClassName)); + } + } catch (ClassNotFoundException cnfe) { + throw new IllegalArgumentException(cnfe.getMessage()); + } + } + + + /** + * Set the resources DirContext object with which this Container is + * associated. + * + * @param resources The newly associated DirContext + */ + @Override + public synchronized void setResources(DirContext resources) { + + if (getState().isAvailable()) { + throw new IllegalStateException + (sm.getString("standardContext.resources.started")); + } + + DirContext oldResources = this.webappResources; + if (oldResources == resources) + return; + + if (resources instanceof BaseDirContext) { + // Caching + ((BaseDirContext) resources).setCached(isCachingAllowed()); + ((BaseDirContext) resources).setCacheTTL(getCacheTTL()); + ((BaseDirContext) resources).setCacheMaxSize(getCacheMaxSize()); + ((BaseDirContext) resources).setCacheObjectMaxSize( + getCacheObjectMaxSize()); + // Alias support + ((BaseDirContext) resources).setAliases(getAliases()); + } + if (resources instanceof FileDirContext) { + filesystemBased = true; + ((FileDirContext) resources).setAllowLinking(isAllowLinking()); + } + this.webappResources = resources; + + // The proxied resources will be refreshed on start + this.resources = null; + + support.firePropertyChange("resources", oldResources, + this.webappResources); + + } + + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + return jspConfigDescriptor; + } + + + // ------------------------------------------------------ Public Properties + + + /** + * Return the Locale to character set mapper class for this Context. + */ + public String getCharsetMapperClass() { + + return (this.charsetMapperClass); + + } + + + /** + * Set the Locale to character set mapper class for this Context. + * + * @param mapper The new mapper class + */ + public void setCharsetMapperClass(String mapper) { + + String oldCharsetMapperClass = this.charsetMapperClass; + this.charsetMapperClass = mapper; + support.firePropertyChange("charsetMapperClass", + oldCharsetMapperClass, + this.charsetMapperClass); + + } + + + /** Get the absolute path to the work dir. + * To avoid duplication. + * + * @return The work path + */ + public String getWorkPath() { + if (getWorkDir() == null) { + return null; + } + File workDir = new File(getWorkDir()); + if (!workDir.isAbsolute()) { + File catalinaHome = engineBase(); + String catalinaHomePath = null; + try { + catalinaHomePath = catalinaHome.getCanonicalPath(); + workDir = new File(catalinaHomePath, + getWorkDir()); + } catch (IOException e) { + log.warn(sm.getString("standardContext.workPath", getName()), + e); + } + } + return workDir.getAbsolutePath(); + } + + /** + * Return the work directory for this Context. + */ + public String getWorkDir() { + + return (this.workDir); + + } + + + /** + * Set the work directory for this Context. + * + * @param workDir The new work directory + */ + public void setWorkDir(String workDir) { + + this.workDir = workDir; + + if (getState().isAvailable()) { + postWorkDirectory(); + } + } + + + /** + * Save config ? + */ + public boolean isSaveConfig() { + return saveConfig; + } + + + /** + * Set save config flag. + */ + public void setSaveConfig(boolean saveConfig) { + this.saveConfig = saveConfig; + } + + + /** + * Return the clearReferencesStatic flag for this Context. + */ + public boolean getClearReferencesStatic() { + + return (this.clearReferencesStatic); + + } + + + /** + * Set the clearReferencesStatic feature for this Context. + * + * @param clearReferencesStatic The new flag value + */ + public void setClearReferencesStatic(boolean clearReferencesStatic) { + + boolean oldClearReferencesStatic = this.clearReferencesStatic; + this.clearReferencesStatic = clearReferencesStatic; + support.firePropertyChange("clearReferencesStatic", + oldClearReferencesStatic, + this.clearReferencesStatic); + + } + + + /** + * Return the clearReferencesStopThreads flag for this Context. + */ + public boolean getClearReferencesStopThreads() { + + return (this.clearReferencesStopThreads); + + } + + + /** + * Set the clearReferencesStopThreads feature for this Context. + * + * @param clearReferencesStopThreads The new flag value + */ + public void setClearReferencesStopThreads( + boolean clearReferencesStopThreads) { + + boolean oldClearReferencesStopThreads = this.clearReferencesStopThreads; + this.clearReferencesStopThreads = clearReferencesStopThreads; + support.firePropertyChange("clearReferencesStopThreads", + oldClearReferencesStopThreads, + this.clearReferencesStopThreads); + + } + + + /** + * Return the clearReferencesStopTimerThreads flag for this Context. + */ + public boolean getClearReferencesStopTimerThreads() { + return (this.clearReferencesStopTimerThreads); + } + + + /** + * Set the clearReferencesStopTimerThreads feature for this Context. + * + * @param clearReferencesStopTimerThreads The new flag value + */ + public void setClearReferencesStopTimerThreads( + boolean clearReferencesStopTimerThreads) { + + boolean oldClearReferencesStopTimerThreads = + this.clearReferencesStopTimerThreads; + this.clearReferencesStopTimerThreads = clearReferencesStopTimerThreads; + support.firePropertyChange("clearReferencesStopTimerThreads", + oldClearReferencesStopTimerThreads, + this.clearReferencesStopTimerThreads); + } + + + /** + * Return the clearReferencesHttpClientKeepAliveThread flag for this + * Context. + */ + public boolean getClearReferencesHttpClientKeepAliveThread() { + return (this.clearReferencesHttpClientKeepAliveThread); + } + + + /** + * Set the clearReferencesHttpClientKeepAliveThread feature for this + * Context. + * + * @param clearReferencesHttpClientKeepAliveThread The new flag value + */ + public void setClearReferencesHttpClientKeepAliveThread( + boolean clearReferencesHttpClientKeepAliveThread) { + this.clearReferencesHttpClientKeepAliveThread = + clearReferencesHttpClientKeepAliveThread; + } + + + public boolean getRenewThreadsWhenStoppingContext() { + return this.renewThreadsWhenStoppingContext; + } + + public void setRenewThreadsWhenStoppingContext( + boolean renewThreadsWhenStoppingContext) { + boolean oldRenewThreadsWhenStoppingContext = + this.renewThreadsWhenStoppingContext; + this.renewThreadsWhenStoppingContext = renewThreadsWhenStoppingContext; + support.firePropertyChange("renewThreadsWhenStoppingContext", + oldRenewThreadsWhenStoppingContext, + this.renewThreadsWhenStoppingContext); + } + + // -------------------------------------------------------- Context Methods + + + /** + * Add a new Listener class name to the set of Listeners + * configured for this application. + * + * @param listener Java class name of a listener class + */ + @Override + public void addApplicationListener(String listener) { + + synchronized (applicationListenersLock) { + String results[] =new String[applicationListeners.length + 1]; + for (int i = 0; i < applicationListeners.length; i++) { + if (listener.equals(applicationListeners[i])) { + log.info(sm.getString( + "standardContext.duplicateListener",listener)); + return; + } + results[i] = applicationListeners[i]; + } + results[applicationListeners.length] = listener; + applicationListeners = results; + } + fireContainerEvent("addApplicationListener", listener); + + // FIXME - add instance if already started? + + } + + + /** + * Add a new application parameter for this application. + * + * @param parameter The new application parameter + */ + @Override + public void addApplicationParameter(ApplicationParameter parameter) { + + synchronized (applicationParametersLock) { + String newName = parameter.getName(); + for (ApplicationParameter p : applicationParameters) { + if (newName.equals(p.getName()) && !p.getOverride()) + return; + } + ApplicationParameter results[] = Arrays.copyOf( + applicationParameters, applicationParameters.length + 1); + results[applicationParameters.length] = parameter; + applicationParameters = results; + } + fireContainerEvent("addApplicationParameter", parameter); + + } + + + /** + * Add a child Container, only if the proposed child is an implementation + * of Wrapper. + * + * @param child Child container to be added + * + * @exception IllegalArgumentException if the proposed container is + * not an implementation of Wrapper + */ + @Override + public void addChild(Container child) { + + // Global JspServlet + Wrapper oldJspServlet = null; + + if (!(child instanceof Wrapper)) { + throw new IllegalArgumentException + (sm.getString("standardContext.notWrapper")); + } + + boolean isJspServlet = "jsp".equals(child.getName()); + + // Allow webapp to override JspServlet inherited from global web.xml. + if (isJspServlet) { + oldJspServlet = (Wrapper) findChild("jsp"); + if (oldJspServlet != null) { + removeChild(oldJspServlet); + } + } + + super.addChild(child); + + if (isJspServlet && oldJspServlet != null) { + /* + * The webapp-specific JspServlet inherits all the mappings + * specified in the global web.xml, and may add additional ones. + */ + String[] jspMappings = oldJspServlet.findMappings(); + for (int i=0; jspMappings!=null && i 0 && + collections[i].findOmittedMethods().length > 0) { + throw new IllegalArgumentException(sm.getString( + "standardContext.securityConstraint.mixHttpMethod")); + } + } + + // Add this constraint to the set for our web application + synchronized (constraintsLock) { + SecurityConstraint results[] = + new SecurityConstraint[constraints.length + 1]; + for (int i = 0; i < constraints.length; i++) + results[i] = constraints[i]; + results[constraints.length] = constraint; + constraints = results; + } + + } + + + + /** + * Add an error page for the specified error or Java exception. + * + * @param errorPage The error page definition to be added + */ + @Override + public void addErrorPage(ErrorPage errorPage) { + // Validate the input parameters + if (errorPage == null) + throw new IllegalArgumentException + (sm.getString("standardContext.errorPage.required")); + String location = errorPage.getLocation(); + if ((location != null) && !location.startsWith("/")) { + if (isServlet22()) { + if(log.isDebugEnabled()) + log.debug(sm.getString("standardContext.errorPage.warning", + location)); + errorPage.setLocation("/" + location); + } else { + throw new IllegalArgumentException + (sm.getString("standardContext.errorPage.error", + location)); + } + } + + // Add the specified error page to our internal collections + String exceptionType = errorPage.getExceptionType(); + if (exceptionType != null) { + synchronized (exceptionPages) { + exceptionPages.put(exceptionType, errorPage); + } + } else { + synchronized (statusPages) { + if (errorPage.getErrorCode() == 200) { + this.okErrorPage = errorPage; + } + statusPages.put(Integer.valueOf(errorPage.getErrorCode()), + errorPage); + } + } + fireContainerEvent("addErrorPage", errorPage); + + } + + + /** + * Add a filter definition to this Context. + * + * @param filterDef The filter definition to be added + */ + @Override + public void addFilterDef(FilterDef filterDef) { + + synchronized (filterDefs) { + filterDefs.put(filterDef.getFilterName(), filterDef); + } + fireContainerEvent("addFilterDef", filterDef); + + } + + + /** + * Add a filter mapping to this Context at the end of the current set + * of filter mappings. + * + * @param filterMap The filter mapping to be added + * + * @exception IllegalArgumentException if the specified filter name + * does not match an existing filter definition, or the filter mapping + * is malformed + */ + @Override + public void addFilterMap(FilterMap filterMap) { + validateFilterMap(filterMap); + // Add this filter mapping to our registered set + filterMaps.add(filterMap); + fireContainerEvent("addFilterMap", filterMap); + } + + + /** + * Add a filter mapping to this Context before the mappings defined in the + * deployment descriptor but after any other mappings added via this method. + * + * @param filterMap The filter mapping to be added + * + * @exception IllegalArgumentException if the specified filter name + * does not match an existing filter definition, or the filter mapping + * is malformed + */ + @Override + public void addFilterMapBefore(FilterMap filterMap) { + validateFilterMap(filterMap); + // Add this filter mapping to our registered set + filterMaps.addBefore(filterMap); + fireContainerEvent("addFilterMap", filterMap); + } + + + /** + * Validate the supplied FilterMap. + */ + private void validateFilterMap(FilterMap filterMap) { + // Validate the proposed filter mapping + String filterName = filterMap.getFilterName(); + String[] servletNames = filterMap.getServletNames(); + String[] urlPatterns = filterMap.getURLPatterns(); + if (findFilterDef(filterName) == null) + throw new IllegalArgumentException + (sm.getString("standardContext.filterMap.name", filterName)); + + if (!filterMap.getMatchAllServletNames() && + !filterMap.getMatchAllUrlPatterns() && + (servletNames.length == 0) && (urlPatterns.length == 0)) + throw new IllegalArgumentException + (sm.getString("standardContext.filterMap.either")); + // FIXME: Older spec revisions may still check this + /* + if ((servletNames.length != 0) && (urlPatterns.length != 0)) + throw new IllegalArgumentException + (sm.getString("standardContext.filterMap.either")); + */ + for (int i = 0; i < urlPatterns.length; i++) { + if (!validateURLPattern(urlPatterns[i])) { + throw new IllegalArgumentException + (sm.getString("standardContext.filterMap.pattern", + urlPatterns[i])); + } + } + } + + /** + * Add the classname of an InstanceListener to be added to each + * Wrapper appended to this Context. + * + * @param listener Java class name of an InstanceListener class + */ + @Override + public void addInstanceListener(String listener) { + + synchronized (instanceListenersLock) { + String results[] =new String[instanceListeners.length + 1]; + for (int i = 0; i < instanceListeners.length; i++) + results[i] = instanceListeners[i]; + results[instanceListeners.length] = listener; + instanceListeners = results; + } + fireContainerEvent("addInstanceListener", listener); + + } + + /** + * Add a Locale Encoding Mapping (see Sec 5.4 of Servlet spec 2.4) + * + * @param locale locale to map an encoding for + * @param encoding encoding to be used for a give locale + */ + @Override + public void addLocaleEncodingMappingParameter(String locale, String encoding){ + getCharsetMapper().addCharsetMappingFromDeploymentDescriptor(locale, encoding); + } + + + /** + * Add a message destination for this web application. + * + * @param md New message destination + */ + public void addMessageDestination(MessageDestination md) { + + synchronized (messageDestinations) { + messageDestinations.put(md.getName(), md); + } + fireContainerEvent("addMessageDestination", md.getName()); + + } + + + /** + * Add a message destination reference for this web application. + * + * @param mdr New message destination reference + */ + public void addMessageDestinationRef + (MessageDestinationRef mdr) { + + namingResources.addMessageDestinationRef(mdr); + fireContainerEvent("addMessageDestinationRef", mdr.getName()); + + } + + + /** + * Add a new MIME mapping, replacing any existing mapping for + * the specified extension. + * + * @param extension Filename extension being mapped + * @param mimeType Corresponding MIME type + */ + @Override + public void addMimeMapping(String extension, String mimeType) { + + synchronized (mimeMappings) { + mimeMappings.put(extension, mimeType); + } + fireContainerEvent("addMimeMapping", extension); + + } + + + /** + * Add a new context initialization parameter. + * + * @param name Name of the new parameter + * @param value Value of the new parameter + * + * @exception IllegalArgumentException if the name or value is missing, + * or if this context initialization parameter has already been + * registered + */ + @Override + public void addParameter(String name, String value) { + // Validate the proposed context initialization parameter + if ((name == null) || (value == null)) + throw new IllegalArgumentException + (sm.getString("standardContext.parameter.required")); + if (parameters.get(name) != null) + throw new IllegalArgumentException + (sm.getString("standardContext.parameter.duplicate", name)); + + // Add this parameter to our defined set + synchronized (parameters) { + parameters.put(name, value); + } + fireContainerEvent("addParameter", name); + + } + + + /** + * Add a security role reference for this web application. + * + * @param role Security role used in the application + * @param link Actual security role to check for + */ + @Override + public void addRoleMapping(String role, String link) { + + synchronized (roleMappings) { + roleMappings.put(role, link); + } + fireContainerEvent("addRoleMapping", role); + + } + + + /** + * Add a new security role for this web application. + * + * @param role New security role + */ + @Override + public void addSecurityRole(String role) { + + synchronized (securityRolesLock) { + String results[] =new String[securityRoles.length + 1]; + for (int i = 0; i < securityRoles.length; i++) + results[i] = securityRoles[i]; + results[securityRoles.length] = role; + securityRoles = results; + } + fireContainerEvent("addSecurityRole", role); + + } + + + /** + * Add a new servlet mapping, replacing any existing mapping for + * the specified pattern. + * + * @param pattern URL pattern to be mapped + * @param name Name of the corresponding servlet to execute + * + * @exception IllegalArgumentException if the specified servlet name + * is not known to this Context + */ + @Override + public void addServletMapping(String pattern, String name) { + addServletMapping(pattern, name, false); + } + + + /** + * Add a new servlet mapping, replacing any existing mapping for + * the specified pattern. + * + * @param pattern URL pattern to be mapped + * @param name Name of the corresponding servlet to execute + * @param jspWildCard true if name identifies the JspServlet + * and pattern contains a wildcard; false otherwise + * + * @exception IllegalArgumentException if the specified servlet name + * is not known to this Context + */ + @Override + public void addServletMapping(String pattern, String name, + boolean jspWildCard) { + // Validate the proposed mapping + if (findChild(name) == null) + throw new IllegalArgumentException + (sm.getString("standardContext.servletMap.name", name)); + String decodedPattern = adjustURLPattern(RequestUtil.URLDecode(pattern)); + if (!validateURLPattern(decodedPattern)) + throw new IllegalArgumentException + (sm.getString("standardContext.servletMap.pattern", decodedPattern)); + + // Add this mapping to our registered set + synchronized (servletMappingsLock) { + String name2 = servletMappings.get(decodedPattern); + if (name2 != null) { + // Don't allow more than one servlet on the same pattern + Wrapper wrapper = (Wrapper) findChild(name2); + wrapper.removeMapping(decodedPattern); + mapper.removeWrapper(decodedPattern); + } + servletMappings.put(decodedPattern, name); + } + Wrapper wrapper = (Wrapper) findChild(name); + wrapper.addMapping(decodedPattern); + + // Update context mapper + mapper.addWrapper(decodedPattern, wrapper, jspWildCard, + resourceOnlyServlets.contains(name)); + + fireContainerEvent("addServletMapping", decodedPattern); + + } + + + /** + * Add a new watched resource to the set recognized by this Context. + * + * @param name New watched resource file name + */ + @Override + public void addWatchedResource(String name) { + + synchronized (watchedResourcesLock) { + String results[] = new String[watchedResources.length + 1]; + for (int i = 0; i < watchedResources.length; i++) + results[i] = watchedResources[i]; + results[watchedResources.length] = name; + watchedResources = results; + } + fireContainerEvent("addWatchedResource", name); + + } + + + /** + * Add a new welcome file to the set recognized by this Context. + * + * @param name New welcome file name + */ + @Override + public void addWelcomeFile(String name) { + + synchronized (welcomeFilesLock) { + // Welcome files from the application deployment descriptor + // completely replace those from the default conf/web.xml file + if (replaceWelcomeFiles) { + fireContainerEvent(CLEAR_WELCOME_FILES_EVENT, null); + welcomeFiles = new String[0]; + setReplaceWelcomeFiles(false); + } + String results[] =new String[welcomeFiles.length + 1]; + for (int i = 0; i < welcomeFiles.length; i++) + results[i] = welcomeFiles[i]; + results[welcomeFiles.length] = name; + welcomeFiles = results; + } + if(this.getState().equals(LifecycleState.STARTED)) + fireContainerEvent(ADD_WELCOME_FILE_EVENT, name); + } + + + /** + * Add the classname of a LifecycleListener to be added to each + * Wrapper appended to this Context. + * + * @param listener Java class name of a LifecycleListener class + */ + @Override + public void addWrapperLifecycle(String listener) { + + synchronized (wrapperLifecyclesLock) { + String results[] =new String[wrapperLifecycles.length + 1]; + for (int i = 0; i < wrapperLifecycles.length; i++) + results[i] = wrapperLifecycles[i]; + results[wrapperLifecycles.length] = listener; + wrapperLifecycles = results; + } + fireContainerEvent("addWrapperLifecycle", listener); + + } + + + /** + * Add the classname of a ContainerListener to be added to each + * Wrapper appended to this Context. + * + * @param listener Java class name of a ContainerListener class + */ + @Override + public void addWrapperListener(String listener) { + + synchronized (wrapperListenersLock) { + String results[] =new String[wrapperListeners.length + 1]; + for (int i = 0; i < wrapperListeners.length; i++) + results[i] = wrapperListeners[i]; + results[wrapperListeners.length] = listener; + wrapperListeners = results; + } + fireContainerEvent("addWrapperListener", listener); + + } + + + /** + * Factory method to create and return a new Wrapper instance, of + * the Java implementation class appropriate for this Context + * implementation. The constructor of the instantiated Wrapper + * will have been called, but no properties will have been set. + */ + @Override + public Wrapper createWrapper() { + + Wrapper wrapper = null; + if (wrapperClass != null) { + try { + wrapper = (Wrapper) wrapperClass.newInstance(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error("createWrapper", t); + return (null); + } + } else { + wrapper = new StandardWrapper(); + } + + synchronized (instanceListenersLock) { + for (int i = 0; i < instanceListeners.length; i++) { + try { + Class clazz = Class.forName(instanceListeners[i]); + InstanceListener listener = + (InstanceListener) clazz.newInstance(); + wrapper.addInstanceListener(listener); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error("createWrapper", t); + return (null); + } + } + } + + synchronized (wrapperLifecyclesLock) { + for (int i = 0; i < wrapperLifecycles.length; i++) { + try { + Class clazz = Class.forName(wrapperLifecycles[i]); + LifecycleListener listener = + (LifecycleListener) clazz.newInstance(); + wrapper.addLifecycleListener(listener); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error("createWrapper", t); + return (null); + } + } + } + + synchronized (wrapperListenersLock) { + for (int i = 0; i < wrapperListeners.length; i++) { + try { + Class clazz = Class.forName(wrapperListeners[i]); + ContainerListener listener = + (ContainerListener) clazz.newInstance(); + wrapper.addContainerListener(listener); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error("createWrapper", t); + return (null); + } + } + } + + return (wrapper); + + } + + + /** + * Return the set of application listener class names configured + * for this application. + */ + @Override + public String[] findApplicationListeners() { + + return (applicationListeners); + + } + + + /** + * Return the set of application parameters for this application. + */ + @Override + public ApplicationParameter[] findApplicationParameters() { + + synchronized (applicationParametersLock) { + return (applicationParameters); + } + + } + + + /** + * Return the security constraints for this web application. + * If there are none, a zero-length array is returned. + */ + @Override + public SecurityConstraint[] findConstraints() { + + return (constraints); + + } + + + /** + * Return the error page entry for the specified HTTP error code, + * if any; otherwise return null. + * + * @param errorCode Error code to look up + */ + @Override + public ErrorPage findErrorPage(int errorCode) { + if (errorCode == 200) { + return (okErrorPage); + } else { + return (statusPages.get(Integer.valueOf(errorCode))); + } + + } + + + /** + * Return the error page entry for the specified Java exception type, + * if any; otherwise return null. + * + * @param exceptionType Exception type to look up + */ + @Override + public ErrorPage findErrorPage(String exceptionType) { + + synchronized (exceptionPages) { + return (exceptionPages.get(exceptionType)); + } + + } + + + /** + * Return the set of defined error pages for all specified error codes + * and exception types. + */ + @Override + public ErrorPage[] findErrorPages() { + + synchronized(exceptionPages) { + synchronized(statusPages) { + ErrorPage results1[] = new ErrorPage[exceptionPages.size()]; + results1 = exceptionPages.values().toArray(results1); + ErrorPage results2[] = new ErrorPage[statusPages.size()]; + results2 = statusPages.values().toArray(results2); + ErrorPage results[] = + new ErrorPage[results1.length + results2.length]; + for (int i = 0; i < results1.length; i++) + results[i] = results1[i]; + for (int i = results1.length; i < results.length; i++) + results[i] = results2[i - results1.length]; + return (results); + } + } + + } + + + /** + * Return the filter definition for the specified filter name, if any; + * otherwise return null. + * + * @param filterName Filter name to look up + */ + @Override + public FilterDef findFilterDef(String filterName) { + + synchronized (filterDefs) { + return (filterDefs.get(filterName)); + } + + } + + + /** + * Return the set of defined filters for this Context. + */ + @Override + public FilterDef[] findFilterDefs() { + + synchronized (filterDefs) { + FilterDef results[] = new FilterDef[filterDefs.size()]; + return (filterDefs.values().toArray(results)); + } + + } + + + /** + * Return the set of filter mappings for this Context. + */ + @Override + public FilterMap[] findFilterMaps() { + return filterMaps.asArray(); + } + + + /** + * Return the set of InstanceListener classes that will be added to + * newly created Wrappers automatically. + */ + @Override + public String[] findInstanceListeners() { + + synchronized (instanceListenersLock) { + return (instanceListeners); + } + + } + + + /** + * FIXME: Fooling introspection ... + */ + public Context findMappingObject() { + return (Context) getMappingObject(); + } + + + /** + * Return the message destination with the specified name, if any; + * otherwise, return null. + * + * @param name Name of the desired message destination + */ + public MessageDestination findMessageDestination(String name) { + + synchronized (messageDestinations) { + return (messageDestinations.get(name)); + } + + } + + + /** + * Return the set of defined message destinations for this web + * application. If none have been defined, a zero-length array + * is returned. + */ + public MessageDestination[] findMessageDestinations() { + + synchronized (messageDestinations) { + MessageDestination results[] = + new MessageDestination[messageDestinations.size()]; + return (messageDestinations.values().toArray(results)); + } + + } + + + /** + * Return the message destination ref with the specified name, if any; + * otherwise, return null. + * + * @param name Name of the desired message destination ref + */ + public MessageDestinationRef + findMessageDestinationRef(String name) { + + return namingResources.findMessageDestinationRef(name); + + } + + + /** + * Return the set of defined message destination refs for this web + * application. If none have been defined, a zero-length array + * is returned. + */ + public MessageDestinationRef[] + findMessageDestinationRefs() { + + return namingResources.findMessageDestinationRefs(); + + } + + + /** + * Return the MIME type to which the specified extension is mapped, + * if any; otherwise return null. + * + * @param extension Extension to map to a MIME type + */ + @Override + public String findMimeMapping(String extension) { + + return (mimeMappings.get(extension)); + + } + + + /** + * Return the extensions for which MIME mappings are defined. If there + * are none, a zero-length array is returned. + */ + @Override + public String[] findMimeMappings() { + + synchronized (mimeMappings) { + String results[] = new String[mimeMappings.size()]; + return + (mimeMappings.keySet().toArray(results)); + } + + } + + + /** + * Return the value for the specified context initialization + * parameter name, if any; otherwise return null. + * + * @param name Name of the parameter to return + */ + @Override + public String findParameter(String name) { + + synchronized (parameters) { + return (parameters.get(name)); + } + + } + + + /** + * Return the names of all defined context initialization parameters + * for this Context. If no parameters are defined, a zero-length + * array is returned. + */ + @Override + public String[] findParameters() { + + synchronized (parameters) { + String results[] = new String[parameters.size()]; + return (parameters.keySet().toArray(results)); + } + + } + + + /** + * For the given security role (as used by an application), return the + * corresponding role name (as defined by the underlying Realm) if there + * is one. Otherwise, return the specified role unchanged. + * + * @param role Security role to map + */ + @Override + public String findRoleMapping(String role) { + + String realRole = null; + synchronized (roleMappings) { + realRole = roleMappings.get(role); + } + if (realRole != null) + return (realRole); + else + return (role); + + } + + + /** + * Return true if the specified security role is defined + * for this application; otherwise return false. + * + * @param role Security role to verify + */ + @Override + public boolean findSecurityRole(String role) { + + synchronized (securityRolesLock) { + for (int i = 0; i < securityRoles.length; i++) { + if (role.equals(securityRoles[i])) + return (true); + } + } + return (false); + + } + + + /** + * Return the security roles defined for this application. If none + * have been defined, a zero-length array is returned. + */ + @Override + public String[] findSecurityRoles() { + + synchronized (securityRolesLock) { + return (securityRoles); + } + + } + + + /** + * Return the servlet name mapped by the specified pattern (if any); + * otherwise return null. + * + * @param pattern Pattern for which a mapping is requested + */ + @Override + public String findServletMapping(String pattern) { + + synchronized (servletMappingsLock) { + return (servletMappings.get(pattern)); + } + + } + + + /** + * Return the patterns of all defined servlet mappings for this + * Context. If no mappings are defined, a zero-length array is returned. + */ + @Override + public String[] findServletMappings() { + + synchronized (servletMappingsLock) { + String results[] = new String[servletMappings.size()]; + return + (servletMappings.keySet().toArray(results)); + } + + } + + + /** + * Return the context-relative URI of the error page for the specified + * HTTP status code, if any; otherwise return null. + * + * @param status HTTP status code to look up + */ + @Override + public String findStatusPage(int status) { + + ErrorPage errorPage = statusPages.get(Integer.valueOf(status)); + if (errorPage!=null) { + return errorPage.getLocation(); + } + return null; + + } + + + /** + * Return the set of HTTP status codes for which error pages have + * been specified. If none are specified, a zero-length array + * is returned. + */ + @Override + public int[] findStatusPages() { + + synchronized (statusPages) { + int results[] = new int[statusPages.size()]; + Iterator elements = statusPages.keySet().iterator(); + int i = 0; + while (elements.hasNext()) + results[i++] = elements.next().intValue(); + return (results); + } + + } + + + /** + * Return true if the specified welcome file is defined + * for this Context; otherwise return false. + * + * @param name Welcome file to verify + */ + @Override + public boolean findWelcomeFile(String name) { + + synchronized (welcomeFilesLock) { + for (int i = 0; i < welcomeFiles.length; i++) { + if (name.equals(welcomeFiles[i])) + return (true); + } + } + return (false); + + } + + + /** + * Return the set of watched resources for this Context. If none are + * defined, a zero length array will be returned. + */ + @Override + public String[] findWatchedResources() { + synchronized (watchedResourcesLock) { + return watchedResources; + } + } + + + /** + * Return the set of welcome files defined for this Context. If none are + * defined, a zero-length array is returned. + */ + @Override + public String[] findWelcomeFiles() { + + synchronized (welcomeFilesLock) { + return (welcomeFiles); + } + + } + + + /** + * Return the set of LifecycleListener classes that will be added to + * newly created Wrappers automatically. + */ + @Override + public String[] findWrapperLifecycles() { + + synchronized (wrapperLifecyclesLock) { + return (wrapperLifecycles); + } + + } + + + /** + * Return the set of ContainerListener classes that will be added to + * newly created Wrappers automatically. + */ + @Override + public String[] findWrapperListeners() { + + synchronized (wrapperListenersLock) { + return (wrapperListeners); + } + + } + + + /** + * Reload this web application, if reloading is supported. + *

+ * IMPLEMENTATION NOTE: This method is designed to deal with + * reloads required by changes to classes in the underlying repositories + * of our class loader. It does not handle changes to the web application + * deployment descriptor. If that has occurred, you should stop this + * Context and create (and start) a new Context instance instead. + * + * @exception IllegalStateException if the reloadable + * property is set to false. + */ + @Override + public synchronized void reload() { + + // Validate our current component state + if (!getState().isAvailable()) + throw new IllegalStateException + (sm.getString("standardContext.notStarted", getName())); + + if(log.isInfoEnabled()) + log.info(sm.getString("standardContext.reloadingStarted", + getName())); + + // Stop accepting requests temporarily + setPaused(true); + + try { + stop(); + } catch (LifecycleException e) { + log.error( + sm.getString("standardContext.stoppingContext", getName()), e); + } + + try { + start(); + } catch (LifecycleException e) { + log.error( + sm.getString("standardContext.startingContext", getName()), e); + } + + setPaused(false); + + if(log.isInfoEnabled()) + log.info(sm.getString("standardContext.reloadingCompleted", + getName())); + + } + + + /** + * Remove the specified application listener class from the set of + * listeners for this application. + * + * @param listener Java class name of the listener to be removed + */ + @Override + public void removeApplicationListener(String listener) { + + synchronized (applicationListenersLock) { + + // Make sure this welcome file is currently present + int n = -1; + for (int i = 0; i < applicationListeners.length; i++) { + if (applicationListeners[i].equals(listener)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + String results[] = new String[applicationListeners.length - 1]; + for (int i = 0; i < applicationListeners.length; i++) { + if (i != n) + results[j++] = applicationListeners[i]; + } + applicationListeners = results; + + } + + // Inform interested listeners + fireContainerEvent("removeApplicationListener", listener); + + // FIXME - behavior if already started? + + } + + + /** + * Remove the application parameter with the specified name from + * the set for this application. + * + * @param name Name of the application parameter to remove + */ + @Override + public void removeApplicationParameter(String name) { + + synchronized (applicationParametersLock) { + + // Make sure this parameter is currently present + int n = -1; + for (int i = 0; i < applicationParameters.length; i++) { + if (name.equals(applicationParameters[i].getName())) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified parameter + int j = 0; + ApplicationParameter results[] = + new ApplicationParameter[applicationParameters.length - 1]; + for (int i = 0; i < applicationParameters.length; i++) { + if (i != n) + results[j++] = applicationParameters[i]; + } + applicationParameters = results; + + } + + // Inform interested listeners + fireContainerEvent("removeApplicationParameter", name); + + } + + + /** + * Add a child Container, only if the proposed child is an implementation + * of Wrapper. + * + * @param child Child container to be added + * + * @exception IllegalArgumentException if the proposed container is + * not an implementation of Wrapper + */ + @Override + public void removeChild(Container child) { + + if (!(child instanceof Wrapper)) { + throw new IllegalArgumentException + (sm.getString("standardContext.notWrapper")); + } + + super.removeChild(child); + + } + + + /** + * Remove the specified security constraint from this web application. + * + * @param constraint Constraint to be removed + */ + @Override + public void removeConstraint(SecurityConstraint constraint) { + + synchronized (constraintsLock) { + + // Make sure this constraint is currently present + int n = -1; + for (int i = 0; i < constraints.length; i++) { + if (constraints[i].equals(constraint)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + SecurityConstraint results[] = + new SecurityConstraint[constraints.length - 1]; + for (int i = 0; i < constraints.length; i++) { + if (i != n) + results[j++] = constraints[i]; + } + constraints = results; + + } + + // Inform interested listeners + fireContainerEvent("removeConstraint", constraint); + + } + + + /** + * Remove the error page for the specified error code or + * Java language exception, if it exists; otherwise, no action is taken. + * + * @param errorPage The error page definition to be removed + */ + @Override + public void removeErrorPage(ErrorPage errorPage) { + + String exceptionType = errorPage.getExceptionType(); + if (exceptionType != null) { + synchronized (exceptionPages) { + exceptionPages.remove(exceptionType); + } + } else { + synchronized (statusPages) { + if (errorPage.getErrorCode() == 200) { + this.okErrorPage = null; + } + statusPages.remove(Integer.valueOf(errorPage.getErrorCode())); + } + } + fireContainerEvent("removeErrorPage", errorPage); + + } + + + /** + * Remove the specified filter definition from this Context, if it exists; + * otherwise, no action is taken. + * + * @param filterDef Filter definition to be removed + */ + @Override + public void removeFilterDef(FilterDef filterDef) { + + synchronized (filterDefs) { + filterDefs.remove(filterDef.getFilterName()); + } + fireContainerEvent("removeFilterDef", filterDef); + + } + + + /** + * Remove a filter mapping from this Context. + * + * @param filterMap The filter mapping to be removed + */ + @Override + public void removeFilterMap(FilterMap filterMap) { + filterMaps.remove(filterMap); + // Inform interested listeners + fireContainerEvent("removeFilterMap", filterMap); + } + + + /** + * Remove a class name from the set of InstanceListener classes that + * will be added to newly created Wrappers. + * + * @param listener Class name of an InstanceListener class to be removed + */ + @Override + public void removeInstanceListener(String listener) { + + synchronized (instanceListenersLock) { + + // Make sure this welcome file is currently present + int n = -1; + for (int i = 0; i < instanceListeners.length; i++) { + if (instanceListeners[i].equals(listener)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + String results[] = new String[instanceListeners.length - 1]; + for (int i = 0; i < instanceListeners.length; i++) { + if (i != n) + results[j++] = instanceListeners[i]; + } + instanceListeners = results; + + } + + // Inform interested listeners + fireContainerEvent("removeInstanceListener", listener); + + } + + + /** + * Remove any message destination with the specified name. + * + * @param name Name of the message destination to remove + */ + public void removeMessageDestination(String name) { + + synchronized (messageDestinations) { + messageDestinations.remove(name); + } + fireContainerEvent("removeMessageDestination", name); + + } + + + /** + * Remove any message destination ref with the specified name. + * + * @param name Name of the message destination ref to remove + */ + public void removeMessageDestinationRef(String name) { + + namingResources.removeMessageDestinationRef(name); + fireContainerEvent("removeMessageDestinationRef", name); + + } + + + /** + * Remove the MIME mapping for the specified extension, if it exists; + * otherwise, no action is taken. + * + * @param extension Extension to remove the mapping for + */ + @Override + public void removeMimeMapping(String extension) { + + synchronized (mimeMappings) { + mimeMappings.remove(extension); + } + fireContainerEvent("removeMimeMapping", extension); + + } + + + /** + * Remove the context initialization parameter with the specified + * name, if it exists; otherwise, no action is taken. + * + * @param name Name of the parameter to remove + */ + @Override + public void removeParameter(String name) { + + synchronized (parameters) { + parameters.remove(name); + } + fireContainerEvent("removeParameter", name); + + } + + + /** + * Remove any security role reference for the specified name + * + * @param role Security role (as used in the application) to remove + */ + @Override + public void removeRoleMapping(String role) { + + synchronized (roleMappings) { + roleMappings.remove(role); + } + fireContainerEvent("removeRoleMapping", role); + + } + + + /** + * Remove any security role with the specified name. + * + * @param role Security role to remove + */ + @Override + public void removeSecurityRole(String role) { + + synchronized (securityRolesLock) { + + // Make sure this security role is currently present + int n = -1; + for (int i = 0; i < securityRoles.length; i++) { + if (role.equals(securityRoles[i])) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified security role + int j = 0; + String results[] = new String[securityRoles.length - 1]; + for (int i = 0; i < securityRoles.length; i++) { + if (i != n) + results[j++] = securityRoles[i]; + } + securityRoles = results; + + } + + // Inform interested listeners + fireContainerEvent("removeSecurityRole", role); + + } + + + /** + * Remove any servlet mapping for the specified pattern, if it exists; + * otherwise, no action is taken. + * + * @param pattern URL pattern of the mapping to remove + */ + @Override + public void removeServletMapping(String pattern) { + + String name = null; + synchronized (servletMappingsLock) { + name = servletMappings.remove(pattern); + } + Wrapper wrapper = (Wrapper) findChild(name); + if( wrapper != null ) { + wrapper.removeMapping(pattern); + } + mapper.removeWrapper(pattern); + fireContainerEvent("removeServletMapping", pattern); + + } + + + /** + * Remove the specified watched resource name from the list associated + * with this Context. + * + * @param name Name of the watched resource to be removed + */ + @Override + public void removeWatchedResource(String name) { + + synchronized (watchedResourcesLock) { + + // Make sure this watched resource is currently present + int n = -1; + for (int i = 0; i < watchedResources.length; i++) { + if (watchedResources[i].equals(name)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified watched resource + int j = 0; + String results[] = new String[watchedResources.length - 1]; + for (int i = 0; i < watchedResources.length; i++) { + if (i != n) + results[j++] = watchedResources[i]; + } + watchedResources = results; + + } + + fireContainerEvent("removeWatchedResource", name); + + } + + + /** + * Remove the specified welcome file name from the list recognized + * by this Context. + * + * @param name Name of the welcome file to be removed + */ + @Override + public void removeWelcomeFile(String name) { + + synchronized (welcomeFilesLock) { + + // Make sure this welcome file is currently present + int n = -1; + for (int i = 0; i < welcomeFiles.length; i++) { + if (welcomeFiles[i].equals(name)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + String results[] = new String[welcomeFiles.length - 1]; + for (int i = 0; i < welcomeFiles.length; i++) { + if (i != n) + results[j++] = welcomeFiles[i]; + } + welcomeFiles = results; + + } + + // Inform interested listeners + if(this.getState().equals(LifecycleState.STARTED)) + fireContainerEvent(REMOVE_WELCOME_FILE_EVENT, name); + + } + + + /** + * Remove a class name from the set of LifecycleListener classes that + * will be added to newly created Wrappers. + * + * @param listener Class name of a LifecycleListener class to be removed + */ + @Override + public void removeWrapperLifecycle(String listener) { + + + synchronized (wrapperLifecyclesLock) { + + // Make sure this welcome file is currently present + int n = -1; + for (int i = 0; i < wrapperLifecycles.length; i++) { + if (wrapperLifecycles[i].equals(listener)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + String results[] = new String[wrapperLifecycles.length - 1]; + for (int i = 0; i < wrapperLifecycles.length; i++) { + if (i != n) + results[j++] = wrapperLifecycles[i]; + } + wrapperLifecycles = results; + + } + + // Inform interested listeners + fireContainerEvent("removeWrapperLifecycle", listener); + + } + + + /** + * Remove a class name from the set of ContainerListener classes that + * will be added to newly created Wrappers. + * + * @param listener Class name of a ContainerListener class to be removed + */ + @Override + public void removeWrapperListener(String listener) { + + + synchronized (wrapperListenersLock) { + + // Make sure this welcome file is currently present + int n = -1; + for (int i = 0; i < wrapperListeners.length; i++) { + if (wrapperListeners[i].equals(listener)) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified constraint + int j = 0; + String results[] = new String[wrapperListeners.length - 1]; + for (int i = 0; i < wrapperListeners.length; i++) { + if (i != n) + results[j++] = wrapperListeners[i]; + } + wrapperListeners = results; + + } + + // Inform interested listeners + fireContainerEvent("removeWrapperListener", listener); + + } + + + /** + * Gets the cumulative processing times of all servlets in this + * StandardContext. + * + * @return Cumulative processing times of all servlets in this + * StandardContext + */ + public long getProcessingTime() { + + long result = 0; + + Container[] children = findChildren(); + if (children != null) { + for( int i=0; i< children.length; i++ ) { + result += ((StandardWrapper)children[i]).getProcessingTime(); + } + } + + return result; + } + + + /** + * Return the real path for a given virtual path, if possible; otherwise + * return null. + * + * @param path The path to the desired resource + */ + @Override + public String getRealPath(String path) { + if (webappResources instanceof BaseDirContext) { + return ((BaseDirContext) webappResources).getRealPath(path); + } + return null; + } + + /** + * hook to register that we need to scan for security annotations. + * @param wrapper The wrapper for the Servlet that was added + */ + public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) { + Servlet s = wrapper.getServlet(); + if (s != null && createdServlets.contains(s)) { + // Mark the wrapper to indicate annotations need to be scanned + wrapper.setServletSecurityAnnotationScanRequired(true); + } + return new ApplicationServletRegistration(wrapper, this); + } + + /** + * hook to track which registrations need annotation scanning + * @param servlet + */ + public void dynamicServletCreated(Servlet servlet) { + createdServlets.add(servlet); + } + + + /** + * A helper class to manage the filter mappings in a Context. + */ + private static final class ContextFilterMaps { + private final Object lock = new Object(); + + /** + * The set of filter mappings for this application, in the order they + * were defined in the deployment descriptor with additional mappings + * added via the {@link ServletContext} possibly both before and after + * those defined in the deployment descriptor. + */ + private FilterMap[] array = new FilterMap[0]; + + /** + * Filter mappings added via {@link ServletContext} may have to be + * inserted before the mappings in the deployment descriptor but must be + * inserted in the order the {@link ServletContext} methods are called. + * This isn't an issue for the mappings added after the deployment + * descriptor - they are just added to the end - but correctly the + * adding mappings before the deployment descriptor mappings requires + * knowing where the last 'before' mapping was added. + */ + private int insertPoint = 0; + + /** + * Return the set of filter mappings. + */ + public FilterMap[] asArray() { + synchronized (lock) { + return array; + } + } + + /** + * Add a filter mapping at the end of the current set of filter + * mappings. + * + * @param filterMap + * The filter mapping to be added + */ + public void add(FilterMap filterMap) { + synchronized (lock) { + FilterMap results[] = Arrays.copyOf(array, array.length + 1); + results[array.length] = filterMap; + array = results; + } + } + + /** + * Add a filter mapping before the mappings defined in the deployment + * descriptor but after any other mappings added via this method. + * + * @param filterMap + * The filter mapping to be added + */ + public void addBefore(FilterMap filterMap) { + synchronized (lock) { + FilterMap results[] = new FilterMap[array.length + 1]; + System.arraycopy(array, 0, results, 0, insertPoint); + System.arraycopy(array, insertPoint, results, insertPoint + 1, + array.length - insertPoint); + results[insertPoint] = filterMap; + array = results; + insertPoint++; + } + } + + /** + * Remove a filter mapping. + * + * @param filterMap The filter mapping to be removed + */ + public void remove(FilterMap filterMap) { + synchronized (lock) { + // Make sure this filter mapping is currently present + int n = -1; + for (int i = 0; i < array.length; i++) { + if (array[i] == filterMap) { + n = i; + break; + } + } + if (n < 0) + return; + + // Remove the specified filter mapping + FilterMap results[] = new FilterMap[array.length - 1]; + System.arraycopy(array, 0, results, 0, n); + System.arraycopy(array, n + 1, results, n, (array.length - 1) + - n); + array = results; + if (n < insertPoint) { + insertPoint--; + } + } + } + } + + // --------------------------------------------------------- Public Methods + + + /** + * Configure and initialize the set of filters for this Context. + * Return true if all filter initialization completed + * successfully, or false otherwise. + */ + public boolean filterStart() { + + if (getLogger().isDebugEnabled()) + getLogger().debug("Starting filters"); + // Instantiate and record a FilterConfig for each defined filter + boolean ok = true; + synchronized (filterConfigs) { + filterConfigs.clear(); + Iterator names = filterDefs.keySet().iterator(); + while (names.hasNext()) { + String name = names.next(); + if (getLogger().isDebugEnabled()) + getLogger().debug(" Starting filter '" + name + "'"); + ApplicationFilterConfig filterConfig = null; + try { + filterConfig = + new ApplicationFilterConfig(this, filterDefs.get(name)); + filterConfigs.put(name, filterConfig); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error + (sm.getString("standardContext.filterStart", name), t); + ok = false; + } + } + } + + return (ok); + + } + + + /** + * Finalize and release the set of filters for this Context. + * Return true if all filter finalization completed + * successfully, or false otherwise. + */ + public boolean filterStop() { + + if (getLogger().isDebugEnabled()) + getLogger().debug("Stopping filters"); + + // Release all Filter and FilterConfig instances + synchronized (filterConfigs) { + Iterator names = filterConfigs.keySet().iterator(); + while (names.hasNext()) { + String name = names.next(); + if (getLogger().isDebugEnabled()) + getLogger().debug(" Stopping filter '" + name + "'"); + ApplicationFilterConfig filterConfig = filterConfigs.get(name); + filterConfig.release(); + } + filterConfigs.clear(); + } + return (true); + + } + + + /** + * Find and return the initialized FilterConfig for the + * specified filter name, if any; otherwise return null. + * + * @param name Name of the desired filter + */ + public FilterConfig findFilterConfig(String name) { + + return (filterConfigs.get(name)); + + } + + + /** + * Configure the set of instantiated application event listeners + * for this Context. Return true if all listeners wre + * initialized successfully, or false otherwise. + */ + public boolean listenerStart() { + + if (log.isDebugEnabled()) + log.debug("Configuring application event listeners"); + + // Instantiate the required listeners + String listeners[] = findApplicationListeners(); + Object results[] = new Object[listeners.length]; + boolean ok = true; + for (int i = 0; i < results.length; i++) { + if (getLogger().isDebugEnabled()) + getLogger().debug(" Configuring event listener class '" + + listeners[i] + "'"); + try { + results[i] = instanceManager.newInstance(listeners[i]); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error + (sm.getString("standardContext.applicationListener", + listeners[i]), t); + ok = false; + } + } + if (!ok) { + getLogger().error(sm.getString("standardContext.applicationSkipped")); + return (false); + } + + // Sort listeners in two arrays + ArrayList eventListeners = new ArrayList(); + ArrayList lifecycleListeners = new ArrayList(); + for (int i = 0; i < results.length; i++) { + if ((results[i] instanceof ServletContextAttributeListener) + || (results[i] instanceof ServletRequestAttributeListener) + || (results[i] instanceof ServletRequestListener) + || (results[i] instanceof HttpSessionAttributeListener)) { + eventListeners.add(results[i]); + } + if ((results[i] instanceof ServletContextListener) + || (results[i] instanceof HttpSessionListener)) { + lifecycleListeners.add(results[i]); + } + } + + //Listeners may have been added by ServletContextInitializers. Put them after the ones we know about. + for (Object eventListener: getApplicationEventListeners()) { + eventListeners.add(eventListener); + } + setApplicationEventListeners(eventListeners.toArray()); + for (Object lifecycleListener: getApplicationLifecycleListeners()) { + lifecycleListeners.add(lifecycleListener); + } + setApplicationLifecycleListeners(lifecycleListeners.toArray()); + + // Send application start events + + if (getLogger().isDebugEnabled()) + getLogger().debug("Sending application start events"); + + // Ensure context is not null + getServletContext(); + context.setNewServletContextListenerAllowed(false); + + Object instances[] = getApplicationLifecycleListeners(); + if (instances == null) + return (ok); + ServletContextEvent event = + new ServletContextEvent(getServletContext()); + for (int i = 0; i < instances.length; i++) { + if (instances[i] == null) + continue; + if (!(instances[i] instanceof ServletContextListener)) + continue; + ServletContextListener listener = + (ServletContextListener) instances[i]; + try { + fireContainerEvent("beforeContextInitialized", listener); + listener.contextInitialized(event); + fireContainerEvent("afterContextInitialized", listener); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + fireContainerEvent("afterContextInitialized", listener); + getLogger().error + (sm.getString("standardContext.listenerStart", + instances[i].getClass().getName()), t); + ok = false; + } + } + return (ok); + + } + + + /** + * Send an application stop event to all interested listeners. + * Return true if all events were sent successfully, + * or false otherwise. + */ + public boolean listenerStop() { + + if (log.isDebugEnabled()) + log.debug("Sending application stop events"); + + boolean ok = true; + Object listeners[] = getApplicationLifecycleListeners(); + if (listeners != null) { + ServletContextEvent event = + new ServletContextEvent(getServletContext()); + for (int i = 0; i < listeners.length; i++) { + int j = (listeners.length - 1) - i; + if (listeners[j] == null) + continue; + if (listeners[j] instanceof ServletContextListener) { + ServletContextListener listener = + (ServletContextListener) listeners[j]; + try { + fireContainerEvent("beforeContextDestroyed", listener); + listener.contextDestroyed(event); + fireContainerEvent("afterContextDestroyed", listener); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + fireContainerEvent("afterContextDestroyed", listener); + getLogger().error + (sm.getString("standardContext.listenerStop", + listeners[j].getClass().getName()), t); + ok = false; + } + } + try { + getInstanceManager().destroyInstance(listeners[j]); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error + (sm.getString("standardContext.listenerStop", + listeners[j].getClass().getName()), t); + ok = false; + } + } + } + + // Annotation processing + listeners = getApplicationEventListeners(); + if (listeners != null) { + for (int i = 0; i < listeners.length; i++) { + int j = (listeners.length - 1) - i; + if (listeners[j] == null) + continue; + try { + getInstanceManager().destroyInstance(listeners[j]); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error + (sm.getString("standardContext.listenerStop", + listeners[j].getClass().getName()), t); + ok = false; + } + } + } + + setApplicationEventListeners(null); + setApplicationLifecycleListeners(null); + + return (ok); + + } + + + /** + * Allocate resources, including proxy. + * Return true if initialization was successfull, + * or false otherwise. + */ + public boolean resourcesStart() { + + boolean ok = true; + + Hashtable env = new Hashtable(); + if (getParent() != null) + env.put(ProxyDirContext.HOST, getParent().getName()); + env.put(ProxyDirContext.CONTEXT, getName()); + + try { + ProxyDirContext proxyDirContext = + new ProxyDirContext(env, webappResources); + if (webappResources instanceof FileDirContext) { + filesystemBased = true; + ((FileDirContext) webappResources).setAllowLinking + (isAllowLinking()); + } + if (webappResources instanceof BaseDirContext) { + ((BaseDirContext) webappResources).setDocBase(getBasePath()); + ((BaseDirContext) webappResources).setCached + (isCachingAllowed()); + ((BaseDirContext) webappResources).setCacheTTL(getCacheTTL()); + ((BaseDirContext) webappResources).setCacheMaxSize + (getCacheMaxSize()); + ((BaseDirContext) webappResources).allocate(); + // Alias support + ((BaseDirContext) webappResources).setAliases(getAliases()); + + if (effectiveMajorVersion >=3 && addWebinfClassesResources) { + try { + DirContext webInfCtx = + (DirContext) webappResources.lookup( + "/WEB-INF/classes"); + // Do the lookup to make sure it exists + webInfCtx.lookup("META-INF/resources"); + ((BaseDirContext) webappResources).addAltDirContext( + webInfCtx); + } catch (NamingException e) { + // Doesn't exist - ignore and carry on + } + } + } + // Register the cache in JMX + if (isCachingAllowed()) { + String contextName = getName(); + if (!contextName.startsWith("/")) { + contextName = "/" + contextName; + } + ObjectName resourcesName = + new ObjectName(this.getDomain() + ":type=Cache,host=" + + getHostname() + ",context=" + contextName); + Registry.getRegistry(null, null).registerComponent + (proxyDirContext.getCache(), resourcesName, null); + } + this.resources = proxyDirContext; + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("standardContext.resourcesStart"), t); + ok = false; + } + + return (ok); + + } + + + /** + * Deallocate resources and destroy proxy. + */ + public boolean resourcesStop() { + + boolean ok = true; + + try { + if (resources != null) { + if (resources instanceof Lifecycle) { + ((Lifecycle) resources).stop(); + } + if (webappResources instanceof BaseDirContext) { + ((BaseDirContext) webappResources).release(); + } + // Unregister the cache in JMX + if (isCachingAllowed()) { + String contextName = getName(); + if (!contextName.startsWith("/")) { + contextName = "/" + contextName; + } + ObjectName resourcesName = + new ObjectName(this.getDomain() + + ":type=Cache,host=" + + getHostname() + ",context=" + + contextName); + Registry.getRegistry(null, null) + .unregisterComponent(resourcesName); + } + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("standardContext.resourcesStop"), t); + ok = false; + } + + this.resources = null; + + return (ok); + + } + + + /** + * Load and initialize all servlets marked "load on startup" in the + * web application deployment descriptor. + * + * @param children Array of wrappers for all currently defined + * servlets (including those not declared load on startup) + */ + public void loadOnStartup(Container children[]) { + + // Collect "load on startup" servlets that need to be initialized + TreeMap> map = + new TreeMap>(); + for (int i = 0; i < children.length; i++) { + Wrapper wrapper = (Wrapper) children[i]; + int loadOnStartup = wrapper.getLoadOnStartup(); + if (loadOnStartup < 0) + continue; + Integer key = Integer.valueOf(loadOnStartup); + ArrayList list = map.get(key); + if (list == null) { + list = new ArrayList(); + map.put(key, list); + } + list.add(wrapper); + } + + // Load the collected "load on startup" servlets + for (ArrayList list : map.values()) { + for (Wrapper wrapper : list) { + try { + wrapper.load(); + } catch (ServletException e) { + getLogger().error(sm.getString("standardWrapper.loadException", + getName()), StandardWrapper.getRootCause(e)); + // NOTE: load errors (including a servlet that throws + // UnavailableException from tht init() method) are NOT + // fatal to application startup + } + } + } + + } + + + /** + * Start this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void startInternal() throws LifecycleException { + + if(log.isDebugEnabled()) + log.debug("Starting " + getBaseName()); + + // Send j2ee.state.starting notification + if (this.getObjectName() != null) { + Notification notification = new Notification("j2ee.state.starting", + this.getObjectName(), sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + + setConfigured(false); + boolean ok = true; + + // Currently this is effectively a NO-OP but needs to be called to + // ensure the NamingResources follows the correct lifecycle + if (namingResources != null) { + namingResources.start(); + } + + // Add missing components as necessary + if (webappResources == null) { // (1) Required by Loader + if (log.isDebugEnabled()) + log.debug("Configuring default Resources"); + try { + if ((getDocBase() != null) && (getDocBase().endsWith(".war")) && + (!(new File(getBasePath())).isDirectory())) + setResources(new WARDirContext()); + else + setResources(new FileDirContext()); + } catch (IllegalArgumentException e) { + log.error("Error initializing resources: " + e.getMessage()); + ok = false; + } + } + if (ok) { + if (!resourcesStart()) { + log.error( "Error in resourceStart()"); + ok = false; + } + } + + if (getLoader() == null) { + WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); + webappLoader.setDelegate(getDelegate()); + setLoader(webappLoader); + } + + // Initialize character set mapper + getCharsetMapper(); + + // Post work directory + postWorkDirectory(); + + // Validate required extensions + boolean dependencyCheck = true; + try { + dependencyCheck = ExtensionValidator.validateApplication + (getResources(), this); + } catch (IOException ioe) { + log.error("Error in dependencyCheck", ioe); + dependencyCheck = false; + } + + if (!dependencyCheck) { + // do not make application available if depency check fails + ok = false; + } + + // Reading the "catalina.useNaming" environment variable + String useNamingProperty = System.getProperty("catalina.useNaming"); + if ((useNamingProperty != null) + && (useNamingProperty.equals("false"))) { + useNaming = false; + } + + if (ok && isUseNaming()) { + if (getNamingContextListener() == null) { + NamingContextListener ncl = new NamingContextListener(); + ncl.setName(getNamingContextName()); + addLifecycleListener(ncl); + setNamingContextListener(ncl); + } + } + + // Standard container startup + if (log.isDebugEnabled()) + log.debug("Processing standard container startup"); + + + // Binding thread + ClassLoader oldCCL = bindThread(); + + try { + + if (ok) { + + // Start our subordinate components, if any + if ((loader != null) && (loader instanceof Lifecycle)) + ((Lifecycle) loader).start(); + + // since the loader just started, the webapp classloader is now + // created. + // By calling unbindThread and bindThread in a row, we setup the + // current Thread CCL to be the webapp classloader + unbindThread(oldCCL); + oldCCL = bindThread(); + + // Initialize logger again. Other components might have used it too early, + // so it should be reset. + logger = null; + getLogger(); + if ((logger != null) && (logger instanceof Lifecycle)) + ((Lifecycle) logger).start(); + + if ((cluster != null) && (cluster instanceof Lifecycle)) + ((Lifecycle) cluster).start(); + if ((realm != null) && (realm instanceof Lifecycle)) + ((Lifecycle) realm).start(); + if ((resources != null) && (resources instanceof Lifecycle)) + ((Lifecycle) resources).start(); + + // Notify our interested LifecycleListeners + fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null); + + // Start our child containers, if not already started + for (Container child : findChildren()) { + if (!child.getState().isAvailable()) { + child.start(); + } + } + + // Start the Valves in our pipeline (including the basic), + // if any + if (pipeline instanceof Lifecycle) { + ((Lifecycle) pipeline).start(); + } + + // Acquire clustered manager + Manager contextManager = null; + if (manager == null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("standardContext.cluster.noManager", + Boolean.valueOf((getCluster() != null)), + Boolean.valueOf(distributable))); + } + if ( (getCluster() != null) && distributable) { + try { + contextManager = getCluster().createManager(getName()); + } catch (Exception ex) { + log.error("standardContext.clusterFail", ex); + ok = false; + } + } else { + contextManager = new StandardManager(); + } + } + + // Configure default manager if none was specified + if (contextManager != null) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("standardContext.manager", + contextManager.getClass().getName())); + } + setManager(contextManager); + } + + if (manager!=null && (getCluster() != null) && distributable) { + //let the cluster know that there is a context that is distributable + //and that it has its own manager + getCluster().registerManager(manager); + } + } + + } finally { + // Unbinding thread + unbindThread(oldCCL); + } + + if (!getConfigured()) { + log.error( "Error getConfigured"); + ok = false; + } + + // We put the resources into the servlet context + if (ok) + getServletContext().setAttribute + (Globals.RESOURCES_ATTR, getResources()); + + // Initialize associated mapper + mapper.setContext(getPath(), welcomeFiles, resources); + + // Binding thread + oldCCL = bindThread(); + + if (ok ) { + if (getInstanceManager() == null) { + javax.naming.Context context = null; + if (isUseNaming() && getNamingContextListener() != null) { + context = getNamingContextListener().getEnvContext(); + } + Map> injectionMap = buildInjectionMap( + getIgnoreAnnotations() ? new NamingResources(): getNamingResources()); + setInstanceManager(new DefaultInstanceManager(context, + injectionMap, this, this.getClass().getClassLoader())); + getServletContext().setAttribute( + InstanceManager.class.getName(), getInstanceManager()); + } + } + + try { + // Create context attributes that will be required + if (ok) { + getServletContext().setAttribute( + JarScanner.class.getName(), getJarScanner()); + } + + // Set up the context init params + mergeParameters(); + + // Call ServletContainerInitializers + for (Map.Entry>> entry : + initializers.entrySet()) { + try { + entry.getKey().onStartup(entry.getValue(), + getServletContext()); + } catch (ServletException e) { + // TODO: Log error + ok = false; + break; + } + } + + // Configure and call application event listeners + if (ok) { + if (!listenerStart()) { + log.error( "Error listenerStart"); + ok = false; + } + } + + try { + // Start manager + if ((manager != null) && (manager instanceof Lifecycle)) { + ((Lifecycle) getManager()).start(); + } + + // Start ContainerBackgroundProcessor thread + super.threadStart(); + } catch(Exception e) { + log.error("Error manager.start()", e); + ok = false; + } + + // Configure and call application filters + if (ok) { + if (!filterStart()) { + log.error("Error filterStart"); + ok = false; + } + } + + // Load and initialize all "load on startup" servlets + if (ok) { + loadOnStartup(findChildren()); + } + + } finally { + // Unbinding thread + unbindThread(oldCCL); + } + + // Set available status depending upon startup success + if (ok) { + if (log.isDebugEnabled()) + log.debug("Starting completed"); + } else { + log.error(sm.getString("standardContext.startFailed", getName())); + } + + startTime=System.currentTimeMillis(); + + // Send j2ee.state.running notification + if (ok && (this.getObjectName() != null)) { + Notification notification = + new Notification("j2ee.state.running", this.getObjectName(), + sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + + // Close all JARs right away to avoid always opening a peak number + // of files on startup + if (getLoader() instanceof WebappLoader) { + ((WebappLoader) getLoader()).closeJARs(true); + } + + // Reinitializing if something went wrong + if (!ok) { + setState(LifecycleState.FAILED); + } else { + setState(LifecycleState.STARTING); + } + } + + private Map> buildInjectionMap(NamingResources namingResources) { + Map> injectionMap = new HashMap>(); + for (Injectable resource: namingResources.findLocalEjbs()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findEjbs()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findEnvironments()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findMessageDestinationRefs()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findResourceEnvRefs()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findResources()) { + addInjectionTarget(resource, injectionMap); + } + for (Injectable resource: namingResources.findServices()) { + addInjectionTarget(resource, injectionMap); + } + return injectionMap; + } + + private void addInjectionTarget(Injectable resource, Map> injectionMap) { + List injectionTargets = resource.getInjectionTargets(); + if (injectionTargets != null && injectionTargets.size() > 0) { + String jndiName = resource.getName(); + for (InjectionTarget injectionTarget: injectionTargets) { + String clazz = injectionTarget.getTargetClass(); + Map injections = injectionMap.get(clazz); + if (injections == null) { + injections = new HashMap(); + injectionMap.put(clazz, injections); + } + injections.put(injectionTarget.getTargetName(), jndiName); + } + } + } + + + + /** + * Merge the context initialization parameters specified in the application + * deployment descriptor with the application parameters described in the + * server configuration, respecting the override property of + * the application parameters appropriately. + */ + private void mergeParameters() { + Map mergedParams = new HashMap(); + + String names[] = findParameters(); + for (int i = 0; i < names.length; i++) { + mergedParams.put(names[i], findParameter(names[i])); + } + + ApplicationParameter params[] = findApplicationParameters(); + for (int i = 0; i < params.length; i++) { + if (params[i].getOverride()) { + if (mergedParams.get(params[i].getName()) == null) { + mergedParams.put(params[i].getName(), + params[i].getValue()); + } + } else { + mergedParams.put(params[i].getName(), params[i].getValue()); + } + } + + ServletContext sc = getServletContext(); + for (Map.Entry entry : mergedParams.entrySet()) { + sc.setInitParameter(entry.getKey(), entry.getValue()); + } + + } + + + /** + * Stop this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void stopInternal() throws LifecycleException { + + // Send j2ee.state.stopping notification + if (this.getObjectName() != null) { + Notification notification = + new Notification("j2ee.state.stopping", this.getObjectName(), + sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + + setState(LifecycleState.STOPPING); + + // Binding thread + ClassLoader oldCCL = bindThread(); + + try { + + // Stop our child containers, if any + final Container[] children = findChildren(); + + ClassLoader old = bindThread(); + try { + for (int i = 0; i < children.length; i++) { + children[i].stop(); + } + + // Stop our filters + filterStop(); + + // Stop ContainerBackgroundProcessor thread + threadStop(); + + if (manager != null && manager instanceof Lifecycle && + ((Lifecycle) manager).getState().isAvailable()) { + ((Lifecycle) manager).stop(); + } + + // Stop our application listeners + listenerStop(); + } finally{ + unbindThread(old); + } + + // Finalize our character set mapper + setCharsetMapper(null); + + // Normal container shutdown processing + if (log.isDebugEnabled()) + log.debug("Processing standard container shutdown"); + + // JNDI resources are unbound in CONFIGURE_STOP_EVENT so stop + // naming resoucres before they are unbound since NamingResoucres + // does a JNDI lookup to retrieve the resource. This needs to be + // after the application has finished with the resource + if (namingResources != null) { + namingResources.stop(); + } + + fireLifecycleEvent(Lifecycle.CONFIGURE_STOP_EVENT, null); + + // Stop the Valves in our pipeline (including the basic), if any + if (pipeline instanceof Lifecycle && + ((Lifecycle) pipeline).getState().isAvailable()) { + ((Lifecycle) pipeline).stop(); + } + + // Clear all application-originated servlet context attributes + if (context != null) + context.clearAttributes(); + + // Stop resources + resourcesStop(); + + if ((realm != null) && (realm instanceof Lifecycle)) { + ((Lifecycle) realm).stop(); + } + if ((cluster != null) && (cluster instanceof Lifecycle)) { + ((Lifecycle) cluster).stop(); + } + if ((logger != null) && (logger instanceof Lifecycle)) { + ((Lifecycle) logger).stop(); + } + if ((loader != null) && (loader instanceof Lifecycle)) { + ((Lifecycle) loader).stop(); + } + + } finally { + + // Unbinding thread + unbindThread(oldCCL); + + } + + // Send j2ee.state.stopped notification + if (this.getObjectName() != null) { + Notification notification = + new Notification("j2ee.state.stopped", this.getObjectName(), + sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + + // Reset application context + context = null; + + // This object will no longer be visible or used. + try { + resetContext(); + } catch( Exception ex ) { + log.error( "Error reseting context " + this + " " + ex, ex ); + } + + //reset the instance manager + instanceManager = null; + + if (log.isDebugEnabled()) + log.debug("Stopping complete"); + + } + + /** Destroy needs to clean up the context completely. + * + * The problem is that undoing all the config in start() and restoring + * a 'fresh' state is impossible. After stop()/destroy()/init()/start() + * we should have the same state as if a fresh start was done - i.e + * read modified web.xml, etc. This can only be done by completely + * removing the context object and remapping a new one, or by cleaning + * up everything. + * + * XXX Should this be done in stop() ? + * + */ + @Override + protected void destroyInternal() throws LifecycleException { + + if ((manager != null) && (manager instanceof Lifecycle)) { + ((Lifecycle) manager).destroy(); + } + if ((realm != null) && (realm instanceof Lifecycle)) { + ((Lifecycle) realm).destroy(); + } + if ((cluster != null) && (cluster instanceof Lifecycle)) { + ((Lifecycle) cluster).destroy(); + } + if ((logger != null) && (logger instanceof Lifecycle)) { + ((Lifecycle) logger).destroy(); + } + if ((loader != null) && (loader instanceof Lifecycle)) { + ((Lifecycle) loader).destroy(); + } + + // If in state NEW when destroy is called, the object name will never + // have been set so the notification can't be created + if (getObjectName() != null) { + // Send j2ee.object.deleted notification + Notification notification = + new Notification("j2ee.object.deleted", this.getObjectName(), + sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + + if (namingResources != null) { + namingResources.destroy(); + } + + synchronized (instanceListenersLock) { + instanceListeners = new String[0]; + } + + super.destroyInternal(); + } + + private void resetContext() throws Exception { + // Restore the original state ( pre reading web.xml in start ) + // If you extend this - override this method and make sure to clean up + + // Don't reset anything that is read from a element since + // elements are read at initialisation will not be read + // again for this object + children = new HashMap(); + startupTime = 0; + startTime = 0; + tldScanTime = 0; + + // Bugzilla 32867 + distributable = false; + + applicationListeners = new String[0]; + applicationEventListenersObjects = new Object[0]; + applicationLifecycleListenersObjects = new Object[0]; + jspConfigDescriptor = new ApplicationJspConfigDescriptor(); + + initializers.clear(); + + createdServlets.clear(); + + if(log.isDebugEnabled()) + log.debug("resetContext " + getObjectName()); + } + + /** + * Return a String representation of this component. + */ + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + if (getParent() != null) { + sb.append(getParent().toString()); + sb.append("."); + } + sb.append("StandardContext["); + sb.append(getName()); + sb.append("]"); + return (sb.toString()); + + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Adjust the URL pattern to begin with a leading slash, if appropriate + * (i.e. we are running a servlet 2.2 application). Otherwise, return + * the specified URL pattern unchanged. + * + * @param urlPattern The URL pattern to be adjusted (if needed) + * and returned + */ + protected String adjustURLPattern(String urlPattern) { + + if (urlPattern == null) + return (urlPattern); + if (urlPattern.startsWith("/") || urlPattern.startsWith("*.")) + return (urlPattern); + if (!isServlet22()) + return (urlPattern); + if(log.isDebugEnabled()) + log.debug(sm.getString("standardContext.urlPattern.patternWarning", + urlPattern)); + return ("/" + urlPattern); + + } + + + /** + * Are we processing a version 2.2 deployment descriptor? + */ + @Override + public boolean isServlet22() { + + if (this.publicId == null) + return (false); + if (this.publicId.equals + (org.apache.catalina.startup.Constants.WebDtdPublicId_22)) + return (true); + else + return (false); + + } + + @Override + public Set addServletSecurity( + ApplicationServletRegistration registration, + ServletSecurityElement servletSecurityElement) { + + Set conflicts = new HashSet(); + + Collection urlPatterns = registration.getMappings(); + for (String urlPattern : urlPatterns) { + boolean foundConflict = false; + + SecurityConstraint[] securityConstraints = + findConstraints(); + for (SecurityConstraint securityConstraint : securityConstraints) { + + SecurityCollection[] collections = + securityConstraint.findCollections(); + for (SecurityCollection collection : collections) { + if (collection.findPattern(urlPattern)) { + // First pattern found will indicate if there is a + // conflict since for any given pattern all matching + // constraints will be from either the descriptor or + // not. It is not permitted to have a mixture + if (collection.isFromDescriptor()) { + // Skip this pattern + foundConflict = true; + conflicts.add(urlPattern); + } else { + // Need to overwrite constraint for this pattern + // so remove every pattern found + + // TODO spec 13.4.2 appears to say only the + // conflicting pattern is overwritten, not the + // entire security constraint. + removeConstraint(securityConstraint); + } + } + if (foundConflict) { + break; + } + } + if (foundConflict) { + break; + } + } + // TODO spec 13.4.2 appears to say that non-conflicting patterns are + // still used. + // TODO you can't calculate the eventual security constraint now, + // you have to wait until the context is started, since application + // code can add url patterns after calling setSecurity. + if (!foundConflict) { + SecurityConstraint[] newSecurityConstraints = + SecurityConstraint.createConstraints( + servletSecurityElement, + urlPattern); + for (SecurityConstraint securityConstraint : + newSecurityConstraints) { + addConstraint(securityConstraint); + } + } + } + + return conflicts; + + } + + + /** + * Return a File object representing the base directory for the + * entire servlet container (i.e. the Engine container if present). + */ + protected File engineBase() { + String base=System.getProperty(Globals.CATALINA_BASE_PROP); + if( base == null ) { + StandardEngine eng=(StandardEngine)this.getParent().getParent(); + base=eng.getBaseDir(); + } + return (new File(base)); + } + + + /** + * Bind current thread, both for CL purposes and for JNDI ENC support + * during : startup, shutdown and realoading of the context. + * + * @return the previous context class loader + */ + protected ClassLoader bindThread() { + + ClassLoader oldContextClassLoader = + Thread.currentThread().getContextClassLoader(); + + if (getResources() == null) + return oldContextClassLoader; + + if (getLoader().getClassLoader() != null) { + Thread.currentThread().setContextClassLoader + (getLoader().getClassLoader()); + } + + DirContextURLStreamHandler.bindThread(getResources()); + + if (isUseNaming()) { + try { + ContextBindings.bindThread(this, this); + } catch (NamingException e) { + // Silent catch, as this is a normal case during the early + // startup stages + } + } + + return oldContextClassLoader; + + } + + + /** + * Unbind thread. + */ + protected void unbindThread(ClassLoader oldContextClassLoader) { + + if (isUseNaming()) { + ContextBindings.unbindThread(this, this); + } + + DirContextURLStreamHandler.unbindThread(); + + Thread.currentThread().setContextClassLoader(oldContextClassLoader); + } + + + + /** + * Get base path. + */ + protected String getBasePath() { + String docBase = null; + Container container = this; + while (container != null) { + if (container instanceof Host) + break; + container = container.getParent(); + } + File file = new File(getDocBase()); + if (!file.isAbsolute()) { + if (container == null) { + docBase = (new File(engineBase(), getDocBase())).getPath(); + } else { + // Use the "appBase" property of this container + file = ((Host) container).getAppBaseFile(); + docBase = (new File(file, getDocBase())).getPath(); + } + } else { + docBase = file.getPath(); + } + return docBase; + } + + + /** + * Get naming context full name. + */ + private String getNamingContextName() { + if (namingContextName == null) { + Container parent = getParent(); + if (parent == null) { + namingContextName = getName(); + } else { + Stack stk = new Stack(); + StringBuilder buff = new StringBuilder(); + while (parent != null) { + stk.push(parent.getName()); + parent = parent.getParent(); + } + while (!stk.empty()) { + buff.append("/" + stk.pop()); + } + buff.append(getName()); + namingContextName = buff.toString(); + } + } + return namingContextName; + } + + + /** + * Naming context listener accessor. + */ + public NamingContextListener getNamingContextListener() { + return namingContextListener; + } + + + /** + * Naming context listener setter. + */ + public void setNamingContextListener(NamingContextListener namingContextListener) { + this.namingContextListener = namingContextListener; + } + + + /** + * Return the request processing paused flag for this Context. + */ + @Override + public boolean getPaused() { + + return (this.paused); + + } + + + public String getHostname() { + Container parentHost = getParent(); + if (parentHost != null) { + hostName = parentHost.getName(); + } + if ((hostName == null) || (hostName.length() < 1)) + hostName = "_"; + return hostName; + } + + + @Override + public boolean fireRequestInitEvent(ServletRequest request) { + + Object instances[] = getApplicationEventListeners(); + + if ((instances != null) && (instances.length > 0)) { + + ServletRequestEvent event = + new ServletRequestEvent(getServletContext(), request); + + for (int i = 0; i < instances.length; i++) { + if (instances[i] == null) + continue; + if (!(instances[i] instanceof ServletRequestListener)) + continue; + ServletRequestListener listener = + (ServletRequestListener) instances[i]; + + try { + listener.requestInitialized(event); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error(sm.getString( + "standardContext.requestListener.requestInit", + instances[i].getClass().getName()), t); + request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); + return false; + } + } + } + return true; + } + + + @Override + public boolean fireRequestDestroyEvent(ServletRequest request) { + Object instances[] = getApplicationEventListeners(); + + if ((instances != null) && (instances.length > 0)) { + + ServletRequestEvent event = + new ServletRequestEvent(getServletContext(), request); + + for (int i = 0; i < instances.length; i++) { + int j = (instances.length -1) -i; + if (instances[j] == null) + continue; + if (!(instances[j] instanceof ServletRequestListener)) + continue; + ServletRequestListener listener = + (ServletRequestListener) instances[j]; + + try { + listener.requestDestroyed(event); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error(sm.getString( + "standardContext.requestListener.requestInit", + instances[j].getClass().getName()), t); + request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); + return false; + } + } + } + return true; + } + + + /** + * Set the appropriate context attribute for our work directory. + */ + private void postWorkDirectory() { + + // Acquire (or calculate) the work directory path + String workDir = getWorkDir(); + if (workDir == null || workDir.length() == 0) { + + // Retrieve our parent (normally a host) name + String hostName = null; + String engineName = null; + String hostWorkDir = null; + Container parentHost = getParent(); + if (parentHost != null) { + hostName = parentHost.getName(); + if (parentHost instanceof StandardHost) { + hostWorkDir = ((StandardHost)parentHost).getWorkDir(); + } + Container parentEngine = parentHost.getParent(); + if (parentEngine != null) { + engineName = parentEngine.getName(); + } + } + if ((hostName == null) || (hostName.length() < 1)) + hostName = "_"; + if ((engineName == null) || (engineName.length() < 1)) + engineName = "_"; + + String temp = getName(); + if (temp.startsWith("/")) + temp = temp.substring(1); + temp = temp.replace('/', '_'); + temp = temp.replace('\\', '_'); + if (temp.length() < 1) + temp = "_"; + if (hostWorkDir != null ) { + workDir = hostWorkDir + File.separator + temp; + } else { + workDir = "work" + File.separator + engineName + + File.separator + hostName + File.separator + temp; + } + setWorkDir(workDir); + } + + // Create this directory if necessary + File dir = new File(workDir); + if (!dir.isAbsolute()) { + File catalinaHome = engineBase(); + String catalinaHomePath = null; + try { + catalinaHomePath = catalinaHome.getCanonicalPath(); + dir = new File(catalinaHomePath, workDir); + } catch (IOException e) { + log.warn(sm.getString("standardContext.workCreateException", + workDir, catalinaHomePath, getName()), e); + } + } + if (!dir.mkdirs() && !dir.isDirectory()) { + log.warn(sm.getString("standardContext.workCreateFail", dir, + getName())); + } + + // Set the appropriate servlet context attribute + if (context == null) { + getServletContext(); + } + context.setAttribute(ServletContext.TEMPDIR, dir); + context.setAttributeReadOnly(ServletContext.TEMPDIR); + } + + + /** + * Set the request processing paused flag for this Context. + * + * @param paused The new request processing paused flag + */ + private void setPaused(boolean paused) { + + this.paused = paused; + + } + + + /** + * Validate the syntax of a proposed <url-pattern> + * for conformance with specification requirements. + * + * @param urlPattern URL pattern to be validated + */ + private boolean validateURLPattern(String urlPattern) { + + if (urlPattern == null) + return (false); + if (urlPattern.indexOf('\n') >= 0 || urlPattern.indexOf('\r') >= 0) { + return (false); + } + if (urlPattern.startsWith("*.")) { + if (urlPattern.indexOf('/') < 0) { + checkUnusualURLPattern(urlPattern); + return (true); + } else + return (false); + } + if ( (urlPattern.startsWith("/")) && + (urlPattern.indexOf("*.") < 0)) { + checkUnusualURLPattern(urlPattern); + return (true); + } else + return (false); + + } + + + /** + * Check for unusual but valid <url-pattern>s. + * See Bugzilla 34805, 43079 & 43080 + */ + private void checkUnusualURLPattern(String urlPattern) { + if (log.isInfoEnabled()) { + if(urlPattern.endsWith("*") && (urlPattern.length() < 2 || + urlPattern.charAt(urlPattern.length()-2) != '/')) { + log.info("Suspicious url pattern: \"" + urlPattern + "\"" + + " in context [" + getName() + "] - see" + + " section SRV.11.2 of the Servlet specification" ); + } + } + } + + + // ------------------------------------------------------------- Operations + + + /** + * JSR77 deploymentDescriptor attribute + * + * @return string deployment descriptor + */ + public String getDeploymentDescriptor() { + + InputStream stream = null; + ServletContext servletContext = getServletContext(); + if (servletContext != null) { + stream = servletContext.getResourceAsStream( + org.apache.catalina.startup.Constants.ApplicationWebXml); + } + if (stream == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(stream)); + String strRead = ""; + while (strRead != null) { + sb.append(strRead); + strRead = br.readLine(); + } + } catch (IOException e) { + return ""; + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException ioe) {/*Ignore*/} + } + } + + return sb.toString(); + } + + + /** + * JSR77 servlets attribute + * + * @return list of all servlets ( we know about ) + */ + public String[] getServlets() { + + String[] result = null; + + Container[] children = findChildren(); + if (children != null) { + result = new String[children.length]; + for( int i=0; i< children.length; i++ ) { + result[i] = children[i].getObjectName().toString(); + } + } + + return result; + } + + + @Override + protected String getObjectNameKeyProperties() { + + StringBuilder keyProperties = + new StringBuilder("j2eeType=WebModule,"); + keyProperties.append(getObjectKeyPropertiesNameOnly()); + keyProperties.append(",J2EEApplication="); + keyProperties.append(getJ2EEApplication()); + keyProperties.append(",J2EEServer="); + keyProperties.append(getJ2EEServer()); + + return keyProperties.toString(); + } + + private String getObjectKeyPropertiesNameOnly() { + StringBuilder result = new StringBuilder("name=//"); + String hostname = getParent().getName(); + if (hostname == null) { + result.append("DEFAULT"); + } else { + result.append(hostname); + } + + String contextName = getName(); + if (!contextName.startsWith("/")) { + result.append('/'); + } + result.append(contextName); + + return result.toString(); + } + + @Override + protected void initInternal() throws LifecycleException { + super.initInternal(); + + if (processTlds) { + this.addLifecycleListener(new TldConfig()); + } + + // Register the naming resources + if (namingResources != null) { + namingResources.init(); + } + + // Send j2ee.object.created notification + if (this.getObjectName() != null) { + Notification notification = new Notification("j2ee.object.created", + this.getObjectName(), sequenceNumber.getAndIncrement()); + broadcaster.sendNotification(notification); + } + } + + + /* Remove a JMX notficationListener + * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + @Override + public void removeNotificationListener(NotificationListener listener, + NotificationFilter filter, Object object) throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener,filter,object); + } + + private MBeanNotificationInfo[] notificationInfo; + + /* Get JMX Broadcaster Info + * @TODO use StringManager for international support! + * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed! + * @see javax.management.NotificationBroadcaster#getNotificationInfo() + */ + @Override + public MBeanNotificationInfo[] getNotificationInfo() { + // FIXME: i18n + if(notificationInfo == null) { + notificationInfo = new MBeanNotificationInfo[]{ + new MBeanNotificationInfo(new String[] { + "j2ee.object.created"}, + Notification.class.getName(), + "web application is created" + ), + new MBeanNotificationInfo(new String[] { + "j2ee.state.starting"}, + Notification.class.getName(), + "change web application is starting" + ), + new MBeanNotificationInfo(new String[] { + "j2ee.state.running"}, + Notification.class.getName(), + "web application is running" + ), + new MBeanNotificationInfo(new String[] { + "j2ee.state.stopping"}, + Notification.class.getName(), + "web application start to stopped" + ), + new MBeanNotificationInfo(new String[] { + "j2ee.object.stopped"}, + Notification.class.getName(), + "web application is stopped" + ), + new MBeanNotificationInfo(new String[] { + "j2ee.object.deleted"}, + Notification.class.getName(), + "web application is deleted" + ) + }; + + } + + return notificationInfo; + } + + + /* Add a JMX-NotificationListener + * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object) + */ + @Override + public void addNotificationListener(NotificationListener listener, + NotificationFilter filter, Object object) throws IllegalArgumentException { + broadcaster.addNotificationListener(listener,filter,object); + } + + + /** + * Remove a JMX-NotificationListener + * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener) + */ + @Override + public void removeNotificationListener(NotificationListener listener) + throws ListenerNotFoundException { + broadcaster.removeNotificationListener(listener); + } + + + // ------------------------------------------------------------- Attributes + + + /** + * Return the naming resources associated with this web application. + */ + public javax.naming.directory.DirContext getStaticResources() { + + return getResources(); + + } + + + /** + * Return the naming resources associated with this web application. + * FIXME: Fooling introspection ... + */ + public javax.naming.directory.DirContext findStaticResources() { + + return getResources(); + + } + + + /** + * Return the naming resources associated with this web application. + */ + public String[] getWelcomeFiles() { + + return findWelcomeFiles(); + + } + + /** + * Set the validation feature of the XML parser used when + * parsing xml instances. + * @param webXmlValidation true to enable xml instance validation + */ + @Override + public void setXmlValidation(boolean webXmlValidation){ + + this.webXmlValidation = webXmlValidation; + + } + + /** + * Get the server.xml attribute's xmlValidation. + * @return true if validation is enabled. + * + */ + @Override + public boolean getXmlValidation(){ + return webXmlValidation; + } + + + /** + * Get the server.xml attribute's xmlNamespaceAware. + * @return true if namespace awarenes is enabled. + */ + @Override + public boolean getXmlNamespaceAware(){ + return webXmlNamespaceAware; + } + + + /** + * Set the namespace aware feature of the XML parser used when + * parsing xml instances. + * @param webXmlNamespaceAware true to enable namespace awareness + */ + @Override + public void setXmlNamespaceAware(boolean webXmlNamespaceAware){ + this.webXmlNamespaceAware= webXmlNamespaceAware; + } + + + /** + * Set the validation feature of the XML parser used when + * parsing tlds files. + * @param tldValidation true to enable xml instance validation + */ + @Override + public void setTldValidation(boolean tldValidation){ + + this.tldValidation = tldValidation; + + } + + /** + * Get the server.xml attribute's webXmlValidation. + * @return true if validation is enabled. + * + */ + @Override + public boolean getTldValidation(){ + return tldValidation; + } + + /** + * Sets the process TLDs attribute. + * + * @param newProcessTlds The new value + */ + public void setProcessTlds(boolean newProcessTlds) { + processTlds = newProcessTlds; + } + + /** + * Returns the processTlds attribute value. + */ + public boolean getProcessTlds() { + return processTlds; + } + + /** + * Get the server.xml <host> attribute's xmlNamespaceAware. + * @return true if namespace awarenes is enabled. + */ + @Override + public boolean getTldNamespaceAware(){ + return tldNamespaceAware; + } + + + /** + * Set the namespace aware feature of the XML parser used when + * parsing xml instances. + * @param tldNamespaceAware true to enable namespace awareness + */ + @Override + public void setTldNamespaceAware(boolean tldNamespaceAware){ + this.tldNamespaceAware= tldNamespaceAware; + } + + + /** + * Support for "stateManageable" JSR77 + */ + public boolean isStateManageable() { + return true; + } + + public void startRecursive() throws LifecycleException { + // nothing to start recursive, the servlets will be started by load-on-startup + start(); + } + + /** + * The J2EE Server ObjectName this module is deployed on. + */ + private String server = null; + + /** + * The Java virtual machines on which this module is running. + */ + private String[] javaVMs = null; + + public String getServer() { + return server; + } + + public String setServer(String server) { + return this.server=server; + } + + public String[] getJavaVMs() { + return javaVMs; + } + + public String[] setJavaVMs(String[] javaVMs) { + return this.javaVMs = javaVMs; + } + + /** + * Gets the time this context was started. + * + * @return Time (in milliseconds since January 1, 1970, 00:00:00) when this + * context was started + */ + public long getStartTime() { + return startTime; + } + + public boolean isEventProvider() { + return false; + } + + public boolean isStatisticsProvider() { + return false; + } + + private abstract static class RunnableWithLifecycleException + implements Runnable { + + protected LifecycleException le = null; + + public LifecycleException getLifecycleException() { + return le; + } + } +} diff --git a/java/org/apache/catalina/core/StandardHost.java b/java/org/apache/catalina/core/StandardHost.java index 3b1fb50..5419f1f 100644 --- a/java/org/apache/catalina/core/StandardHost.java +++ b/java/org/apache/catalina/core/StandardHost.java @@ -1,851 +1,857 @@ -/* - * 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 java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.regex.Pattern; - -import org.apache.catalina.Container; -import org.apache.catalina.Context; -import org.apache.catalina.Globals; -import org.apache.catalina.Host; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.Valve; -import org.apache.catalina.loader.WebappClassLoader; -import org.apache.catalina.mbeans.MBeanUtils; -import org.apache.catalina.valves.ValveBase; -import org.apache.tomcat.util.ExceptionUtils; - - -/** - * Standard implementation of the Host 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 $Id$ - */ - -public class StandardHost extends ContainerBase implements Host { - - private static final org.apache.juli.logging.Log log= - org.apache.juli.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]; - - private final Object aliasesLock = new Object(); - - - /** - * The application root for this Host. - */ - private String appBase = "webapps"; - private volatile File appBaseFile = null; - - /** - * The XML root for this Host. - */ - private String xmlBase = null; - - /** - * 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 deploy on startup flag for this Host. - */ - private boolean deployOnStartup = true; - - - /** - * deploy Context XML config files property. - */ - private boolean deployXML = true; - - - /** - * Should XML files be copied to $CATALINA_BASE/conf// by - * default when a web application is deployed? - */ - private boolean copyXML = false; - - - /** - * 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; - - - /** - * Should we create directories upon startup for appBase and xmlBase - */ - private boolean createDirs = true; - - - /** - * Track the class loaders for the child web applications so memory leaks - * can be detected. - */ - private Map childClassLoaders = - new WeakHashMap(); - - - /** - * Any file or directory in {@link #appBase} that this pattern matches will - * be ignored by the automatic deployment process (both - * {@link #deployOnStartup} and {@link #autoDeploy}). - */ - private Pattern deployIgnore = null; - - - // ------------------------------------------------------------- Properties - - - /** - * Return the application root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - */ - @Override - public String getAppBase() { - return (this.appBase); - } - - - /** - * ({@inheritDoc} - */ - @Override - public File getAppBaseFile() { - - if (appBaseFile != null) { - return appBaseFile; - } - - File file = new File(getAppBase()); - - // If not absolute, make it absolute - if (!file.isAbsolute()) { - // This system property should always be set - file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), - file.getPath()); - } - - // Make it canonical if possible - try { - file = file.getCanonicalFile(); - } catch (IOException ioe) { - // Ignore - } - - this.appBaseFile = file; - return file; - } - - - /** - * 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 - */ - @Override - public void setAppBase(String appBase) { - - String oldAppBase = this.appBase; - this.appBase = appBase; - support.firePropertyChange("appBase", oldAppBase, this.appBase); - this.appBaseFile = null; - } - - - /** - * Return the XML root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - * If null, defaults to ${catalina.base}/conf/ directory - */ - @Override - public String getXmlBase() { - - return (this.xmlBase); - - } - - - /** - * Set the Xml root for this Host. This can be an absolute - * pathname, a relative pathname, or a URL. - * If null, defaults to ${catalina.base}/conf/ directory - * - * @param xmlBase The new XML root - */ - @Override - public void setXmlBase(String xmlBase) { - - String oldXmlBase = this.xmlBase; - this.xmlBase = xmlBase; - support.firePropertyChange("xmlBase", oldXmlBase, this.xmlBase); - - } - - - /** - * Returns true if the Host will attempt to create directories for appBase and xmlBase - * unless they already exist. - */ - @Override - public boolean getCreateDirs() { - return createDirs; - } - - /** - * Set to true if the Host should attempt to create directories for xmlBase and appBase upon startup - * @param createDirs - */ - @Override - public void setCreateDirs(boolean createDirs) { - this.createDirs = createDirs; - } - - /** - * Return the value of the auto deploy flag. If true, it indicates that - * this host's child webapps will be dynamically deployed. - */ - @Override - public boolean getAutoDeploy() { - - return (this.autoDeploy); - - } - - - /** - * Set the auto deploy flag value for this host. - * - * @param autoDeploy The new auto deploy flag - */ - @Override - 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. - */ - @Override - 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 - */ - @Override - 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 value of the deploy on startup flag. If true, it indicates - * that this host's child webapps should be discovered and automatically - * deployed at startup time. - */ - @Override - public boolean getDeployOnStartup() { - - return (this.deployOnStartup); - - } - - - /** - * Set the deploy on startup flag value for this host. - * - * @param deployOnStartup The new deploy on startup flag - */ - @Override - 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 copy XML config file flag for this component. - */ - public boolean isCopyXML() { - - return (this.copyXML); - - } - - - /** - * Set the copy XML config file flag for this component. - * - * @param copyXML The new copy XML flag - */ - public void setCopyXML(boolean copyXML) { - - this.copyXML= copyXML; - - } - - - /** - * 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. - */ - @Override - 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 - */ - @Override - public void setName(String name) { - - if (name == null) - throw new IllegalArgumentException - (sm.getString("standardHost.nullName")); - - name = name.toLowerCase(Locale.ENGLISH); // 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; - - } - - - /** - * Host work directory base. - */ - public String getWorkDir() { - - return (workDir); - } - - - /** - * Host work directory base. - */ - public void setWorkDir(String workDir) { - - this.workDir = workDir; - } - - - /** - * Return the regular expression that defines the files and directories in - * the host's {@link #appBase} that will be ignored by the automatic - * deployment process. - */ - @Override - public String getDeployIgnore() { - if (deployIgnore == null) { - return null; - } - return this.deployIgnore.toString(); - } - - - /** - * Return the compiled regular expression that defines the files and - * directories in the host's {@link #appBase} that will be ignored by the - * automatic deployment process. - */ - @Override - public Pattern getDeployIgnorePattern() { - return this.deployIgnore; - } - - - /** - * Set the regular expression that defines the files and directories in - * the host's {@link #appBase} that will be ignored by the automatic - * deployment process. - */ - @Override - public void setDeployIgnore(String deployIgnore) { - String oldDeployIgnore; - if (this.deployIgnore == null) { - oldDeployIgnore = null; - } else { - oldDeployIgnore = this.deployIgnore.toString(); - } - if (deployIgnore == null) { - this.deployIgnore = null; - } else { - this.deployIgnore = Pattern.compile(deployIgnore); - } - support.firePropertyChange("deployIgnore", - oldDeployIgnore, - deployIgnore); - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Add an alias name that should be mapped to this same Host. - * - * @param alias The alias to be added - */ - @Override - public void addAlias(String alias) { - - alias = alias.toLowerCase(Locale.ENGLISH); - - synchronized (aliasesLock) { - // 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 - */ - @Override - public void addChild(Container child) { - - child.addLifecycleListener(new MemoryLeakTrackingListener()); - - if (!(child instanceof Context)) - throw new IllegalArgumentException - (sm.getString("standardHost.notContext")); - super.addChild(child); - - } - - - /** - * Used to ensure the regardless of {@link Context} implementation, a record - * is kept of the class loader used every time a context starts. - */ - private class MemoryLeakTrackingListener implements LifecycleListener { - @Override - public void lifecycleEvent(LifecycleEvent event) { - if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { - if (event.getSource() instanceof Context) { - Context context = ((Context) event.getSource()); - childClassLoaders.put(context.getLoader().getClassLoader(), - context.getServletContext().getContextPath()); - } - } - } - } - - - /** - * Attempt to identify the contexts that have a class loader memory leak. - * This is usually triggered on context reload. Note: This method attempts - * to force a full garbage collection. This should be used with extreme - * caution on a production system. - */ - public String[] findReloadedContextMemoryLeaks() { - - System.gc(); - - List result = new ArrayList(); - - for (Map.Entry entry : - childClassLoaders.entrySet()) { - ClassLoader cl = entry.getKey(); - if (cl instanceof WebappClassLoader) { - if (!((WebappClassLoader) cl).isStarted()) { - result.add(entry.getValue()); - } - } - } - - return result.toArray(new String[result.size()]); - } - - /** - * Return the set of alias names for this Host. If none are defined, - * a zero length array is returned. - */ - @Override - public String[] findAliases() { - - synchronized (aliasesLock) { - return (this.aliases); - } - - } - - - /** - * Return descriptive information about this Container implementation and - * the corresponding version number, in the format - * <description>/<version>. - */ - @Override - public String getInfo() { - - return (info); - - } - - - /** - * Remove the specified alias name from the aliases for this Host. - * - * @param alias Alias name to be removed - */ - @Override - public void removeAlias(String alias) { - - alias = alias.toLowerCase(Locale.ENGLISH); - - synchronized (aliasesLock) { - - // 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. - */ - @Override - public String toString() { - - StringBuilder sb = new StringBuilder(); - if (getParent() != null) { - sb.append(getParent().toString()); - sb.append("."); - } - sb.append("StandardHost["); - sb.append(getName()); - sb.append("]"); - return (sb.toString()); - - } - - /** - * Start this component and implement the requirements - * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. - * - * @exception LifecycleException if this component detects a fatal error - * that prevents this component from being used - */ - @Override - protected synchronized void startInternal() throws LifecycleException { - - // Set error report valve - String errorValve = getErrorReportValveClass(); - if ((errorValve != null) && (!errorValve.equals(""))) { - try { - boolean found = false; - Valve[] valves = getPipeline().getValves(); - for (Valve valve : valves) { - if (errorValve.equals(valve.getClass().getName())) { - found = true; - break; - } - } - if(!found) { - Valve valve = - (Valve) Class.forName(errorValve).newInstance(); - getPipeline().addValve(valve); - } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString( - "standardHost.invalidErrorReportValveClass", - errorValve), t); - } - } - super.startInternal(); - } - - - // -------------------- JMX -------------------- - /** - * Return the MBean Names of the Valves associated with this Host - * - * @exception Exception if an MBean cannot be created or registered - */ - public String [] getValveNames() - throws Exception - { - Valve [] valves = this.getPipeline().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() { - synchronized (aliasesLock) { - return aliases; - } - } - - @Override - protected String getObjectNameKeyProperties() { - - StringBuilder keyProperties = new StringBuilder("type=Host"); - keyProperties.append(MBeanUtils.getContainerKeyProperties(this)); - - return keyProperties.toString(); - } - -} +/* + * 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 java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ExecutorService; +import java.util.regex.Pattern; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Globals; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.Valve; +import org.apache.catalina.loader.WebappClassLoader; +import org.apache.catalina.mbeans.MBeanUtils; +import org.apache.catalina.valves.ValveBase; +import org.apache.tomcat.util.ExceptionUtils; + + +/** + * Standard implementation of the Host 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 $Id$ + */ + +public class StandardHost extends ContainerBase implements Host { + + private static final org.apache.juli.logging.Log log= + org.apache.juli.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]; + + private final Object aliasesLock = new Object(); + + + /** + * The application root for this Host. + */ + private String appBase = "webapps"; + private volatile File appBaseFile = null; + + /** + * The XML root for this Host. + */ + private String xmlBase = null; + + /** + * 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 deploy on startup flag for this Host. + */ + private boolean deployOnStartup = true; + + + /** + * deploy Context XML config files property. + */ + private boolean deployXML = true; + + + /** + * Should XML files be copied to $CATALINA_BASE/conf// by + * default when a web application is deployed? + */ + private boolean copyXML = false; + + + /** + * 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; + + + /** + * Should we create directories upon startup for appBase and xmlBase + */ + private boolean createDirs = true; + + + /** + * Track the class loaders for the child web applications so memory leaks + * can be detected. + */ + private Map childClassLoaders = + new WeakHashMap(); + + + /** + * Any file or directory in {@link #appBase} that this pattern matches will + * be ignored by the automatic deployment process (both + * {@link #deployOnStartup} and {@link #autoDeploy}). + */ + private Pattern deployIgnore = null; + + + // ------------------------------------------------------------- Properties + + @Override + public ExecutorService getStartStopExecutor() { + return startStopExecutor; + } + + + /** + * Return the application root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + */ + @Override + public String getAppBase() { + return (this.appBase); + } + + + /** + * ({@inheritDoc} + */ + @Override + public File getAppBaseFile() { + + if (appBaseFile != null) { + return appBaseFile; + } + + File file = new File(getAppBase()); + + // If not absolute, make it absolute + if (!file.isAbsolute()) { + // This system property should always be set + file = new File(System.getProperty(Globals.CATALINA_BASE_PROP), + file.getPath()); + } + + // Make it canonical if possible + try { + file = file.getCanonicalFile(); + } catch (IOException ioe) { + // Ignore + } + + this.appBaseFile = file; + return file; + } + + + /** + * 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 + */ + @Override + public void setAppBase(String appBase) { + + String oldAppBase = this.appBase; + this.appBase = appBase; + support.firePropertyChange("appBase", oldAppBase, this.appBase); + this.appBaseFile = null; + } + + + /** + * Return the XML root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + * If null, defaults to ${catalina.base}/conf/ directory + */ + @Override + public String getXmlBase() { + + return (this.xmlBase); + + } + + + /** + * Set the Xml root for this Host. This can be an absolute + * pathname, a relative pathname, or a URL. + * If null, defaults to ${catalina.base}/conf/ directory + * + * @param xmlBase The new XML root + */ + @Override + public void setXmlBase(String xmlBase) { + + String oldXmlBase = this.xmlBase; + this.xmlBase = xmlBase; + support.firePropertyChange("xmlBase", oldXmlBase, this.xmlBase); + + } + + + /** + * Returns true if the Host will attempt to create directories for appBase and xmlBase + * unless they already exist. + */ + @Override + public boolean getCreateDirs() { + return createDirs; + } + + /** + * Set to true if the Host should attempt to create directories for xmlBase and appBase upon startup + * @param createDirs + */ + @Override + public void setCreateDirs(boolean createDirs) { + this.createDirs = createDirs; + } + + /** + * Return the value of the auto deploy flag. If true, it indicates that + * this host's child webapps will be dynamically deployed. + */ + @Override + public boolean getAutoDeploy() { + + return (this.autoDeploy); + + } + + + /** + * Set the auto deploy flag value for this host. + * + * @param autoDeploy The new auto deploy flag + */ + @Override + 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. + */ + @Override + 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 + */ + @Override + 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 value of the deploy on startup flag. If true, it indicates + * that this host's child webapps should be discovered and automatically + * deployed at startup time. + */ + @Override + public boolean getDeployOnStartup() { + + return (this.deployOnStartup); + + } + + + /** + * Set the deploy on startup flag value for this host. + * + * @param deployOnStartup The new deploy on startup flag + */ + @Override + 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 copy XML config file flag for this component. + */ + public boolean isCopyXML() { + + return (this.copyXML); + + } + + + /** + * Set the copy XML config file flag for this component. + * + * @param copyXML The new copy XML flag + */ + public void setCopyXML(boolean copyXML) { + + this.copyXML= copyXML; + + } + + + /** + * 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. + */ + @Override + 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 + */ + @Override + public void setName(String name) { + + if (name == null) + throw new IllegalArgumentException + (sm.getString("standardHost.nullName")); + + name = name.toLowerCase(Locale.ENGLISH); // 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; + + } + + + /** + * Host work directory base. + */ + public String getWorkDir() { + + return (workDir); + } + + + /** + * Host work directory base. + */ + public void setWorkDir(String workDir) { + + this.workDir = workDir; + } + + + /** + * Return the regular expression that defines the files and directories in + * the host's {@link #appBase} that will be ignored by the automatic + * deployment process. + */ + @Override + public String getDeployIgnore() { + if (deployIgnore == null) { + return null; + } + return this.deployIgnore.toString(); + } + + + /** + * Return the compiled regular expression that defines the files and + * directories in the host's {@link #appBase} that will be ignored by the + * automatic deployment process. + */ + @Override + public Pattern getDeployIgnorePattern() { + return this.deployIgnore; + } + + + /** + * Set the regular expression that defines the files and directories in + * the host's {@link #appBase} that will be ignored by the automatic + * deployment process. + */ + @Override + public void setDeployIgnore(String deployIgnore) { + String oldDeployIgnore; + if (this.deployIgnore == null) { + oldDeployIgnore = null; + } else { + oldDeployIgnore = this.deployIgnore.toString(); + } + if (deployIgnore == null) { + this.deployIgnore = null; + } else { + this.deployIgnore = Pattern.compile(deployIgnore); + } + support.firePropertyChange("deployIgnore", + oldDeployIgnore, + deployIgnore); + } + + + // --------------------------------------------------------- Public Methods + + + /** + * Add an alias name that should be mapped to this same Host. + * + * @param alias The alias to be added + */ + @Override + public void addAlias(String alias) { + + alias = alias.toLowerCase(Locale.ENGLISH); + + synchronized (aliasesLock) { + // 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 + */ + @Override + public void addChild(Container child) { + + child.addLifecycleListener(new MemoryLeakTrackingListener()); + + if (!(child instanceof Context)) + throw new IllegalArgumentException + (sm.getString("standardHost.notContext")); + super.addChild(child); + + } + + + /** + * Used to ensure the regardless of {@link Context} implementation, a record + * is kept of the class loader used every time a context starts. + */ + private class MemoryLeakTrackingListener implements LifecycleListener { + @Override + public void lifecycleEvent(LifecycleEvent event) { + if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { + if (event.getSource() instanceof Context) { + Context context = ((Context) event.getSource()); + childClassLoaders.put(context.getLoader().getClassLoader(), + context.getServletContext().getContextPath()); + } + } + } + } + + + /** + * Attempt to identify the contexts that have a class loader memory leak. + * This is usually triggered on context reload. Note: This method attempts + * to force a full garbage collection. This should be used with extreme + * caution on a production system. + */ + public String[] findReloadedContextMemoryLeaks() { + + System.gc(); + + List result = new ArrayList(); + + for (Map.Entry entry : + childClassLoaders.entrySet()) { + ClassLoader cl = entry.getKey(); + if (cl instanceof WebappClassLoader) { + if (!((WebappClassLoader) cl).isStarted()) { + result.add(entry.getValue()); + } + } + } + + return result.toArray(new String[result.size()]); + } + + /** + * Return the set of alias names for this Host. If none are defined, + * a zero length array is returned. + */ + @Override + public String[] findAliases() { + + synchronized (aliasesLock) { + return (this.aliases); + } + + } + + + /** + * Return descriptive information about this Container implementation and + * the corresponding version number, in the format + * <description>/<version>. + */ + @Override + public String getInfo() { + + return (info); + + } + + + /** + * Remove the specified alias name from the aliases for this Host. + * + * @param alias Alias name to be removed + */ + @Override + public void removeAlias(String alias) { + + alias = alias.toLowerCase(Locale.ENGLISH); + + synchronized (aliasesLock) { + + // 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. + */ + @Override + public String toString() { + + StringBuilder sb = new StringBuilder(); + if (getParent() != null) { + sb.append(getParent().toString()); + sb.append("."); + } + sb.append("StandardHost["); + sb.append(getName()); + sb.append("]"); + return (sb.toString()); + + } + + /** + * Start this component and implement the requirements + * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}. + * + * @exception LifecycleException if this component detects a fatal error + * that prevents this component from being used + */ + @Override + protected synchronized void startInternal() throws LifecycleException { + + // Set error report valve + String errorValve = getErrorReportValveClass(); + if ((errorValve != null) && (!errorValve.equals(""))) { + try { + boolean found = false; + Valve[] valves = getPipeline().getValves(); + for (Valve valve : valves) { + if (errorValve.equals(valve.getClass().getName())) { + found = true; + break; + } + } + if(!found) { + Valve valve = + (Valve) Class.forName(errorValve).newInstance(); + getPipeline().addValve(valve); + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString( + "standardHost.invalidErrorReportValveClass", + errorValve), t); + } + } + super.startInternal(); + } + + + // -------------------- JMX -------------------- + /** + * Return the MBean Names of the Valves associated with this Host + * + * @exception Exception if an MBean cannot be created or registered + */ + public String [] getValveNames() + throws Exception + { + Valve [] valves = this.getPipeline().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() { + synchronized (aliasesLock) { + return aliases; + } + } + + @Override + protected String getObjectNameKeyProperties() { + + StringBuilder keyProperties = new StringBuilder("type=Host"); + keyProperties.append(MBeanUtils.getContainerKeyProperties(this)); + + return keyProperties.toString(); + } + +} diff --git a/java/org/apache/catalina/core/mbeans-descriptors.xml b/java/org/apache/catalina/core/mbeans-descriptors.xml index b9253c2..426e2e2 100644 --- a/java/org/apache/catalina/core/mbeans-descriptors.xml +++ b/java/org/apache/catalina/core/mbeans-descriptors.xml @@ -1,1843 +1,1851 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java index c1d5175..8997531 100644 --- a/java/org/apache/catalina/startup/ContextConfig.java +++ b/java/org/apache/catalina/startup/ContextConfig.java @@ -126,12 +126,6 @@ public class ContextConfig protected static final LoginConfig DUMMY_LOGIN_CONFIG = new LoginConfig("NONE", null, null, null); - /** - * The Digester we will use to process web application - * context files. - */ - protected static Digester contextDigester = null; - /** * The set of Authenticators that we know how to configure. The key is @@ -142,32 +136,6 @@ public class ContextConfig /** - * The Digesters available to process web deployment descriptor - * files. - */ - protected static Digester[] webDigesters = new Digester[4]; - - - /** - * The Digesters available to process web fragment deployment - * descriptor files. - */ - protected static Digester[] webFragmentDigesters = new Digester[4]; - - - /** - * The Rules used to parse the web.xml - */ - protected static WebRuleSet webRuleSet = new WebRuleSet(false); - - - /** - * The Rules used to parse the web-fragment.xml - */ - protected static WebRuleSet webFragmentRuleSet = new WebRuleSet(true); - - - /** * Deployment count. */ protected static long deploymentCount = 0L; @@ -235,12 +203,14 @@ public class ContextConfig * deployment descriptor files. */ protected Digester webDigester = null; + protected WebRuleSet webRuleSet = null; /** * The Digester we will use to process web fragment * deployment descriptor files. */ protected Digester webFragmentDigester = null; + protected WebRuleSet webFragmentRuleSet = null; // ------------------------------------------------------------- Properties @@ -484,60 +454,21 @@ public class ContextConfig /** - * Create (if necessary) and return a Digester configured to process the + * Create and return a Digester configured to process the * web application deployment descriptor (web.xml). */ public void createWebXmlDigester(boolean namespaceAware, boolean validation) { - if (!namespaceAware && !validation) { - if (webDigesters[0] == null) { - webDigesters[0] = DigesterFactory.newDigester(validation, - namespaceAware, webRuleSet); - webFragmentDigesters[0] = DigesterFactory.newDigester(validation, - namespaceAware, webFragmentRuleSet); - webDigesters[0].getParser(); - webFragmentDigesters[0].getParser(); - } - webDigester = webDigesters[0]; - webFragmentDigester = webFragmentDigesters[0]; - - } else if (!namespaceAware && validation) { - if (webDigesters[1] == null) { - webDigesters[1] = DigesterFactory.newDigester(validation, - namespaceAware, webRuleSet); - webFragmentDigesters[1] = DigesterFactory.newDigester(validation, - namespaceAware, webFragmentRuleSet); - webDigesters[1].getParser(); - webFragmentDigesters[1].getParser(); - } - webDigester = webDigesters[1]; - webFragmentDigester = webFragmentDigesters[1]; - - } else if (namespaceAware && !validation) { - if (webDigesters[2] == null) { - webDigesters[2] = DigesterFactory.newDigester(validation, - namespaceAware, webRuleSet); - webFragmentDigesters[2] = DigesterFactory.newDigester(validation, - namespaceAware, webFragmentRuleSet); - webDigesters[2].getParser(); - webFragmentDigesters[2].getParser(); - } - webDigester = webDigesters[2]; - webFragmentDigester = webFragmentDigesters[2]; - - } else { - if (webDigesters[3] == null) { - webDigesters[3] = DigesterFactory.newDigester(validation, - namespaceAware, webRuleSet); - webFragmentDigesters[3] = DigesterFactory.newDigester(validation, - namespaceAware, webFragmentRuleSet); - webDigesters[3].getParser(); - webFragmentDigesters[3].getParser(); - } - webDigester = webDigesters[3]; - webFragmentDigester = webFragmentDigesters[3]; - } + webRuleSet = new WebRuleSet(false); + webDigester = DigesterFactory.newDigester(validation, + namespaceAware, webRuleSet); + webDigester.getParser(); + + webFragmentRuleSet = new WebRuleSet(true); + webFragmentDigester = DigesterFactory.newDigester(validation, + namespaceAware, webFragmentRuleSet); + webFragmentDigester.getParser(); } @@ -575,7 +506,7 @@ public class ContextConfig /** * Process the default configuration file, if it exists. */ - protected void contextConfig() { + protected void contextConfig(Digester digester) { // Open the default context.xml file, if it exists if( defaultContextXml==null && context instanceof StandardContext ) { @@ -592,7 +523,7 @@ public class ContextConfig if (defaultContextFile.exists()) { try { URL defaultContextUrl = defaultContextFile.toURI().toURL(); - processContextConfig(defaultContextUrl); + processContextConfig(digester, defaultContextUrl); } catch (MalformedURLException e) { log.error(sm.getString( "contextConfig.badUrl", defaultContextFile), e); @@ -604,7 +535,7 @@ public class ContextConfig if (hostContextFile.exists()) { try { URL hostContextUrl = hostContextFile.toURI().toURL(); - processContextConfig(hostContextUrl); + processContextConfig(digester, hostContextUrl); } catch (MalformedURLException e) { log.error(sm.getString( "contextConfig.badUrl", hostContextFile), e); @@ -612,7 +543,7 @@ public class ContextConfig } } if (context.getConfigFile() != null) - processContextConfig(context.getConfigFile()); + processContextConfig(digester, context.getConfigFile()); } @@ -620,7 +551,7 @@ public class ContextConfig /** * Process a context.xml. */ - protected void processContextConfig(URL contextXml) { + protected void processContextConfig(Digester digester, URL contextXml) { if (log.isDebugEnabled()) log.debug("Processing context [" + context.getName() @@ -646,44 +577,42 @@ public class ContextConfig if (source == null) return; - synchronized (contextDigester) { - try { - source.setByteStream(stream); - contextDigester.setClassLoader(this.getClass().getClassLoader()); - contextDigester.setUseContextClassLoader(false); - contextDigester.push(context.getParent()); - contextDigester.push(context); - XmlErrorHandler errorHandler = new XmlErrorHandler(); - contextDigester.setErrorHandler(errorHandler); - contextDigester.parse(source); - if (errorHandler.getWarnings().size() > 0 || - errorHandler.getErrors().size() > 0) { - errorHandler.logFindings(log, contextXml.toString()); - ok = false; - } - if (log.isDebugEnabled()) - log.debug("Successfully processed context [" + context.getName() - + "] configuration file [" + contextXml + "]"); - } catch (SAXParseException e) { - log.error(sm.getString("contextConfig.contextParse", - context.getName()), e); - log.error(sm.getString("contextConfig.defaultPosition", - "" + e.getLineNumber(), - "" + e.getColumnNumber())); - ok = false; - } catch (Exception e) { - log.error(sm.getString("contextConfig.contextParse", - context.getName()), e); + + try { + source.setByteStream(stream); + digester.setClassLoader(this.getClass().getClassLoader()); + digester.setUseContextClassLoader(false); + digester.push(context.getParent()); + digester.push(context); + XmlErrorHandler errorHandler = new XmlErrorHandler(); + digester.setErrorHandler(errorHandler); + digester.parse(source); + if (errorHandler.getWarnings().size() > 0 || + errorHandler.getErrors().size() > 0) { + errorHandler.logFindings(log, contextXml.toString()); ok = false; - } finally { - contextDigester.reset(); - try { - if (stream != null) { - stream.close(); - } - } catch (IOException e) { - log.error(sm.getString("contextConfig.contextClose"), e); + } + if (log.isDebugEnabled()) + log.debug("Successfully processed context [" + context.getName() + + "] configuration file [" + contextXml + "]"); + } catch (SAXParseException e) { + log.error(sm.getString("contextConfig.contextParse", + context.getName()), e); + log.error(sm.getString("contextConfig.defaultPosition", + "" + e.getLineNumber(), + "" + e.getColumnNumber())); + ok = false; + } catch (Exception e) { + log.error(sm.getString("contextConfig.contextParse", + context.getName()), e); + ok = false; + } finally { + try { + if (stream != null) { + stream.close(); } + } catch (IOException e) { + log.error(sm.getString("contextConfig.contextClose"), e); } } } @@ -836,17 +765,15 @@ public class ContextConfig protected void init() { // Called from StandardContext.init() - if (contextDigester == null){ - contextDigester = createContextDigester(); - contextDigester.getParser(); - } + Digester contextDigester = createContextDigester(); + contextDigester.getParser(); if (log.isDebugEnabled()) log.debug(sm.getString("contextConfig.init")); context.setConfigured(false); ok = true; - contextConfig(); + contextConfig(contextDigester); createWebXmlDigester(context.getXmlNamespaceAware(), context.getXmlValidation()); @@ -1229,7 +1156,7 @@ public class ContextConfig defaults.add(getDefaultWebXmlFragment()); WebXml webXml = createWebXml(); - + // Parse context level web.xml InputSource contextWebXml = getContextWebXmlSource(); parseWebXml(contextWebXml, webXml, false); @@ -1802,9 +1729,6 @@ public class ContextConfig XmlErrorHandler handler = new XmlErrorHandler(); - // Web digesters and rulesets are shared between contexts but are not - // thread safe. Whilst there should only be one thread at a time - // processing a config, play safe and sync. Digester digester; WebRuleSet ruleSet; if (fragment) { @@ -1815,41 +1739,36 @@ public class ContextConfig ruleSet = webRuleSet; } - // Sync on the ruleSet since the same ruleSet is shared across all four - // digesters - synchronized(ruleSet) { - - digester.push(dest); - digester.setErrorHandler(handler); - - if(log.isDebugEnabled()) { - log.debug(sm.getString("contextConfig.applicationStart", - source.getSystemId())); - } + digester.push(dest); + digester.setErrorHandler(handler); + + if(log.isDebugEnabled()) { + log.debug(sm.getString("contextConfig.applicationStart", + source.getSystemId())); + } - try { - digester.parse(source); + try { + digester.parse(source); - if (handler.getWarnings().size() > 0 || - handler.getErrors().size() > 0) { - ok = false; - handler.logFindings(log, source.getSystemId()); - } - } catch (SAXParseException e) { - log.error(sm.getString("contextConfig.applicationParse", - source.getSystemId()), e); - log.error(sm.getString("contextConfig.applicationPosition", - "" + e.getLineNumber(), - "" + e.getColumnNumber())); - ok = false; - } catch (Exception e) { - log.error(sm.getString("contextConfig.applicationParse", - source.getSystemId()), e); + if (handler.getWarnings().size() > 0 || + handler.getErrors().size() > 0) { ok = false; - } finally { - digester.reset(); - ruleSet.recycle(); - } + handler.logFindings(log, source.getSystemId()); + } + } catch (SAXParseException e) { + log.error(sm.getString("contextConfig.applicationParse", + source.getSystemId()), e); + log.error(sm.getString("contextConfig.applicationPosition", + "" + e.getLineNumber(), + "" + e.getColumnNumber())); + ok = false; + } catch (Exception e) { + log.error(sm.getString("contextConfig.applicationParse", + source.getSystemId()), e); + ok = false; + } finally { + digester.reset(); + ruleSet.recycle(); } } @@ -2239,7 +2158,8 @@ public class ContextConfig /** * process filter annotation and merge with existing one! - * FIXME: refactoring method to long and has redundant subroutines with processAnnotationWebServlet! + * FIXME: refactoring method too long and has redundant subroutines with + * processAnnotationWebServlet! * @param className * @param ae * @param fragment diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java index faa033d..4b0be0b 100644 --- a/java/org/apache/catalina/startup/HostConfig.java +++ b/java/org/apache/catalina/startup/HostConfig.java @@ -1,1456 +1,1543 @@ -/* - * 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.startup; - - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.management.ObjectName; - -import org.apache.catalina.Container; -import org.apache.catalina.Context; -import org.apache.catalina.Engine; -import org.apache.catalina.Globals; -import org.apache.catalina.Host; -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleEvent; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.core.ContainerBase; -import org.apache.catalina.core.StandardHost; -import org.apache.catalina.util.ContextName; -import org.apache.catalina.util.IOTools; -import org.apache.juli.logging.Log; -import org.apache.juli.logging.LogFactory; -import org.apache.tomcat.util.ExceptionUtils; -import org.apache.tomcat.util.digester.Digester; -import org.apache.tomcat.util.modeler.Registry; -import org.apache.tomcat.util.res.StringManager; - - -/** - * Startup event listener for a Host that configures the properties - * of that Host, and the associated defined contexts. - * - * @author Craig R. McClanahan - * @author Remy Maucherat - * @version $Id$ - */ -public class HostConfig - implements LifecycleListener { - - private static final Log log = LogFactory.getLog( HostConfig.class ); - - // ----------------------------------------------------- Instance Variables - - - /** - * Config base. - */ - protected File configBase = null; - - - /** - * The Java class name of the Context configuration class we should use. - */ - protected String configClass = "org.apache.catalina.startup.ContextConfig"; - - - /** - * The Java class name of the Context implementation we should use. - */ - protected String contextClass = "org.apache.catalina.core.StandardContext"; - - - /** - * The Host we are associated with. - */ - protected Host host = null; - - - /** - * The JMX ObjectName of this component. - */ - protected ObjectName oname = null; - - - /** - * The string resources for this package. - */ - protected static final StringManager sm = - StringManager.getManager(Constants.Package); - - - /** - * Should we deploy XML Context config files packaged with WAR files and - * directories? - */ - protected boolean deployXML = false; - - - /** - * Should XML files be copied to $CATALINA_BASE/conf// by - * default when a web application is deployed? - */ - protected boolean copyXML = false; - - - /** - * Should we unpack WAR files when auto-deploying applications in the - * appBase directory? - */ - protected boolean unpackWARs = false; - - - /** - * Map of deployed applications. - */ - protected HashMap deployed = - new HashMap(); - - - /** - * List of applications which are being serviced, and shouldn't be - * deployed/undeployed/redeployed at the moment. - */ - protected ArrayList serviced = new ArrayList(); - - - /** - * The Digester instance used to parse context descriptors. - */ - protected static Digester digester = createDigester(); - - /** - * The list of Wars in the appBase to be ignored because they are invalid - * (e.g. contain /../ sequences). - */ - protected Set invalidWars = new HashSet(); - - // ------------------------------------------------------------- Properties - - - /** - * Return the Context configuration class name. - */ - public String getConfigClass() { - - return (this.configClass); - - } - - - /** - * Set the Context configuration class name. - * - * @param configClass The new Context configuration class name. - */ - public void setConfigClass(String configClass) { - - this.configClass = configClass; - - } - - - /** - * Return the Context implementation class name. - */ - public String getContextClass() { - - return (this.contextClass); - - } - - - /** - * Set the Context implementation class name. - * - * @param contextClass The new Context implementation class name. - */ - public void setContextClass(String contextClass) { - - this.contextClass = contextClass; - - } - - - /** - * Return the deploy XML config file flag for this component. - */ - public boolean isDeployXML() { - - return (this.deployXML); - - } - - - /** - * Set the deploy XML config file flag for this component. - * - * @param deployXML The new deploy XML flag - */ - public void setDeployXML(boolean deployXML) { - - this.deployXML= deployXML; - - } - - - /** - * Return the copy XML config file flag for this component. - */ - public boolean isCopyXML() { - - return (this.copyXML); - - } - - - /** - * Set the copy XML config file flag for this component. - * - * @param copyXML The new copy XML flag - */ - public void setCopyXML(boolean copyXML) { - - this.copyXML= copyXML; - - } - - - /** - * Return the unpack WARs flag. - */ - public boolean isUnpackWARs() { - - return (this.unpackWARs); - - } - - - /** - * Set the unpack WARs flag. - * - * @param unpackWARs The new unpack WARs flag - */ - public void setUnpackWARs(boolean unpackWARs) { - - this.unpackWARs = unpackWARs; - - } - - - // --------------------------------------------------------- Public Methods - - - /** - * Process the START event for an associated Host. - * - * @param event The lifecycle event that has occurred - */ - @Override - public void lifecycleEvent(LifecycleEvent event) { - - if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) - check(); - - // Identify the host we are associated with - try { - host = (Host) event.getLifecycle(); - if (host instanceof StandardHost) { - setCopyXML(((StandardHost) host).isCopyXML()); - setDeployXML(((StandardHost) host).isDeployXML()); - setUnpackWARs(((StandardHost) host).isUnpackWARs()); - } - } catch (ClassCastException e) { - log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e); - return; - } - - // Process the event that has occurred - if (event.getType().equals(Lifecycle.START_EVENT)) - start(); - else if (event.getType().equals(Lifecycle.STOP_EVENT)) - stop(); - - } - - - /** - * Add a serviced application to the list. - */ - public synchronized void addServiced(String name) { - serviced.add(name); - } - - - /** - * Is application serviced ? - * @return state of the application - */ - public synchronized boolean isServiced(String name) { - return (serviced.contains(name)); - } - - - /** - * Removed a serviced application from the list. - */ - public synchronized void removeServiced(String name) { - serviced.remove(name); - } - - - /** - * Get the instant where an application was deployed. - * @return 0L if no application with that name is deployed, or the instant - * on which the application was deployed - */ - public long getDeploymentTime(String name) { - DeployedApplication app = deployed.get(name); - if (app == null) { - return 0L; - } - - return app.timestamp; - } - - - /** - * Has the specified application been deployed? Note applications defined - * in server.xml will not have been deployed. - * @return true if the application has been deployed and - * false if the application has not been deployed or does not - * exist - */ - public boolean isDeployed(String name) { - DeployedApplication app = deployed.get(name); - if (app == null) { - return false; - } - - return true; - } - - - // ------------------------------------------------------ Protected Methods - - - /** - * Create the digester which will be used to parse context config files. - */ - protected static Digester createDigester() { - Digester digester = new Digester(); - digester.setValidating(false); - // Add object creation rule - digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext", - "className"); - // Set the properties on that object (it doesn't matter if extra - // properties are set) - digester.addSetProperties("Context"); - return (digester); - } - - protected File returnCanonicalPath(String path) { - File file = new File(path); - File base = new File(System.getProperty(Globals.CATALINA_BASE_PROP)); - if (!file.isAbsolute()) - file = new File(base,path); - try { - return file.getCanonicalFile(); - } catch (IOException e) { - return file; - } - } - - - /** - * Return a File object representing the "configuration root" directory - * for our associated Host. - */ - protected File configBase() { - - if (configBase != null) { - return configBase; - } - - if (host.getXmlBase()!=null) { - configBase = returnCanonicalPath(host.getXmlBase()); - } else { - StringBuilder xmlDir = new StringBuilder("conf"); - Container parent = host.getParent(); - if (parent instanceof Engine) { - xmlDir.append('/'); - xmlDir.append(parent.getName()); - } - xmlDir.append('/'); - xmlDir.append(host.getName()); - configBase = returnCanonicalPath(xmlDir.toString()); - } - return (configBase); - - } - - /** - * Get the name of the configBase. - * For use with JMX management. - */ - public String getConfigBaseName() { - return configBase().getAbsolutePath(); - } - - - /** - * Deploy applications for any directories or WAR files that are found - * in our "application root" directory. - */ - protected void deployApps() { - - File appBase = host.getAppBaseFile(); - File configBase = configBase(); - String[] filteredAppPaths = filterAppPaths(appBase.list()); - // Deploy XML descriptors from configBase - deployDescriptors(configBase, configBase.list()); - // Deploy WARs, and loop if additional descriptors are found - deployWARs(appBase, filteredAppPaths); - // Deploy expanded folders - deployDirectories(appBase, filteredAppPaths); - - } - - - /** - * Filter the list of application file paths to remove those that match - * the regular expression defined by {@link Host#getDeployIgnore()}. - * - * @param unfilteredAppPaths The list of application paths to filtert - * - * @return The filtered list of application paths - */ - protected String[] filterAppPaths(String[] unfilteredAppPaths) { - Pattern filter = host.getDeployIgnorePattern(); - if (filter == null) { - return unfilteredAppPaths; - } - - List filteredList = new ArrayList(); - Matcher matcher = null; - for (String appPath : unfilteredAppPaths) { - if (matcher == null) { - matcher = filter.matcher(appPath); - } else { - matcher.reset(appPath); - } - if (matcher.matches()) { - if (log.isDebugEnabled()) { - log.debug(sm.getString("hostConfig.ignorePath", appPath)); - } - } else { - filteredList.add(appPath); - } - } - return filteredList.toArray(new String[filteredList.size()]); - } - - - /** - * Deploy applications for any directories or WAR files that are found - * in our "application root" directory. - */ - protected void deployApps(String name) { - - File appBase = host.getAppBaseFile(); - File configBase = configBase(); - ContextName cn = new ContextName(name); - String baseName = cn.getBaseName(); - - // Deploy XML descriptors from configBase - File xml = new File(configBase, baseName + ".xml"); - if (xml.exists()) - deployDescriptor(cn, xml); - // Deploy WARs, and loop if additional descriptors are found - File war = new File(appBase, baseName + ".war"); - if (war.exists()) - deployWAR(cn, war); - // Deploy expanded folders - File dir = new File(appBase, baseName); - if (dir.exists()) - deployDirectory(cn, dir); - } - - - /** - * Deploy XML context descriptors. - */ - protected void deployDescriptors(File configBase, String[] files) { - - if (files == null) - return; - - for (int i = 0; i < files.length; i++) { - File contextXml = new File(configBase, files[i]); - - if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) { - ContextName cn = new ContextName(files[i]); - String name = cn.getName(); - - if (isServiced(name)) - continue; - - deployDescriptor(cn, contextXml); - } - } - } - - - /** - * @param cn - * @param contextXml - */ - protected void deployDescriptor(ContextName cn, File contextXml) { - if (deploymentExists(cn.getName())) { - return; - } - - DeployedApplication deployedApp = new DeployedApplication(cn.getName()); - - // Assume this is a configuration descriptor and deploy it - if(log.isInfoEnabled()) { - log.info(sm.getString("hostConfig.deployDescriptor", - contextXml.getAbsolutePath())); - } - - Context context = null; - try { - synchronized (digester) { - try { - context = (Context) digester.parse(contextXml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - contextXml.getAbsolutePath())); - return; - } - } finally { - digester.reset(); - } - } - - Class clazz = Class.forName(host.getConfigClass()); - LifecycleListener listener = - (LifecycleListener) clazz.newInstance(); - context.addLifecycleListener(listener); - - context.setConfigFile(contextXml.toURI().toURL()); - context.setName(cn.getName()); - context.setPath(cn.getPath()); - context.setWebappVersion(cn.getVersion()); - // Add the associated docBase to the redeployed list if it's a WAR - boolean isExternalWar = false; - boolean isExternal = false; - if (context.getDocBase() != null) { - File docBase = new File(context.getDocBase()); - if (!docBase.isAbsolute()) { - docBase = new File(host.getAppBaseFile(), context.getDocBase()); - } - // If external docBase, register .xml as redeploy first - if (!docBase.getCanonicalPath().startsWith( - host.getAppBaseFile().getAbsolutePath() + File.separator)) { - isExternal = true; - deployedApp.redeployResources.put( - contextXml.getAbsolutePath(), - Long.valueOf(contextXml.lastModified())); - deployedApp.redeployResources.put(docBase.getAbsolutePath(), - Long.valueOf(docBase.lastModified())); - if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) { - isExternalWar = true; - } - } else { - log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified", - docBase)); - // Ignore specified docBase - context.setDocBase(null); - } - } - host.addChild(context); - // Get paths for WAR and expanded WAR in appBase - - // default to appBase dir + name - File expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName()); - if (context.getDocBase() != null) { - // first assume docBase is absolute - expandedDocBase = new File(context.getDocBase()); - if (!expandedDocBase.isAbsolute()) { - // if docBase specified and relative, it must be relative to appBase - expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase()); - } - } - // Add the eventual unpacked WAR and all the resources which will be - // watched inside it - if (isExternalWar && unpackWARs) { - deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), - Long.valueOf(expandedDocBase.lastModified())); - deployedApp.redeployResources.put(contextXml.getAbsolutePath(), - Long.valueOf(contextXml.lastModified())); - addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context); - } else { - // Find an existing matching war and expanded folder - if (!isExternal) { - File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war"); - if (warDocBase.exists()) { - deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), - Long.valueOf(warDocBase.lastModified())); - } - } - if (expandedDocBase.exists()) { - deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), - Long.valueOf(expandedDocBase.lastModified())); - addWatchedResources(deployedApp, - expandedDocBase.getAbsolutePath(), context); - } else { - addWatchedResources(deployedApp, null, context); - } - // Add the context XML to the list of files which should trigger a redeployment - if (!isExternal) { - deployedApp.redeployResources.put( - contextXml.getAbsolutePath(), - Long.valueOf(contextXml.lastModified())); - } - } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployDescriptor.error", - contextXml.getAbsolutePath()), t); - } - - if (context != null && host.findChild(context.getName()) != null) { - deployed.put(context.getName(), deployedApp); - } - } - - - /** - * Deploy WAR files. - */ - protected void deployWARs(File appBase, String[] files) { - - if (files == null) - return; - - for (int i = 0; i < files.length; i++) { - - if (files[i].equalsIgnoreCase("META-INF")) - continue; - if (files[i].equalsIgnoreCase("WEB-INF")) - continue; - File war = new File(appBase, files[i]); - if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") && war.isFile() - && !invalidWars.contains(files[i]) ) { - - ContextName cn = new ContextName(files[i]); - - // Check for WARs with /../ /./ or similar sequences in the name - if (!validateContextPath(appBase, cn.getBaseName())) { - log.error(sm.getString( - "hostConfig.illegalWarName", files[i])); - invalidWars.add(files[i]); - continue; - } - - if (isServiced(cn.getName())) - continue; - - deployWAR(cn, war); - } - } - } - - - private boolean validateContextPath(File appBase, String contextPath) { - // More complicated than the ideal as the canonical path may or may - // not end with File.separator for a directory - - StringBuilder docBase; - String canonicalDocBase = null; - - try { - String canonicalAppBase = appBase.getCanonicalPath(); - docBase = new StringBuilder(canonicalAppBase); - if (canonicalAppBase.endsWith(File.separator)) { - docBase.append(contextPath.substring(1).replace( - '/', File.separatorChar)); - } else { - docBase.append(contextPath.replace('/', File.separatorChar)); - } - // At this point docBase should be canonical but will not end - // with File.separator - - canonicalDocBase = - (new File(docBase.toString())).getCanonicalPath(); - - // If the canonicalDocBase ends with File.separator, add one to - // docBase before they are compared - if (canonicalDocBase.endsWith(File.separator)) { - docBase.append(File.separator); - } - } catch (IOException ioe) { - return false; - } - - // Compare the two. If they are not the same, the contextPath must - // have /../ like sequences in it - return canonicalDocBase.equals(docBase.toString()); - } - - /** - * @param cn - * @param war - */ - protected void deployWAR(ContextName cn, File war) { - - if (deploymentExists(cn.getName())) - return; - - // Checking for a nested /META-INF/context.xml - JarFile jar = null; - JarEntry entry = null; - InputStream istream = null; - BufferedOutputStream ostream = null; - File xml; - if (copyXML) { - xml = new File(configBase(), cn.getBaseName() + ".xml"); - } else { - xml = new File(host.getAppBaseFile(), - cn.getBaseName() + "/META-INF/context.xml"); - } - boolean xmlInWar = false; - - if (deployXML && !xml.exists()) { - try { - jar = new JarFile(war); - entry = jar.getJarEntry(Constants.ApplicationContextXml); - if (entry != null) { - xmlInWar = true; - } - if ((copyXML || unpackWARs) && xmlInWar) { - istream = jar.getInputStream(entry); - - ostream = - new BufferedOutputStream - (new FileOutputStream(xml), 1024); - byte buffer[] = new byte[1024]; - while (true) { - int n = istream.read(buffer); - if (n < 0) { - break; - } - ostream.write(buffer, 0, n); - } - ostream.flush(); - ostream.close(); - ostream = null; - istream.close(); - istream = null; - } - } catch (IOException e) { - /* Ignore */ - } finally { - if (ostream != null) { - try { - ostream.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - ostream = null; - } - if (istream != null) { - try { - istream.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - istream = null; - } - entry = null; - if (jar != null) { - try { - jar.close(); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - } - jar = null; - } - } - } - - DeployedApplication deployedApp = new DeployedApplication(cn.getName()); - - // Deploy the application in this WAR file - if(log.isInfoEnabled()) - log.info(sm.getString("hostConfig.deployWar", - war.getAbsolutePath())); - - try { - Context context = null; - if (deployXML && xml.exists()) { - synchronized (digester) { - try { - context = (Context) digester.parse(xml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - war.getAbsolutePath())); - return; - } - } finally { - digester.reset(); - } - } - context.setConfigFile(xml.toURI().toURL()); - } else if (deployXML && xmlInWar) { - synchronized (digester) { - try { - jar = new JarFile(war); - entry = - jar.getJarEntry(Constants.ApplicationContextXml); - istream = jar.getInputStream(entry); - context = (Context) digester.parse(istream); - - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - war.getAbsolutePath())); - return; - } - context.setConfigFile(new URL("jar:" + - war.toURI().toString() + "!/" + - Constants.ApplicationContextXml)); - } finally { - if (istream != null) { - try { - istream.close(); - } catch (IOException e) { - /* Ignore */ - } - istream = null; - } - entry = null; - if (jar != null) { - try { - jar.close(); - } catch (IOException e) { - /* Ignore */ - } - jar = null; - } - digester.reset(); - } - } - } else { - context = (Context) Class.forName(contextClass).newInstance(); - } - - // Populate redeploy resources with the WAR file - deployedApp.redeployResources.put - (war.getAbsolutePath(), Long.valueOf(war.lastModified())); - - if (deployXML && xml.exists() && copyXML) { - deployedApp.redeployResources.put(xml.getAbsolutePath(), - Long.valueOf(xml.lastModified())); - } - - Class clazz = Class.forName(host.getConfigClass()); - LifecycleListener listener = - (LifecycleListener) clazz.newInstance(); - context.addLifecycleListener(listener); - - context.setName(cn.getName()); - context.setPath(cn.getPath()); - context.setWebappVersion(cn.getVersion()); - context.setDocBase(cn.getBaseName() + ".war"); - host.addChild(context); - // If we're unpacking WARs, the docBase will be mutated after - // starting the context - if (unpackWARs && (context.getDocBase() != null)) { - File docBase = new File(host.getAppBaseFile(), cn.getBaseName()); - deployedApp.redeployResources.put(docBase.getAbsolutePath(), - Long.valueOf(docBase.lastModified())); - addWatchedResources(deployedApp, docBase.getAbsolutePath(), - context); - if (deployXML && !copyXML && (xmlInWar || xml.exists())) { - deployedApp.redeployResources.put(xml.getAbsolutePath(), - Long.valueOf(xml.lastModified())); - } - } else { - addWatchedResources(deployedApp, null, context); - } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployWar.error", - war.getAbsolutePath()), t); - } - - deployed.put(cn.getName(), deployedApp); - } - - - /** - * Deploy directories. - */ - protected void deployDirectories(File appBase, String[] files) { - - if (files == null) - return; - - 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()) { - ContextName cn = new ContextName(files[i]); - - if (isServiced(cn.getName())) - continue; - - deployDirectory(cn, dir); - } - } - } - - - /** - * @param cn - * @param dir - */ - protected void deployDirectory(ContextName cn, File dir) { - - if (deploymentExists(cn.getName())) - return; - - DeployedApplication deployedApp = new DeployedApplication(cn.getName()); - - // Deploy the application in this directory - if( log.isInfoEnabled() ) - log.info(sm.getString("hostConfig.deployDir", - dir.getAbsolutePath())); - try { - Context context = null; - File xml = new File(dir, Constants.ApplicationContextXml); - File xmlCopy = null; - if (deployXML && xml.exists()) { - synchronized (digester) { - try { - context = (Context) digester.parse(xml); - if (context == null) { - log.error(sm.getString( - "hostConfig.deployDescriptor.error", - xml)); - return; - } - } finally { - digester.reset(); - } - } - if (copyXML) { - xmlCopy = new File(configBase(), cn.getBaseName() + ".xml"); - InputStream is = null; - OutputStream os = null; - try { - is = new FileInputStream(xml); - os = new FileOutputStream(xmlCopy); - IOTools.flow(is, os); - // Don't catch IOE - let the outer try/catch handle it - } finally { - try { - if (is != null) is.close(); - } catch (IOException e){ - // Ignore - } - try { - if (os != null) os.close(); - } catch (IOException e){ - // Ignore - } - } - context.setConfigFile(xmlCopy.toURI().toURL()); - } else { - context.setConfigFile(xml.toURI().toURL()); - } - } else { - context = (Context) Class.forName(contextClass).newInstance(); - } - - Class clazz = Class.forName(host.getConfigClass()); - LifecycleListener listener = - (LifecycleListener) clazz.newInstance(); - context.addLifecycleListener(listener); - - context.setName(cn.getName()); - context.setPath(cn.getPath()); - context.setWebappVersion(cn.getVersion()); - context.setDocBase(cn.getBaseName()); - host.addChild(context); - deployedApp.redeployResources.put(dir.getAbsolutePath(), - Long.valueOf(dir.lastModified())); - if (deployXML && xml.exists()) { - if (xmlCopy == null) { - deployedApp.redeployResources.put( - xml.getAbsolutePath(), - Long.valueOf(xml.lastModified())); - } else { - deployedApp.redeployResources.put( - xmlCopy.getAbsolutePath(), - Long.valueOf(xmlCopy.lastModified())); - } - } - addWatchedResources(deployedApp, dir.getAbsolutePath(), context); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.error(sm.getString("hostConfig.deployDir.error", - dir.getAbsolutePath()), t); - } - - deployed.put(cn.getName(), deployedApp); - } - - - /** - * Check if a webapp is already deployed in this host. - * - * @param contextName of the context which will be checked - */ - protected boolean deploymentExists(String contextName) { - return (deployed.containsKey(contextName) || - (host.findChild(contextName) != null)); - } - - - /** - * Add watched resources to the specified Context. - * @param app HostConfig deployed app - * @param docBase web app docBase - * @param context web application context - */ - protected void addWatchedResources(DeployedApplication app, String docBase, - Context context) { - // FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, - // WEB-INF/*.xml), where we would only check if at least one - // resource is newer than app.timestamp - File docBaseFile = null; - if (docBase != null) { - docBaseFile = new File(docBase); - if (!docBaseFile.isAbsolute()) { - docBaseFile = new File(host.getAppBaseFile(), docBase); - } - } - String[] watchedResources = context.findWatchedResources(); - for (int i = 0; i < watchedResources.length; i++) { - File resource = new File(watchedResources[i]); - if (!resource.isAbsolute()) { - if (docBase != null) { - resource = new File(docBaseFile, watchedResources[i]); - } else { - if(log.isDebugEnabled()) - log.debug("Ignoring non-existent WatchedResource '" + - resource.getAbsolutePath() + "'"); - continue; - } - } - if(log.isDebugEnabled()) - log.debug("Watching WatchedResource '" + - resource.getAbsolutePath() + "'"); - app.reloadResources.put(resource.getAbsolutePath(), - Long.valueOf(resource.lastModified())); - } - } - - - /** - * Check resources for redeployment and reloading. - */ - protected synchronized void checkResources(DeployedApplication app) { - String[] resources = - app.redeployResources.keySet().toArray(new String[0]); - for (int i = 0; i < resources.length; i++) { - File resource = new File(resources[i]); - if (log.isDebugEnabled()) - log.debug("Checking context[" + app.name + - "] redeploy resource " + resource); - if (resource.exists()) { - long lastModified = - app.redeployResources.get(resources[i]).longValue(); - if ((!resource.isDirectory()) && - resource.lastModified() > lastModified) { - // Undeploy application - if (log.isInfoEnabled()) - log.info(sm.getString("hostConfig.undeploy", app.name)); - ContainerBase context = - (ContainerBase) host.findChild(app.name); - try { - host.removeChild(context); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.warn(sm.getString - ("hostConfig.context.remove", app.name), t); - } - // Delete other redeploy resources - for (int j = i + 1; j < resources.length; j++) { - try { - File current = new File(resources[j]); - current = current.getCanonicalFile(); - if ((current.getAbsolutePath().startsWith( - host.getAppBaseFile().getAbsolutePath() + - File.separator)) - || (current.getAbsolutePath().startsWith( - configBase().getAbsolutePath()))) { - if (log.isDebugEnabled()) - log.debug("Delete " + current); - ExpandWar.delete(current); - } - } catch (IOException e) { - log.warn(sm.getString - ("hostConfig.canonicalizing", app.name), e); - } - } - deployed.remove(app.name); - return; - } - } else { - // There is a chance the the resource was only missing - // temporarily eg renamed during a text editor save - try { - Thread.sleep(500); - } catch (InterruptedException e1) { - // Ignore - } - // Recheck the resource to see if it was really deleted - if (resource.exists()) { - continue; - } - long lastModified = - app.redeployResources.get(resources[i]).longValue(); - if (lastModified == 0L) { - continue; - } - // Undeploy application - if (log.isInfoEnabled()) - log.info(sm.getString("hostConfig.undeploy", app.name)); - ContainerBase context = - (ContainerBase) host.findChild(app.name); - try { - host.removeChild(context); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - log.warn(sm.getString - ("hostConfig.context.remove", app.name), t); - } - // Delete all redeploy resources - for (int j = i + 1; j < resources.length; j++) { - try { - File current = new File(resources[j]); - current = current.getCanonicalFile(); - if ((current.getAbsolutePath().startsWith( - host.getAppBaseFile().getAbsolutePath() + File.separator)) - || (current.getAbsolutePath().startsWith( - configBase().getAbsolutePath()))) { - if (log.isDebugEnabled()) - log.debug("Delete " + current); - ExpandWar.delete(current); - } - } catch (IOException e) { - log.warn(sm.getString - ("hostConfig.canonicalizing", app.name), e); - } - } - // Delete reload resources as well (to remove any remaining .xml - // descriptor) - String[] resources2 = - app.reloadResources.keySet().toArray(new String[0]); - for (int j = 0; j < resources2.length; j++) { - try { - File current = new File(resources2[j]); - current = current.getCanonicalFile(); - if ((current.getAbsolutePath().startsWith( - host.getAppBaseFile().getAbsolutePath() + File.separator)) - || ((current.getAbsolutePath().startsWith( - configBase().getAbsolutePath()) - && (current.getAbsolutePath().endsWith(".xml"))))) { - if (log.isDebugEnabled()) - log.debug("Delete " + current); - ExpandWar.delete(current); - } - } catch (IOException e) { - log.warn(sm.getString - ("hostConfig.canonicalizing", app.name), e); - } - } - deployed.remove(app.name); - return; - } - } - resources = app.reloadResources.keySet().toArray(new String[0]); - for (int i = 0; i < resources.length; i++) { - File resource = new File(resources[i]); - if (log.isDebugEnabled()) - log.debug("Checking context[" + app.name + - "] reload resource " + resource); - long lastModified = - app.reloadResources.get(resources[i]).longValue(); - if ((!resource.exists() && lastModified != 0L) - || (resource.lastModified() != lastModified)) { - // Reload application - if(log.isInfoEnabled()) - log.info(sm.getString("hostConfig.reload", app.name)); - Container context = host.findChild(app.name); - try { - // Might not have started if start failed last time - if (context.getState().isAvailable()) { - context.stop(); - } - } catch (Exception e) { - log.warn(sm.getString - ("hostConfig.context.restart", app.name), e); - } - // If the context was not started (for example an error - // in web.xml) we'll still get to try to start - try { - context.start(); - } catch (Exception e) { - log.warn(sm.getString - ("hostConfig.context.restart", app.name), e); - } - // Update times - app.reloadResources.put(resources[i], - Long.valueOf(resource.lastModified())); - app.timestamp = System.currentTimeMillis(); - return; - } - } - } - - - /** - * Process a "start" event for this Host. - */ - public void start() { - - if (log.isDebugEnabled()) - log.debug(sm.getString("hostConfig.start")); - - try { - ObjectName hostON = host.getObjectName(); - oname = new ObjectName - (hostON.getDomain() + ":type=Deployer,host=" + host.getName()); - Registry.getRegistry(null, null).registerComponent - (this, oname, this.getClass().getName()); - } catch (Exception e) { - log.error(sm.getString("hostConfig.jmx.register", oname), e); - } - - if (host.getCreateDirs()) { - File[] dirs = new File[] {host.getAppBaseFile(),configBase()}; - for (int i=0; i redeployResources = - new LinkedHashMap(); - - /** - * Any modification of the specified (static) resources will cause a - * reload of the application. This will typically contain resources - * such as the web.xml of a webapp, but can be configured to contain - * additional descriptors. - * The value is the last modification time. - */ - public HashMap reloadResources = - new HashMap(); - - /** - * Instant where the application was last put in service. - */ - public long timestamp = System.currentTimeMillis(); - } - -} +/* + * 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.startup; + + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.management.ObjectName; + +import org.apache.catalina.Container; +import org.apache.catalina.Context; +import org.apache.catalina.Engine; +import org.apache.catalina.Globals; +import org.apache.catalina.Host; +import org.apache.catalina.Lifecycle; +import org.apache.catalina.LifecycleEvent; +import org.apache.catalina.LifecycleListener; +import org.apache.catalina.core.ContainerBase; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.util.ContextName; +import org.apache.catalina.util.IOTools; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.digester.Digester; +import org.apache.tomcat.util.modeler.Registry; +import org.apache.tomcat.util.res.StringManager; + + +/** + * Startup event listener for a Host that configures the properties + * of that Host, and the associated defined contexts. + * + * @author Craig R. McClanahan + * @author Remy Maucherat + * @version $Id$ + */ +public class HostConfig + implements LifecycleListener { + + private static final Log log = LogFactory.getLog( HostConfig.class ); + + // ----------------------------------------------------- Instance Variables + + + /** + * Config base. + */ + protected File configBase = null; + + + /** + * The Java class name of the Context configuration class we should use. + */ + protected String configClass = "org.apache.catalina.startup.ContextConfig"; + + + /** + * The Java class name of the Context implementation we should use. + */ + protected String contextClass = "org.apache.catalina.core.StandardContext"; + + + /** + * The Host we are associated with. + */ + protected Host host = null; + + + /** + * The JMX ObjectName of this component. + */ + protected ObjectName oname = null; + + + /** + * The string resources for this package. + */ + protected static final StringManager sm = + StringManager.getManager(Constants.Package); + + + /** + * Should we deploy XML Context config files packaged with WAR files and + * directories? + */ + protected boolean deployXML = false; + + + /** + * Should XML files be copied to $CATALINA_BASE/conf// by + * default when a web application is deployed? + */ + protected boolean copyXML = false; + + + /** + * Should we unpack WAR files when auto-deploying applications in the + * appBase directory? + */ + protected boolean unpackWARs = false; + + + /** + * Map of deployed applications. + */ + protected Map deployed = + new ConcurrentHashMap(); + + + /** + * List of applications which are being serviced, and shouldn't be + * deployed/undeployed/redeployed at the moment. + */ + protected ArrayList serviced = new ArrayList(); + + + /** + * The Digester instance used to parse context descriptors. + */ + protected static Digester digester = createDigester(); + + /** + * The list of Wars in the appBase to be ignored because they are invalid + * (e.g. contain /../ sequences). + */ + protected Set invalidWars = new HashSet(); + + // ------------------------------------------------------------- Properties + + + /** + * Return the Context configuration class name. + */ + public String getConfigClass() { + + return (this.configClass); + + } + + + /** + * Set the Context configuration class name. + * + * @param configClass The new Context configuration class name. + */ + public void setConfigClass(String configClass) { + + this.configClass = configClass; + + } + + + /** + * Return the Context implementation class name. + */ + public String getContextClass() { + + return (this.contextClass); + + } + + + /** + * Set the Context implementation class name. + * + * @param contextClass The new Context implementation class name. + */ + public void setContextClass(String contextClass) { + + this.contextClass = contextClass; + + } + + + /** + * Return the deploy XML config file flag for this component. + */ + public boolean isDeployXML() { + + return (this.deployXML); + + } + + + /** + * Set the deploy XML config file flag for this component. + * + * @param deployXML The new deploy XML flag + */ + public void setDeployXML(boolean deployXML) { + + this.deployXML= deployXML; + + } + + + /** + * Return the copy XML config file flag for this component. + */ + public boolean isCopyXML() { + + return (this.copyXML); + + } + + + /** + * Set the copy XML config file flag for this component. + * + * @param copyXML The new copy XML flag + */ + public void setCopyXML(boolean copyXML) { + + this.copyXML= copyXML; + + } + + + /** + * Return the unpack WARs flag. + */ + public boolean isUnpackWARs() { + + return (this.unpackWARs); + + } + + + /** + * Set the unpack WARs flag. + * + * @param unpackWARs The new unpack WARs flag + */ + public void setUnpackWARs(boolean unpackWARs) { + + this.unpackWARs = unpackWARs; + + } + + + // --------------------------------------------------------- Public Methods + + + /** + * Process the START event for an associated Host. + * + * @param event The lifecycle event that has occurred + */ + @Override + public void lifecycleEvent(LifecycleEvent event) { + + if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) + check(); + + // Identify the host we are associated with + try { + host = (Host) event.getLifecycle(); + if (host instanceof StandardHost) { + setCopyXML(((StandardHost) host).isCopyXML()); + setDeployXML(((StandardHost) host).isDeployXML()); + setUnpackWARs(((StandardHost) host).isUnpackWARs()); + } + } catch (ClassCastException e) { + log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e); + return; + } + + // Process the event that has occurred + if (event.getType().equals(Lifecycle.START_EVENT)) + start(); + else if (event.getType().equals(Lifecycle.STOP_EVENT)) + stop(); + + } + + + /** + * Add a serviced application to the list. + */ + public synchronized void addServiced(String name) { + serviced.add(name); + } + + + /** + * Is application serviced ? + * @return state of the application + */ + public synchronized boolean isServiced(String name) { + return (serviced.contains(name)); + } + + + /** + * Removed a serviced application from the list. + */ + public synchronized void removeServiced(String name) { + serviced.remove(name); + } + + + /** + * Get the instant where an application was deployed. + * @return 0L if no application with that name is deployed, or the instant + * on which the application was deployed + */ + public long getDeploymentTime(String name) { + DeployedApplication app = deployed.get(name); + if (app == null) { + return 0L; + } + + return app.timestamp; + } + + + /** + * Has the specified application been deployed? Note applications defined + * in server.xml will not have been deployed. + * @return true if the application has been deployed and + * false if the application has not been deployed or does not + * exist + */ + public boolean isDeployed(String name) { + DeployedApplication app = deployed.get(name); + if (app == null) { + return false; + } + + return true; + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Create the digester which will be used to parse context config files. + */ + protected static Digester createDigester() { + Digester digester = new Digester(); + digester.setValidating(false); + // Add object creation rule + digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext", + "className"); + // Set the properties on that object (it doesn't matter if extra + // properties are set) + digester.addSetProperties("Context"); + return (digester); + } + + protected File returnCanonicalPath(String path) { + File file = new File(path); + File base = new File(System.getProperty(Globals.CATALINA_BASE_PROP)); + if (!file.isAbsolute()) + file = new File(base,path); + try { + return file.getCanonicalFile(); + } catch (IOException e) { + return file; + } + } + + + /** + * Return a File object representing the "configuration root" directory + * for our associated Host. + */ + protected File configBase() { + + if (configBase != null) { + return configBase; + } + + if (host.getXmlBase()!=null) { + configBase = returnCanonicalPath(host.getXmlBase()); + } else { + StringBuilder xmlDir = new StringBuilder("conf"); + Container parent = host.getParent(); + if (parent instanceof Engine) { + xmlDir.append('/'); + xmlDir.append(parent.getName()); + } + xmlDir.append('/'); + xmlDir.append(host.getName()); + configBase = returnCanonicalPath(xmlDir.toString()); + } + return (configBase); + + } + + /** + * Get the name of the configBase. + * For use with JMX management. + */ + public String getConfigBaseName() { + return configBase().getAbsolutePath(); + } + + + /** + * Deploy applications for any directories or WAR files that are found + * in our "application root" directory. + */ + protected void deployApps() { + + File appBase = host.getAppBaseFile(); + File configBase = configBase(); + String[] filteredAppPaths = filterAppPaths(appBase.list()); + // Deploy XML descriptors from configBase + deployDescriptors(configBase, configBase.list()); + // Deploy WARs, and loop if additional descriptors are found + deployWARs(appBase, filteredAppPaths); + // Deploy expanded folders + deployDirectories(appBase, filteredAppPaths); + + } + + + /** + * Filter the list of application file paths to remove those that match + * the regular expression defined by {@link Host#getDeployIgnore()}. + * + * @param unfilteredAppPaths The list of application paths to filtert + * + * @return The filtered list of application paths + */ + protected String[] filterAppPaths(String[] unfilteredAppPaths) { + Pattern filter = host.getDeployIgnorePattern(); + if (filter == null) { + return unfilteredAppPaths; + } + + List filteredList = new ArrayList(); + Matcher matcher = null; + for (String appPath : unfilteredAppPaths) { + if (matcher == null) { + matcher = filter.matcher(appPath); + } else { + matcher.reset(appPath); + } + if (matcher.matches()) { + if (log.isDebugEnabled()) { + log.debug(sm.getString("hostConfig.ignorePath", appPath)); + } + } else { + filteredList.add(appPath); + } + } + return filteredList.toArray(new String[filteredList.size()]); + } + + + /** + * Deploy applications for any directories or WAR files that are found + * in our "application root" directory. + */ + protected void deployApps(String name) { + + File appBase = host.getAppBaseFile(); + File configBase = configBase(); + ContextName cn = new ContextName(name); + String baseName = cn.getBaseName(); + + if (deploymentExists(baseName)) { + return; + } + + // Deploy XML descriptors from configBase + File xml = new File(configBase, baseName + ".xml"); + if (xml.exists()) + deployDescriptor(cn, xml); + // Deploy WARs, and loop if additional descriptors are found + File war = new File(appBase, baseName + ".war"); + if (war.exists()) + deployWAR(cn, war); + // Deploy expanded folders + File dir = new File(appBase, baseName); + if (dir.exists()) + deployDirectory(cn, dir); + } + + + /** + * Deploy XML context descriptors. + */ + protected void deployDescriptors(File configBase, String[] files) { + + if (files == null) + return; + + ExecutorService es = host.getStartStopExecutor(); + List> results = new ArrayList>(); + + for (int i = 0; i < files.length; i++) { + File contextXml = new File(configBase, files[i]); + + if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".xml")) { + ContextName cn = new ContextName(files[i]); + + if (isServiced(cn.getName()) || deploymentExists(cn.getName())) + continue; + + results.add( + es.submit(new DeployDescriptor(this, cn, contextXml))); + } + } + + for (Future result : results) { + try { + result.get(); + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDescriptor.threaded.error"), e); + } + } + } + + + /** + * @param cn + * @param contextXml + */ + protected void deployDescriptor(ContextName cn, File contextXml) { + + DeployedApplication deployedApp = new DeployedApplication(cn.getName()); + + // Assume this is a configuration descriptor and deploy it + if(log.isInfoEnabled()) { + log.info(sm.getString("hostConfig.deployDescriptor", + contextXml.getAbsolutePath())); + } + + Context context = null; + try { + synchronized (digester) { + try { + context = (Context) digester.parse(contextXml); + if (context == null) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + contextXml.getAbsolutePath())); + return; + } + } finally { + digester.reset(); + } + } + + Class clazz = Class.forName(host.getConfigClass()); + LifecycleListener listener = + (LifecycleListener) clazz.newInstance(); + context.addLifecycleListener(listener); + + context.setConfigFile(contextXml.toURI().toURL()); + context.setName(cn.getName()); + context.setPath(cn.getPath()); + context.setWebappVersion(cn.getVersion()); + // Add the associated docBase to the redeployed list if it's a WAR + boolean isExternalWar = false; + boolean isExternal = false; + if (context.getDocBase() != null) { + File docBase = new File(context.getDocBase()); + if (!docBase.isAbsolute()) { + docBase = new File(host.getAppBaseFile(), context.getDocBase()); + } + // If external docBase, register .xml as redeploy first + if (!docBase.getCanonicalPath().startsWith( + host.getAppBaseFile().getAbsolutePath() + File.separator)) { + isExternal = true; + deployedApp.redeployResources.put( + contextXml.getAbsolutePath(), + Long.valueOf(contextXml.lastModified())); + deployedApp.redeployResources.put(docBase.getAbsolutePath(), + Long.valueOf(docBase.lastModified())); + if (docBase.getAbsolutePath().toLowerCase(Locale.ENGLISH).endsWith(".war")) { + isExternalWar = true; + } + } else { + log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified", + docBase)); + // Ignore specified docBase + context.setDocBase(null); + } + } + host.addChild(context); + // Get paths for WAR and expanded WAR in appBase + + // default to appBase dir + name + File expandedDocBase = new File(host.getAppBaseFile(), cn.getBaseName()); + if (context.getDocBase() != null) { + // first assume docBase is absolute + expandedDocBase = new File(context.getDocBase()); + if (!expandedDocBase.isAbsolute()) { + // if docBase specified and relative, it must be relative to appBase + expandedDocBase = new File(host.getAppBaseFile(), context.getDocBase()); + } + } + // Add the eventual unpacked WAR and all the resources which will be + // watched inside it + if (isExternalWar && unpackWARs) { + deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), + Long.valueOf(expandedDocBase.lastModified())); + deployedApp.redeployResources.put(contextXml.getAbsolutePath(), + Long.valueOf(contextXml.lastModified())); + addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context); + } else { + // Find an existing matching war and expanded folder + if (!isExternal) { + File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war"); + if (warDocBase.exists()) { + deployedApp.redeployResources.put(warDocBase.getAbsolutePath(), + Long.valueOf(warDocBase.lastModified())); + } + } + if (expandedDocBase.exists()) { + deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(), + Long.valueOf(expandedDocBase.lastModified())); + addWatchedResources(deployedApp, + expandedDocBase.getAbsolutePath(), context); + } else { + addWatchedResources(deployedApp, null, context); + } + // Add the context XML to the list of files which should trigger a redeployment + if (!isExternal) { + deployedApp.redeployResources.put( + contextXml.getAbsolutePath(), + Long.valueOf(contextXml.lastModified())); + } + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployDescriptor.error", + contextXml.getAbsolutePath()), t); + } + + if (context != null && host.findChild(context.getName()) != null) { + deployed.put(context.getName(), deployedApp); + } + } + + + /** + * Deploy WAR files. + */ + protected void deployWARs(File appBase, String[] files) { + + if (files == null) + return; + + ExecutorService es = host.getStartStopExecutor(); + List> results = new ArrayList>(); + + for (int i = 0; i < files.length; i++) { + + if (files[i].equalsIgnoreCase("META-INF")) + continue; + if (files[i].equalsIgnoreCase("WEB-INF")) + continue; + File war = new File(appBase, files[i]); + if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") && war.isFile() + && !invalidWars.contains(files[i]) ) { + + ContextName cn = new ContextName(files[i]); + + // Check for WARs with /../ /./ or similar sequences in the name + if (!validateContextPath(appBase, cn.getBaseName())) { + log.error(sm.getString( + "hostConfig.illegalWarName", files[i])); + invalidWars.add(files[i]); + continue; + } + + if (isServiced(cn.getName()) || deploymentExists(cn.getName())) + continue; + + results.add(es.submit(new DeployWar(this, cn, war))); + } + } + + for (Future result : results) { + try { + result.get(); + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployWar.threaded.error"), e); + } + } + } + + + private boolean validateContextPath(File appBase, String contextPath) { + // More complicated than the ideal as the canonical path may or may + // not end with File.separator for a directory + + StringBuilder docBase; + String canonicalDocBase = null; + + try { + String canonicalAppBase = appBase.getCanonicalPath(); + docBase = new StringBuilder(canonicalAppBase); + if (canonicalAppBase.endsWith(File.separator)) { + docBase.append(contextPath.substring(1).replace( + '/', File.separatorChar)); + } else { + docBase.append(contextPath.replace('/', File.separatorChar)); + } + // At this point docBase should be canonical but will not end + // with File.separator + + canonicalDocBase = + (new File(docBase.toString())).getCanonicalPath(); + + // If the canonicalDocBase ends with File.separator, add one to + // docBase before they are compared + if (canonicalDocBase.endsWith(File.separator)) { + docBase.append(File.separator); + } + } catch (IOException ioe) { + return false; + } + + // Compare the two. If they are not the same, the contextPath must + // have /../ like sequences in it + return canonicalDocBase.equals(docBase.toString()); + } + + /** + * @param cn + * @param war + */ + protected void deployWAR(ContextName cn, File war) { + + // Checking for a nested /META-INF/context.xml + JarFile jar = null; + JarEntry entry = null; + InputStream istream = null; + BufferedOutputStream ostream = null; + File xml; + if (copyXML) { + xml = new File(configBase(), cn.getBaseName() + ".xml"); + } else { + xml = new File(host.getAppBaseFile(), + cn.getBaseName() + "/META-INF/context.xml"); + } + boolean xmlInWar = false; + + if (deployXML && !xml.exists()) { + try { + jar = new JarFile(war); + entry = jar.getJarEntry(Constants.ApplicationContextXml); + if (entry != null) { + xmlInWar = true; + } + if ((copyXML || unpackWARs) && xmlInWar) { + istream = jar.getInputStream(entry); + + ostream = + new BufferedOutputStream + (new FileOutputStream(xml), 1024); + byte buffer[] = new byte[1024]; + while (true) { + int n = istream.read(buffer); + if (n < 0) { + break; + } + ostream.write(buffer, 0, n); + } + ostream.flush(); + ostream.close(); + ostream = null; + istream.close(); + istream = null; + } + } catch (IOException e) { + /* Ignore */ + } finally { + if (ostream != null) { + try { + ostream.close(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + } + ostream = null; + } + if (istream != null) { + try { + istream.close(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + } + istream = null; + } + entry = null; + if (jar != null) { + try { + jar.close(); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + } + jar = null; + } + } + } + + DeployedApplication deployedApp = new DeployedApplication(cn.getName()); + + // Deploy the application in this WAR file + if(log.isInfoEnabled()) + log.info(sm.getString("hostConfig.deployWar", + war.getAbsolutePath())); + + try { + Context context = null; + if (deployXML && xml.exists()) { + synchronized (digester) { + try { + context = (Context) digester.parse(xml); + if (context == null) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + war.getAbsolutePath())); + return; + } + } finally { + digester.reset(); + } + } + context.setConfigFile(xml.toURI().toURL()); + } else if (deployXML && xmlInWar) { + synchronized (digester) { + try { + jar = new JarFile(war); + entry = + jar.getJarEntry(Constants.ApplicationContextXml); + istream = jar.getInputStream(entry); + context = (Context) digester.parse(istream); + + if (context == null) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + war.getAbsolutePath())); + return; + } + context.setConfigFile(new URL("jar:" + + war.toURI().toString() + "!/" + + Constants.ApplicationContextXml)); + } finally { + if (istream != null) { + try { + istream.close(); + } catch (IOException e) { + /* Ignore */ + } + istream = null; + } + entry = null; + if (jar != null) { + try { + jar.close(); + } catch (IOException e) { + /* Ignore */ + } + jar = null; + } + digester.reset(); + } + } + } else { + context = (Context) Class.forName(contextClass).newInstance(); + } + + // Populate redeploy resources with the WAR file + deployedApp.redeployResources.put + (war.getAbsolutePath(), Long.valueOf(war.lastModified())); + + if (deployXML && xml.exists() && copyXML) { + deployedApp.redeployResources.put(xml.getAbsolutePath(), + Long.valueOf(xml.lastModified())); + } + + Class clazz = Class.forName(host.getConfigClass()); + LifecycleListener listener = + (LifecycleListener) clazz.newInstance(); + context.addLifecycleListener(listener); + + context.setName(cn.getName()); + context.setPath(cn.getPath()); + context.setWebappVersion(cn.getVersion()); + context.setDocBase(cn.getBaseName() + ".war"); + host.addChild(context); + // If we're unpacking WARs, the docBase will be mutated after + // starting the context + if (unpackWARs && (context.getDocBase() != null)) { + File docBase = new File(host.getAppBaseFile(), cn.getBaseName()); + deployedApp.redeployResources.put(docBase.getAbsolutePath(), + Long.valueOf(docBase.lastModified())); + addWatchedResources(deployedApp, docBase.getAbsolutePath(), + context); + if (deployXML && !copyXML && (xmlInWar || xml.exists())) { + deployedApp.redeployResources.put(xml.getAbsolutePath(), + Long.valueOf(xml.lastModified())); + } + } else { + addWatchedResources(deployedApp, null, context); + } + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployWar.error", + war.getAbsolutePath()), t); + } + + deployed.put(cn.getName(), deployedApp); + } + + + /** + * Deploy directories. + */ + protected void deployDirectories(File appBase, String[] files) { + + if (files == null) + return; + + ExecutorService es = host.getStartStopExecutor(); + List> results = new ArrayList>(); + + 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()) { + ContextName cn = new ContextName(files[i]); + + if (isServiced(cn.getName()) || deploymentExists(cn.getName())) + continue; + + results.add(es.submit(new DeployDirectory(this, cn, dir))); + } + } + + for (Future result : results) { + try { + result.get(); + } catch (Exception e) { + log.error(sm.getString( + "hostConfig.deployDir.threaded.error"), e); + } + } + } + + + /** + * @param cn + * @param dir + */ + protected void deployDirectory(ContextName cn, File dir) { + + DeployedApplication deployedApp = new DeployedApplication(cn.getName()); + + // Deploy the application in this directory + if( log.isInfoEnabled() ) + log.info(sm.getString("hostConfig.deployDir", + dir.getAbsolutePath())); + try { + Context context = null; + File xml = new File(dir, Constants.ApplicationContextXml); + File xmlCopy = null; + if (deployXML && xml.exists()) { + synchronized (digester) { + try { + context = (Context) digester.parse(xml); + if (context == null) { + log.error(sm.getString( + "hostConfig.deployDescriptor.error", + xml)); + return; + } + } finally { + digester.reset(); + } + } + if (copyXML) { + xmlCopy = new File(configBase(), cn.getBaseName() + ".xml"); + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(xml); + os = new FileOutputStream(xmlCopy); + IOTools.flow(is, os); + // Don't catch IOE - let the outer try/catch handle it + } finally { + try { + if (is != null) is.close(); + } catch (IOException e){ + // Ignore + } + try { + if (os != null) os.close(); + } catch (IOException e){ + // Ignore + } + } + context.setConfigFile(xmlCopy.toURI().toURL()); + } else { + context.setConfigFile(xml.toURI().toURL()); + } + } else { + context = (Context) Class.forName(contextClass).newInstance(); + } + + Class clazz = Class.forName(host.getConfigClass()); + LifecycleListener listener = + (LifecycleListener) clazz.newInstance(); + context.addLifecycleListener(listener); + + context.setName(cn.getName()); + context.setPath(cn.getPath()); + context.setWebappVersion(cn.getVersion()); + context.setDocBase(cn.getBaseName()); + host.addChild(context); + deployedApp.redeployResources.put(dir.getAbsolutePath(), + Long.valueOf(dir.lastModified())); + if (deployXML && xml.exists()) { + if (xmlCopy == null) { + deployedApp.redeployResources.put( + xml.getAbsolutePath(), + Long.valueOf(xml.lastModified())); + } else { + deployedApp.redeployResources.put( + xmlCopy.getAbsolutePath(), + Long.valueOf(xmlCopy.lastModified())); + } + } + addWatchedResources(deployedApp, dir.getAbsolutePath(), context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.error(sm.getString("hostConfig.deployDir.error", + dir.getAbsolutePath()), t); + } + + deployed.put(cn.getName(), deployedApp); + } + + + /** + * Check if a webapp is already deployed in this host. + * + * @param contextName of the context which will be checked + */ + protected boolean deploymentExists(String contextName) { + return (deployed.containsKey(contextName) || + (host.findChild(contextName) != null)); + } + + + /** + * Add watched resources to the specified Context. + * @param app HostConfig deployed app + * @param docBase web app docBase + * @param context web application context + */ + protected void addWatchedResources(DeployedApplication app, String docBase, + Context context) { + // FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, + // WEB-INF/*.xml), where we would only check if at least one + // resource is newer than app.timestamp + File docBaseFile = null; + if (docBase != null) { + docBaseFile = new File(docBase); + if (!docBaseFile.isAbsolute()) { + docBaseFile = new File(host.getAppBaseFile(), docBase); + } + } + String[] watchedResources = context.findWatchedResources(); + for (int i = 0; i < watchedResources.length; i++) { + File resource = new File(watchedResources[i]); + if (!resource.isAbsolute()) { + if (docBase != null) { + resource = new File(docBaseFile, watchedResources[i]); + } else { + if(log.isDebugEnabled()) + log.debug("Ignoring non-existent WatchedResource '" + + resource.getAbsolutePath() + "'"); + continue; + } + } + if(log.isDebugEnabled()) + log.debug("Watching WatchedResource '" + + resource.getAbsolutePath() + "'"); + app.reloadResources.put(resource.getAbsolutePath(), + Long.valueOf(resource.lastModified())); + } + } + + + /** + * Check resources for redeployment and reloading. + */ + protected synchronized void checkResources(DeployedApplication app) { + String[] resources = + app.redeployResources.keySet().toArray(new String[0]); + for (int i = 0; i < resources.length; i++) { + File resource = new File(resources[i]); + if (log.isDebugEnabled()) + log.debug("Checking context[" + app.name + + "] redeploy resource " + resource); + if (resource.exists()) { + long lastModified = + app.redeployResources.get(resources[i]).longValue(); + if ((!resource.isDirectory()) && + resource.lastModified() > lastModified) { + // Undeploy application + if (log.isInfoEnabled()) + log.info(sm.getString("hostConfig.undeploy", app.name)); + ContainerBase context = + (ContainerBase) host.findChild(app.name); + try { + host.removeChild(context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.warn(sm.getString + ("hostConfig.context.remove", app.name), t); + } + // Delete other redeploy resources + for (int j = i + 1; j < resources.length; j++) { + try { + File current = new File(resources[j]); + current = current.getCanonicalFile(); + if ((current.getAbsolutePath().startsWith( + host.getAppBaseFile().getAbsolutePath() + + File.separator)) + || (current.getAbsolutePath().startsWith( + configBase().getAbsolutePath()))) { + if (log.isDebugEnabled()) + log.debug("Delete " + current); + ExpandWar.delete(current); + } + } catch (IOException e) { + log.warn(sm.getString + ("hostConfig.canonicalizing", app.name), e); + } + } + deployed.remove(app.name); + return; + } + } else { + // There is a chance the the resource was only missing + // temporarily eg renamed during a text editor save + try { + Thread.sleep(500); + } catch (InterruptedException e1) { + // Ignore + } + // Recheck the resource to see if it was really deleted + if (resource.exists()) { + continue; + } + long lastModified = + app.redeployResources.get(resources[i]).longValue(); + if (lastModified == 0L) { + continue; + } + // Undeploy application + if (log.isInfoEnabled()) + log.info(sm.getString("hostConfig.undeploy", app.name)); + ContainerBase context = + (ContainerBase) host.findChild(app.name); + try { + host.removeChild(context); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + log.warn(sm.getString + ("hostConfig.context.remove", app.name), t); + } + // Delete all redeploy resources + for (int j = i + 1; j < resources.length; j++) { + try { + File current = new File(resources[j]); + current = current.getCanonicalFile(); + if ((current.getAbsolutePath().startsWith( + host.getAppBaseFile().getAbsolutePath() + File.separator)) + || (current.getAbsolutePath().startsWith( + configBase().getAbsolutePath()))) { + if (log.isDebugEnabled()) + log.debug("Delete " + current); + ExpandWar.delete(current); + } + } catch (IOException e) { + log.warn(sm.getString + ("hostConfig.canonicalizing", app.name), e); + } + } + // Delete reload resources as well (to remove any remaining .xml + // descriptor) + String[] resources2 = + app.reloadResources.keySet().toArray(new String[0]); + for (int j = 0; j < resources2.length; j++) { + try { + File current = new File(resources2[j]); + current = current.getCanonicalFile(); + if ((current.getAbsolutePath().startsWith( + host.getAppBaseFile().getAbsolutePath() + File.separator)) + || ((current.getAbsolutePath().startsWith( + configBase().getAbsolutePath()) + && (current.getAbsolutePath().endsWith(".xml"))))) { + if (log.isDebugEnabled()) + log.debug("Delete " + current); + ExpandWar.delete(current); + } + } catch (IOException e) { + log.warn(sm.getString + ("hostConfig.canonicalizing", app.name), e); + } + } + deployed.remove(app.name); + return; + } + } + resources = app.reloadResources.keySet().toArray(new String[0]); + for (int i = 0; i < resources.length; i++) { + File resource = new File(resources[i]); + if (log.isDebugEnabled()) + log.debug("Checking context[" + app.name + + "] reload resource " + resource); + long lastModified = + app.reloadResources.get(resources[i]).longValue(); + if ((!resource.exists() && lastModified != 0L) + || (resource.lastModified() != lastModified)) { + // Reload application + if(log.isInfoEnabled()) + log.info(sm.getString("hostConfig.reload", app.name)); + Container context = host.findChild(app.name); + try { + // Might not have started if start failed last time + if (context.getState().isAvailable()) { + context.stop(); + } + } catch (Exception e) { + log.warn(sm.getString + ("hostConfig.context.restart", app.name), e); + } + // If the context was not started (for example an error + // in web.xml) we'll still get to try to start + try { + context.start(); + } catch (Exception e) { + log.warn(sm.getString + ("hostConfig.context.restart", app.name), e); + } + // Update times + app.reloadResources.put(resources[i], + Long.valueOf(resource.lastModified())); + app.timestamp = System.currentTimeMillis(); + return; + } + } + } + + + /** + * Process a "start" event for this Host. + */ + public void start() { + + if (log.isDebugEnabled()) + log.debug(sm.getString("hostConfig.start")); + + try { + ObjectName hostON = host.getObjectName(); + oname = new ObjectName + (hostON.getDomain() + ":type=Deployer,host=" + host.getName()); + Registry.getRegistry(null, null).registerComponent + (this, oname, this.getClass().getName()); + } catch (Exception e) { + log.error(sm.getString("hostConfig.jmx.register", oname), e); + } + + if (host.getCreateDirs()) { + File[] dirs = new File[] {host.getAppBaseFile(),configBase()}; + for (int i=0; i redeployResources = + new LinkedHashMap(); + + /** + * Any modification of the specified (static) resources will cause a + * reload of the application. This will typically contain resources + * such as the web.xml of a webapp, but can be configured to contain + * additional descriptors. + * The value is the last modification time. + */ + public HashMap reloadResources = + new HashMap(); + + /** + * Instant where the application was last put in service. + */ + public long timestamp = System.currentTimeMillis(); + } + + private static class DeployDescriptor implements Runnable { + + private HostConfig config; + private ContextName cn; + private File descriptor; + + public DeployDescriptor(HostConfig config, ContextName cn, + File descriptor) { + this.config = config; + this.cn = cn; + this.descriptor= descriptor; + } + + @Override + public void run() { + config.deployDescriptor(cn, descriptor); + } + } + + private static class DeployWar implements Runnable { + + private HostConfig config; + private ContextName cn; + private File war; + + public DeployWar(HostConfig config, ContextName cn, File war) { + this.config = config; + this.cn = cn; + this.war = war; + } + + @Override + public void run() { + config.deployWAR(cn, war); + } + } + + private static class DeployDirectory implements Runnable { + + private HostConfig config; + private ContextName cn; + private File dir; + + public DeployDirectory(HostConfig config, ContextName cn, File dir) { + this.config = config; + this.cn = cn; + this.dir = dir; + } + + @Override + public void run() { + config.deployDirectory(cn, dir); + } + } +} diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties index 2d18b44..4baddc1 100644 --- a/java/org/apache/catalina/startup/LocalStrings.properties +++ b/java/org/apache/catalina/startup/LocalStrings.properties @@ -83,11 +83,14 @@ hostConfig.createDirs=Unable to create directory for deployment: {0} hostConfig.deploy=Deploying web application directory {0} hostConfig.deployDescriptor=Deploying configuration descriptor {0} hostConfig.deployDescriptor.error=Error deploying configuration descriptor {0} +hostConfig.deployDescriptor.threaded.error=Error waiting for multi-thread deployment of context descriptors to complete hostConfig.deployDescriptor.localDocBaseSpecified=A docBase {0} inside the host appBase has been specified, and will be ignored hostConfig.deployDir=Deploying web application directory {0} hostConfig.deployDir.error=Error deploying web application directory {0} +hostConfig.deployDir.threaded.error=Error waiting for multi-thread deployment of directories to completehostConfig.deployWar=Deploying web application archive {0} hostConfig.deployWar=Deploying web application archive {0} hostConfig.deployWar.error=Error deploying web application archive {0} +hostConfig.deployWar.threaded.error=Error waiting for multi-thread deployment of WAR files to complete hostConfig.deploy.error=Exception while deploying web application directory {0} hostConfig.deploying=Deploying discovered web applications hostConfig.expand=Expanding web application archive {0} diff --git a/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java b/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java deleted file mode 100644 index 43e4411..0000000 --- a/java/org/apache/tomcat/util/threads/DedicatedThreadExecutor.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.tomcat.util.threads; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadFactory; - -/** - * A utility class to execute a {@link Callable} in a dedicated thread. - * It can be used either with an instance to reuse the same thread for each call - * to {@link #execute(Callable)} or with the static method - * {@link #executeInOwnThread(Callable)}. When using an instance, - * {@link #shutdown()} must be called when the instance is no longer needed to - * dispose of the dedicated thread. - */ -public class DedicatedThreadExecutor { - private final SingleThreadFactory singleThreadFactory = - new SingleThreadFactory(); - private final ExecutorService executorService = - Executors.newSingleThreadExecutor(singleThreadFactory); - - /** - * Executes the given {@link Callable} with the thread spawned for the - * current {@link DedicatedThreadExecutor} instance, and returns its result. - * - * @param - * the type of the returned value - * @param callable - * @return the completed result - */ - public V execute(final Callable callable) { - final Future futureTask = executorService.submit(callable); - - boolean interrupted = false; - V result; - while (true) { - try { - result = futureTask.get(); - break; - } catch (InterruptedException e) { - // keep waiting - interrupted = true; - } catch (ExecutionException e) { - throw new RuntimeException(e); - } - } - if (interrupted) { - // set interruption status so that the caller may react to it - Thread.currentThread().interrupt(); - } - return result; - } - - /** - * Stops the dedicated thread and waits for its death. - */ - public void shutdown() { - executorService.shutdown(); - if (singleThreadFactory.singleThread != null) { - boolean interrupted = false; - while (true) { - try { - singleThreadFactory.singleThread.join(); - singleThreadFactory.singleThread = null; - break; - } catch (InterruptedException e) { - // keep waiting - interrupted = true; - } - } - if (interrupted) { - // set interruption status so that the caller may react to it - Thread.currentThread().interrupt(); - } - } - } - - /** - * Executes the given {@link Callable} in a new thread and returns the - * result after the thread is stopped. - * - * @param - * @param callable - * @return the completed result - */ - public static V executeInOwnThread( - final Callable callable) { - DedicatedThreadExecutor executor = new DedicatedThreadExecutor(); - try { - return executor.execute(callable); - } finally { - executor.shutdown(); - } - - } - - // we use a ThreadFactory so that we can later call Thread.join(). - // Indeed, calling shutdown() on an ExecutorService will eventually stop the - // thread but it might still be alive when execute() returns (race - // condition). - // This can lead to false alarms about potential memory leaks because the - // thread may have a web application class loader for its context class - // loader. - private static class SingleThreadFactory implements ThreadFactory { - private volatile Thread singleThread; - - @Override - public Thread newThread(Runnable r) { - if (singleThread != null) { - throw new IllegalStateException( - "should not have been called more than once"); - } - singleThread = new Thread(r); - singleThread.setDaemon(true); - return singleThread; - } - - } -} diff --git a/test/org/apache/tomcat/util/threads/DedicatedThreadExecutorTest.java b/test/org/apache/tomcat/util/threads/DedicatedThreadExecutorTest.java deleted file mode 100644 index 8d13ccf..0000000 --- a/test/org/apache/tomcat/util/threads/DedicatedThreadExecutorTest.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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.tomcat.util.threads; - -import java.util.concurrent.Callable; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; - -import org.junit.Test; - -public class DedicatedThreadExecutorTest { - private Thread dedicatedThread; - - @Test - public void testExecute() { - final Thread testingThread = Thread.currentThread(); - DedicatedThreadExecutor executor = new DedicatedThreadExecutor(); - Long result = executor.execute(new Callable() { - @Override - public Long call() throws Exception { - dedicatedThread = Thread.currentThread(); - assertNotSame(testingThread, dedicatedThread); - return Long.valueOf(123); - } - }); - assertEquals(123, result.longValue()); - - //check that the same thread is reused - executor.execute(new Callable() { - @Override - public Void call() throws Exception { - assertSame(dedicatedThread, Thread.currentThread()); - return null; - } - }); - - executor.shutdown(); - assertFalse(dedicatedThread.isAlive()); - } - - @Test - public void testExecuteInOwnThread() { - final Thread testingThread = Thread.currentThread(); - Long result = - DedicatedThreadExecutor.executeInOwnThread(new Callable() { - @Override - public Long call() throws Exception { - dedicatedThread = Thread.currentThread(); - assertNotSame(testingThread, dedicatedThread); - return Long.valueOf(456); - } - }); - assertEquals(456, result.longValue()); - assertFalse(dedicatedThread.isAlive()); - } - -} diff --git a/webapps/docs/config/engine.xml b/webapps/docs/config/engine.xml index 07ddec4..c05993b 100644 --- a/webapps/docs/config/engine.xml +++ b/webapps/docs/config/engine.xml @@ -1,257 +1,268 @@ - - - -]> - - - &project; - - - Craig R. McClanahan - The Engine Container - - - - -
- -
- -
- -

The Engine element represents the entire request - processing machinery associated with a particular Catalina - Service. It receives and processes - all requests from one or more Connectors, - and returns the completed response to the Connector for ultimate - transmission back to the client.

- -

Exactly one Engine element MUST be nested inside - a Service element, following all of the - corresponding Connector elements associated with this Service.

- -
- - -
- - - -

All implementations of Engine - support the following attributes:

- - - - -

This value represents the delay in seconds between the - invocation of the backgroundProcess method on this engine and - its child containers, including all hosts and contexts. - Child containers will not be invoked if their delay value is not - negative (which would mean they are using their own processing - thread). Setting this to a positive value will cause - a thread to be spawn. After waiting the specified amount of time, - the thread will invoke the backgroundProcess method on this engine - and all its child containers. If not specified, the default value for - this attribute is 10, which represent a 10 seconds delay.

-
- - -

Java class name of the implementation to use. This class must - implement the org.apache.catalina.Engine interface. - If not specified, the standard value (defined below) will be used.

-
- - -

The default host name, which identifies the - Host that will process requests directed - to host names on this server, but which are not configured in - this configuration file. This name MUST match the name - attributes of one of the Host elements - nested immediately inside.

-
- - -

Identifier which must be used in load balancing scenarios to enable - session affinity. The identifier, which must be unique across all - Tomcat servers which participate in the cluster, will be appended to - the generated session identifier, therefore allowing the front end - proxy to always forward a particular session to the same Tomcat - instance.

-
- - -

Logical name of this Engine, used in log and error messages. When - using multiple Service elements in the same - Server, each Engine MUST be assigned a unique - name.

-
- -
- -
- - - - -

The standard implementation of Engine is - org.apache.catalina.core.StandardEngine. - It supports the following additional attributes (in addition to the - common attributes listed above):

- - - - - -
- - -
- - -
- -

You can nest one or more Host elements inside - this Engine element, each representing a different virtual - host associated with this server. At least one Host - is required, and one of the nested Hosts MUST - have a name that matches the name specified for the - defaultHost attribute, listed above.

- -

You can nest at most one instance of the following utility components - by nesting a corresponding element inside your Engine - element:

-
    -
  • Realm - - Configure a realm that will allow its - database of users, and their associated roles, to be shared across all - Hosts and Contexts - nested inside this Engine, unless overridden by a - Realm configuration at a lower level.
  • -
- -
- - -
- - - - -

An engine is associated with the - org.apache.catalina.core.ContainerBase.[enginename] - log category. Note that the brackets are actually part of the name, - don't omit them.

- -
- - - - -

When you run a web server, one of the output files normally generated - is an access log, which generates one line of information for - each request processed by the server, in a standard format. Catalina - includes an optional Valve implementation that - can create access logs in the same standard format created by web servers, - or in any number of custom formats.

- -

You can ask Catalina to create an access log for all requests - processed by an Engine, - Host, or Context - by nesting a Valve element like this:

- - -<Engine name="Standalone" ...> - ... - <Valve className="org.apache.catalina.valves.AccessLogValve" - prefix="catalina_access_log." suffix=".txt" - pattern="common"/> - ... -</Engine> - - -

See Access Log Valve - for more information on the configuration attributes that are - supported.

- -
- - - - -

If you have implemented a Java object that needs to know when this - Engine is started or stopped, you can declare it by - nesting a Listener element inside this element. The - class name you specify must implement the - org.apache.catalina.LifecycleListener interface, and - it will be notified about the occurrence of the corresponding - lifecycle events. Configuration of such a listener looks like this:

- - -<Engine name="Standalone" ...> - ... - <Listener className="com.mycompany.mypackage.MyListener" ... > - ... -</Engine> - - -

Note that a Listener can have any number of additional properties - that may be configured from this element. Attribute names are matched - to corresponding JavaBean property names using the standard property - method naming patterns.

- -
- - - - -

You can ask Catalina to check the IP address, or host name, on every - incoming request directed to the surrounding - Engine, Host, or - Context element. The remote address or name - will be checked against configured "accept" and/or "deny" - filters, which are defined using java.util.regex Regular - Expression syntax. Requests that come from locations that are - not accepted will be rejected with an HTTP "Forbidden" error. - Example filter declarations:

- - -<Engine name="Standalone" ...> - ... - <Valve className="org.apache.catalina.valves.RemoteHostValve" - allow=".*\.mycompany\.com|www\.yourcompany\.com"/> - <Valve className="org.apache.catalina.valves.RemoteAddrValve" - deny="192\.168\.1\.\d+"/> - ... -</Engine> - - -

See Remote Address Filter - and Remote Host Filter for - more information about the configuration options that are supported.

- -
- - -
- - - - - -
+ + + +]> + + + &project; + + + Craig R. McClanahan + The Engine Container + + + + +
+ +
+ +
+ +

The Engine element represents the entire request + processing machinery associated with a particular Catalina + Service. It receives and processes + all requests from one or more Connectors, + and returns the completed response to the Connector for ultimate + transmission back to the client.

+ +

Exactly one Engine element MUST be nested inside + a Service element, following all of the + corresponding Connector elements associated with this Service.

+ +
+ + +
+ + + +

All implementations of Engine + support the following attributes:

+ + + + +

This value represents the delay in seconds between the + invocation of the backgroundProcess method on this engine and + its child containers, including all hosts and contexts. + Child containers will not be invoked if their delay value is not + negative (which would mean they are using their own processing + thread). Setting this to a positive value will cause + a thread to be spawn. After waiting the specified amount of time, + the thread will invoke the backgroundProcess method on this engine + and all its child containers. If not specified, the default value for + this attribute is 10, which represent a 10 seconds delay.

+
+ + +

Java class name of the implementation to use. This class must + implement the org.apache.catalina.Engine interface. + If not specified, the standard value (defined below) will be used.

+
+ + +

The default host name, which identifies the + Host that will process requests directed + to host names on this server, but which are not configured in + this configuration file. This name MUST match the name + attributes of one of the Host elements + nested immediately inside.

+
+ + +

Identifier which must be used in load balancing scenarios to enable + session affinity. The identifier, which must be unique across all + Tomcat servers which participate in the cluster, will be appended to + the generated session identifier, therefore allowing the front end + proxy to always forward a particular session to the same Tomcat + instance.

+
+ + +

Logical name of this Engine, used in log and error messages. When + using multiple Service elements in the same + Server, each Engine MUST be assigned a unique + name.

+
+ + +

The number of threads this Engine will use to start + child Host elements in parallel. The special + value of 0 will result in the value of + Runtime.getRuntime().availableProcessors() being used. + Negative values will result in + Runtime.getRuntime().availableProcessors() + value being + used unless this is less than 1 in which case 1 thread will be used. If + not specified, the default value of 1 will be used.

+
+ +
+ +
+ + + + +

The standard implementation of Engine is + org.apache.catalina.core.StandardEngine. + It supports the following additional attributes (in addition to the + common attributes listed above):

+ + + + + +
+ + +
+ + +
+ +

You can nest one or more Host elements inside + this Engine element, each representing a different virtual + host associated with this server. At least one Host + is required, and one of the nested Hosts MUST + have a name that matches the name specified for the + defaultHost attribute, listed above.

+ +

You can nest at most one instance of the following utility components + by nesting a corresponding element inside your Engine + element:

+
    +
  • Realm - + Configure a realm that will allow its + database of users, and their associated roles, to be shared across all + Hosts and Contexts + nested inside this Engine, unless overridden by a + Realm configuration at a lower level.
  • +
+ +
+ + +
+ + + + +

An engine is associated with the + org.apache.catalina.core.ContainerBase.[enginename] + log category. Note that the brackets are actually part of the name, + don't omit them.

+ +
+ + + + +

When you run a web server, one of the output files normally generated + is an access log, which generates one line of information for + each request processed by the server, in a standard format. Catalina + includes an optional Valve implementation that + can create access logs in the same standard format created by web servers, + or in any number of custom formats.

+ +

You can ask Catalina to create an access log for all requests + processed by an Engine, + Host, or Context + by nesting a Valve element like this:

+ + +<Engine name="Standalone" ...> + ... + <Valve className="org.apache.catalina.valves.AccessLogValve" + prefix="catalina_access_log." suffix=".txt" + pattern="common"/> + ... +</Engine> + + +

See Access Log Valve + for more information on the configuration attributes that are + supported.

+ +
+ + + + +

If you have implemented a Java object that needs to know when this + Engine is started or stopped, you can declare it by + nesting a Listener element inside this element. The + class name you specify must implement the + org.apache.catalina.LifecycleListener interface, and + it will be notified about the occurrence of the corresponding + lifecycle events. Configuration of such a listener looks like this:

+ + +<Engine name="Standalone" ...> + ... + <Listener className="com.mycompany.mypackage.MyListener" ... > + ... +</Engine> + + +

Note that a Listener can have any number of additional properties + that may be configured from this element. Attribute names are matched + to corresponding JavaBean property names using the standard property + method naming patterns.

+ +
+ + + + +

You can ask Catalina to check the IP address, or host name, on every + incoming request directed to the surrounding + Engine, Host, or + Context element. The remote address or name + will be checked against configured "accept" and/or "deny" + filters, which are defined using java.util.regex Regular + Expression syntax. Requests that come from locations that are + not accepted will be rejected with an HTTP "Forbidden" error. + Example filter declarations:

+ + +<Engine name="Standalone" ...> + ... + <Valve className="org.apache.catalina.valves.RemoteHostValve" + allow=".*\.mycompany\.com|www\.yourcompany\.com"/> + <Valve className="org.apache.catalina.valves.RemoteAddrValve" + deny="192\.168\.1\.\d+"/> + ... +</Engine> + + +

See Remote Address Filter + and Remote Host Filter for + more information about the configuration options that are supported.

+ +
+ + +
+ + + + + +
diff --git a/webapps/docs/config/host.xml b/webapps/docs/config/host.xml index 53660f5..ba8f972 100644 --- a/webapps/docs/config/host.xml +++ b/webapps/docs/config/host.xml @@ -1,671 +1,684 @@ - - - -]> - - - &project; - - - Craig R. McClanahan - Remy Maucherat - Yoav Shapira - The Host Container - - - - -
- -
- -
- -

The Host element represents a virtual host, - which is an association of a network name for a server (such as - "www.mycompany.com" with the particular server on which Tomcat is running. - For clients to be able to connect to a Tomcat server using its network name, - this name must be registered in the Domain Name Service (DNS) server - that manages the Internet domain you belong to - contact your Network - Administrator for more information.

- -

In many cases, System Administrators wish to associate more than - one network name (such as www.mycompany.com and - company.com) with the same virtual host and applications. - This can be accomplished using the Host - Name Aliases feature discussed below.

- -

One or more Host elements are nested inside an - Engine element. Inside the Host element, you - can nest Context elements for the web - applications associated with this virtual host. Exactly one of the Hosts - associated with each Engine MUST have a name matching the - defaultHost attribute of that Engine.

- -

Clients normally use host names to identify the server they wish to connect - to. This host name is also included in the HTTP request headers. Tomcat - extracts the host name from the HTTP headers and looks for a - Host with a matching name. If no match is found, the request - is routed to the default host. The name of the default host does not have to - match a DNS name (although it can) since any request where the DNS name does - not match the name of a Host element will be routed to the - default host.

- -
-

The description below uses the variable name $CATALINA_BASE to refer the - base directory against which most relative paths are resolved. If you have - not configured Tomcat for multiple instances by setting a CATALINA_BASE - directory, then $CATALINA_BASE will be set to the value of $CATALINA_HOME, - the directory into which you have installed Tomcat.

-
- -
- - -
- - - -

All implementations of Host - support the following attributes:

- - - - -

The Application Base directory for this virtual host. - This is the pathname of a directory that may contain web applications - to be deployed on this virtual host. You may specify an - absolute pathname, or a pathname that is relative to the - $CATALINA_BASE directory. See - Automatic Application - Deployment for more information on automatic recognition and - deployment of web applications. If not specified, the default of - webapps will be used.

-
- - -

The XML Base directory for this virtual host. - This is the pathname of a directory that may contain context XML - descriptors to be deployed on this virtual host. You may specify an - absolute pathname for this directory, or a pathname that is relative - to the $CATALINA_BASE directory. See - Automatic Application - Deployment for more information on automatic recognition and - deployment of web applications. If not specified the default of - [engine_name]/[host_name] will be used.

-
- - -

If set to true, Tomcat will attempt to create the directories defined - by the attributes appBase and xmlBase during - the startup phase. The default value is true. If set to - true, and directory creation fails, an error message will be printed out - but will not halt the startup sequence.

-
- - -

This flag value indicates if Tomcat should check periodically for new - or updated web applications while Tomcat is running. If true, Tomcat - periodically checks the appBase and xmlBase - directories and deploys any new web applications or context XML - descriptors found. Updated web applications or context XML descriptors - will trigger a reload of the web application. The flag's value defaults - to true. See - Automatic Application - Deployment for more information.

-
- - -

This value represents the delay in seconds between the - invocation of the backgroundProcess method on this host and - its child containers, including all contexts. - Child containers will not be invoked if their delay value is not - negative (which would mean they are using their own processing - thread). Setting this to a positive value will cause - a thread to be spawn. After waiting the specified amount of time, - the thread will invoke the backgroundProcess method on this host - and all its child containers. A host will use background processing to - perform live web application deployment related tasks. If not - specified, the default value for this attribute is -1, which means - the host will rely on the background processing thread of its parent - engine.

-
- - -

Java class name of the implementation to use. This class must - implement the org.apache.catalina.Host interface. - If not specified, the standard value (defined below) will be used.

-
- - -

A regular expression defining paths to ignore when - autoDeploy and deployOnStartup are set. This - allows you to keep your configuration in a version control system, for - example, and not deploy a .svn or CVS folder that happens to be in the - appBase.

-

This regular expression is relative to appBase. It is - also anchored, meaning the match is performed against the - entire file/directory name. So, foo matches only a file or - directory named foo but not foo.war, - foobar, or myfooapp. To match anything with - "foo", you could use .*foo.*.

-

See Automatic Application - Deployment for more information.

-
- - -

This flag value indicates if web applications from this host should - be automatically deployed when Tomcat starts. The flag's value defaults - to true. See - Automatic Application - Deployment for more information.

-
- - -

Usually the network name of this virtual host, as registered in your - Domain Name Service server. Regardless of the case used to - specify the host name, Tomcat will convert it to lower case internally. - One of the Hosts nested within an Engine MUST - have a name that matches the defaultHost setting for that - Engine. See Host Name Aliases for - information on how to assign more than one network name to the same - virtual host.

-
- -
- -
- - - - -

The standard implementation of Host is - org.apache.catalina.core.StandardHost. - It supports the following additional attributes (in addition to the - common attributes listed above):

- - - - -

Set to true if you want a context XML descriptor - embedded inside the application (located at - /META-INF/context.xml) to be copied to xmlBase - when the application is deployed. On subsequent starts, the copied - context XML descriptor will be used in preference to any context XML - descriptor embedded inside the application even if the descriptor - embedded inside the application is more recent. The flag's value - defaults to false. Note if deployXML - is false, this attribute will have no effect.

-
- - -

Set to false if you want to disable parsing the context - XML descriptor embedded inside the application (located at - /META-INF/context.xml). Security conscious environments - should set this to false to prevent applications from - interacting with the container's configuration. The administrator will - then be responsible for providing an external context configuration - file, and putting it in the location defined by the - xmlBase attribute. The flag's value defaults to - true.

-
- - -

Java class name of the error reporting valve which will be used - by this Host. The responsibility of this valve is to output error - reports. Setting this property allows to customize the look of the - error pages which will be generated by Tomcat. This class must - implement the - org.apache.catalina.Valve interface. If none is specified, - the value org.apache.catalina.valves.ErrorReportValve - will be used by default.

-
- - -

Set to true if you want web applications that are - placed in the appBase directory as web application - archive (WAR) files to be unpacked into a corresponding disk directory - structure, false to run such web applications directly - from a WAR file. WAR files located outside of the Host's - appBase will not be expanded. See - Automatic Application - Deployment for more information.

-
- - -

Pathname to a scratch directory to be used by applications for - this Host. Each application will have its own sub directory with - temporary read-write use. Configuring a Context workDir will override - use of the Host workDir configuration. This directory will be made - visible to servlets in the web application by a servlet context - attribute (of type java.io.File) named - javax.servlet.context.tempdir as described in the - Servlet Specification. If not specified, a suitable directory - underneath $CATALINA_BASE/work will be provided.

-
- -
- -
- - -
- - -
- -

You can nest one or more Context elements - inside this Host element, each representing a different web - application associated with this virtual host.

- -

You can nest at most one instance of the following utility components - by nesting a corresponding element inside your Host - element:

-
    -
  • Realm - - Configure a realm that will allow its - database of users, and their associated roles, to be shared across all - Contexts nested inside this Host (unless - overridden by a Realm configuration - at a lower level).
  • -
- -
- - -
- - - - -

A host is associated with the - org.apache.catalina.core.ContainerBase.[engine_name].[host_name] - log category. Note that the brackets are part of the name, - don't omit them.

- -
- - - - -

When you run a web server, one of the output files normally generated - is an access log, which generates one line of information for - each request processed by the server, in a standard format. Catalina - includes an optional Valve implementation that - can create access logs in the same standard format created by web servers, - or in any number of custom formats.

- -

You can ask Catalina to create an access log for all requests - processed by an Engine, - Host, or Context - by nesting a Valve element like this:

- - -<Host name="localhost" ...> - ... - <Valve className="org.apache.catalina.valves.AccessLogValve" - prefix="localhost_access_log." suffix=".txt" - pattern="common"/> - ... -</Host> - - -

See Access Log Valve - for more information on the configuration attributes that are - supported.

- -
- - - - -

If you are using the standard Host implementation, - the following actions take place automatically when Catalina is first - started, if the deployOnStartup property is set to - true (which is the default value):

-
    -
  • Any XML file in the Host's xmlBase directory (by - default $CATALINA_BASE/conf/[engine_name]/[host_name]) is - assumed to be a context XML descriptor containing a - Context element (and its associated - sub-elements) for a single web application. The web applications - associated with each of these context XML descriptor files will be - deployed first.
    - The docBase attribute of this <Context> - element must only be set if the docBase is outside the Host's - appBase. For web applications located inside the Host's - appBase, the docBase will be the name of the - XML file with ".xml" replaced with ".war" for a web application archive - or the name of the XML file with ".xml" removed for a directory.
    - The path attribute must not be set. The context path used - will be a slash character ("/") followed by the name of the XML file - (less the .xml extension). Multi-level context paths may be defined - using #, e.g. foo#bar.xml for a context path of - /foo/bar. The default web application that has a context - path of / may be defined by using a file called - ROOT.xml.
  • -
  • Any web application archive file within the Host's appBase - directory that has not already been deployed as a result of a context - XML descriptor, does not have a corresponding directory of the same - name (without the ".war" extension), and is not excluded by - deployIgnore will be deployed next. The context path - used will be a slash character ("/") followed by the web application - archive name less the ".war" extension. The one exception to this rule - is that a web application archive named "ROOT.war" will be deployed with - a context path of /. Multi-level contexts may be defined by - using #, e.g. use a WAR named foo#bar.war for a context - path of /foo/bar.
    - If the unpackWARs attribute is true, the web - application archive file will be expanded to a directory of the same - name (without the ".war" extension".
    - Note: If you re-deploy an updated WAR file while Tomcat is stopped, be - sure to delete the associated expanded directory before restarting - Tomcat, so that the updated WAR file will be re-expanded when Tomcat - restarts.
    - If copyXml is true (it is false - by default), any web application archive file within the Hosts's - appBase directory that does not have a corresponding - context XML descriptor (with a ".xml" extension rather than a ".war" - extension) in the Host's xmlBase will be scanned to see - if it contains a context XML descriptor (located at - /META-INF/context.xml) and if one is found the descriptor - will be copied to the xmlBase directory and renamed. -
  • -
  • Finally, any sub-directory within the Host's appBase that - has not already been deployed as a result of a context XML descriptor - and is not excluded by deployIgnore will be deployed. - The context path used will be a slash character ("/") followed by the - directory name, unless the directory name is ROOT, in which case the - context path will /. Multi-level contexts may be defined by - using #, e.g. use a directory named foo#bar for a context - path of /foo/bar.
    - If copyXml is true (it is false - by default), any directory within the Hosts's appBase - directory that does not have a corresponding context XML descriptor in - the Host's xmlBase will be scanned to see if it contains - a context XML descriptor (located at /META-INF/context.xml) - and if one is found the descriptor will be copied to the - xmlBase directory and renamed. -
  • -
- -

In addition to the automatic deployment that occurs at startup time, - you can also request that new XML configuration files, WAR files, or - sub-directories that are dropped in to the appBase (or - xmlBase in the case of an XML configuration file) directory - while Tomcat is running will be automatically deployed, according to the - rules described above. The auto deployer will also track web applications - for the following changes: -

    -
  • An update to the WEB-INF/web.xml file will trigger a reload of the - web application
  • -
  • Deleting a WAR file will trigger an undeploy of the application with - the removal of any associated expanded directory, context file and - work directory. Any current user sessions will not be persisted.
  • -
  • Deleting a directory will trigger an undeploy of the application - with the removal of any associated context file and work directory. - Any current user sessions will not be persisted. If there is an - associated WAR file, it will not be deleted and the application will - be redeployed from the WAR file the next time the auto deployer checks - for changes.
  • -
  • Deleting a context file will trigger an undeploy of the application - with the removal of any associated work directory. Any current user - sessions will not be persisted. If there is an associated WAR file - and/or directory, they will not be deleted and the application will be - redeployed from the WAR file (or from directory if there is no WAR - file) the next time the auto deployer checks for changes.
  • -
  • Updating a WAR file will trigger an undeploy of the application with - the removal of any associated expanded directory, context file and - work directory. Any current user sessions will not be persisted.
  • -
  • Updating a directory (not the directory contents) will trigger an - undeploy of the application with the removal of any associated context - file and work directory. Any current user sessions will not be - persisted. The application will be redeployed the next time the auto - deployer checks for changes.
  • -
  • Updating a context file will trigger an undeploy of the application - with the removal of any associated work directory. Any current user - sessions will not be persisted. The application will be redeployed the - next time the auto deployer checks for changes.
  • -
-

- -

When using automatic deployment, the docBase defined by - an XML Context file should be outside of the - appBase directory. If this is not the case, difficulties - may be experienced deploying the web application or the application may - be deployed twice. The deployIgnore attribute can be used - to avoid this situation.

- -

Finally, note that if you are defining contexts explicitly in server.xml, - you should probably turn off automatic application deployment or specify - deployIgnore carefully. Otherwise, the web applications - will each be deployed twice, and that may cause problems for the - applications.

- -
- - - - -

In many server environments, Network Administrators have configured - more than one network name (in the Domain Name Service (DNS) - server), that resolve to the IP address of the same server. Normally, - each such network name would be configured as a separate - Host element in conf/server.xml, each - with its own set of web applications.

- -

However, in some circumstances, it is desirable that two or more - network names should resolve to the same virtual host, - running the same set of applications. A common use case for this - scenario is a corporate web site, where it is desirable that users - be able to utilize either www.mycompany.com or - company.com to access exactly the same content and - applications.

- -

This is accomplished by utilizing one or more Alias - elements nested inside your Host element. For - example:

- -<Host name="www.mycompany.com" ...> - ... - <Alias>mycompany.com</Alias> - ... -</Host> - - -

In order for this strategy to be effective, all of the network names - involved must be registered in your DNS server to resolve to the - same computer that is running this instance of Catalina.

- -
- - - - -

If you have implemented a Java object that needs to know when this - Host is started or stopped, you can declare it by - nesting a Listener element inside this element. The - class name you specify must implement the - org.apache.catalina.LifecycleListener interface, and - it will be notified about the occurrence of the corresponding - lifecycle events. Configuration of such a listener looks like this:

- - -<Host name="localhost" ...> - ... - <Listener className="com.mycompany.mypackage.MyListener" ... > - ... -</Host> - - -

Note that a Listener can have any number of additional properties - that may be configured from this element. Attribute names are matched - to corresponding JavaBean property names using the standard property - method naming patterns.

- -
- - - - -

You can ask Catalina to check the IP address, or host name, on every - incoming request directed to the surrounding - Engine, Host, or - Context element. The remote address or name - will be checked against configured "accept" and/or "deny" - filters, which are defined using java.util.regex Regular - Expression syntax. Requests that come from locations that are - not accepted will be rejected with an HTTP "Forbidden" error. - Example filter declarations:

- - -<Host name="localhost" ...> - ... - <Valve className="org.apache.catalina.valves.RemoteHostValve" - allow=".*\.mycompany\.com|www\.yourcompany\.com"/> - <Valve className="org.apache.catalina.valves.RemoteAddrValve" - deny="192\.168\.1\.\d+"/> - ... -</Host> - - -

See Remote Address Filter - and Remote Host Filter for - more information about the configuration options that are supported.

- -
- - - - -

In many environments, but particularly in portal environments, it - is desireable to have a user challenged to authenticate themselves only - once over a set of web applications deployed on a particular virtual - host. This can be accomplished by nesting an element like this inside - the Host element for this virtual host:

- - -<Host name="localhost" ...> - ... - <Valve className="org.apache.catalina.authenticator.SingleSignOn"/> - ... -</Host> - - -

The Single Sign On facility operates according to the following rules: -

-
    -
  • All web applications configured for this virtual host must share the - same Realm. In practice, that means you can - nest the Realm element inside this Host element (or the surrounding - Engine element), but not inside a - Context element for one of the involved - web applications.
  • -
  • As long as the user accesses only unprotected resources in any of the - web applications on this virtual host, they will not be challenged - to authenticate themselves.
  • -
  • As soon as the user accesses a protected resource in - any web application associated with this virtual - host, the user will be challenged to authenticate himself or herself, - using the login method defined for the web application currently - being accessed.
  • -
  • Once authenticated, the roles associated with this user will be - utilized for access control decisions across all - of the associated web applications, without challenging the user - to authenticate themselves to each application individually.
  • -
  • As soon as the user logs out of one web application (for example, - by invalidating the corresponding session if form - based login is used), the user's sessions in all - web applications will be invalidated. Any subsequent attempt to - access a protected resource in any application will require the - user to authenticate himself or herself again.
  • -
  • The Single Sign On feature utilizes HTTP cookies to transmit a token - that associates each request with the saved user identity, so it can - only be utilized in client environments that support cookies.
  • -
- -
- - - - -

Many web servers can automatically map a request URI starting with - a tilde character ("~") and a username to a directory (commonly named - public_html) in that user's home directory on the server. - You can accomplish the same thing in Catalina by using a special - Listener element like this (on a Unix system that - uses the /etc/passwd file to identify valid users):

- - -<Host name="localhost" ...> - ... - <Listener className="org.apache.catalina.startup.UserConfig" - directoryName="public_html" - userClass="org.apache.catalina.startup.PasswdUserDatabase"/> - ... -</Host> - - -

On a server where /etc/passwd is not in use, you can - request Catalina to consider all directories found in a specified base - directory (such as c:\Homes in this example) to be - considered "user home" directories for the purposes of this directive:

- - -<Host name="localhost" ...> - ... - <Listener className="org.apache.catalina.startup.UserConfig" - directoryName="public_html" - homeBase=c:\Homes" - userClass="org.apache.catalina.startup.HomesUserDatabase"/> - ... -</Host> - - -

If a user home directory has been set up for a user named - craigmcc, then its contents will be visible from a - client browser by making a request to a URL like:

- - -http://www.mycompany.com:8080/~craigmcc - - -

Successful use of this feature requires recognition of the following - considerations:

-
    -
  • Each user web application will be deployed with characteristics - established by the global and host level default context settings.
  • -
  • It is legal to include more than one instance of this Listener - element. This would only be useful, however, in circumstances - where you wanted to configure more than one "homeBase" directory.
  • -
  • The operating system username under which Catalina is executed - MUST have read access to each user's web application directory, - and all of its contents.
  • -
- -
- - -
- - - - - -
+ + + +]> + + + &project; + + + Craig R. McClanahan + Remy Maucherat + Yoav Shapira + The Host Container + + + + +
+ +
+ +
+ +

The Host element represents a virtual host, + which is an association of a network name for a server (such as + "www.mycompany.com" with the particular server on which Tomcat is running. + For clients to be able to connect to a Tomcat server using its network name, + this name must be registered in the Domain Name Service (DNS) server + that manages the Internet domain you belong to - contact your Network + Administrator for more information.

+ +

In many cases, System Administrators wish to associate more than + one network name (such as www.mycompany.com and + company.com) with the same virtual host and applications. + This can be accomplished using the Host + Name Aliases feature discussed below.

+ +

One or more Host elements are nested inside an + Engine element. Inside the Host element, you + can nest Context elements for the web + applications associated with this virtual host. Exactly one of the Hosts + associated with each Engine MUST have a name matching the + defaultHost attribute of that Engine.

+ +

Clients normally use host names to identify the server they wish to connect + to. This host name is also included in the HTTP request headers. Tomcat + extracts the host name from the HTTP headers and looks for a + Host with a matching name. If no match is found, the request + is routed to the default host. The name of the default host does not have to + match a DNS name (although it can) since any request where the DNS name does + not match the name of a Host element will be routed to the + default host.

+ +
+

The description below uses the variable name $CATALINA_BASE to refer the + base directory against which most relative paths are resolved. If you have + not configured Tomcat for multiple instances by setting a CATALINA_BASE + directory, then $CATALINA_BASE will be set to the value of $CATALINA_HOME, + the directory into which you have installed Tomcat.

+
+ +
+ + +
+ + + +

All implementations of Host + support the following attributes:

+ + + + +

The Application Base directory for this virtual host. + This is the pathname of a directory that may contain web applications + to be deployed on this virtual host. You may specify an + absolute pathname, or a pathname that is relative to the + $CATALINA_BASE directory. See + Automatic Application + Deployment for more information on automatic recognition and + deployment of web applications. If not specified, the default of + webapps will be used.

+
+ + +

The XML Base directory for this virtual host. + This is the pathname of a directory that may contain context XML + descriptors to be deployed on this virtual host. You may specify an + absolute pathname for this directory, or a pathname that is relative + to the $CATALINA_BASE directory. See + Automatic Application + Deployment for more information on automatic recognition and + deployment of web applications. If not specified the default of + [engine_name]/[host_name] will be used.

+
+ + +

If set to true, Tomcat will attempt to create the directories defined + by the attributes appBase and xmlBase during + the startup phase. The default value is true. If set to + true, and directory creation fails, an error message will be printed out + but will not halt the startup sequence.

+
+ + +

This flag value indicates if Tomcat should check periodically for new + or updated web applications while Tomcat is running. If true, Tomcat + periodically checks the appBase and xmlBase + directories and deploys any new web applications or context XML + descriptors found. Updated web applications or context XML descriptors + will trigger a reload of the web application. The flag's value defaults + to true. See + Automatic Application + Deployment for more information.

+
+ + +

This value represents the delay in seconds between the + invocation of the backgroundProcess method on this host and + its child containers, including all contexts. + Child containers will not be invoked if their delay value is not + negative (which would mean they are using their own processing + thread). Setting this to a positive value will cause + a thread to be spawn. After waiting the specified amount of time, + the thread will invoke the backgroundProcess method on this host + and all its child containers. A host will use background processing to + perform live web application deployment related tasks. If not + specified, the default value for this attribute is -1, which means + the host will rely on the background processing thread of its parent + engine.

+
+ + +

Java class name of the implementation to use. This class must + implement the org.apache.catalina.Host interface. + If not specified, the standard value (defined below) will be used.

+
+ + +

A regular expression defining paths to ignore when + autoDeploy and deployOnStartup are set. This + allows you to keep your configuration in a version control system, for + example, and not deploy a .svn or CVS folder that happens to be in the + appBase.

+

This regular expression is relative to appBase. It is + also anchored, meaning the match is performed against the + entire file/directory name. So, foo matches only a file or + directory named foo but not foo.war, + foobar, or myfooapp. To match anything with + "foo", you could use .*foo.*.

+

See Automatic Application + Deployment for more information.

+
+ + +

This flag value indicates if web applications from this host should + be automatically deployed when Tomcat starts. The flag's value defaults + to true. See + Automatic Application + Deployment for more information.

+
+ + +

Usually the network name of this virtual host, as registered in your + Domain Name Service server. Regardless of the case used to + specify the host name, Tomcat will convert it to lower case internally. + One of the Hosts nested within an Engine MUST + have a name that matches the defaultHost setting for that + Engine. See Host Name Aliases for + information on how to assign more than one network name to the same + virtual host.

+
+ + +

The number of threads this Host will use to start + child Context elements in parallel. The same + thread pool will be used to deploy new + Contexts if automatic deployment is being + used. The special value of 0 will result in the value of + Runtime.getRuntime().availableProcessors() being used. + Negative values will result in + Runtime.getRuntime().availableProcessors() + value being + used unless this is less than 1 in which case 1 thread will be used. If + not specified, the default value of 1 will be used.

+
+ +
+ +
+ + + + +

The standard implementation of Host is + org.apache.catalina.core.StandardHost. + It supports the following additional attributes (in addition to the + common attributes listed above):

+ + + + +

Set to true if you want a context XML descriptor + embedded inside the application (located at + /META-INF/context.xml) to be copied to xmlBase + when the application is deployed. On subsequent starts, the copied + context XML descriptor will be used in preference to any context XML + descriptor embedded inside the application even if the descriptor + embedded inside the application is more recent. The flag's value + defaults to false. Note if deployXML + is false, this attribute will have no effect.

+
+ + +

Set to false if you want to disable parsing the context + XML descriptor embedded inside the application (located at + /META-INF/context.xml). Security conscious environments + should set this to false to prevent applications from + interacting with the container's configuration. The administrator will + then be responsible for providing an external context configuration + file, and putting it in the location defined by the + xmlBase attribute. The flag's value defaults to + true.

+
+ + +

Java class name of the error reporting valve which will be used + by this Host. The responsibility of this valve is to output error + reports. Setting this property allows to customize the look of the + error pages which will be generated by Tomcat. This class must + implement the + org.apache.catalina.Valve interface. If none is specified, + the value org.apache.catalina.valves.ErrorReportValve + will be used by default.

+
+ + +

Set to true if you want web applications that are + placed in the appBase directory as web application + archive (WAR) files to be unpacked into a corresponding disk directory + structure, false to run such web applications directly + from a WAR file. WAR files located outside of the Host's + appBase will not be expanded. See + Automatic Application + Deployment for more information.

+
+ + +

Pathname to a scratch directory to be used by applications for + this Host. Each application will have its own sub directory with + temporary read-write use. Configuring a Context workDir will override + use of the Host workDir configuration. This directory will be made + visible to servlets in the web application by a servlet context + attribute (of type java.io.File) named + javax.servlet.context.tempdir as described in the + Servlet Specification. If not specified, a suitable directory + underneath $CATALINA_BASE/work will be provided.

+
+ +
+ +
+ + +
+ + +
+ +

You can nest one or more Context elements + inside this Host element, each representing a different web + application associated with this virtual host.

+ +

You can nest at most one instance of the following utility components + by nesting a corresponding element inside your Host + element:

+
    +
  • Realm - + Configure a realm that will allow its + database of users, and their associated roles, to be shared across all + Contexts nested inside this Host (unless + overridden by a Realm configuration + at a lower level).
  • +
+ +
+ + +
+ + + + +

A host is associated with the + org.apache.catalina.core.ContainerBase.[engine_name].[host_name] + log category. Note that the brackets are part of the name, + don't omit them.

+ +
+ + + + +

When you run a web server, one of the output files normally generated + is an access log, which generates one line of information for + each request processed by the server, in a standard format. Catalina + includes an optional Valve implementation that + can create access logs in the same standard format created by web servers, + or in any number of custom formats.

+ +

You can ask Catalina to create an access log for all requests + processed by an Engine, + Host, or Context + by nesting a Valve element like this:

+ + +<Host name="localhost" ...> + ... + <Valve className="org.apache.catalina.valves.AccessLogValve" + prefix="localhost_access_log." suffix=".txt" + pattern="common"/> + ... +</Host> + + +

See Access Log Valve + for more information on the configuration attributes that are + supported.

+ +
+ + + + +

If you are using the standard Host implementation, + the following actions take place automatically when Catalina is first + started, if the deployOnStartup property is set to + true (which is the default value):

+
    +
  • Any XML file in the Host's xmlBase directory (by + default $CATALINA_BASE/conf/[engine_name]/[host_name]) is + assumed to be a context XML descriptor containing a + Context element (and its associated + sub-elements) for a single web application. The web applications + associated with each of these context XML descriptor files will be + deployed first.
    + The docBase attribute of this <Context> + element must only be set if the docBase is outside the Host's + appBase. For web applications located inside the Host's + appBase, the docBase will be the name of the + XML file with ".xml" replaced with ".war" for a web application archive + or the name of the XML file with ".xml" removed for a directory.
    + The path attribute must not be set. The context path used + will be a slash character ("/") followed by the name of the XML file + (less the .xml extension). Multi-level context paths may be defined + using #, e.g. foo#bar.xml for a context path of + /foo/bar. The default web application that has a context + path of / may be defined by using a file called + ROOT.xml.
  • +
  • Any web application archive file within the Host's appBase + directory that has not already been deployed as a result of a context + XML descriptor, does not have a corresponding directory of the same + name (without the ".war" extension), and is not excluded by + deployIgnore will be deployed next. The context path + used will be a slash character ("/") followed by the web application + archive name less the ".war" extension. The one exception to this rule + is that a web application archive named "ROOT.war" will be deployed with + a context path of /. Multi-level contexts may be defined by + using #, e.g. use a WAR named foo#bar.war for a context + path of /foo/bar.
    + If the unpackWARs attribute is true, the web + application archive file will be expanded to a directory of the same + name (without the ".war" extension".
    + Note: If you re-deploy an updated WAR file while Tomcat is stopped, be + sure to delete the associated expanded directory before restarting + Tomcat, so that the updated WAR file will be re-expanded when Tomcat + restarts.
    + If copyXml is true (it is false + by default), any web application archive file within the Hosts's + appBase directory that does not have a corresponding + context XML descriptor (with a ".xml" extension rather than a ".war" + extension) in the Host's xmlBase will be scanned to see + if it contains a context XML descriptor (located at + /META-INF/context.xml) and if one is found the descriptor + will be copied to the xmlBase directory and renamed. +
  • +
  • Finally, any sub-directory within the Host's appBase that + has not already been deployed as a result of a context XML descriptor + and is not excluded by deployIgnore will be deployed. + The context path used will be a slash character ("/") followed by the + directory name, unless the directory name is ROOT, in which case the + context path will /. Multi-level contexts may be defined by + using #, e.g. use a directory named foo#bar for a context + path of /foo/bar.
    + If copyXml is true (it is false + by default), any directory within the Hosts's appBase + directory that does not have a corresponding context XML descriptor in + the Host's xmlBase will be scanned to see if it contains + a context XML descriptor (located at /META-INF/context.xml) + and if one is found the descriptor will be copied to the + xmlBase directory and renamed. +
  • +
+ +

In addition to the automatic deployment that occurs at startup time, + you can also request that new XML configuration files, WAR files, or + sub-directories that are dropped in to the appBase (or + xmlBase in the case of an XML configuration file) directory + while Tomcat is running will be automatically deployed, according to the + rules described above. The auto deployer will also track web applications + for the following changes: +

    +
  • An update to the WEB-INF/web.xml file will trigger a reload of the + web application
  • +
  • Deleting a WAR file will trigger an undeploy of the application with + the removal of any associated expanded directory, context file and + work directory. Any current user sessions will not be persisted.
  • +
  • Deleting a directory will trigger an undeploy of the application + with the removal of any associated context file and work directory. + Any current user sessions will not be persisted. If there is an + associated WAR file, it will not be deleted and the application will + be redeployed from the WAR file the next time the auto deployer checks + for changes.
  • +
  • Deleting a context file will trigger an undeploy of the application + with the removal of any associated work directory. Any current user + sessions will not be persisted. If there is an associated WAR file + and/or directory, they will not be deleted and the application will be + redeployed from the WAR file (or from directory if there is no WAR + file) the next time the auto deployer checks for changes.
  • +
  • Updating a WAR file will trigger an undeploy of the application with + the removal of any associated expanded directory, context file and + work directory. Any current user sessions will not be persisted.
  • +
  • Updating a directory (not the directory contents) will trigger an + undeploy of the application with the removal of any associated context + file and work directory. Any current user sessions will not be + persisted. The application will be redeployed the next time the auto + deployer checks for changes.
  • +
  • Updating a context file will trigger an undeploy of the application + with the removal of any associated work directory. Any current user + sessions will not be persisted. The application will be redeployed the + next time the auto deployer checks for changes.
  • +
+

+ +

When using automatic deployment, the docBase defined by + an XML Context file should be outside of the + appBase directory. If this is not the case, difficulties + may be experienced deploying the web application or the application may + be deployed twice. The deployIgnore attribute can be used + to avoid this situation.

+ +

Finally, note that if you are defining contexts explicitly in server.xml, + you should probably turn off automatic application deployment or specify + deployIgnore carefully. Otherwise, the web applications + will each be deployed twice, and that may cause problems for the + applications.

+ +
+ + + + +

In many server environments, Network Administrators have configured + more than one network name (in the Domain Name Service (DNS) + server), that resolve to the IP address of the same server. Normally, + each such network name would be configured as a separate + Host element in conf/server.xml, each + with its own set of web applications.

+ +

However, in some circumstances, it is desirable that two or more + network names should resolve to the same virtual host, + running the same set of applications. A common use case for this + scenario is a corporate web site, where it is desirable that users + be able to utilize either www.mycompany.com or + company.com to access exactly the same content and + applications.

+ +

This is accomplished by utilizing one or more Alias + elements nested inside your Host element. For + example:

+ +<Host name="www.mycompany.com" ...> + ... + <Alias>mycompany.com</Alias> + ... +</Host> + + +

In order for this strategy to be effective, all of the network names + involved must be registered in your DNS server to resolve to the + same computer that is running this instance of Catalina.

+ +
+ + + + +

If you have implemented a Java object that needs to know when this + Host is started or stopped, you can declare it by + nesting a Listener element inside this element. The + class name you specify must implement the + org.apache.catalina.LifecycleListener interface, and + it will be notified about the occurrence of the corresponding + lifecycle events. Configuration of such a listener looks like this:

+ + +<Host name="localhost" ...> + ... + <Listener className="com.mycompany.mypackage.MyListener" ... > + ... +</Host> + + +

Note that a Listener can have any number of additional properties + that may be configured from this element. Attribute names are matched + to corresponding JavaBean property names using the standard property + method naming patterns.

+ +
+ + + + +

You can ask Catalina to check the IP address, or host name, on every + incoming request directed to the surrounding + Engine, Host, or + Context element. The remote address or name + will be checked against configured "accept" and/or "deny" + filters, which are defined using java.util.regex Regular + Expression syntax. Requests that come from locations that are + not accepted will be rejected with an HTTP "Forbidden" error. + Example filter declarations:

+ + +<Host name="localhost" ...> + ... + <Valve className="org.apache.catalina.valves.RemoteHostValve" + allow=".*\.mycompany\.com|www\.yourcompany\.com"/> + <Valve className="org.apache.catalina.valves.RemoteAddrValve" + deny="192\.168\.1\.\d+"/> + ... +</Host> + + +

See Remote Address Filter + and Remote Host Filter for + more information about the configuration options that are supported.

+ +
+ + + + +

In many environments, but particularly in portal environments, it + is desireable to have a user challenged to authenticate themselves only + once over a set of web applications deployed on a particular virtual + host. This can be accomplished by nesting an element like this inside + the Host element for this virtual host:

+ + +<Host name="localhost" ...> + ... + <Valve className="org.apache.catalina.authenticator.SingleSignOn"/> + ... +</Host> + + +

The Single Sign On facility operates according to the following rules: +

+
    +
  • All web applications configured for this virtual host must share the + same Realm. In practice, that means you can + nest the Realm element inside this Host element (or the surrounding + Engine element), but not inside a + Context element for one of the involved + web applications.
  • +
  • As long as the user accesses only unprotected resources in any of the + web applications on this virtual host, they will not be challenged + to authenticate themselves.
  • +
  • As soon as the user accesses a protected resource in + any web application associated with this virtual + host, the user will be challenged to authenticate himself or herself, + using the login method defined for the web application currently + being accessed.
  • +
  • Once authenticated, the roles associated with this user will be + utilized for access control decisions across all + of the associated web applications, without challenging the user + to authenticate themselves to each application individually.
  • +
  • As soon as the user logs out of one web application (for example, + by invalidating the corresponding session if form + based login is used), the user's sessions in all + web applications will be invalidated. Any subsequent attempt to + access a protected resource in any application will require the + user to authenticate himself or herself again.
  • +
  • The Single Sign On feature utilizes HTTP cookies to transmit a token + that associates each request with the saved user identity, so it can + only be utilized in client environments that support cookies.
  • +
+ +
+ + + + +

Many web servers can automatically map a request URI starting with + a tilde character ("~") and a username to a directory (commonly named + public_html) in that user's home directory on the server. + You can accomplish the same thing in Catalina by using a special + Listener element like this (on a Unix system that + uses the /etc/passwd file to identify valid users):

+ + +<Host name="localhost" ...> + ... + <Listener className="org.apache.catalina.startup.UserConfig" + directoryName="public_html" + userClass="org.apache.catalina.startup.PasswdUserDatabase"/> + ... +</Host> + + +

On a server where /etc/passwd is not in use, you can + request Catalina to consider all directories found in a specified base + directory (such as c:\Homes in this example) to be + considered "user home" directories for the purposes of this directive:

+ + +<Host name="localhost" ...> + ... + <Listener className="org.apache.catalina.startup.UserConfig" + directoryName="public_html" + homeBase=c:\Homes" + userClass="org.apache.catalina.startup.HomesUserDatabase"/> + ... +</Host> + + +

If a user home directory has been set up for a user named + craigmcc, then its contents will be visible from a + client browser by making a request to a URL like:

+ + +http://www.mycompany.com:8080/~craigmcc + + +

Successful use of this feature requires recognition of the following + considerations:

+
    +
  • Each user web application will be deployed with characteristics + established by the global and host level default context settings.
  • +
  • It is legal to include more than one instance of this Listener + element. This would only be useful, however, in circumstances + where you wanted to configure more than one "homeBase" directory.
  • +
  • The operating system username under which Catalina is executed + MUST have read access to each user's web application directory, + and all of its contents.
  • +
+ +
+ + +
+ + + + + +