/*
* 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.File;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.management.ObjectName;
import org.apache.catalina.AccessLog;
import org.apache.catalina.Cluster;
import org.apache.catalina.Container;
import org.apache.catalina.ContainerEvent;
import org.apache.catalina.ContainerListener;
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.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Loader;
import org.apache.catalina.Pipeline;
import org.apache.catalina.Realm;
import org.apache.catalina.Server;
import org.apache.catalina.Valve;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.ContextName;
import org.apache.catalina.util.LifecycleMBeanBase;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.MultiThrowable;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.threads.InlineExecutorService;
/**
* 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
*/
public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
private static final Log log = 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<Void > {
private final 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 final HashMap<String,Container> children = new HashMap<>();
/**
* The processor delay for this component .
*/
protected int 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 .
*/
protected final List<ContainerListener> listeners = new CopyOnWriteArrayList<>();
/**
* The Logger implementation with which this Container is associated .
*/
protected Log logger = null ;
/**
* Associated logger name .
*/
protected String logName = null ;
/**
* The cluster with which this Container is associated .
*/
protected Cluster cluster = null ;
private final 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 .
*/
protected final Pipeline pipeline = new StandardPipeline(this );
/**
* The Realm with which this Container is associated .
*/
private volatile Realm realm = null ;
/**
* Lock used to control access to the Realm .
*/
private final ReadWriteLock realmLock = new ReentrantReadWriteLock();
/**
* The string manager for this package .
*/
protected static final StringManager sm = StringManager.getManager(ContainerBase.class );
/**
* Will children be started automatically when they are added .
*/
protected boolean startChildren = true ;
/**
* The property change support for this component .
*/
protected final 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 .
*/
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 ExecutorService startStopExecutor;
// ------------------------------------------------------------- Properties
@Override
public int getStartStopThreads() {
return startStopThreads;
}
@Override
public void setStartStopThreads(int startStopThreads) {
int oldStartStopThreads = this .startStopThreads;
this .startStopThreads = startStopThreads;
// 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
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 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 = "/" ;
} else if (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();
}
return null ;
} 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
public void setCluster(Cluster cluster) {
Cluster oldCluster = null ;
Lock writeLock = clusterLock.writeLock();
writeLock.lock();
try {
// Change components if necessary
oldCluster = this .cluster;
if (oldCluster == cluster) {
return ;
}
this .cluster = cluster;
// Stop the old component if necessary
if (getState().isAvailable() && (oldCluster instanceof Lifecycle)) {
try {
((Lifecycle) oldCluster).stop();
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.cluster.stop" ), e);
}
}
// Start the new component if necessary
if (cluster != null ) {
cluster.setContainer(this );
}
if (getState().isAvailable() && (cluster instanceof Lifecycle)) {
try {
((Lifecycle) cluster).start();
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.cluster.start" ), e);
}
}
} finally {
writeLock.unlock();
}
// 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
public void setName(String name) {
if (name == null ) {
throw new 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
*/
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
* < 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
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
* < 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
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 < 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();
}
return null ;
} finally {
l.unlock();
}
}
protected Realm getRealmInternal() {
Lock l = realmLock.readLock();
l.lock();
try {
return realm;
} finally {
l.unlock();
}
}
/**
* Set the Realm with which this Container is associated .
*
* @ param realm The newly associated Realm
*/
@Override
public void 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();
}
}
// ------------------------------------------------------ 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 < 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
public void addChild(Container child) {
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void > 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(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) {
throw new IllegalStateException(sm.getString("containerBase.child.start" ), e);
}
}
/**
* Add a container event listener to this component .
*
* @ param listener The listener to add
*/
@Override
public void addContainerListener(ContainerListener listener) {
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
* < code > null < / code >
*
* @ 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) {
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
public void removeChild(Container child) {
if (child == null ) {
return ;
}
try {
if (child.getState().isAvailable()) {
child.stop();
}
} catch (LifecycleException e) {
log.error(sm.getString("containerBase.child.stop" ), e);
}
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);
}
synchronized (children) {
if (children.get(child.getName()) == null ) {
return ;
}
children.remove(child.getName());
}
}
/**
* Remove a container event listener from this component .
*
* @ param listener The listener to remove
*/
@Override
public void removeContainerListener(ContainerListener listener) {
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);
}
private void 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
protected synchronized void startInternal() throws LifecycleException {
reconfigureStartStopExecutor(getStartStopThreads());
// 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 ) {
throw new 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();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0 ) {
monitorFuture = Container.getService(ContainerBase.this ).getServer().getUtilityExecutor()
.scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0 , 60 , TimeUnit.SECONDS);
}
}
/**
* 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
if (monitorFuture != null ) {
monitorFuture.cancel(true );
monitorFuture = null ;
}
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<Future<Void >> results = new ArrayList<>(children.length);
for (Container child : children) {
results.add(startStopExecutor.submit(new StopChild(child)));
}
boolean fail = false ;
for (Future<Void > 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
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).stop();
}
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).stop();
}
// If init fails, this may be null
if (startStopExecutor != null ) {
startStopExecutor.shutdownNow();
startStopExecutor = null ;
}
}
@Override
protected void destroyInternal() throws LifecycleException {
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).destroy();
}
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).destroy();
}
// 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 ;
}
Cluster cluster = getClusterInternal();
if (cluster != null ) {
try {
cluster.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.cluster" , cluster), e);
}
}
Realm realm = getRealmInternal();
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(PERIODIC_EVENT, null );
}
@Override
public File getCatalinaBase() {
if (parent == null ) {
return null ;
}
return parent.getCatalinaBase();
}
@Override
public File getCatalinaHome() {
if (parent == null ) {
return null ;
}
return parent.getCatalinaHome();
}
// ------------------------------------------------------ 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);
// 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 ) {
return null ;
} 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());
} else if (c instanceof Context) {
keyProperties.insert(0 , ",context=" );
ContextName cn = new ContextName(c.getName(), false );
keyProperties.insert(9 , cn.getDisplayName());
} else if (c instanceof Host) {
keyProperties.insert(0 , ",host=" );
keyProperties.insert(6 , c.getName());
} else if (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 ]);
}
// -------------------- Background Thread --------------------
/**
* Start the background thread that will periodically check for session timeouts .
*/
protected void threadStart() {
if (backgroundProcessorDelay > 0 &&
(getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState())) &&
(backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) {
if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) {
// There was an error executing the scheduled task, get it and log it
try {
backgroundProcessorFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error(sm.getString("containerBase.backgroundProcess.error" ), e);
}
}
backgroundProcessorFuture = Container.getService(this ).getServer().getUtilityExecutor()
.scheduleWithFixedDelay(new ContainerBackgroundProcessor(), backgroundProcessorDelay,
backgroundProcessorDelay, TimeUnit.SECONDS);
}
}
/**
* Stop the background thread that is periodically checking for session timeouts .
*/
protected void threadStop() {
if (backgroundProcessorFuture != null ) {
backgroundProcessorFuture.cancel(true );
backgroundProcessorFuture = null ;
}
}
@Override
public final String toString() {
StringBuilder sb = new StringBuilder();
Container parent = getParent();
if (parent != null ) {
sb.append(parent.toString());
sb.append('.' );
}
sb.append(this .getClass().getSimpleName());
sb.append('[' );
sb.append(getName());
sb.append(']' );
return sb.toString();
}
// ------------------------------- ContainerBackgroundProcessor Inner Class
protected class ContainerBackgroundProcessorMonitor implements Runnable {
@Override
public void run() {
if (getState().isAvailable()) {
threadStart();
}
}
}
/**
* Private runnable 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() {
processChildren(ContainerBase.this );
}
protected void processChildren(Container container) {
ClassLoader originalClassLoader = null ;
try {
if (container instanceof Context) {
Loader loader = ((Context) container).getLoader();
// Loader will be null for FailedContext instances
if (loader == null ) {
return ;
}
// Ensure background processing for Contexts and Wrappers
// is performed under the web app's class loader
originalClassLoader = ((Context) container).bind(false , null );
}
container.backgroundProcess();
Container[] children = container.findChildren();
for (Container child : children) {
if (child.getBackgroundProcessorDelay() <= 0 ) {
processChildren(child);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("containerBase.backgroundProcess.error" ), t);
} finally {
if (container instanceof Context) {
((Context) container).unbind(false , originalClassLoader);
}
}
}
}
// ---------------------------- Inner classes used with start/stop Executor
private static class StartChild implements Callable<Void > {
private Container child;
StartChild(Container child) {
this .child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null ;
}
}
private static class StopChild implements Callable<Void > {
private Container child;
StopChild(Container child) {
this .child = child;
}
@Override
public Void call() throws LifecycleException {
if (child.getState().isAvailable()) {
child.stop();
}
return null ;
}
}
}
Messung V0.5 in Prozent C=94 H=92 G=92
¤ Dauer der Verarbeitung: 0.28 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland