Polish tasks example

This commit is contained in:
Janne Valkealahti
2015-05-29 15:07:44 +01:00
parent b73e22864f
commit 7fa8c12c30
5 changed files with 147 additions and 67 deletions

View File

@@ -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

View File

@@ -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[]
}

View File

@@ -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) {

View File

@@ -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")

View File

@@ -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) {