[Major] Changed State machine implementation

This commit is contained in:
Robert von Burg 2017-06-13 10:26:21 +02:00
parent 5471a3176b
commit 5bf7cd8b72
5 changed files with 101 additions and 114 deletions

View File

@ -15,15 +15,11 @@
*/ */
package li.strolch.model; package li.strolch.model;
import java.util.Iterator; import java.util.Set;
import java.util.Map.Entry; import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchException;
import li.strolch.model.activity.Activity; import li.strolch.model.activity.Activity;
import li.strolch.model.activity.IActivityElement;
import li.strolch.utils.dbc.DBC; import li.strolch.utils.dbc.DBC;
/** /**
@ -34,15 +30,14 @@ public enum State {
CREATED("Created"), //$NON-NLS-1$ CREATED("Created"), //$NON-NLS-1$
PLANNING("Planning"), //$NON-NLS-1$ PLANNING("Planning"), //$NON-NLS-1$
PLANNED("Planned"), //$NON-NLS-1$ PLANNED("Planned"), //$NON-NLS-1$
STARTING("Starting"), //$NON-NLS-1$
EXECUTION("Execution"), //$NON-NLS-1$ EXECUTION("Execution"), //$NON-NLS-1$
STOPPED("Stopped"), //$NON-NLS-1$
WARNING("Warning"), //$NON-NLS-1$ WARNING("Warning"), //$NON-NLS-1$
ERROR("Error"), //$NON-NLS-1$ ERROR("Error"), //$NON-NLS-1$
STOPPED("Stopped"), //$NON-NLS-1$
EXECUTED("Executed"), //$NON-NLS-1$ EXECUTED("Executed"), //$NON-NLS-1$
CLOSED("Closed"); //$NON-NLS-1$ CLOSED("Closed"); //$NON-NLS-1$
private static final Logger logger = LoggerFactory.getLogger(State.class);
private String state; private String state;
private State(String state) { private State(String state) {
@ -75,6 +70,13 @@ public enum State {
return this == EXECUTION || this == STOPPED || this == WARNING || this == ERROR; return this == EXECUTION || this == STOPPED || this == WARNING || this == ERROR;
} }
/**
* @return true if the state is {@link #ERROR} or {@link #STOPPED}
*/
public boolean inErrorPhase() {
return this == State.ERROR || this == State.STOPPED;
}
/** /**
* @return true if the state is one of {@link #STOPPED}, {@link #WARNING} or {@link #ERROR} * @return true if the state is one of {@link #STOPPED}, {@link #WARNING} or {@link #ERROR}
*/ */
@ -86,7 +88,7 @@ public enum State {
* @return true if the state is {@link #CLOSED} * @return true if the state is {@link #CLOSED}
*/ */
public boolean inClosedPhase() { public boolean inClosedPhase() {
return this == CLOSED; return this == EXECUTED || this == CLOSED;
} }
/** /**
@ -117,6 +119,20 @@ public enum State {
return this == State.EXECUTION; return this == State.EXECUTION;
} }
/**
* @return true if the state is {@link #WARNING}
*/
public boolean isInWarning() {
return this == State.WARNING;
}
/**
* @return true if the state is {@link #ERROR}
*/
public boolean isInError() {
return this == State.ERROR;
}
/** /**
* @return true if the state is {@link #EXECUTED} * @return true if the state is {@link #EXECUTED}
*/ */
@ -132,31 +148,31 @@ public enum State {
} }
/** /**
* @return true if {@link #inExecutionPhase()} but not executed and not already in warning * @return true if {@link #EXECUTION}
*/ */
public boolean canSetToWarning() { public boolean canSetToWarning() {
return inExecutionPhase(); return this == EXECUTION;
} }
/** /**
* @return true if {@link #inExecutionPhase()} but not executed and not already stopped * @return true if {@link #ERROR}
*/ */
public boolean canSetToStopped() { public boolean canSetToStopped() {
return inExecutionPhase(); return this == State.ERROR;
} }
/** /**
* @return true if {@link #inExecutionPhase()} but not executed and not already in error * @return true if {@link #STARTING} or {@link #EXECUTION} or {@link #WARNING}
*/ */
public boolean canSetToError() { public boolean canSetToError() {
return inExecutionPhase(); return this == State.STARTING || this == State.EXECUTION || this == State.WARNING;
} }
/** /**
* @return true if {@link #inExecutionPhase()} but not executed * @return true if {@link #EXECUTION} or {@link #WARNING} or {@link #STOPPED}
*/ */
public boolean canSetToExecuted() { public boolean canSetToExecuted() {
return inExecutionPhase(); return this == State.EXECUTION || this == State.WARNING || this == State.STOPPED;
} }
public static State parse(String s) { public static State parse(String s) {
@ -169,86 +185,49 @@ public enum State {
throw new StrolchException("No State for " + s); throw new StrolchException("No State for " + s);
} }
public static State max(State state1, State state2) {
return state1.ordinal() >= state2.ordinal() ? state1 : state2;
}
public static State min(State state1, State state2) {
return state1.ordinal() <= state2.ordinal() ? state1 : state2;
}
public static State getState(Activity activity) { public static State getState(Activity activity) {
Iterator<Entry<String, IActivityElement>> elementIterator = activity.elementIterator(); Set<State> states = activity.elementStream().map(e -> e.getValue().getState()).collect(Collectors.toSet());
IActivityElement first = elementIterator.next().getValue(); // if only one state
State state = first.getState(); if (states.size() == 1)
return states.iterator().next();
while (elementIterator.hasNext()) { // error
IActivityElement child = elementIterator.next().getValue(); if (states.contains(State.ERROR))
State childState = child.getState(); return State.ERROR;
// error trumps all // stopped
if (childState == State.ERROR) { if (states.contains(State.STOPPED))
state = State.ERROR; return State.STOPPED;
break;
}
// then in execution warning // warning
if (childState.inExecutionWarningPhase()) { if (states.contains(State.WARNING))
if (state.inExecutionWarningPhase()) return State.WARNING;
state = State.max(state, childState);
else
state = childState;
}
// then execution // execution
else if (childState.inExecutionPhase() || childState == State.EXECUTED) { if (states.contains(State.EXECUTION) || states.contains(State.STARTING))
if (!state.inExecutionWarningPhase()) { return State.EXECUTION;
if (state.inExecutionPhase() || state == State.EXECUTED) if (states.contains(State.EXECUTED) && (states.contains(State.CREATED) || states.contains(State.PLANNING)
state = State.min(state, childState); || states.contains(State.PLANNED)))
else return State.EXECUTION;
state = State.EXECUTION;
}
}
// then planning // executed
else if (childState.inPlanningPhase()) { if (states.contains(State.EXECUTED) && (states.contains(State.CLOSED)))
if (state.inExecutionPhase() || state == State.EXECUTED) { return State.EXECUTED;
if (!state.inExecutionWarningPhase())
state = State.EXECUTION;
} else {
if (state.inPlanningPhase())
state = State.min(state, childState);
else if ((state.inClosedPhase() || state.inCreatedPhase()) && childState.inPlanningPhase())
state = State.PLANNING;
}
}
// then created // planning
else if (childState.inCreatedPhase()) { if (states.contains(State.PLANNING))
if (state.inExecutionPhase() || state == State.EXECUTED) { return State.PLANNING;
if (!state.inExecutionWarningPhase()) if (states.contains(State.PLANNED) && (states.contains(State.CREATED) || states.contains(State.PLANNING)))
state = State.EXECUTION; return State.PLANNING;
} else {
if (state.inPlanningPhase()) {
state = State.PLANNING;
}
}
}
// then closed // planned
else if (childState.inClosedPhase()) { if (states.contains(State.PLANNED) && (states.contains(State.CLOSED)))
state = State.min(state, childState); return State.PLANNED;
}
// should never occur // should never happen, unless new state is introduced
else { throw new IllegalStateException("Unhandled situation with states: "
logger.warn("Else case for getState() child: " + child.getLocator() + " childState: " + childState + states.stream().map(e -> e.state).collect(Collectors.joining(", ")));
+ " state: " + state);
}
}
return state;
} }
} }

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream;
import li.strolch.exception.StrolchException; import li.strolch.exception.StrolchException;
import li.strolch.exception.StrolchModelException; import li.strolch.exception.StrolchModelException;
@ -324,6 +325,15 @@ public class Activity extends AbstractStrolchRootElement
return this.elements.entrySet().iterator(); return this.elements.entrySet().iterator();
} }
/**
* @return the stream for entries, which include the id as key and the {@link IActivityElement} as value
*/
public Stream<Entry<String, IActivityElement>> elementStream() {
if (this.elements == null)
return Collections.<String, IActivityElement> emptyMap().entrySet().stream();
return this.elements.entrySet().stream();
}
@Override @Override
public Long getStart() { public Long getStart() {
Long start = Long.MAX_VALUE; Long start = Long.MAX_VALUE;

View File

@ -312,8 +312,8 @@ public class StateTest {
this.subAction.setState(State.STOPPED); this.subAction.setState(State.STOPPED);
this.subSubAction1.setState(State.WARNING); this.subSubAction1.setState(State.WARNING);
assertEquals(State.WARNING, this.activity.getState()); assertEquals(State.STOPPED, this.activity.getState());
assertEquals(State.WARNING, this.subActivity.getState()); assertEquals(State.STOPPED, this.subActivity.getState());
assertEquals(State.WARNING, this.subSubActivity.getState()); assertEquals(State.WARNING, this.subSubActivity.getState());
this.action.setState(State.STOPPED); this.action.setState(State.STOPPED);
@ -397,8 +397,8 @@ public class StateTest {
this.subSubAction1.setState(State.PLANNED); this.subSubAction1.setState(State.PLANNED);
this.subSubAction2.setState(State.PLANNED); this.subSubAction2.setState(State.PLANNED);
assertEquals(State.PLANNING, this.activity.getState()); assertEquals(State.PLANNED, this.activity.getState());
assertEquals(State.PLANNING, this.subActivity.getState()); assertEquals(State.PLANNED, this.subActivity.getState());
assertEquals(State.PLANNED, this.subSubActivity.getState()); assertEquals(State.PLANNED, this.subSubActivity.getState());
} }
} }

