| /* |
| * 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; |
| |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.LifecycleState; |
| import org.apache.juli.logging.Log; |
| import org.apache.juli.logging.LogFactory; |
| import org.apache.tomcat.util.ExceptionUtils; |
| import org.apache.tomcat.util.res.StringManager; |
| |
| |
| /** |
| * Base implementation of the {@link Lifecycle} interface that implements the |
| * state transition rules for {@link Lifecycle#start()} and |
| * {@link Lifecycle#stop()} |
| */ |
| public abstract class LifecycleBase implements Lifecycle { |
| |
| private static final Log log = LogFactory.getLog(LifecycleBase.class); |
| |
| private static final StringManager sm = |
| StringManager.getManager("org.apache.catalina.util"); |
| |
| |
| /** |
| * Used to handle firing lifecycle events. |
| * TODO: Consider merging LifecycleSupport into this class. |
| */ |
| private final LifecycleSupport lifecycle = new LifecycleSupport(this); |
| |
| |
| /** |
| * The current state of the source component. |
| */ |
| private volatile LifecycleState state = LifecycleState.NEW; |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addLifecycleListener(LifecycleListener listener) { |
| lifecycle.addLifecycleListener(listener); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public LifecycleListener[] findLifecycleListeners() { |
| return lifecycle.findLifecycleListeners(); |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public void removeLifecycleListener(LifecycleListener listener) { |
| lifecycle.removeLifecycleListener(listener); |
| } |
| |
| |
| /** |
| * Allow sub classes to fire {@link Lifecycle} events. |
| * |
| * @param type Event type |
| * @param data Data associated with event. |
| */ |
| protected void fireLifecycleEvent(String type, Object data) { |
| lifecycle.fireLifecycleEvent(type, data); |
| } |
| |
| |
| @Override |
| public final synchronized void init() throws LifecycleException { |
| if (!state.equals(LifecycleState.NEW)) { |
| invalidTransition(Lifecycle.BEFORE_INIT_EVENT); |
| } |
| setStateInternal(LifecycleState.INITIALIZING, null, false); |
| |
| try { |
| initInternal(); |
| } catch (Throwable t) { |
| ExceptionUtils.handleThrowable(t); |
| setStateInternal(LifecycleState.FAILED, null, false); |
| throw new LifecycleException( |
| sm.getString("lifecycleBase.initFail",toString()), t); |
| } |
| |
| setStateInternal(LifecycleState.INITIALIZED, null, false); |
| } |
| |
| |
| protected abstract void initInternal() throws LifecycleException; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public final synchronized void start() throws LifecycleException { |
| |
| if (LifecycleState.STARTING_PREP.equals(state) || |
| LifecycleState.STARTING.equals(state) || |
| LifecycleState.STARTED.equals(state)) { |
| |
| if (log.isDebugEnabled()) { |
| Exception e = new LifecycleException(); |
| log.debug(sm.getString("lifecycleBase.alreadyStarted", |
| toString()), e); |
| } else if (log.isInfoEnabled()) { |
| log.info(sm.getString("lifecycleBase.alreadyStarted", |
| toString())); |
| } |
| |
| return; |
| } |
| |
| if (state.equals(LifecycleState.NEW)) { |
| init(); |
| } else if (state.equals(LifecycleState.FAILED)){ |
| stop(); |
| } else if (!state.equals(LifecycleState.INITIALIZED) && |
| !state.equals(LifecycleState.STOPPED)) { |
| invalidTransition(Lifecycle.BEFORE_START_EVENT); |
| } |
| |
| setStateInternal(LifecycleState.STARTING_PREP, null, false); |
| |
| try { |
| startInternal(); |
| } catch (Throwable t) { |
| ExceptionUtils.handleThrowable(t); |
| setStateInternal(LifecycleState.FAILED, null, false); |
| throw new LifecycleException( |
| sm.getString("lifecycleBase.startFail",toString()), t); |
| } |
| |
| if (state.equals(LifecycleState.FAILED) || |
| state.equals(LifecycleState.MUST_STOP)) { |
| stop(); |
| } else { |
| // Shouldn't be necessary but acts as a check that sub-classes are |
| // doing what they are supposed to. |
| if (!state.equals(LifecycleState.STARTING)) { |
| invalidTransition(Lifecycle.AFTER_START_EVENT); |
| } |
| |
| setStateInternal(LifecycleState.STARTED, null, false); |
| } |
| } |
| |
| |
| /** |
| * 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 |
| */ |
| protected abstract void startInternal() throws LifecycleException; |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public final synchronized void stop() 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); |
| } else if (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) && |
| !state.equals(LifecycleState.MUST_STOP)) { |
| invalidTransition(Lifecycle.BEFORE_STOP_EVENT); |
| } |
| |
| 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); |
| } |
| |
| try { |
| stopInternal(); |
| } catch (Throwable t) { |
| ExceptionUtils.handleThrowable(t); |
| setStateInternal(LifecycleState.FAILED, null, false); |
| throw new LifecycleException( |
| sm.getString("lifecycleBase.stopFail",toString()), t); |
| } |
| |
| if (state.equals(LifecycleState.MUST_DESTROY)) { |
| // Complete stop process first |
| setStateInternal(LifecycleState.STOPPED, null, false); |
| |
| destroy(); |
| } else if (!state.equals(LifecycleState.FAILED)){ |
| // Shouldn't be necessary but acts as a check that sub-classes are |
| // doing what they are supposed to. |
| if (!state.equals(LifecycleState.STOPPING)) { |
| invalidTransition(Lifecycle.AFTER_STOP_EVENT); |
| } |
| |
| setStateInternal(LifecycleState.STOPPED, null, false); |
| } |
| } |
| |
| |
| /** |
| * 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 |
| */ |
| protected abstract void stopInternal() throws LifecycleException; |
| |
| |
| @Override |
| public final synchronized void destroy() throws LifecycleException { |
| if (LifecycleState.FAILED.equals(state)) { |
| try { |
| // Triggers clean-up |
| stop(); |
| } catch (LifecycleException e) { |
| // Just log. Still want to destroy. |
| log.warn(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); |
| } else if (log.isInfoEnabled()) { |
| log.info(sm.getString("lifecycleBase.alreadyDestroyed", |
| toString())); |
| } |
| |
| return; |
| } |
| |
| if (!state.equals(LifecycleState.STOPPED) && |
| !state.equals(LifecycleState.FAILED) && |
| !state.equals(LifecycleState.NEW) && |
| !state.equals(LifecycleState.INITIALIZED)) { |
| invalidTransition(Lifecycle.BEFORE_DESTROY_EVENT); |
| } |
| |
| setStateInternal(LifecycleState.DESTROYING, null, false); |
| |
| try { |
| destroyInternal(); |
| } catch (Throwable t) { |
| ExceptionUtils.handleThrowable(t); |
| setStateInternal(LifecycleState.FAILED, null, false); |
| throw new LifecycleException( |
| sm.getString("lifecycleBase.destroyFail",toString()), t); |
| } |
| |
| setStateInternal(LifecycleState.DESTROYED, null, false); |
| } |
| |
| |
| protected abstract void destroyInternal() throws LifecycleException; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public LifecycleState getState() { |
| return state; |
| } |
| |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @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 |
| */ |
| protected synchronized void 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 |
| */ |
| protected synchronized void setState(LifecycleState state, Object data) |
| throws LifecycleException { |
| setStateInternal(state, data, true); |
| } |
| |
| private synchronized void setStateInternal(LifecycleState state, |
| Object data, boolean check) throws LifecycleException { |
| |
| 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()); |
| } |
| } |
| |
| this.state = state; |
| String lifecycleEvent = state.getLifecycleEvent(); |
| if (lifecycleEvent != null) { |
| fireLifecycleEvent(lifecycleEvent, data); |
| } |
| } |
| |
| private void invalidTransition(String type) throws LifecycleException { |
| String msg = sm.getString("lifecycleBase.invalidTransition", type, |
| toString(), state); |
| throw new LifecycleException(msg); |
| } |
| } |