Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/main/java/io/iworkflow/core/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@
import io.iworkflow.gen.models.WorkflowGetSearchAttributesResponse;
import io.iworkflow.gen.models.WorkflowSearchRequest;
import io.iworkflow.gen.models.WorkflowSearchResponse;
import io.iworkflow.gen.models.WorkflowStateOptions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static io.iworkflow.core.WorkflowState.shouldSkipWaitUntil;

public class Client {
private final Registry registry;

Expand Down Expand Up @@ -159,8 +162,16 @@ private String doStartWorkflow(
throw new WorkflowDefinitionException(String.format("input cannot be assigned to the starting state, input type: %s, starting state input type: %s", input.getClass(), registeredInputType));
}

if (stateDef.getWorkflowState().getStateOptions() != null) {
unregisterWorkflowOptions.startStateOptions(stateDef.getWorkflowState().getStateOptions());
WorkflowStateOptions stateOptions = stateDef.getWorkflowState().getStateOptions();
if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
if (stateOptions == null) {
stateOptions = new WorkflowStateOptions().skipWaitUntil(true);
} else {
stateOptions.skipWaitUntil(true);
}
}
if (stateOptions != null) {
unregisterWorkflowOptions.startStateOptions(stateOptions);
}
if (options != null) {
unregisterWorkflowOptions.workflowIdReusePolicy(options.getWorkflowIdReusePolicy());
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/io/iworkflow/core/ObjectWorkflow.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@

/**
* This is the interface to define an object workflow definition.
* Most of the time, the implementation only needs to return static value for each method.
* <p>
* For a dynamic workflow definition, the implementation can return different values based on different constructor inputs.
* To invokes/interact with a dynamic workflows, applications may need to use {@link UnregisteredClient} instead of {@link Client}
* ObjectWorkflow is a top level concept in iWF. Any object that is long-lasting(at least a few seconds) can be modeled as an "ObjectWorkflow".
*/
public interface ObjectWorkflow {
/**
Expand Down
37 changes: 33 additions & 4 deletions src/main/java/io/iworkflow/core/WorkflowState.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import io.iworkflow.core.persistence.Persistence;
import io.iworkflow.gen.models.WorkflowStateOptions;

import java.lang.reflect.Method;

public interface WorkflowState<I> {

/**
Expand All @@ -15,7 +17,13 @@ public interface WorkflowState<I> {
Class<I> getInputType();

/**
* Implement this method to execute the commands set up condition for the {@link #execute} API
* Optionally implement this method to set up condition for the state.
* If implemented, this will be the first API invoked when state started.
* Then the state will be waiting until the requested commands are completed.
* If not implemented, the state will invoke the {@link #execute} directly
* <p>
* The condition is setup using commands. There are three types commands in a {@link CommandRequest}: signal, timer and interStateChannel;
* Also with three types of {@link io.iworkflow.gen.models.CommandWaitingType}
*
* @param context the context info of this API invocation, like workflow start time, workflowId, etc
* @param input the state input which is deserialized by {@link ObjectEncoder} with {@link #getInputType}
Expand All @@ -28,13 +36,20 @@ public interface WorkflowState<I> {
* Note that any write API will be recorded to server after the whole start API response is accepted.
* @return the requested commands for this step
*/
CommandRequest waitUntil(
default CommandRequest waitUntil(
final Context context, I input,
final Persistence persistence,
final Communication communication);
final Communication communication) {
/*
* leaving this method with default implementation means the state doesn't have any condition for setup.
* iWF will omit the waitUntil step and invoke the {@link #execute} API directly
*/
throw new IllegalStateException("this exception will never be thrown.");
}

/**
* Implement this method to execute the state business, when requested commands are ready
* Implement this method to execute the state business, when requested commands are ready if {@link #waitUntil} is implemented
* If {@link #waitUntil} is not implemented, the state will invoke this API directly
*
* @param context the context info of this API invocation, like workflow start time, workflowId, etc
* @param input the state input which is deserialized by {@link ObjectEncoder} with {@link #getInputType}
Expand Down Expand Up @@ -82,6 +97,20 @@ default String getStateId() {
default WorkflowStateOptions getStateOptions() {
return null;
}

static boolean shouldSkipWaitUntil(final WorkflowState state) {
final Class<? extends WorkflowState> stateClass = state.getClass();
final Method waitUntilMethod;
try {
waitUntilMethod = stateClass.getMethod("waitUntil", Context.class, Object.class, Persistence.class, Communication.class);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(e);
}
if (waitUntilMethod.getDeclaringClass().equals(WorkflowState.class)) {
return true;
}
return false;
}
}


14 changes: 11 additions & 3 deletions src/main/java/io/iworkflow/core/mapper/StateMovementMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.iworkflow.gen.models.WorkflowStateOptions;

import static io.iworkflow.core.StateMovement.RESERVED_STATE_ID_PREFIX;
import static io.iworkflow.core.WorkflowState.shouldSkipWaitUntil;

public class StateMovementMapper {

Expand All @@ -17,9 +18,16 @@ public static StateMovement toGenerated(io.iworkflow.core.StateMovement stateMov
.stateInput(objectEncoder.encode(input));
if (!stateMovement.getStateId().startsWith(RESERVED_STATE_ID_PREFIX)) {
final StateDef stateDef = registry.getWorkflowState(workflowType, stateMovement.getStateId());
final WorkflowStateOptions options = stateDef.getWorkflowState().getStateOptions();
if (options != null) {
movement.stateOptions(options);
WorkflowStateOptions stateOptions = stateDef.getWorkflowState().getStateOptions();
if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
if (stateOptions == null) {
stateOptions = new WorkflowStateOptions().skipWaitUntil(true);
} else {
stateOptions.skipWaitUntil(true);
}
}
if (stateOptions != null) {
movement.stateOptions(stateOptions);
}
}
return movement;
Expand Down
39 changes: 39 additions & 0 deletions src/test/java/io/iworkflow/integ/SkipWaitUntilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package io.iworkflow.integ;

import io.iworkflow.core.Client;
import io.iworkflow.core.ClientOptions;
import io.iworkflow.core.ImmutableWorkflowOptions;
import io.iworkflow.core.WorkflowOptions;
import io.iworkflow.gen.models.WorkflowConfig;
import io.iworkflow.gen.models.WorkflowIDReusePolicy;
import io.iworkflow.integ.basic.SkipWaitUntilWorkflow;
import io.iworkflow.spring.TestSingletonWorkerService;
import io.iworkflow.spring.controller.WorkflowRegistry;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.util.concurrent.ExecutionException;

public class SkipWaitUntilTest {

@BeforeEach
public void setup() throws ExecutionException, InterruptedException {
TestSingletonWorkerService.startWorkerIfNotUp();
}

@Test
public void testSkipWaitUntil() throws InterruptedException {
final Client client = new Client(WorkflowRegistry.registry, ClientOptions.localDefault);
final String wfId = "testSkipWaitUntil-" + System.currentTimeMillis() / 1000;
final WorkflowOptions startOptions = ImmutableWorkflowOptions.builder()
.workflowIdReusePolicy(WorkflowIDReusePolicy.REJECT_DUPLICATE)
.workflowConfigOverride(new WorkflowConfig().continueAsNewThreshold(1))
.build();
final int input = 0;
client.startWorkflow(SkipWaitUntilWorkflow.class, wfId, 10, input, startOptions);
// wait for workflow to finish
final Integer output = client.getSimpleWorkflowResultWithWait(Integer.class, wfId);
Assertions.assertEquals(input + 2, output);
}
}
22 changes: 22 additions & 0 deletions src/test/java/io/iworkflow/integ/basic/SkipWaitUntilState1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.iworkflow.integ.basic;

import io.iworkflow.core.Context;
import io.iworkflow.core.StateDecision;
import io.iworkflow.core.WorkflowState;
import io.iworkflow.core.command.CommandResults;
import io.iworkflow.core.communication.Communication;
import io.iworkflow.core.persistence.Persistence;

public class SkipWaitUntilState1 implements WorkflowState<Integer> {

@Override
public Class<Integer> getInputType() {
return Integer.class;
}

@Override
public StateDecision execute(final Context context, final Integer input, final CommandResults commandResults, Persistence persistence, final Communication communication) {
final int output = input + 1;
return StateDecision.singleNextState(SkipWaitUntilState2.class, output);
}
}
22 changes: 22 additions & 0 deletions src/test/java/io/iworkflow/integ/basic/SkipWaitUntilState2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package io.iworkflow.integ.basic;

import io.iworkflow.core.Context;
import io.iworkflow.core.StateDecision;
import io.iworkflow.core.WorkflowState;
import io.iworkflow.core.command.CommandResults;
import io.iworkflow.core.communication.Communication;
import io.iworkflow.core.persistence.Persistence;

public class SkipWaitUntilState2 implements WorkflowState<Integer> {

@Override
public Class<Integer> getInputType() {
return Integer.class;
}

@Override
public StateDecision execute(final Context context, final Integer input, final CommandResults commandResults, Persistence persistence, final Communication communication) {
final int output = input + 1;
return StateDecision.gracefulCompleteWorkflow(output);
}
}
20 changes: 20 additions & 0 deletions src/test/java/io/iworkflow/integ/basic/SkipWaitUntilWorkflow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.iworkflow.integ.basic;

import io.iworkflow.core.ObjectWorkflow;
import io.iworkflow.core.StateDef;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Component
public class SkipWaitUntilWorkflow implements ObjectWorkflow {

@Override
public List<StateDef> getWorkflowStates() {
return Arrays.asList(
StateDef.startingState(new SkipWaitUntilState1()),
StateDef.nonStartingState(new SkipWaitUntilState2())
);
}
}