package net.sourceforge.javautil.lifecycle.impl;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import net.sourceforge.javautil.common.event.IEventPropagator;
import net.sourceforge.javautil.common.event.EventProxy;
import net.sourceforge.javautil.common.proxy.CollectionTargetProxy;
import net.sourceforge.javautil.common.shutdown.Shutdown;
import net.sourceforge.javautil.lifecycle.ILifecycle;
import net.sourceforge.javautil.lifecycle.LifecycleEvent;
import net.sourceforge.javautil.lifecycle.LifecycleException;
import net.sourceforge.javautil.lifecycle.ILifecycleListener;
import net.sourceforge.javautil.lifecycle.ILifecyclePhase;
import net.sourceforge.javautil.lifecycle.LifecyclePhaseException;
import net.sourceforge.javautil.lifecycle.ILifecycle.PhaseType;
import net.sourceforge.javautil.lifecycle.LifecycleEvent.TransitionType;
/**
* A standard starting point for most implementations.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public abstract class LifecycleAbstract<T> implements ILifecycle<T> {
protected final T managed;
protected final IEventPropagator<LifecycleEvent, ILifecycleListener>
propagator = EventProxy.createProxy(ILifecycleListener.class, LifecycleEvent.class);
protected PhaseType current = PhaseType.INSTANTIATED;
protected ILifecyclePhase<T> from = null;
protected PhaseType target = null;
public LifecycleAbstract(T managed) {
this.managed = managed;
}
public synchronized void create() {
if (this.target == null) this.target = PhaseType.CREATE;
if (this.current == PhaseType.INSTANTIATED) {
ILifecyclePhase<T> init = this.getPhase(PhaseType.INITIALIZE);
if (init != null) this.execute(init); else this.fireBefore(PhaseType.INITIALIZE);
this.current = PhaseType.INITIALIZE;
if (init == null) this.fireAfter(PhaseType.INITIALIZE);
}
if (this.current == PhaseType.INITIALIZE) {
ILifecyclePhase<T> create = this.getPhase(PhaseType.CREATE);
if (create != null) this.execute(create); else this.fireBefore(PhaseType.CREATE);
this.current = PhaseType.CREATE;
if (create == null) this.fireAfter(PhaseType.CREATE);
} else {
throw new LifecycleException("Not in correct phase for creation: " + this.current, this);
}
this.target = null;
}
public synchronized void start() {
this.target = PhaseType.START;
if (this.current == PhaseType.INSTANTIATED) this.create();
if (this.current == PhaseType.CREATE || this.current == PhaseType.STOP) {
List<ILifecyclePhase<T>> prestart = this.getPhases(PhaseType.PRESTART);
if (prestart.size() > 0) this.execute(prestart); else this.fireBefore(PhaseType.PRESTART);
this.current = PhaseType.PRESTART;
if (prestart.size() == 0) this.fireAfter(PhaseType.PRESTART);
}
if (this.current == PhaseType.PRESTART) {
ILifecyclePhase<T> start = this.getPhase(PhaseType.START);
if (start != null) this.execute(start); else this.fireBefore(PhaseType.START);
this.current = PhaseType.START;
if (start == null) this.fireAfter(PhaseType.START);
} else {
throw new LifecycleException("Not in correct phase for starting: " + this.current, this);
}
this.target = null;
}
public synchronized void stop() {
if (this.current == PhaseType.START) {
this.target = PhaseType.STOP;
ILifecyclePhase<T> stop = this.getPhase(PhaseType.STOP);
if (stop != null) this.execute(stop); else this.fireBefore(PhaseType.STOP);
this.current = PhaseType.STOP;
if (stop == null) this.fireAfter(PhaseType.STOP);
this.target = null;
} else {
throw new LifecycleException("Not in correct phase for stopping: " + this.current, this);
}
}
public synchronized void destroy() {
this.target = PhaseType.DESTROY;
if (this.current == PhaseType.START) this.stop();
if (this.current == PhaseType.STOP) {
List<ILifecyclePhase<T>> poststop = this.getPhases(PhaseType.POSTSTOP);
if (poststop.size() > 0) this.execute(poststop); this.fireBefore(PhaseType.POSTSTOP);
this.current = PhaseType.POSTSTOP;
if (poststop.size() == 0) this.fireAfter(PhaseType.POSTSTOP);
}
if (this.current == PhaseType.POSTSTOP || this.current == PhaseType.CREATE) {
ILifecyclePhase<T> destroy = this.getPhase(PhaseType.DESTROY);
if (destroy != null) this.execute(destroy); this.fireBefore(PhaseType.DESTROY);
this.current = PhaseType.DESTROY;
if (destroy == null) this.fireAfter(PhaseType.DESTROY);
this.target = null;
} else {
throw new LifecycleException("Not in correct phase for destroying: " + this.current, this);
}
}
public PhaseType getCurrentPhase() { return this.current; }
public T getManaged() { return this.managed; }
public void addListener(ILifecycleListener listener) { this.propagator.addListener(listener); }
public void removeListener(ILifecycleListener listener) { this.propagator.removeListener(listener); }
public void addListener(Object listener) { this.propagator.addListener(listener); }
public void removeListener(Object listener) { this.propagator.removeListener(listener); }
public synchronized void shutdown() { this.destroy(); }
public synchronized boolean isPartialState() { return this.target != null; }
public synchronized void continueLifecycle () {
if (this.target == null) return;
if (this.current != this.target) {
switch (this.target) {
case CREATE: this.create(); break;
case DESTROY: this.destroy(); break;
case START: this.start(); break;
case STOP: this.stop(); break;
default:
throw new LifecycleException("Could not continue lifecycle from failure", this);
}
}
}
/**
* @param phases The phases to execute
*/
protected void execute (List<ILifecyclePhase<T>> phases) {
int start = 0;
if (this.from != null) start = phases.indexOf(this.from);
for (int p=start; p<phases.size(); p++) {
this.execute(phases.get(p));
}
}
/**
* @param phase The phase to execute
*/
protected void execute (ILifecyclePhase<T> phase) {
this.fireBefore(phase.getType());
try {
this.from = phase;
phase.execute(this.managed);
this.from = null;
} catch (Exception e) {
phase.rollback(this.managed);
this.fireFailure(phase.getType());
throw new LifecyclePhaseException(e, this, phase);
}
this.fireAfter(phase.getType());
}
protected void fireBefore (PhaseType type) {
((ILifecycleListener)this.propagator).before(new LifecycleEvent("before", this, type, TransitionType.Before));
}
protected void fireAfter (PhaseType type) {
((ILifecycleListener)this.propagator).after(new LifecycleEvent("after", this, type, TransitionType.After));
}
protected void fireFailure (PhaseType type) {
((ILifecycleListener)this.propagator).failure(new LifecycleEvent("exception", this, type, TransitionType.Failure));
}
/**
* @param type The type of phase
* @return The first phase for the specified type, or null if no such phase is available
*/
protected abstract ILifecyclePhase<T> getPhase (PhaseType type);
/**
* @param type The type of phase
* @return A list, possibly empty, of phases corresponding to the type
*/
protected abstract List<ILifecyclePhase<T>> getPhases (PhaseType type);
}