/* * 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.util;
/** * Base implementation of the {@link Lifecycle} interface that implements the * state transition rules for {@link Lifecycle#start()} and * {@link Lifecycle#stop()}
*/ publicabstractclass LifecycleBase implements Lifecycle {
privatestaticfinal StringManager sm = StringManager.getManager(LifecycleBase.class);
/** * The list of registered LifecycleListeners for event notifications.
*/ privatefinal List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/** * The current state of the source component.
*/ privatevolatile LifecycleState state = LifecycleState.NEW;
privateboolean throwOnFailure = true;
/** * Will a {@link LifecycleException} thrown by a sub-class during * {@link #initInternal()}, {@link #startInternal()}, * {@link #stopInternal()} or {@link #destroyInternal()} be re-thrown for * the caller to handle or will it be logged instead? * * @return {@code true} if the exception will be re-thrown, otherwise * {@code false}
*/ publicboolean getThrowOnFailure() { return throwOnFailure;
}
/** * Configure if a {@link LifecycleException} thrown by a sub-class during * {@link #initInternal()}, {@link #startInternal()}, * {@link #stopInternal()} or {@link #destroyInternal()} will be re-thrown * for the caller to handle or if it will be logged instead. * * @param throwOnFailure {@code true} if the exception should be re-thrown, * otherwise {@code false}
*/ publicvoid setThrowOnFailure(boolean throwOnFailure) { this.throwOnFailure = throwOnFailure;
}
/** * Allow sub classes to fire {@link Lifecycle} events. * * @param type Event type * @param data Data associated with event.
*/ protectedvoid fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data); for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal(); if (state.equals(LifecycleState.FAILED)) { // This is a 'controlled' failure. The component put itself into the // FAILED state so call stop() to complete the clean-up.
stop();
} elseif (!state.equals(LifecycleState.STARTING)) { // Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to.
invalidTransition(AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) { // This is an 'uncontrolled' failure so put the component into the // FAILED state and throw an exception.
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
/** * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STARTING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#START_EVENT} event. * * If a component fails to start it may either throw a * {@link LifecycleException} which will cause it's parent to fail to start * or it can place itself in the error state in which case {@link #stop()} * will be called on the failed component but the parent component will * continue to start normally. * * @throws LifecycleException Start error occurred
*/ protectedabstractvoid startInternal() throws LifecycleException;
if (LifecycleState.STOPPING_PREP.equals(state) || LifecycleState.STOPPING.equals(state) ||
LifecycleState.STOPPED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStopped", toString()), e);
} elseif (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStopped", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
state = LifecycleState.STOPPED; return;
}
if (!state.equals(LifecycleState.STARTED) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(BEFORE_STOP_EVENT);
}
try { if (state.equals(LifecycleState.FAILED)) { // Don't transition to STOPPING_PREP as that would briefly mark the // component as available but do ensure the BEFORE_STOP_EVENT is // fired
fireLifecycleEvent(BEFORE_STOP_EVENT, null);
} else {
setStateInternal(LifecycleState.STOPPING_PREP, null, false);
}
stopInternal();
// Shouldn't be necessary but acts as a check that sub-classes are // doing what they are supposed to. if (!state.equals(LifecycleState.STOPPING) && !state.equals(LifecycleState.FAILED)) {
invalidTransition(AFTER_STOP_EVENT);
}
setStateInternal(LifecycleState.STOPPED, null, false);
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.stopFail", toString());
} finally { if (thisinstanceof Lifecycle.SingleUse) { // Complete stop process first
setStateInternal(LifecycleState.STOPPED, null, false);
destroy();
}
}
}
/** * Sub-classes must ensure that the state is changed to * {@link LifecycleState#STOPPING} during the execution of this method. * Changing state will trigger the {@link Lifecycle#STOP_EVENT} event. * * @throws LifecycleException Stop error occurred
*/ protectedabstractvoid stopInternal() throws LifecycleException;
@Override publicfinalsynchronizedvoid destroy() throws LifecycleException { if (LifecycleState.FAILED.equals(state)) { try { // Triggers clean-up
stop();
} catch (LifecycleException e) { // Just log. Still want to destroy.
log.error(sm.getString("lifecycleBase.destroyStopFail", toString()), e);
}
}
if (LifecycleState.DESTROYING.equals(state) || LifecycleState.DESTROYED.equals(state)) { if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyDestroyed", toString()), e);
} elseif (log.isInfoEnabled() && !(thisinstanceof Lifecycle.SingleUse)) { // Rather than have every component that might need to call // destroy() check for SingleUse, don't log an info message if // multiple calls are made to destroy()
log.info(sm.getString("lifecycleBase.alreadyDestroyed", toString()));
}
/** * Sub-classes implement this method to perform any instance destruction * required. * * @throws LifecycleException If the destruction fails
*/ protectedabstractvoid destroyInternal() throws LifecycleException;
@Override public LifecycleState getState() { return state;
}
@Override public String getStateName() { return getState().toString();
}
/** * Provides a mechanism for sub-classes to update the component state. * Calling this method will automatically fire any associated * {@link Lifecycle} event. It will also check that any attempted state * transition is valid for a sub-class. * * @param state The new state for this component * @throws LifecycleException when attempting to set an invalid state
*/ protectedsynchronizedvoid setState(LifecycleState state) throws LifecycleException {
setStateInternal(state, null, true);
}
/** * Provides a mechanism for sub-classes to update the component state. * Calling this method will automatically fire any associated * {@link Lifecycle} event. It will also check that any attempted state * transition is valid for a sub-class. * * @param state The new state for this component * @param data The data to pass to the associated {@link Lifecycle} event * @throws LifecycleException when attempting to set an invalid state
*/ protectedsynchronizedvoid setState(LifecycleState state, Object data) throws LifecycleException {
setStateInternal(state, data, true);
}
if (log.isDebugEnabled()) {
log.debug(sm.getString("lifecycleBase.setState", this, state));
}
if (check) { // Must have been triggered by one of the abstract methods (assume // code in this class is correct) // null is never a valid state if (state == null) {
invalidTransition("null"); // Unreachable code - here to stop eclipse complaining about // a possible NPE further down the method return;
}
// Any method can transition to failed // startInternal() permits STARTING_PREP to STARTING // stopInternal() permits STOPPING_PREP to STOPPING and FAILED to // STOPPING if (!(state == LifecycleState.FAILED ||
(this.state == LifecycleState.STARTING_PREP &&
state == LifecycleState.STARTING) ||
(this.state == LifecycleState.STOPPING_PREP &&
state == LifecycleState.STOPPING) ||
(this.state == LifecycleState.FAILED &&
state == LifecycleState.STOPPING))) { // No other transition permitted
invalidTransition(state.name());
}
}
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.