Polish tasks example
This commit is contained in:
@@ -500,22 +500,25 @@ all extended state variables(T1, T2 and T3) are TRUE.
|
||||
include::samples/demo/tasks/Application.java[tags=snippetAC]
|
||||
----
|
||||
|
||||
Actions below will simply send event to a state machine to request
|
||||
next step which would be either fallback or continue back to ready.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/tasks/Application.java[tags=snippetAD]
|
||||
----
|
||||
|
||||
Currently default region execution is synchronous but it can be
|
||||
changed to asynchronous by changing `TaskExecutor`. Task will simulate
|
||||
work by sleeping 2 seconds so you'll able to see how actions in
|
||||
regions are executed parallel.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/tasks/Application.java[tags=snippetAE]
|
||||
----
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/tasks/Application.java[tags=snippetAF]
|
||||
----
|
||||
|
||||
Lets see an example how this state machine actually works.
|
||||
Lets see an examples how this state machine actually works.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
@@ -525,12 +528,44 @@ Entry state READY
|
||||
|
||||
sm>tasks run
|
||||
Entry state TASKS
|
||||
run task on T1
|
||||
run task on T2
|
||||
run task on T3
|
||||
run task on T1 done
|
||||
run task on T2
|
||||
run task on T1
|
||||
run task on T2 done
|
||||
run task on T1 done
|
||||
run task on T3 done
|
||||
Entry state T2
|
||||
Entry state T3
|
||||
Entry state T1
|
||||
Entry state T1E
|
||||
Entry state T2E
|
||||
Entry state T3E
|
||||
Exit state TASKS
|
||||
Entry state JOIN
|
||||
Exit state JOIN
|
||||
Entry state READY
|
||||
----
|
||||
|
||||
In above we can execute tasks multiple times.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
sm>tasks list
|
||||
Tasks {T1=true, T3=true, T2=true}
|
||||
|
||||
sm>tasks fail T1
|
||||
|
||||
sm>tasks list
|
||||
Tasks {T1=false, T3=true, T2=true}
|
||||
|
||||
sm>tasks run
|
||||
Entry state TASKS
|
||||
run task on T1
|
||||
run task on T3
|
||||
run task on T2
|
||||
run task on T1 done
|
||||
run task on T3 done
|
||||
run task on T2 done
|
||||
Entry state T1
|
||||
Entry state T3
|
||||
Entry state T2
|
||||
@@ -540,11 +575,55 @@ Entry state T3E
|
||||
Exit state TASKS
|
||||
Entry state JOIN
|
||||
Exit state JOIN
|
||||
Entry state ERROR
|
||||
Entry state AUTOMATIC
|
||||
Exit state AUTOMATIC
|
||||
Exit state ERROR
|
||||
Entry state READY
|
||||
|
||||
sm>
|
||||
----
|
||||
|
||||
In above, if we simulate failure for task T1, it is fixed
|
||||
automatically.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
sm>tasks list
|
||||
Tasks {T1=true, T3=true, T2=true}
|
||||
|
||||
sm>tasks fail T2
|
||||
|
||||
sm>tasks run
|
||||
Entry state TASKS
|
||||
run task on T2
|
||||
run task on T1
|
||||
run task on T3
|
||||
run task on T2 done
|
||||
run task on T1 done
|
||||
run task on T3 done
|
||||
Entry state T2
|
||||
Entry state T1
|
||||
Entry state T3
|
||||
Entry state T1E
|
||||
Entry state T2E
|
||||
Entry state T3E
|
||||
Exit state TASKS
|
||||
Entry state JOIN
|
||||
Exit state JOIN
|
||||
Entry state ERROR
|
||||
Entry state AUTOMATIC
|
||||
Exit state AUTOMATIC
|
||||
Entry state MANUAL
|
||||
|
||||
sm>tasks fix
|
||||
Exit state MANUAL
|
||||
Exit state ERROR
|
||||
Entry state READY
|
||||
----
|
||||
|
||||
In above if we simulate failure for either task T2 or T3, state
|
||||
machine goes to MANUAL state where problem needs to be fixed manually
|
||||
before we're able to go back to READY state.
|
||||
|
||||
== Washer
|
||||
|
||||
Washer is a sample demonstrating a use of a history state to recover a
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
@Configuration
|
||||
public class Application {
|
||||
@@ -122,7 +123,6 @@ public class Application {
|
||||
.withInternal()
|
||||
.source(States.MANUAL)
|
||||
.action(fixAction())
|
||||
.state(States.ERROR)
|
||||
.event(Events.FIX);
|
||||
}
|
||||
//end::snippetAB[]
|
||||
@@ -135,8 +135,9 @@ public class Application {
|
||||
@Override
|
||||
public boolean evaluate(StateContext<States, Events> context) {
|
||||
Map<Object, Object> variables = context.getExtendedState().getVariables();
|
||||
return !(variables.get("T1").equals(true) && variables.get("T2").equals(true) && variables
|
||||
.get("T3").equals(true));
|
||||
return !(ObjectUtils.nullSafeEquals(variables.get("T1"), true)
|
||||
&& ObjectUtils.nullSafeEquals(variables.get("T2"), true)
|
||||
&& ObjectUtils.nullSafeEquals(variables.get("T3"), true));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -150,7 +151,9 @@ public class Application {
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Map<Object, Object> variables = context.getExtendedState().getVariables();
|
||||
if (variables.get("T1").equals(true)) {
|
||||
if (ObjectUtils.nullSafeEquals(variables.get("T1"), true)
|
||||
&& ObjectUtils.nullSafeEquals(variables.get("T2"), true)
|
||||
&& ObjectUtils.nullSafeEquals(variables.get("T3"), true)) {
|
||||
context.getStateMachine().sendEvent(Events.CONTINUE);
|
||||
} else {
|
||||
context.getStateMachine().sendEvent(Events.FALLBACK);
|
||||
@@ -166,30 +169,28 @@ public class Application {
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Map<Object, Object> variables = context.getExtendedState().getVariables();
|
||||
if (variables.get("T1").equals(true) && variables.get("T2").equals(true)
|
||||
&& variables.get("T3").equals(true)) {
|
||||
context.getStateMachine().sendEvent(Events.CONTINUE);
|
||||
}
|
||||
variables.put("T1", true);
|
||||
variables.put("T2", true);
|
||||
variables.put("T3", true);
|
||||
context.getStateMachine().sendEvent(Events.CONTINUE);
|
||||
}
|
||||
};
|
||||
}
|
||||
//end::snippetAD[]
|
||||
|
||||
//tag::snippetAE[]
|
||||
@Bean
|
||||
public Tasks tasks() {
|
||||
return new Tasks();
|
||||
}
|
||||
//end::snippetAE[]
|
||||
|
||||
//tag::snippetAF[]
|
||||
//tag::snippetAE[]
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
|
||||
taskExecutor.setCorePoolSize(5);
|
||||
return taskExecutor;
|
||||
}
|
||||
//end::snippetAF[]
|
||||
//end::snippetAE[]
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,10 @@ public class Tasks {
|
||||
stateMachine.sendEvent(Events.RUN);
|
||||
}
|
||||
|
||||
public void cont() {
|
||||
stateMachine.sendEvent(Events.CONTINUE);
|
||||
}
|
||||
|
||||
public void fix(String task) {
|
||||
if (tasks.containsKey(task)) {
|
||||
tasks.put(task, true);
|
||||
}
|
||||
public void fix() {
|
||||
tasks.put("T1", true);
|
||||
tasks.put("T2", true);
|
||||
tasks.put("T3", true);
|
||||
stateMachine.sendEvent(Events.FIX);
|
||||
}
|
||||
|
||||
@@ -68,44 +64,33 @@ public class Tasks {
|
||||
|
||||
@StatesOnTransition(target = States.T1)
|
||||
public void taskT1(ExtendedState extendedState) {
|
||||
log.info("run task on T1");
|
||||
sleep(2000);
|
||||
extendedState.getVariables().put("T1", tasks.get("T1"));
|
||||
log.info("run task on T1 done");
|
||||
runTask("T1", extendedState);
|
||||
}
|
||||
|
||||
@StatesOnTransition(target = States.T2)
|
||||
public void taskT2(ExtendedState extendedState) {
|
||||
log.info("run task on T2");
|
||||
sleep(2000);
|
||||
extendedState.getVariables().put("T2", tasks.get("T2"));
|
||||
log.info("run task on T2 done");
|
||||
runTask("T2", extendedState);
|
||||
}
|
||||
|
||||
@StatesOnTransition(target = States.T3)
|
||||
public void taskT3(ExtendedState extendedState) {
|
||||
log.info("run task on T3");
|
||||
sleep(2000);
|
||||
extendedState.getVariables().put("T3", tasks.get("T3"));
|
||||
log.info("run task on T3 done");
|
||||
runTask("T3", extendedState);
|
||||
}
|
||||
|
||||
//tag::snippetA[]
|
||||
@StatesOnTransition(target = States.AUTOMATIC)
|
||||
public void automaticFix(ExtendedState extendedState) {
|
||||
Map<Object, Object> variables = extendedState.getVariables();
|
||||
if (variables.get("T1").equals(false)) {
|
||||
variables.put("T1", true);
|
||||
tasks.put("T1", true);
|
||||
}
|
||||
variables.put("T1", true);
|
||||
tasks.put("T1", true);
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
@StatesOnTransition(target = States.MANUAL)
|
||||
public void manualFix(ExtendedState extendedState) {
|
||||
Map<Object, Object> variables = extendedState.getVariables();
|
||||
if (variables.get("T2").equals(false)) {
|
||||
variables.put("T2", true);
|
||||
tasks.put("T2", true);
|
||||
}
|
||||
private void runTask(String task, ExtendedState extendedState) {
|
||||
log.info("run task on " + task);
|
||||
sleep(2000);
|
||||
extendedState.getVariables().put(task, tasks.get(task));
|
||||
log.info("run task on " + task + " done");
|
||||
}
|
||||
|
||||
private static void sleep(long millis) {
|
||||
|
||||
@@ -37,9 +37,9 @@ public class TasksCommands implements CommandMarker {
|
||||
return tasks.toString();
|
||||
}
|
||||
|
||||
@CliCommand(value = "tasks fix", help = "Fix task")
|
||||
public void fix(@CliOption(key = {"", "task"}, help = "Task id") String task) {
|
||||
tasks.fix(task);
|
||||
@CliCommand(value = "tasks fix", help = "Fix tasks")
|
||||
public void fix() {
|
||||
tasks.fix();
|
||||
}
|
||||
|
||||
@CliCommand(value = "tasks fail", help = "Fail task")
|
||||
|
||||
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -54,9 +55,8 @@ public class TasksTests {
|
||||
|
||||
@Test
|
||||
public void testInitialState() throws InterruptedException {
|
||||
assertThat(listener.stateChangedLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(listener.stateChangedCount, is(1));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
Map<Object, Object> variables = machine.getExtendedState().getVariables();
|
||||
assertThat(variables.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -65,6 +65,8 @@ public class TasksTests {
|
||||
tasks.run();
|
||||
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
Map<Object, Object> variables = machine.getExtendedState().getVariables();
|
||||
assertThat(variables.size(), is(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -74,10 +76,16 @@ public class TasksTests {
|
||||
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
|
||||
Map<Object, Object> variables = machine.getExtendedState().getVariables();
|
||||
assertThat(variables.size(), is(3));
|
||||
|
||||
listener.reset(9, 0, 0);
|
||||
tasks.run();
|
||||
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
|
||||
variables = machine.getExtendedState().getVariables();
|
||||
assertThat(variables.size(), is(3));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,11 +100,17 @@ public class TasksTests {
|
||||
|
||||
@Test
|
||||
public void testFailManualFix() throws InterruptedException {
|
||||
listener.reset(9, 0, 0);
|
||||
listener.reset(11, 0, 0);
|
||||
tasks.fail("T2");
|
||||
tasks.run();
|
||||
tasks.fix("T2");
|
||||
tasks.cont();
|
||||
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
|
||||
|
||||
Map<Object, Object> variables = machine.getExtendedState().getVariables();
|
||||
assertThat(variables.size(), is(3));
|
||||
|
||||
assertThat(machine.getState().getIds(), contains(States.ERROR, States.MANUAL));
|
||||
listener.reset(1, 0, 0);
|
||||
tasks.fix();
|
||||
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
}
|
||||
@@ -111,8 +125,9 @@ public class TasksTests {
|
||||
tasks = context.getBean(Tasks.class);
|
||||
listener = context.getBean(TestListener.class);
|
||||
machine.start();
|
||||
// lets do a little sleep to wait sm to start
|
||||
Thread.sleep(1000);
|
||||
assertThat(listener.stateChangedLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(listener.stateChangedCount, is(1));
|
||||
assertThat(machine.getState().getIds(), contains(States.READY));
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -152,8 +167,8 @@ public class TasksTests {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<States, Events> from, State<States, Events> to) {
|
||||
stateChangedLatch.countDown();
|
||||
stateChangedCount++;
|
||||
stateChangedLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,8 +185,8 @@ public class TasksTests {
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<States, Events> transition) {
|
||||
transitionLatch.countDown();
|
||||
transitionCount++;
|
||||
transitionLatch.countDown();
|
||||
}
|
||||
|
||||
public void reset(int c1, int c2, int c3) {
|
||||
|
||||
Reference in New Issue
Block a user