package net.sourceforge.javautil.lifecycle.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassMethod;
import net.sourceforge.javautil.lifecycle.ILifecycle;
import net.sourceforge.javautil.lifecycle.ILifecyclePhase;
import net.sourceforge.javautil.lifecycle.ILifecycle.PhaseType;
import net.sourceforge.javautil.lifecycle.annotation.LifecycleRollback;
import net.sourceforge.javautil.lifecycle.annotation.Phase;
/**
* This will allow POJO's (Plain Old Java Object) to annotate methods with {@link Phase}
* that will be executed as part of it's {@link ILifecycle} management. This supports the
* {@link PostConstruct} annotation and maps the corresponding method to the {@link PhaseType#INITIALIZE}
* phase. This also supports the {@link PreDestroy} as an {@link PhaseType#POSTSTOP} equivalent.<br/><br/>
*
* In addition this supports the {@link LifecycleRollback} annotation mapping corresponding roll-back
* methods to the {@link PhaseType}'s specified. If no {@link PhaseType} is specified the method will
* be considered the default roll-back method for all {@link PhaseType} for which other roll-back methods
* are not specified.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class LifecyclePojo<T> extends LifecycleAbstract<T> {
/**
* The phases that make up the life-cycle of the target POJO
*/
protected final List<ILifecyclePhase<T>> phases = new ArrayList<ILifecyclePhase<T>>();
protected final Map<PhaseType, ClassMethod> rollbacks = new LinkedHashMap<PhaseType, ClassMethod>();
public LifecyclePojo(T managed) {
super(managed);
boolean initialize = false;
boolean create = false;
boolean start = false;
boolean stop = false;
boolean destroy = false;
ClassDescriptor desc = ClassCache.getFor(managed.getClass());
ClassMethod postConstruct = desc.getMethod(PostConstruct.class);
if (postConstruct != null) {
this.phases.add(new PojoPhase(PhaseType.INITIALIZE, "Post Construct", 0, postConstruct));
initialize = true;
}
ClassMethod preDestroy = desc.getMethod(PreDestroy.class);
if (preDestroy != null)
this.phases.add(new PojoPhase(PhaseType.POSTSTOP, "Pre Destroy", 1000, preDestroy));
ClassMethod[] methods = desc.getMethods(Phase.class);
for (ClassMethod method : methods) {
PojoPhase phase = new PojoPhase(method.getAnnotation(Phase.class), method);
switch (phase.getType()) {
case INSTANTIATED:
throw new UnsupportedOperationException("Cannot specify instantiated phase");
case INITIALIZE:
if (initialize) throw new UnsupportedOperationException("Can only specify one initialize phase"); else initialize = true;
break;
case CREATE:
if (create) throw new UnsupportedOperationException("Can only specify one create phase"); else create = true;
break;
case START:
if (start) throw new UnsupportedOperationException("Can only specify one start phase"); else start = true;
break;
case STOP:
if (stop) throw new UnsupportedOperationException("Can only specify one stop phase"); else stop = true;
break;
case DESTROY:
if (destroy) throw new UnsupportedOperationException("Can only specify one destroy phase"); else destroy = true;
break;
default:
}
this.phases.add(phase);
}
ClassMethod[] rollbacks = desc.getMethods(LifecycleRollback.class);
for (ClassMethod method : rollbacks) {
LifecycleRollback lr = method.getAnnotation(LifecycleRollback.class);
if (lr.value().length == 0) this.rollbacks.put(null, method);
else for (PhaseType type : lr.value()) {
this.rollbacks.put(type, method);
}
}
Collections.sort(phases);
}
@Override protected ILifecyclePhase<T> getPhase(ILifecycle.PhaseType type) {
for (int p=0; p<phases.size(); p++) {
if (phases.get(p).getType() == type) return phases.get(p);
}
return null;
}
@Override protected List<ILifecyclePhase<T>> getPhases(ILifecycle.PhaseType type) {
List<ILifecyclePhase<T>> phases = new ArrayList<ILifecyclePhase<T>>();
for (int p=0; p<this.phases.size(); p++) {
if (this.phases.get(p).getType() == type) phases.add(this.phases.get(p));
}
return phases;
}
/**
* A wrapper for the actual Pojo method that will be executed.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class PojoPhase<T> extends LifecyclePhaseAbstract<T> {
protected final ClassMethod method;
public PojoPhase(Phase phase, ClassMethod method) {
super(phase.value(), phase.description(), phase.priority());
this.method = method;
}
public PojoPhase(PhaseType phase, String description, int priority, ClassMethod method) {
super(phase, description, priority);
this.method = method;
}
public void execute(T target) {
boolean accessible = method.getJavaMember().isAccessible();
try {
if (!accessible) method.getJavaMember().setAccessible(true);
this.method.invoke(target);
} finally {
if (!accessible) method.getJavaMember().setAccessible(false);
}
}
public void rollback(T target) {
ClassMethod rollback = rollbacks.get(this.type);
if (rollback == null) rollback = rollbacks.get(null);
if (rollback != null) rollback.invoke(target, this);
}
}
}