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

View File

@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Stream;
import li.strolch.exception.StrolchException;
import li.strolch.exception.StrolchModelException;
@ -324,6 +325,15 @@ public class Activity extends AbstractStrolchRootElement
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
public Long getStart() {
Long start = Long.MAX_VALUE;

View File

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

View File

@ -117,15 +117,23 @@ public abstract class ExecutionPolicy extends StrolchPolicy {
* @param action
* 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());
command.setActivity(action.getRootElement());
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 {
getContainer().getPrivilegeHandler().runAs(StrolchConstants.SYSTEM_USER_AGENT, runnable);
}
}

View File

@ -1,7 +1,6 @@
package li.strolch.execution.policy;
import li.strolch.agent.api.ComponentContainer;
import li.strolch.command.UpdateActivityCommand;
import li.strolch.model.State;
import li.strolch.model.activity.Action;
import li.strolch.persistence.api.StrolchTransaction;
@ -19,11 +18,20 @@ public class SimpleExecution extends ExecutionPolicy {
super(container, tx);
}
protected void toStarting(Action action) {
setActionState(action, State.STARTING);
}
@Override
public void toExecution(Action action) {
setActionState(action, State.EXECUTION);
}
@Override
public void toWarning(Action action) {
setActionState(action, State.WARNING);
}
@Override
public void toExecuted(Action action) {
setActionState(action, State.EXECUTED);
@ -41,23 +49,6 @@ public class SimpleExecution extends ExecutionPolicy {
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
public void undo() {
logger.error("Can not undo execution policy " + getClass());