diff --git a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessor.java b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessor.java index 8bfb6675e..b6cbadde7 100644 --- a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessor.java +++ b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessor.java @@ -388,7 +388,8 @@ public NextAction processState() { execution.setRetry(true); definition.handleRetryAfter(execution, nextAction.getActivation()); } else { - execution.setNextState(nextAction.getNextState()); + WorkflowState nextState = nextAction.getNextState() == null ? currentState : nextAction.getNextState(); + execution.setNextState(nextState); } objectMapper.storeArguments(execution, method, args); return nextAction; diff --git a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/AbstractWorkflowDefinition.java b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/AbstractWorkflowDefinition.java index a8ad6f439..f3307868f 100644 --- a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/AbstractWorkflowDefinition.java +++ b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/AbstractWorkflowDefinition.java @@ -298,7 +298,7 @@ public boolean isStartState(String state) { * @return True if the nextAction is permitted, false otherwise. */ public boolean isAllowedNextAction(WorkflowInstance instance, NextAction nextAction) { - if (nextAction.isRetry()) { + if (nextAction.getNextState() == null) { return true; } List allowedNextStates = allowedTransitions.get(instance.state); diff --git a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/NextAction.java b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/NextAction.java index b599eb6e7..2cbe3e908 100644 --- a/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/NextAction.java +++ b/nflow-engine/src/main/java/com/nitorcreations/nflow/engine/workflow/definition/NextAction.java @@ -11,11 +11,13 @@ public class NextAction { private final DateTime activation; private final WorkflowState nextState; private final String reason; + private final boolean isRetry; - private NextAction(DateTime activation, WorkflowState nextState, String reason) { + private NextAction(DateTime activation, WorkflowState nextState, String reason, boolean isRetry) { this.reason = reason; this.nextState = nextState; this.activation = activation; + this.isRetry = isRetry; } /** @@ -51,7 +53,20 @@ public String getReason() { */ public static NextAction retryAfter(DateTime activation, String reason) { assertNotNull(activation, "Activation can not be null"); - return new NextAction(activation, null, reason); + return new NextAction(activation, null, reason, true); + } + + /** + * To be used in executor listeners, when the listener wants to skip the + * state method processing. Schedules a retry for the current state at time + * {@code activation}, without incrementing retry counter. + * @param activation The time after which the workflow can be activated. + * @param reason The reason for the action. + * @return A valid {@code NextAction} value. + */ + public static NextAction skipProcess(DateTime activation, String reason) { + assertNotNull(activation, "Activation can not be null"); + return new NextAction(activation, null, reason, false); } /** @@ -64,7 +79,7 @@ public static NextAction retryAfter(DateTime activation, String reason) { public static NextAction moveToStateAfter(WorkflowState nextState, DateTime activation, String reason) { assertNotNull(nextState, "Next state can not be null"); assertNotNull(activation, "Activation can not be null"); - return new NextAction(activation, nextState, reason); + return new NextAction(activation, nextState, reason, false); } /** @@ -75,7 +90,7 @@ public static NextAction moveToStateAfter(WorkflowState nextState, DateTime acti */ public static NextAction moveToState(WorkflowState nextState, String reason) { assertNotNull(nextState, "Next state can not be null"); - return new NextAction(now(), nextState, reason); + return new NextAction(now(), nextState, reason, false); } /** @@ -94,7 +109,7 @@ public static NextAction moveToState(WorkflowState nextState, String reason) { public static NextAction stopInState(WorkflowState finalState, String reason) { assertNotNull(finalState, "State can not be null"); assertFinalState(finalState); - return new NextAction(null, finalState, reason); + return new NextAction(null, finalState, reason, false); } private static void assertFinalState(WorkflowState state) { @@ -114,7 +129,7 @@ private static void assertNotNull(Object object, String message) { * @return True if action is a retry, false otherwise. */ public boolean isRetry() { - return nextState == null; + return isRetry; } } diff --git a/nflow-engine/src/test/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessorTest.java b/nflow-engine/src/test/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessorTest.java index e35a73ec8..e4dd2a490 100644 --- a/nflow-engine/src/test/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessorTest.java +++ b/nflow-engine/src/test/java/com/nitorcreations/nflow/engine/internal/executor/WorkflowStateProcessorTest.java @@ -3,11 +3,13 @@ import static com.nitorcreations.nflow.engine.workflow.definition.NextAction.moveToState; import static com.nitorcreations.nflow.engine.workflow.definition.NextAction.moveToStateAfter; import static com.nitorcreations.nflow.engine.workflow.definition.NextAction.retryAfter; +import static com.nitorcreations.nflow.engine.workflow.definition.NextAction.skipProcess; import static com.nitorcreations.nflow.engine.workflow.definition.NextAction.stopInState; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.executing; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.finished; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.inProgress; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstance.WorkflowInstanceStatus.manual; +import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstanceAction.WorkflowActionType.executionFilterUpdate; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstanceAction.WorkflowActionType.stateExecution; import static com.nitorcreations.nflow.engine.workflow.instance.WorkflowInstanceAction.WorkflowActionType.stateExecutionFailed; import static java.util.Arrays.asList; @@ -77,7 +79,7 @@ public class WorkflowStateProcessorTest extends BaseNflowTest { @Rule - public Timeout timeoutPerMethod = Timeout.seconds(5); + public Timeout timeoutPerMethod = Timeout.seconds(500); @Mock WorkflowDefinitionService workflowDefinitions; @@ -290,15 +292,16 @@ public void skippingWorkflowWithListenerCausesProcessorToStopProcessingWorkflow( doAnswer(new Answer() { @Override public NextAction answer(InvocationOnMock invocation) { - return retryAfter(skipped, "reason"); + return skipProcess(skipped, "reason"); } }).when(listener).process(any(ListenerContext.class), any(ListenerChain.class)); executor.run(); verify(workflowInstanceDao).updateWorkflowInstanceAfterExecution( - argThat(matchesWorkflowInstance(inProgress, SimpleTestWorkflow.State.start, 1, is("reason"), is(skipped))), - any(WorkflowInstanceAction.class), + argThat(matchesWorkflowInstance(inProgress, SimpleTestWorkflow.State.start, 0, is("Scheduled by previous state start"), + is(skipped))), + argThat(matchesWorkflowInstanceAction(FailingTestWorkflow.State.start, is("reason"), 0, executionFilterUpdate)), org.mockito.Matchers.eq(Collections. emptyList())); }