Better support for parallel regions

- Generally fixes #68
- First attempt to externalize event execution from state machine
  into its own class backed by an interface. This relates to #7
- Change of various places to have better support if execution
  is done in threads.
- Not yet a central place where concurrency can be defines, thus
  currently rely on global taskExecutor bean when can be overridded
  from a default which is SyncTaskExecutor. Futher work for
  that in separate tickets.
- Change tasks sample to use a thread pool.
- Change of concept how initial state/transition is handled, no
  longer handled manually in lifecycle method, thus giving a change
  for initial transition to execute its actions in a multiple threads.
This commit is contained in:
Janne Valkealahti
2015-05-24 17:38:41 +01:00
parent 517bf69364
commit 2b3db24345
18 changed files with 866 additions and 267 deletions

View File

@@ -8,6 +8,8 @@ import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.shell.Bootstrap;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
@@ -127,8 +129,7 @@ public class Application {
@Override
public void execute(StateContext<States, Events> context) {
Map<Object, Object> variables = context.getExtendedState().getVariables();
if (variables.get("T1").equals(false)) {
variables.put("T1", true);
if (variables.get("T1").equals(true)) {
context.getStateMachine().sendEvent(Events.CONTINUE);
} else {
context.getStateMachine().sendEvent(Events.FALLBACK);
@@ -157,6 +158,13 @@ public class Application {
return new Tasks();
}
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
return taskExecutor;
}
}
//end::snippetA[]

View File

@@ -54,19 +54,25 @@ 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");
}
@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");
}
@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");
}
@StatesOnTransition(target = States.AUTOMATIC)
@@ -87,6 +93,13 @@ public class Tasks {
}
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
}
}
@Override
public String toString() {
return "Tasks " + tasks;

View File

@@ -46,22 +46,22 @@ public class TasksTests {
@Test
public void testRunOnce() throws InterruptedException {
listener.reset(3, 0, 0);
listener.reset(9, 0, 0);
tasks.run();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), contains(States.READY));
}
@Test
public void testRunTwice() throws InterruptedException {
listener.reset(3, 0, 0);
listener.reset(9, 0, 0);
tasks.run();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), contains(States.READY));
listener.reset(3, 0, 0);
listener.reset(9, 0, 0);
tasks.run();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), contains(States.READY));
}
@@ -70,19 +70,19 @@ public class TasksTests {
listener.reset(11, 0, 0);
tasks.fail("T1");
tasks.run();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(11));
assertThat(machine.getState().getIds(), contains(States.READY));
}
@Test
public void testFailManualFix() throws InterruptedException {
listener.reset(3, 0, 0);
listener.reset(9, 0, 0);
tasks.fail("T2");
tasks.run();
tasks.fix("T2");
tasks.cont();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedLatch.await(6, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), contains(States.READY));
}