View File

@ -117,15 +117,23 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
* @param action * @param action
* the action to set to warning state * the action to set to warning state
*/ */
public void toWarning(Action action) { public abstract void toWarning(Action action);
action.setState(State.WARNING); protected void setActionState(Action action, State state) {
action.setState(state);
UpdateActivityCommand command = new UpdateActivityCommand(getContainer(), tx()); UpdateActivityCommand command = new UpdateActivityCommand(getContainer(), tx());
command.setActivity(action.getRootElement()); command.setActivity(action.getRootElement());
command.doCommand(); command.doCommand();
logger.warn("Action " + action.getLocator() + " is now in WARNING!"); String msg = "Action " + action.getLocator() + " is now in state " + state;
if (state == State.ERROR)
logger.error(msg);
else if (state == State.STOPPED)
logger.warn(msg);
else
logger.info(msg);
} }
/** /**
@ -177,5 +185,4 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
protected void runAsAgent(PrivilegedRunnable runnable) throws PrivilegeException { protected void runAsAgent(PrivilegedRunnable runnable) throws PrivilegeException {
getContainer().getPrivilegeHandler().runAs(StrolchConstants.SYSTEM_USER_AGENT, runnable); getContainer().getPrivilegeHandler().runAs(StrolchConstants.SYSTEM_USER_AGENT, runnable);
} }
} }

View File

@ -1,7 +1,6 @@
package li.strolch.execution.policy; package li.strolch.execution.policy;
import li.strolch.agent.api.ComponentContainer; import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.UpdateActivityCommand;
import li.strolch.model.State; import li.strolch.model.State;
import li.strolch.model.activity.Action; import li.strolch.model.activity.Action;
import li.strolch.persistence.api.StrolchTransaction; import li.strolch.persistence.api.StrolchTransaction;
@ -19,11 +18,20 @@ public class SimpleExecution extends ExecutionPolicy {
super(container, tx); super(container, tx);
} }
protected void toStarting(Action action) {
setActionState(action, State.STARTING);
}
@Override @Override
public void toExecution(Action action) { public void toExecution(Action action) {
setActionState(action, State.EXECUTION); setActionState(action, State.EXECUTION);
} }
@Override
public void toWarning(Action action) {
setActionState(action, State.WARNING);
}
@Override @Override
public void toExecuted(Action action) { public void toExecuted(Action action) {
setActionState(action, State.EXECUTED); setActionState(action, State.EXECUTED);
@ -41,23 +49,6 @@ public class SimpleExecution extends ExecutionPolicy {
setActionState(action, State.ERROR); setActionState(action, State.ERROR);
} }
protected void setActionState(Action action, State state) {
action.setState(state);
UpdateActivityCommand command = new UpdateActivityCommand(getContainer(), tx());
command.setActivity(action.getRootElement());
command.doCommand();
String msg = "Action " + action.getLocator() + " is now in state " + state;
if (state == State.ERROR)
logger.error(msg);
else if (state == State.STOPPED)
logger.warn(msg);
else
logger.info(msg);
}
@Override @Override
public void undo() { public void undo() {
logger.error("Can not undo execution policy " + getClass()); logger.error("Can not undo execution policy " + getClass());