222 lines
6.8 KiB
Java
222 lines
6.8 KiB
Java
package li.strolch.execution.command;
|
|
|
|
import static li.strolch.model.State.EXECUTION;
|
|
import static li.strolch.model.StrolchModelConstants.PolicyConstants.PARAM_ATOMIC_PARALLEL_EXECUTION;
|
|
|
|
import java.util.Iterator;
|
|
import java.util.Map.Entry;
|
|
|
|
import li.strolch.execution.Controller;
|
|
import li.strolch.execution.policy.ConfirmationPolicy;
|
|
import li.strolch.execution.policy.ExecutionPolicy;
|
|
import li.strolch.model.State;
|
|
import li.strolch.model.activity.Action;
|
|
import li.strolch.model.activity.Activity;
|
|
import li.strolch.model.activity.IActivityElement;
|
|
import li.strolch.model.activity.TimeOrderingVisitor;
|
|
import li.strolch.model.parameter.BooleanParameter;
|
|
import li.strolch.model.visitor.IActivityElementVisitor;
|
|
import li.strolch.persistence.api.StrolchTransaction;
|
|
import li.strolch.utils.dbc.DBC;
|
|
|
|
public class PlanAndExecuteActivityCommand extends BasePlanningAndExecutionCommand
|
|
implements TimeOrderingVisitor, IActivityElementVisitor<Void> {
|
|
|
|
private Controller controller;
|
|
private boolean needsRetriggerOfExecution;
|
|
|
|
public PlanAndExecuteActivityCommand(StrolchTransaction tx) {
|
|
super(tx);
|
|
}
|
|
|
|
public void setController(Controller controller) {
|
|
this.controller = controller;
|
|
}
|
|
|
|
private ExecutionPolicy getExecutionPolicy(Action action) {
|
|
return this.controller.refreshExecutionPolicy(tx(), action);
|
|
}
|
|
|
|
public boolean needsRetriggerOfExecution() {
|
|
return this.needsRetriggerOfExecution;
|
|
}
|
|
|
|
@Override
|
|
public void validate() {
|
|
DBC.PRE.assertNotNull("controller can not be null!", this.controller);
|
|
}
|
|
|
|
@Override
|
|
public void doCommand() {
|
|
Activity activity = this.controller.getActivity();
|
|
State currentState = activity.getState();
|
|
activity.accept(this);
|
|
updateOrderState(tx(), activity, currentState, activity.getState());
|
|
}
|
|
|
|
@Override
|
|
public Void visitActivity(Activity activity) {
|
|
activity.getTimeOrdering().accept(this, activity);
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public Void visitAction(Action action) {
|
|
planAndExecute(action);
|
|
return null;
|
|
}
|
|
|
|
private void planAndExecute(Action action) {
|
|
if (planAction(action))
|
|
executeAction(action);
|
|
}
|
|
|
|
private void executeAction(Action action) {
|
|
State currentState = action.getState();
|
|
if (currentState.compareTo(State.EXECUTED) >= 0 || currentState == EXECUTION)
|
|
return;
|
|
if (currentState == State.CREATED || currentState == State.PLANNING)
|
|
throw new IllegalStateException("Action " + action.getLocator() + " is in illegal state " + currentState);
|
|
|
|
if (!currentState.canSetToExecution()) {
|
|
logger.warn("Action " + action.getLocator() + " can not be executed with state " + currentState);
|
|
return;
|
|
}
|
|
|
|
tx().lock(action.getResourceLocator());
|
|
|
|
ConfirmationPolicy confirmationPolicy = getConfirmationPolicy(action);
|
|
ExecutionPolicy executionPolicy = getExecutionPolicy(action);
|
|
|
|
// we catch all exceptions because we can't undo, thus need to set the state to ERROR in this case
|
|
// this is only required because we execute actions in same TX as we set to-executed any previous actions
|
|
try {
|
|
|
|
if (executionPolicy.isStopped()) {
|
|
this.controller.removeExecutionPolicy(action);
|
|
executionPolicy = getExecutionPolicy(action);
|
|
}
|
|
|
|
if (!executionPolicy.isExecutable(action)) {
|
|
logger.info("Action " + action.getLocator() + " is not yet executable.");
|
|
return;
|
|
}
|
|
|
|
logger.info("Action " + action.getLocator() + " is now being executed...");
|
|
|
|
executionPolicy.toExecution(action);
|
|
confirmationPolicy.doConfirmation(action);
|
|
|
|
if (action.getState() == State.EXECUTED)
|
|
this.needsRetriggerOfExecution = true;
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Failed to set " + action.getLocator() + " to execution due to " + e.getMessage(), e);
|
|
|
|
try {
|
|
executionPolicy.stop();
|
|
} catch (Exception ex) {
|
|
logger.error("Failed to stop execution policy for " + action.getLocator(), e);
|
|
}
|
|
|
|
action.setState(State.ERROR);
|
|
tx().update(action.getRootElement());
|
|
|
|
confirmationPolicy.toError(action);
|
|
}
|
|
}
|
|
|
|
private boolean planActivity(Activity activity) {
|
|
PlanActivityCommand planning = new PlanActivityCommand(tx());
|
|
planning.setActivity(activity);
|
|
planning.validateAndDoCommand();
|
|
return activity.getState().compareTo(State.PLANNED) >= 0;
|
|
}
|
|
|
|
private void unplanActivity(Activity activity) {
|
|
UnplanActivityCommand planning = new UnplanActivityCommand(tx());
|
|
planning.setActivity(activity);
|
|
planning.validateAndDoCommand();
|
|
}
|
|
|
|
private boolean planAction(Action action) {
|
|
State currentState = action.getState();
|
|
if (currentState.compareTo(State.PLANNED) >= 0)
|
|
return true;
|
|
|
|
getPlanningPolicy(action).plan(action);
|
|
if (action.getState().compareTo(State.PLANNED) >= 0) {
|
|
// planning is complete, so we can now confirm it
|
|
getConfirmationPolicy(action).toPlanned(action);
|
|
return true;
|
|
}
|
|
|
|
if (currentState != action.getState() && action.isResourceDefined())
|
|
getConfirmationPolicy(action).doConfirmation(action);
|
|
logger.info("Failed to plan action " + action.getLocator() + ", thus no execution possible");
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public void visitSeries(Activity activity) {
|
|
if (activity.inClosedPhase())
|
|
return;
|
|
|
|
Iterator<Entry<String, IActivityElement>> iter = activity.elementIterator();
|
|
while (iter.hasNext()) {
|
|
IActivityElement element = iter.next().getValue();
|
|
State state = element.getState();
|
|
if (element.inClosedPhase())
|
|
continue;
|
|
|
|
// in series, we can never have two Actions in execution, so if we found the action in execution, we stop
|
|
if (element instanceof Action && state.inExecutionPhase() && state != State.STOPPED)
|
|
break;
|
|
|
|
// in series, we stop when the first action is set to execution
|
|
element.accept(this);
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void visitParallel(Activity activity) {
|
|
if (activity.inClosedPhase())
|
|
return;
|
|
|
|
BooleanParameter atomicExecutionP = activity.findParameter(PARAM_ATOMIC_PARALLEL_EXECUTION, false);
|
|
boolean atomicExecution = atomicExecutionP != null && atomicExecutionP.getValue();
|
|
|
|
if (atomicExecution) {
|
|
if (!planActivity(activity)) {
|
|
unplanActivity(activity);
|
|
return;
|
|
}
|
|
|
|
// stop execution if at least one action is not executable from this entire tree
|
|
boolean anyActionNotExecutable = activity.streamActionsDeep().anyMatch(this::isActionExecutable);
|
|
if (anyActionNotExecutable)
|
|
return;
|
|
}
|
|
|
|
Iterator<Entry<String, IActivityElement>> iter = activity.elementIterator();
|
|
while (iter.hasNext()) {
|
|
IActivityElement element = iter.next().getValue();
|
|
if (element.inClosedPhase())
|
|
continue;
|
|
|
|
// in parallel, we execute all the actions in the activity
|
|
element.accept(this);
|
|
}
|
|
}
|
|
|
|
private boolean isActionExecutable(Action action) {
|
|
if (!action.getState().canSetToExecution())
|
|
return false;
|
|
boolean executable = getExecutionPolicy(action).isExecutable(action);
|
|
if (!executable)
|
|
logger.info("Action " + action.getLocator() + " is not yet executable!");
|
|
return !executable;
|
|
}
|
|
}
|