Commit e67d1922 authored by Joerg Domaschka's avatar Joerg Domaschka

ported lifecylce handling to error aware state machine.

parent e08d7f7e
......@@ -5,3 +5,4 @@ target/
.project
.settings
/bin/
/classes/
......@@ -89,6 +89,12 @@ public enum LifecycleHandlerType implements State, HandlerType {
* may be used to release external resources
*/
POST_STOP(PostStopHandler.class, DefaultFactories.POST_STOP_FACTORY),
INIT_FAILED(VoidHandler.class, DefaultFactories.VOID_FACTORY),
INSTALL_FAILED(VoidHandler.class, DefaultFactories.VOID_FACTORY),
STARTUP_FAILED(VoidHandler.class, DefaultFactories.VOID_FACTORY),
TERMINATION_FAILED(VoidHandler.class, DefaultFactories.VOID_FACTORY),
UNKNOWN(VoidHandler.class, DefaultFactories.VOID_FACTORY),
;
private final Class<? extends LifecycleHandler> handlerType;
......
package de.uniulm.omi.cloudiator.lance.lifecycle;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionAction;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionException;
final class DefaultLifecycleTransition<T extends LifecycleHandler> implements TransitionAction {
private final LifecycleHandlerType handlerType;
private final Class<T> handlerClass;
private final LifecycleStore store;
private final ExecutionContext ctx;
DefaultLifecycleTransition(LifecycleStore storeParam, ExecutionContext ctxParam,
LifecycleHandlerType handlerTypeParam, Class<T> classParam) {
handlerType = handlerTypeParam;
handlerClass = classParam;
store = storeParam;
ctx = ctxParam;
}
@Override
public void transit(Object[] params) throws TransitionException {
T h = store.getHandler(handlerType, handlerClass);
try { h.execute(ctx); }
catch(LifecycleException lce) {
throw new TransitionException(lce);
}
}
}
......@@ -25,7 +25,9 @@ import de.uniulm.omi.cloudiator.lance.lca.container.port.DownstreamAddress;
import de.uniulm.omi.cloudiator.lance.lca.container.port.PortDiff;
import de.uniulm.omi.cloudiator.lance.lca.registry.RegistrationException;
import de.uniulm.omi.cloudiator.lance.lifecycle.detector.PortUpdateHandler;
import de.uniulm.omi.cloudiator.lance.util.state.StateMachine;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareStateMachine;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareStateMachineBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -39,7 +41,7 @@ public final class LifecycleController {
final LifecycleStore store;
final ExecutionContext ec;
private final StateMachine<LifecycleHandlerType> machine;
private final ErrorAwareStateMachine<LifecycleHandlerType> machine;
private final LifecycleActionInterceptor interceptor;
private final GlobalRegistryAccessor accessor;
......@@ -48,26 +50,44 @@ public final class LifecycleController {
ExecutionContext ecParam) {
store = storeParam;
ec = ecParam;
machine = LifecycleControllerTransitions.buildStateMachine(store, ec);
interceptor = interceptorParam;
accessor = accessorParam;
machine = initStateMachine();
}
private ErrorAwareStateMachine<LifecycleHandlerType> initStateMachine() {
ErrorAwareStateMachineBuilder<LifecycleHandlerType> builder =
new ErrorAwareStateMachineBuilder<>(LifecycleHandlerType.NEW, LifecycleHandlerType.UNKNOWN);
LifecycleTransitionHelper.createInitAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createPreInstallAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createInstallAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createSkipInstallAction(builder.getTransitionBuilder());
LifecycleTransitionHelper.createPostInstallAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createPreStartAction(builder.getTransitionBuilder(), store, ec);
StartTransitionAction.createStartAction(builder.getTransitionBuilder(), store, ec, interceptor);
LifecycleTransitionHelper.createPostStartAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createPreStopAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createStopAction(builder.getTransitionBuilder(), store, ec);
LifecycleTransitionHelper.createPostStopAction(builder.getTransitionBuilder(), store, ec);
return builder.build();
}
private void preRun(HandlerType type) throws ContainerException {
interceptor.prepare(type);
private void preRun(HandlerType from, LifecycleHandlerType to) throws ContainerException {
interceptor.prepare(from);
}
private void postRun(HandlerType type) throws ContainerException {
interceptor.postprocess(type);
private void postRun(HandlerType from, LifecycleHandlerType to) throws ContainerException {
interceptor.postprocess(from);
}
void run(LifecycleHandlerType type) {
void run(LifecycleHandlerType from, LifecycleHandlerType to) {
try {
preRun(type);
machine.transit(type);
postRun(type);
updateStateInRegistry(type);
preRun(from, to);
machine.transit(from, to);
postRun(from, to);
updateStateInRegistry(from);
} catch (ContainerException ce) {
LOGGER
.warn("Exception when executing state transition. this is not thoroughly handled.",
......@@ -87,50 +107,44 @@ public final class LifecycleController {
}
public synchronized void blockingInit() {
run(LifecycleHandlerType.NEW); // moves to INIT
run(LifecycleHandlerType.NEW, LifecycleHandlerType.INIT); // executes INIT handler
}
public synchronized void skipInstall() {
run(LifecycleHandlerType.INIT); // moves to PRE_INSTALL
run(LifecycleHandlerType.PRE_INSTALL); // moves to INSTALL
run(LifecycleHandlerType.INSTALL); // moves to POST_INSTALL
// moves from INIT to INSTALL without running any handlers
run(LifecycleHandlerType.INIT, LifecycleHandlerType.PRE_INSTALL);
}
public synchronized void blockingInstall() {
run(LifecycleHandlerType.INIT); // moves to PRE_INSTALL
run(LifecycleHandlerType.PRE_INSTALL); // moves to INSTALL
run(LifecycleHandlerType.INIT, LifecycleHandlerType.PRE_INSTALL); // executes pre_install handler
run(LifecycleHandlerType.PRE_INSTALL, LifecycleHandlerType.INSTALL); // executes INSTALL handler
}
public synchronized void blockingConfigure() {
run(LifecycleHandlerType.INSTALL); // moves to POST_INSTALL
run(LifecycleHandlerType.POST_INSTALL); // moves to PRE_START
run(LifecycleHandlerType.INSTALL, LifecycleHandlerType.POST_INSTALL); // executes POST_INSTALL handler
run(LifecycleHandlerType.POST_INSTALL, LifecycleHandlerType.PRE_START); // executes PRE_START handler
}
public synchronized void blockingStart() throws LifecycleException {
run(LifecycleHandlerType.PRE_START); // moves to START and calls 'start handler'
StartDetectorHandler.runStartDetector(interceptor, store.getStartDetector(), ec);
// calls 'start handler' and verifies start-up via start detector
run(LifecycleHandlerType.PRE_START, LifecycleHandlerType.START);
// FIXME: establish periodic invocation of stop detector
getLogger().warn("TODO: periodically run stop detector");
run(LifecycleHandlerType.START); // moves to POST_START
// invokes POST_START handler and installs stop detector
run(LifecycleHandlerType.START, LifecycleHandlerType.POST_START);
}
public synchronized void blockingStop() {
//throw new UnsupportedOperationException(
// "not calling stop handler; this is not part of the state machine (yet).");
//machine.transit(LifecycleHandlerType.PRE_STOP);
getLogger().warn("running POST_START state!");
run(LifecycleHandlerType.POST_START);
getLogger().warn("running PRE_STOP state!");
run(LifecycleHandlerType.PRE_STOP);
run(LifecycleHandlerType.POST_START, LifecycleHandlerType.PRE_STOP);
run(LifecycleHandlerType.PRE_STOP, LifecycleHandlerType.STOP);
//FIXME: is a stop detector action required at this point?
getLogger().warn("Stopping instance, running PRE_STOP state!");
run(LifecycleHandlerType.STOP);
//machine.transit(LifecycleHandlerType.STOP);
run(LifecycleHandlerType.STOP, LifecycleHandlerType.POST_STOP);
}
public synchronized void blockingUpdatePorts(@SuppressWarnings("unused") OutPort port,
PortUpdateHandler handler, PortDiff<DownstreamAddress> diff) throws ContainerException {
boolean preprocessed = false;
......
package de.uniulm.omi.cloudiator.lance.lifecycle;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.InitHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.InstallHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PostInstallHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PostStartHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PostStopHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PreInstallHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PreStartHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.PreStopHandler;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.StopHandler;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareTransitionBuilder;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionAction;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionException;
final class LifecycleTransitionHelper {
private static final TransitionAction EMPTY_TRANSITION_ACTION = new TransitionAction() {
@Override
public void transit(Object[] params) throws TransitionException {
LifecycleController.getLogger().info("running empty transition");
}
};
static void createInitAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.NEW).
setEndState(LifecycleHandlerType.INIT).
setErrorState(LifecycleHandlerType.INIT_FAILED).
addTransitionAction(new DefaultLifecycleTransition<InitHandler>(storeParam, ctxParam, LifecycleHandlerType.INIT, InitHandler.class));
builder.buildAndRegister();
}
static void createPreInstallAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.INIT).
setEndState(LifecycleHandlerType.PRE_INSTALL).
setErrorState(LifecycleHandlerType.INSTALL_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PreInstallHandler>(storeParam, ctxParam, LifecycleHandlerType.PRE_INSTALL, PreInstallHandler.class));
builder.buildAndRegister();
}
static void createInstallAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.PRE_INSTALL).
setEndState(LifecycleHandlerType.INSTALL).
setErrorState(LifecycleHandlerType.INSTALL_FAILED).
addTransitionAction(new DefaultLifecycleTransition<InstallHandler>(storeParam, ctxParam, LifecycleHandlerType.INSTALL, InstallHandler.class));
builder.buildAndRegister();
}
static void createPostInstallAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.INSTALL).
setEndState(LifecycleHandlerType.POST_INSTALL).
setErrorState(LifecycleHandlerType.INSTALL_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PostInstallHandler>(storeParam, ctxParam, LifecycleHandlerType.POST_INSTALL, PostInstallHandler.class));
builder.buildAndRegister();
}
static void createPreStartAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.POST_INSTALL).
setEndState(LifecycleHandlerType.PRE_START).
setErrorState(LifecycleHandlerType.STARTUP_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PreStartHandler>(storeParam, ctxParam, LifecycleHandlerType.PRE_START, PreStartHandler.class));
builder.buildAndRegister();
}
static void createPostStartAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.START).
setEndState(LifecycleHandlerType.POST_START).
setErrorState(LifecycleHandlerType.STARTUP_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PostStartHandler>(storeParam, ctxParam, LifecycleHandlerType.POST_START, PostStartHandler.class));
builder.buildAndRegister();
}
static void createPreStopAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.POST_START).
setEndState(LifecycleHandlerType.PRE_STOP).
setErrorState(LifecycleHandlerType.TERMINATION_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PreStopHandler>(storeParam, ctxParam, LifecycleHandlerType.PRE_STOP, PreStopHandler.class));
builder.buildAndRegister();
}
static void createStopAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.PRE_STOP).
setEndState(LifecycleHandlerType.STOP).
setErrorState(LifecycleHandlerType.TERMINATION_FAILED).
addTransitionAction(new DefaultLifecycleTransition<StopHandler>(storeParam, ctxParam, LifecycleHandlerType.STOP, StopHandler.class));
builder.buildAndRegister();
}
static void createPostStopAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam) {
builder.setStartState(LifecycleHandlerType.STOP).
setEndState(LifecycleHandlerType.POST_STOP).
setErrorState(LifecycleHandlerType.TERMINATION_FAILED).
addTransitionAction(new DefaultLifecycleTransition<PostStopHandler>(storeParam, ctxParam, LifecycleHandlerType.POST_STOP, PostStopHandler.class));
builder.buildAndRegister();
}
public static void createSkipInstallAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder) {
builder.setStartState(LifecycleHandlerType.INIT).
setEndState(LifecycleHandlerType.INSTALL).
addTransitionAction(EMPTY_TRANSITION_ACTION).
buildAndRegister();
}
}
package de.uniulm.omi.cloudiator.lance.lifecycle;
import de.uniulm.omi.cloudiator.lance.lifecycle.handlers.StartHandler;
import de.uniulm.omi.cloudiator.lance.util.state.ErrorAwareTransitionBuilder;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionAction;
import de.uniulm.omi.cloudiator.lance.util.state.TransitionException;
final class StartTransitionAction implements TransitionAction {
private final LifecycleStore store;
private final ExecutionContext ctx;
private final LifecycleActionInterceptor interceptor;
StartTransitionAction(LifecycleStore storeParam, ExecutionContext ctxParam, LifecycleActionInterceptor interceptorParam) {
store = storeParam;
ctx = ctxParam;
interceptor = interceptorParam;
}
static void createStartAction(ErrorAwareTransitionBuilder<LifecycleHandlerType> builder, LifecycleStore storeParam, ExecutionContext ctxParam, LifecycleActionInterceptor interceptorParam) {
builder.setStartState(LifecycleHandlerType.PRE_START).
setEndState(LifecycleHandlerType.START).
setErrorState(LifecycleHandlerType.STARTUP_FAILED).
addTransitionAction(new StartTransitionAction(storeParam, ctxParam, interceptorParam));
builder.buildAndRegister();
}
@Override
public void transit(Object[] params) throws TransitionException {
StartHandler startHandler = store.getHandler(LifecycleHandlerType.START, StartHandler.class);
try {
startHandler.execute(ctx);
StartDetectorHandler.runStartDetector(interceptor, store.getStartDetector(), ctx);
} catch(LifecycleException lce) {
throw new TransitionException(lce);
}
}
}
package de.uniulm.omi.cloudiator.lance.util.state;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.uniulm.omi.cloudiator.lance.lca.container.ContainerStatus;
public final class ErrorAwareStateMachine<T extends Enum<?> & State > {
static interface TransitionHandle<S extends Enum<?> & State > {
S waitForTransitionEnd();
}
final static Logger LOGGER = LoggerFactory.getLogger(ErrorAwareStateMachine.class);
private static final Object[] DEFAULT_PARAMS = new Object[0];
// only one transition at a time.
private final ExecutorService executor = Executors.newFixedThreadPool(1);
private final Set<T> states = new HashSet<>();
private final List<ErrorAwareStateTransition<T>> transitions;
private final ErrorAwareStateMachineState<T> localState;
private final Stack<Throwable> collectedExceptions = new Stack<>();
ErrorAwareStateMachine(T init, T genericErrorState, List<T> states, List<ErrorAwareStateTransition<T>> st) {
assert init != null : "init state cannot be null";
localState = new ErrorAwareStateMachineState<>(init, genericErrorState, collectedExceptions);
this.transitions = st;
this.states.addAll(states);
}
/**
* @param startState the start state of the transition
* @param params set of application specific parameters to be passed through to the
* transition
*/
public final void transit(T fromState, T toState, Object[] params) {
synchronized(localState) {
localState.cleanTransitionExceptions();
ErrorAwareStateTransition<T> transition = findTransition(fromState, toState);
localState.validateReadyForTransition(transition);
transition.executeTransition(localState, params, executor);
}
}
public final T getState() {
synchronized(localState) {
localState.cleanTransitionExceptions();
return localState.getCurrentState();
}
}
public final boolean assertCurrentState(T checkStatus) {
// no synchronization needed here //
T currentStatus = getState();
return currentStatus == checkStatus;
}
public T waitForEndOfCurrentTransition() {
TransitionHandle<T> handle = null;
synchronized(localState){
localState.cleanTransitionExceptions();
if(!localState.hasOngoingTransition()) {
return localState.getCurrentState();
}
handle = localState.getTransitionHandle();
}
return handle.waitForTransitionEnd();
}
private ErrorAwareStateTransition<T> findTransition(T fromState, T toState) {
ErrorAwareStateTransition<T> found = null;
for(ErrorAwareStateTransition<T> t : transitions) {
if(t.isStartState(fromState) && t.hasEndState(toState)) {
if(found == null)
found = t;
else {
LOGGER.error("multiple transitions with same start and end state found. picking any.");
}
}
}
if(found != null)
return found;
throw new IllegalStateException("no transition found for startState: " + fromState + ": " + transitions);
}
public Throwable collectExceptions() {
synchronized(localState){
if(collectedExceptions.isEmpty())
throw new IllegalStateException("collectException shall only be called when something bad has actually happened.");
Throwable ret = collectedExceptions.pop();
while(!collectedExceptions.isEmpty()) {
Throwable up = collectedExceptions.pop();
ret = addAsLastCause(up, ret);
}
collectedExceptions.clear();
return ret;
}
}
private static Throwable addAsLastCause(Throwable t, Throwable cause) {
assert t != null;
Throwable x = t;
while(x.getCause() != null)
x = x.getCause();
x.initCause(cause);
return t;
}
public boolean isGenericErrorState(T stat) {
return localState.isGenericErrorState(stat);
}
}
package de.uniulm.omi.cloudiator.lance.util.state;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class ErrorAwareStateMachine<T extends Enum<?> & State > {
private static final Object[] DEFAULT_PARAMS = new Object[]{};
static interface TransitionHandle<S extends Enum<?> & State > {
S waitForTransitionEnd();
}
final static Logger LOGGER = LoggerFactory.getLogger(ErrorAwareStateMachine.class);
// only one transition at a time.
private final ExecutorService executor = Executors.newFixedThreadPool(1);
private final Set<T> states = new HashSet<>();
private final List<ErrorAwareStateTransition<T>> transitions;
private final ErrorAwareStateMachineState<T> localState;
private final Stack<Throwable> collectedExceptions = new Stack<>();
ErrorAwareStateMachine(T init, T genericErrorState, List<T> states, List<ErrorAwareStateTransition<T>> st) {
assert init != null : "init state cannot be null";
localState = new ErrorAwareStateMachineState<>(init, genericErrorState, collectedExceptions);
this.transitions = st;
this.states.addAll(states);
}
/**
* @param startState the start state of the transition
* @param params set of application specific parameters to be passed through to the
* transition
*/
public final void transit(T fromState, T toState, Object[] params) {
synchronized(localState) {
localState.cleanTransitionExceptions();
ErrorAwareStateTransition<T> transition = findTransition(fromState, toState);
localState.validateReadyForTransition(transition);
transition.executeTransition(localState, params, executor);
}
}
public final T getState() {
synchronized(localState) {
localState.cleanTransitionExceptions();
return localState.getCurrentState();
}
}
public final boolean assertCurrentState(T checkStatus) {
// no synchronization needed here //
T currentStatus = getState();
return currentStatus == checkStatus;
}
public T waitForEndOfCurrentTransition() {
TransitionHandle<T> handle = null;
synchronized(localState){
localState.cleanTransitionExceptions();
if(!localState.hasOngoingTransition()) {
return localState.getCurrentState();
}
handle = localState.getTransitionHandle();
}
return handle.waitForTransitionEnd();
}
private ErrorAwareStateTransition<T> findTransition(T fromState, T toState) {
ErrorAwareStateTransition<T> found = null;
for(ErrorAwareStateTransition<T> t : transitions) {
if(t.isStartState(fromState) && t.hasEndState(toState)) {
if(found == null)
found = t;