/*
* Copyright (c) 1998-2010 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 com.caucho.util.Alarm;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Lifecycle class.
*/
public final class Lifecycle implements LifecycleState {
private final Logger _log;
private String _name;
private Level _level = Level.FINE;
private Level _lowLevel = Level.FINER;
private final AtomicInteger _state = new AtomicInteger();
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;
}
/**
* Returns the current state.
*/
public int getState()
{
return _state.get();
}
/**
* 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 getStateName(_state.get());
}
/**
* 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 _state.get() == IS_INITIALIZING;
}
/**
* Returns true for the init state.
*/
public boolean isInit()
{
return _state.get() == IS_INIT;
}
/**
* Returns true for the init state.
*/
public boolean isBeforeInit()
{
return _state.get() < IS_INIT;
}
/**
* Returns true for the init state.
*/
public boolean isAfterInit()
{
return _state.get() >= IS_INIT;
}
/**
* Returns true if the service is starting.
*/
public boolean isStarting()
{
return _state.get() == IS_STARTING;
}
/**
* Returns true if the service is starting.
*/
public boolean isAfterStarting()
{
return _state.get() >= IS_STARTING;
}
/**
* Returns true for the warmup state.
*/
public boolean isWarmup()
{
return _state.get() == IS_WARMUP;
}
/**
* Returns true for the initializing state.
*/
public boolean isBeforeActive()
{
return _state.get() < IS_ACTIVE;
}
/**
* Returns true for the closing states
*/
public boolean isAfterActive()
{
return IS_ACTIVE < _state.get();
}
/**
* Wait for a period of time until the service starts.
*/
public boolean waitForActive(long timeout)
{
int state = _state.get();
if (state == IS_ACTIVE)
return true;
else if (state > IS_ACTIVE)
return false;
long waitEnd = Alarm.getCurrentTime() + timeout;
synchronized (this) {
while ((state = _state.get()) < IS_ACTIVE
&& Alarm.getCurrentTime() < waitEnd) {
if (state == IS_ACTIVE)
return true;
else if (IS_ACTIVE < state)
return false;
else if (Alarm.isTest())
return false;
try {
long delta = waitEnd - Alarm.getCurrentTime();
if (delta > 0) {
wait(delta);
}
} catch (InterruptedException e) {
}
}
}
return _state.get() == IS_ACTIVE;
}
/**
* Returns true for the active state.
*/
public boolean isActive()
{
return _state.get() == IS_ACTIVE;
}
/**
* Returns true for the a runnable state, including warmup
*/
public boolean isRunnable()
{
int state = _state.get();
return IS_WARMUP <= state && state <= IS_ACTIVE;
}
/**
* Returns true for the failed state.
*/
public boolean isError()
{
return isFailed();
}
/**
* Returns true for the failed state.
*/
public boolean isFailed()
{
return _state.get() == IS_FAILED;
}
/**
* Returns true if the state is stopping.
*/
public boolean isStopping()
{
return IS_STOPPING <= _state.get();
}
/**
* Returns true if the state is stopping.
*/
public boolean isStopped()
{
return IS_STOPPING <= _state.get();
}
/**
* Returns true if the state is closed
*/
public boolean isDestroying()
{
return IS_DESTROYING <= _state.get();
}
/**
* Returns true if the state is closed
*/
public boolean isDestroyed()
{
return IS_DESTROYED <= _state.get();
}
/**
* Changes to the initializing state.
*
* @return true if the transition is allowed
*/
public boolean toInitializing()
{
return toNextState(IS_INITIALIZING);
}
/**
* Changes to the init state.
*
* @return true if the transition is allowed
*/
public boolean toInit()
{
return toNextState(IS_INIT);
}
/**
* Changes to the init from the stopped state.
*
* @return true if the transition is allowed
*/
public boolean toPostInit()
{
if (_state.compareAndSet(IS_STOPPED, IS_INIT)) {
_lastChangeTime = Alarm.getCurrentTime();
notifyListeners(IS_STOPPED, IS_INIT);
return true;
}
else
return _state.get() == IS_INIT;
}
/**
* Changes to the starting state.
*
* @return true if the transition is allowed
*/
public boolean toStarting()
{
int state;
do {
state = _state.get();
if (IS_STARTING <= state && state != IS_STOPPED)
return false;
} while (! _state.compareAndSet(state, IS_STARTING));
_lastChangeTime = Alarm.getCurrentTime();
if (_log != null && _log.isLoggable(_level) && _log.isLoggable(Level.FINE))
_log.fine(_name + " starting");
notifyListeners(state, IS_STARTING);
return true;
}
/**
* Changes to the active state.
*
* @return true if the transition is allowed
*/
public boolean toActive()
{
int state;
do {
state = _state.get();
if (IS_ACTIVE <= state && state != IS_STOPPED)
return false;
} while (! _state.compareAndSet(state, IS_ACTIVE));
_lastChangeTime = Alarm.getCurrentTime();
if (_log != null && _log.isLoggable(_level))
_log.log(_level, _name + " active");
notifyListeners(state, IS_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()
{
int state;
do {
state = _state.get();
if (IS_DESTROYING <= state)
return false;
} while (! _state.compareAndSet(state, IS_FAILED));
_lastChangeTime = Alarm.getCurrentTime();
if (_log != null && _log.isLoggable(_level))
_log.log(_level, _name + " fail");
notifyListeners(state, IS_FAILED);
_failCount++;
return true;
}
/**
* Changes to the stopping state.
*
* @return true if the transition is allowed
*/
public boolean toStopping()
{
int state;
do {
state = _state.get();
if (IS_STOPPING <= state || state == IS_STARTING)
return false;
} while (! _state.compareAndSet(state, IS_STOPPING));
_lastChangeTime = Alarm.getCurrentTime();
if (_log != null && _log.isLoggable(_level))
_log.log(_level, _name + " stopping");
notifyListeners(state, IS_STOPPING);
return true;
}
/**
* Changes to the stopped state.
*
* @return true if the transition is allowed
*/
public boolean toStop()
{
return toNextState(IS_STOPPED);
}
/**
* Changes to the destroying state.
*
* @return true if the transition is allowed
*/
public boolean toDestroying()
{
return toNextState(IS_DESTROYING);
}
/**
* Changes to the closed state.
*
* @return true if the transition is allowed
*/
public boolean toDestroy()
{
return toNextState(IS_DESTROYED);
}
/**
* Changes to the next state.
*
* @return true if the transition is allowed
*/
private boolean toNextState(int newState)
{
int state;
do {
state = _state.get();
if (newState <= state)
return false;
} while (! _state.compareAndSet(state, newState));
_lastChangeTime = Alarm.getCurrentTime();
if (_log != null && _log.isLoggable(_lowLevel))
_log.log(_lowLevel, _name + " " + getStateName(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(int oldState, int 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() + "]";
}
}