/* * 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;
/** * Abstract implementation of the <b>Container</b> interface, providing common functionality required by nearly every * implementation. Classes extending this base class must may implement a replacement for <code>invoke()</code>. * <p> * 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 <code>invoke()</code> method of this class, utilizing the "Chain of * Responsibility" design pattern. A subclass should encapsulate its own processing functionality as a * <code>Valve</code>, and configure this Valve into the pipeline by calling <code>setBasic()</code>. * <p> * This implementation fires property change events, per the JavaBeans design pattern, for changes in singleton * properties. In addition, it fires the following <code>ContainerEvent</code> events to listeners who register * themselves with <code>addContainerListener()</code>: * <table border=1> * <caption>ContainerEvents fired by this implementation</caption> * <tr> * <th>Type</th> * <th>Data</th> * <th>Description</th> * </tr> * <tr> * <td><code>addChild</code></td> * <td><code>Container</code></td> * <td>Child container added to this Container.</td> * </tr> * <tr> * <td><code>{@link #getPipeline() pipeline}.addValve</code></td> * <td><code>Valve</code></td> * <td>Valve added to this Container.</td> * </tr> * <tr> * <td><code>removeChild</code></td> * <td><code>Container</code></td> * <td>Child container removed from this Container.</td> * </tr> * <tr> * <td><code>{@link #getPipeline() pipeline}.removeValve</code></td> * <td><code>Valve</code></td> * <td>Valve removed from this Container.</td> * </tr> * <tr> * <td><code>start</code></td> * <td><code>null</code></td> * <td>Container was started.</td> * </tr> * <tr> * <td><code>stop</code></td> * <td><code>null</code></td> * <td>Container was stopped.</td> * </tr> * </table> * Subclasses that fire additional events should document them in the class comments of the implementation class. * * @author Craig R. McClanahan
*/ publicabstractclass ContainerBase extends LifecycleMBeanBase implements Container {
/** * 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.
*/ protectedclass PrivilegedAddChild implements PrivilegedAction<Void> {
/** * The child Containers belonging to this Container, keyed by name.
*/ protectedfinal HashMap<String,Container> children = new HashMap<>();
/** * The processor delay for this component.
*/ protectedint backgroundProcessorDelay = -1;
/** * The future allowing control of the background processor.
*/ protected ScheduledFuture<?> backgroundProcessorFuture; protected ScheduledFuture<?> monitorFuture;
/** * The container event listeners for this Container. Implemented as a CopyOnWriteArrayList since listeners may * invoke methods to add/remove themselves or other listeners and with a ReadWriteLock that would trigger a * deadlock.
*/ protectedfinal List<ContainerListener> listeners = new CopyOnWriteArrayList<>();
/** * The Logger implementation with which this Container is associated.
*/ protected Log logger = null;
/** * The cluster with which this Container is associated.
*/ protected Cluster cluster = null; privatefinal ReadWriteLock clusterLock = new ReentrantReadWriteLock();
/** * 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.
*/ protectedfinal Pipeline pipeline = new StandardPipeline(this);
/** * The Realm with which this Container is associated.
*/ privatevolatile Realm realm = null;
/** * Lock used to control access to the Realm.
*/ privatefinal ReadWriteLock realmLock = new ReentrantReadWriteLock();
/** * The string manager for this package.
*/ protectedstaticfinal StringManager sm = StringManager.getManager(ContainerBase.class);
/** * Will children be started automatically when they are added.
*/ protectedboolean startChildren = true;
/** * The property change support for this component.
*/ protectedfinal PropertyChangeSupport support = new PropertyChangeSupport(this);
/** * The access log to use for requests normally handled by this container that have been handled earlier in the * processing chain.
*/ protectedvolatile AccessLog accessLog = null; privatevolatileboolean accessLogScanComplete = false;
/** * The number of threads available to process start and stop events for any children associated with this container.
*/ privateint startStopThreads = 1; protected ExecutorService startStopExecutor;
// Use local copies to ensure thread safety if (oldStartStopThreads != startStopThreads && startStopExecutor != null) {
reconfigureStartStopExecutor(getStartStopThreads());
}
}
/** * 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 publicint 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 publicvoid setBackgroundProcessorDelay(int delay) {
backgroundProcessorDelay = delay;
}
/** * Return the Logger for this Container.
*/
@Override public Log getLogger() { if (logger != null) { return logger;
}
logger = LogFactory.getLog(getLogName()); return logger;
}
/** * @return the abbreviated name of this container for logging messages
*/
@Override public String getLogName() {
if (logName != null) { return logName;
}
String loggerName = null;
Container current = this; while (current != null) {
String name = current.getName(); if ((name == null) || (name.equals(""))) {
name = "/";
} elseif (name.startsWith("##")) {
name = "/" + name;
}
loggerName = "[" + name + "]" + ((loggerName != null) ? ("." + loggerName) : "");
current = current.getParent();
}
logName = ContainerBase.class.getName() + "." + loggerName; return logName;
}
/** * 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 <code>null</code>.
*/
@Override public Cluster getCluster() {
Lock readLock = clusterLock.readLock();
readLock.lock(); try { if (cluster != null) { return cluster;
}
if (parent != null) { return parent.getCluster();
}
returnnull;
} finally {
readLock.unlock();
}
}
/* * Provide access to just the cluster component attached to this container.
*/ protected Cluster getClusterInternal() {
Lock readLock = clusterLock.readLock();
readLock.lock(); try { return cluster;
} finally {
readLock.unlock();
}
}
/** * Set the Cluster with which this Container is associated. * * @param cluster The newly associated Cluster
*/
@Override publicvoid setCluster(Cluster cluster) {
// Report this property change to interested listeners
support.firePropertyChange("cluster", oldCluster, 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 publicvoid setName(String name) { if (name == null) { thrownew IllegalArgumentException(sm.getString("containerBase.nullName"));
}
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. * * @return <code>true</code> if the children will be started
*/ publicboolean 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
*/ publicvoid setStartChildren(boolean startChildren) {
/** * Return the Container for which this Container is a child, if there is one. If there is no defined parent, return * <code>null</code>.
*/
@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 publicvoid setParent(Container container) {
/** * Return the parent class loader (if any) for this web application. This call is meaningful only * <strong>after</strong> 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 * <strong>before</strong> 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 publicvoid 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() { returnthis.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 <code>null</code>.
*/
@Override public Realm getRealm() {
Lock l = realmLock.readLock();
l.lock(); try { if (realm != null) { return realm;
} if (parent != null) { return parent.getRealm();
} returnnull;
} finally {
l.unlock();
}
}
/** * Set the Realm with which this Container is associated. * * @param realm The newly associated Realm
*/
@Override publicvoid setRealm(Realm realm) {
Lock l = realmLock.writeLock();
l.lock(); try { // 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 instanceof Lifecycle)) { try {
((Lifecycle) oldRealm).stop();
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.realm.stop"), e);
}
}
// Start the new component if necessary if (realm != null) {
realm.setContainer(this);
} if (getState().isAvailable() && (realm instanceof Lifecycle)) { try {
((Lifecycle) realm).start();
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.realm.start"), e);
}
}
// Report this property change to interested listeners
support.firePropertyChange("realm", oldRealm, this.realm);
} finally {
l.unlock();
}
/** * 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 <code>setParent()</code> method must be called, with this Container as an * argument. This method may thrown an <code>IllegalArgumentException</code> 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 <code>setParent()</code> 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 publicvoid addChild(Container child) { if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> dp = new PrivilegedAddChild(child);
AccessController.doPrivileged(dp);
} else {
addChildInternal(child);
}
}
synchronized (children) { if (children.get(child.getName()) != null) { thrownew IllegalArgumentException(sm.getString("containerBase.child.notUnique", child.getName()));
}
child.setParent(this); // May throw IAE
children.put(child.getName(), child);
}
fireContainerEvent(ADD_CHILD_EVENT, 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 try { if ((getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) && startChildren) {
child.start();
}
} catch (LifecycleException e) { thrownew IllegalStateException(sm.getString("containerBase.child.start"), e);
}
}
/** * Add a container event listener to this component. * * @param listener The listener to add
*/
@Override publicvoid addContainerListener(ContainerListener listener) {
listeners.add(listener);
}
/** * Add a property change listener to this component. * * @param listener The listener to add
*/
@Override publicvoid addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/** * Return the child Container, associated with this Container, with the specified name (if any); otherwise, return * <code>null</code> * * @param name Name of the child Container to be retrieved
*/
@Override public Container findChild(String name) { if (name == null) { returnnull;
} 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) { return children.values().toArray(new Container[0]);
}
}
/** * 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() { return listeners.toArray(new ContainerListener[0]);
}
/** * Remove an existing child Container from association with this parent Container. * * @param child Existing child Container to be removed
*/
@Override publicvoid removeChild(Container child) {
boolean destroy = false; 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();
destroy = true;
}
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.child.destroy"), e);
}
if (!destroy) {
fireContainerEvent(REMOVE_CHILD_EVENT, child);
}
/** * Remove a container event listener from this component. * * @param listener The listener to remove
*/
@Override publicvoid removeContainerListener(ContainerListener listener) {
listeners.remove(listener);
}
/** * Remove a property change listener from this component. * * @param listener The listener to remove
*/
@Override publicvoid removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
privatevoid reconfigureStartStopExecutor(int threads) { if (threads == 1) { // Use a fake executor if (!(startStopExecutor instanceof InlineExecutorService)) {
startStopExecutor = new InlineExecutorService();
}
} else { // Delegate utility execution to the Service
Server server = Container.getService(this).getServer();
server.setUtilityThreads(threads);
startStopExecutor = server.getUtilityExecutor();
}
}
/** * 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 protectedsynchronizedvoid startInternal() throws LifecycleException {
// Start our subordinate components, if any
logger = null;
getLogger();
Cluster cluster = getClusterInternal(); if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal(); if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
Container[] children = findChildren();
List<Future<Void>> results = new ArrayList<>(children.length); for (Container child : children) {
results.add(startStopExecutor.submit(new StartChild(child)));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) { try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e); if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
} if (multiThrowable != null) { thrownew LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
/** * 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 protectedsynchronizedvoid stopInternal() throws LifecycleException {
// 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<Future<Void>> results = new ArrayList<>(children.length); for (Container child : children) {
results.add(startStopExecutor.submit(new StopChild(child)));
}
// 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 publicvoid logAccess(Request request, Response response, long time, boolean useDefault) {
if (getParent() != null) { // No need to use default logger once request/response has been logged // once
getParent().logAccess(request, response, time, (useDefault && !logged));
}
}
/** * 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
*/ publicsynchronizedvoid 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 publicvoid backgroundProcess() {
/** * 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 publicvoid fireContainerEvent(String type, Object data) {
if (listeners.size() < 1) { return;
}
ContainerEvent event = new ContainerEvent(this, type, data); // Note for each uses an iterator internally so this is safe for (ContainerListener listener : listeners) {
listener.containerEvent(event);
}
}
// -------------------- JMX and Registration --------------------
@Override protected String getDomainInternal() {
Container p = this.getParent(); if (p == null) { returnnull;
} else { return p.getDomain();
}
}
@Override public String getMBeanKeyProperties() {
Container c = this;
StringBuilder keyProperties = new StringBuilder(); int containerCount = 0;
// Work up container hierarchy, add a component to the name for // each container while (!(c instanceof Engine)) { if (c instanceof Wrapper) {
keyProperties.insert(0, ",servlet=");
keyProperties.insert(9, c.getName());
} elseif (c instanceof Context) {
keyProperties.insert(0, ",context=");
ContextName cn = new ContextName(c.getName(), false);
keyProperties.insert(9, cn.getDisplayName());
} elseif (c instanceof Host) {
keyProperties.insert(0, ",host=");
keyProperties.insert(6, c.getName());
} elseif (c == null) { // May happen in unit testing and/or some embedding scenarios
keyProperties.append(",container");
keyProperties.append(containerCount++);
keyProperties.append("=null"); break;
} else { // Should never happen...
keyProperties.append(",container");
keyProperties.append(containerCount++);
keyProperties.append('=');
keyProperties.append(c.getName());
}
c = c.getParent();
} return keyProperties.toString();
}
public ObjectName[] getChildren() {
List<ObjectName> names; synchronized (children) {
names = new ArrayList<>(children.size()); for (Container next : children.values()) { if (next instanceof ContainerBase) {
names.add(next.getObjectName());
}
}
} return names.toArray(new ObjectName[0]);
}
/** * Private runnable class to invoke the backgroundProcess method of this container and its children after a fixed * delay.
*/ protectedclass ContainerBackgroundProcessor implements Runnable {
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.25Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.