/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.lifecycle; import static com.caucho.lifecycle.LifecycleState.ACTIVE; import static com.caucho.lifecycle.LifecycleState.DESTROYED; import static com.caucho.lifecycle.LifecycleState.DESTROYING; import static com.caucho.lifecycle.LifecycleState.FAILED; import static com.caucho.lifecycle.LifecycleState.INIT; import static com.caucho.lifecycle.LifecycleState.INITIALIZING; import static com.caucho.lifecycle.LifecycleState.STARTING; import static com.caucho.lifecycle.LifecycleState.STOPPED; import static com.caucho.lifecycle.LifecycleState.STOPPED_IDLE; import static com.caucho.lifecycle.LifecycleState.STOPPING; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Level; import java.util.logging.Logger; import com.caucho.util.Alarm; /** * Lifecycle class. */ public final class Lifecycle { private final Logger _log; private String _name; private Level _level = Level.FINE; private Level _lowLevel = Level.FINER; private final AtomicReference<LifecycleState> _state = new AtomicReference<LifecycleState>(LifecycleState.NEW); private long _activeCount; private long _failCount; private long _lastFailTime; private long _lastChangeTime; private ArrayList<WeakReference<LifecycleListener>> _listeners; /** * Creates an anonymous lifecycle. */ public Lifecycle() { _log = null; } /** * Creates an lifecycle with logger and name. */ public Lifecycle(Logger log) { _log = log; } /** * Creates an lifecycle with logger and name. */ public Lifecycle(Logger log, String name) { _log = log; _name = name; } /** * Creates an lifecycle with logger, a name, and a level. */ public Lifecycle(Logger log, String name, Level level) { _log = log; _name = name; setLevel(level); } /** * Gets the lifecycle name. */ public String getName() { return _name; } /** * Sets the lifecycle name, and the level to Level.INFO. */ public void setName(String name) { _name = name; } /** * Gets the lifecycle logging level. */ public Level getLevel() { return _level; } /** * Sets the lifecycle logging level. */ public void setLevel(Level level) { _level = level; if (level.intValue() < _lowLevel.intValue()) _lowLevel = level; } public final LifecycleState getState() { return _state.get(); } /** * Returns the current state. */ public int getStateOrdinal() { return _state.get().ordinal(); } /** * Returns the state name for the passed state. */ /* public static String getStateName(int state) { switch (state) { case IS_NEW: return "new"; case IS_INITIALIZING: return "initializing"; case IS_INIT: return "init"; case IS_STARTING: return "starting"; case IS_ACTIVE: return "active"; case IS_FAILED: return "failed"; case IS_STOPPING: return "stopping"; case IS_STOPPED: return "stopped"; case IS_DESTROYING: return "destroying"; case IS_DESTROYED: return "destroyed"; default: return "unknown"; } } */ /** * Returns the current state name. */ public String getStateName() { return getState().toString(); } /** * Returns the last lifecycle change time. */ public long getLastChangeTime() { return _lastChangeTime; } /** * Returns the last failure time. */ public long getLastFailTime() { return _lastFailTime; } /** * Returns the number of times the lifecycle has switched to active. */ public long getActiveCount() { return _activeCount; } /** * Returns the number of times the lifecycle has switched to failing. */ public long getFailCount() { return _failCount; } /** * Returns true for the initializing state. */ public boolean isInitializing() { return getState().isInitializing(); } /** * Returns true for the init state. */ public boolean isInit() { return getState().isInit(); } /** * Returns true for the init state. */ public boolean isBeforeInit() { return getState().isBeforeInit(); } /** * True for any state after initialization. */ public boolean isAfterInit() { return getState().isAfterInit(); } /** * True for an idle state, where any request would transition to an * active state. */ public boolean isIdle() { return getState().isIdle(); } /** * Returns true if the service is starting. */ public boolean isStarting() { return getState().isStarting(); } /** * Returns true if the service is starting. */ /* public boolean isAfterStarting() { return getState().isAfterStarting(); } */ /** * Returns true for the warmup state. */ public boolean isWarmup() { return getState().isWarmup(); } /** * Returns true for the initializing state. */ public boolean isBeforeActive() { return getState().isBeforeActive(); } /** * Returns true for the closing states */ /* public boolean isAfterActive() { return getState().isAfterActive(); } */ /** * Wait for a period of time until the service starts. */ public boolean waitForActive(long timeout) { LifecycleState state = getState(); if (state.isActive()) return true; else if (state.isAfterActive()) return false; // server/1d2j long waitEnd = Alarm.getCurrentTimeActual() + timeout; synchronized (this) { while ((state = _state.get()).isBeforeActive() && Alarm.getCurrentTimeActual() < waitEnd) { if (state.isActive()) return true; else if (state.isAfterActive()) return false; try { long delta = waitEnd - Alarm.getCurrentTimeActual(); if (delta > 0) { wait(delta); } } catch (InterruptedException e) { } } } return _state.get().isActive(); } /** * Returns true for the active state. */ public boolean isActive() { return getState().isActive(); } /** * Returns true for the a runnable state, including warmup */ public boolean isRunnable() { return getState().isRunnable(); /* int state = _state.get(); return IS_WARMUP <= state && state <= IS_ACTIVE; */ } /** * Returns true for the closing states */ public boolean isAfterStopping() { return getState().isAfterStopping(); } /** * Returns true for the failed state. */ public boolean isError() { return getState().isError(); } /** * Returns true for the failed state. */ public boolean isFailed() { return getState().isError(); } /** * True if a stop for a restart would be allowed. */ public boolean isAllowStopFromRestart() { return getState().isAllowStopFromRestart(); } /** * Returns true if the state is stopping. */ public boolean isStopping() { return getState().isStopping(); } /** * Returns true if the state is stopping. */ public boolean isStopped() { return getState().isStopped(); } /** * Returns true if the state is closed */ public boolean isDestroying() { return getState().isDestroying(); } /** * Returns true if the state is closed */ public boolean isDestroyed() { return getState().isDestroyed(); } /** * Changes to the initializing state. * * @return true if the transition is allowed */ public boolean toInitializing() { return toNextState(INITIALIZING); } /** * Changes to the init state. * * @return true if the transition is allowed */ public boolean toInit() { return toNextState(INIT); } /** * Changes to the init from the stopped state. * * @return true if the transition is allowed */ public boolean toPostInit() { if (_state.compareAndSet(STOPPED, INIT)) { _lastChangeTime = Alarm.getCurrentTime(); notifyListeners(STOPPED, INIT); return true; } else return _state.get().isInit(); } /** * Changes to the starting state. * * @return true if the transition is allowed */ public boolean toStarting() { LifecycleState state; do { state = _state.get(); if (state.isAfterStarting() && ! state.isStopped()) return false; } while (! _state.compareAndSet(state, STARTING)); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_level) && _log.isLoggable(Level.FINE)) _log.fine(_name + " starting"); notifyListeners(state, STARTING); return true; } /** * Changes to the active state. * * @return true if the transition is allowed */ public boolean toActive() { LifecycleState state; do { state = _state.get(); if (state.isAfterActive() && ! state.isStopped()) return false; } while (! _state.compareAndSet(state, ACTIVE)); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_level)) _log.log(_level, _name + " active"); notifyListeners(state, ACTIVE); synchronized (this) { notifyAll(); } return true; } /** * Changes to the error state. * * @return true if the transition is allowed */ public boolean toError() { return toFail(); } /** * Changes to the failed state. * * @return true if the transition is allowed */ public boolean toFail() { LifecycleState state; do { state = _state.get(); if (state.isAfterDestroying()) return false; } while (! _state.compareAndSet(state, FAILED)); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_level)) _log.log(_level, _name + " fail"); notifyListeners(state, FAILED); _failCount++; return true; } /** * Changes to the stopping state. * * @return true if the transition is allowed */ public boolean toStopping() { LifecycleState state; do { state = _state.get(); if (state.isAfterStopping() || state.isStarting()) return false; } while (! _state.compareAndSet(state, STOPPING)); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_level)) _log.log(_level, _name + " stopping"); notifyListeners(state, STOPPING); return true; } /** * Changes to the idle (stopped) state. * * @return true if the transition is allowed */ public boolean toIdle() { return toState(STOPPED_IDLE); } /** * Changes to the stopped state. * * @return true if the transition is allowed */ public boolean toStop() { return toNextState(STOPPED); } /** * Changes to the destroying state. * * @return true if the transition is allowed */ public boolean toDestroying() { return toNextState(DESTROYING); } /** * Changes to the closed state. * * @return true if the transition is allowed */ public boolean toDestroy() { return toNextState(DESTROYED); } /** * Changes to the next state. * * @return true if the transition is allowed */ private boolean toNextState(LifecycleState newState) { LifecycleState state; do { state = _state.get(); if (newState.ordinal() <= state.ordinal()) return false; } while (! _state.compareAndSet(state, newState)); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_lowLevel)) { _log.log(_lowLevel, _name + " " + newState); } notifyListeners(state, newState); return true; } /** * Changes to the next state. * * @return true if the transition is allowed */ private boolean toState(LifecycleState newState) { LifecycleState state = _state.getAndSet(newState); _lastChangeTime = Alarm.getCurrentTime(); if (_log != null && _log.isLoggable(_lowLevel)) _log.log(_lowLevel, _name + " " + newState); notifyListeners(state, newState); return true; } // // listeners // /** * Adds a listener to detect lifecycle changes. */ public void addListener(LifecycleListener listener) { synchronized (this) { if (isDestroyed()) { IllegalStateException e = new IllegalStateException("attempted to add listener to a destroyed lifecyle " + this); if (_log != null) _log.log(Level.WARNING, e.toString(), e); else Logger.getLogger(Lifecycle.class.getName()).log(Level.WARNING, e.toString(), e); return; } if (_listeners == null) _listeners = new ArrayList<WeakReference<LifecycleListener>>(); for (int i = _listeners.size() - 1; i >= 0; i--) { LifecycleListener oldListener = _listeners.get(i).get(); if (listener == oldListener) return; else if (oldListener == null) _listeners.remove(i); } _listeners.add(new WeakReference<LifecycleListener>(listener)); } } /** * Removes a listener. */ public void removeListener(LifecycleListener listener) { synchronized (this) { if (_listeners == null) return; for (int i = _listeners.size() - 1; i >= 0; i--) { LifecycleListener oldListener = _listeners.get(i).get(); if (listener == oldListener) { _listeners.remove(i); return; } else if (oldListener == null) _listeners.remove(i); } } } /** * Returns the listeners. */ private void notifyListeners(LifecycleState oldState, LifecycleState newState) { // initial must be outside synchronized to avoid any blocking // for fail-safe shutdown if (_listeners == null) { return; } synchronized (this) { if (_listeners != null) { for (int i = 0; i < _listeners.size(); i++) { LifecycleListener listener = _listeners.get(i).get(); if (listener != null) { listener.lifecycleEvent(oldState, newState); } else { _listeners.remove(i); i--; } } } } } /** * Copies from a target state. * * @return true if the transition is allowed */ public void copyState(Lifecycle source) { _state.set(source._state.get()); } /** * Debug string value. */ public String toString() { if (_name != null) return "Lifecycle[" + _name + ", " + getStateName() + "]"; else return "Lifecycle[" + getStateName() + "]"; } }