Persisting StateMachineContext for SM with nested regions and Fork/Join

- fixing issue when StateMachineContext is incorrectly persisted for
  SM with nested regions and Fork/Join configuration
- Fixes #723
This commit is contained in:
Hamid Mortazavi
2019-04-07 12:27:10 +01:00
committed by Janne Valkealahti
parent 39ab4a0732
commit bd2518cce9
2 changed files with 129 additions and 17 deletions

View File

@@ -88,7 +88,9 @@ public abstract class AbstractStateMachinePersister<S, E, T> implements StateMac
S id = null;
State<S, E> state = stateMachine.getState();
if (state.isSubmachineState()) {
id = getDeepState(state);
StateMachine<S, E> submachine = ((AbstractState<S, E>) state).getSubmachine();
id = submachine.getState().getId();
childs.add(buildStateMachineContext(submachine));
} else if (state.isOrthogonal()) {
Collection<Region<S, E>> regions = ((AbstractState<S, E>)state).getRegions();
for (Region<S, E> r : regions) {
@@ -103,8 +105,8 @@ public abstract class AbstractStateMachinePersister<S, E, T> implements StateMac
// building history state mappings
Map<S, S> historyStates = new HashMap<S, S>();
PseudoState<S, E> historyState = ((AbstractStateMachine<S, E>) stateMachine).getHistoryState();
if (historyState != null) {
historyStates.put(null, ((HistoryPseudoState<S, E>)historyState).getState().getId());
if (historyState != null && ((HistoryPseudoState<S, E>)historyState).getState() != null) {
historyStates.put(null, ((HistoryPseudoState<S, E>) historyState).getState().getId());
}
Collection<State<S, E>> states = stateMachine.getStates();
for (State<S, E> ss : states) {
@@ -121,12 +123,4 @@ public abstract class AbstractStateMachinePersister<S, E, T> implements StateMac
}
return new DefaultStateMachineContext<S, E>(childs, id, null, null, extendedState, historyStates, stateMachine.getId());
}
private S getDeepState(State<S, E> state) {
Collection<S> ids1 = state.getIds();
@SuppressWarnings("unchecked")
S[] ids2 = (S[]) ids1.toArray();
// TODO: can this be empty as then we'd get error?
return ids2[ids2.length-1];
}
}

View File

@@ -73,19 +73,19 @@ public class DefaultStateMachinePersisterTests {
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S1I"));
assertThat(context.getChilds().isEmpty(), is(true));
assertThat(context.getChilds().size(), is(1));
machine.sendEvent("E2");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S11I"));
assertThat(context.getChilds().isEmpty(), is(true));
assertThat(context.getState(), is("S11"));
assertThat(context.getChilds().size(), is(1));
machine.sendEvent("E3");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S111"));
assertThat(context.getChilds().isEmpty(), is(true));
assertThat(context.getState(), is("S11"));
assertThat(context.getChilds().size(), is(1));
}
@Test
@@ -125,6 +125,53 @@ public class DefaultStateMachinePersisterTests {
assertThat(context.getChilds().get(1).getState(), anyOf(is("S12"), is("S222")));
}
@Test
public void testDeepNestedRegionsAndFork() throws Exception {
StateMachine<String, String> machine = buildDeepNestedRegionsAndFork();
machine.start();
InMemoryStateMachinePersist1 persist = new InMemoryStateMachinePersist1();
StateMachinePersister<String, String, String> persister = new DefaultStateMachinePersister<>(persist);
persister.persist(machine, "xxx");
StateMachineContext<String, String> context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S2"));
machine.sendEvent("E1");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S3"));
assertThat(context.getChilds().size(), is(1));
assertThat(context.getChilds().get(0).getChilds().size(), is(2));
machine.sendEvent("E2");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S3"));
assertThat(context.getChilds().size(), is(1));
assertThat(context.getChilds().get(0).getChilds().size(), is(2));
machine.sendEvent("E3");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S3"));
assertThat(context.getChilds().size(), is(1));
assertThat(context.getChilds().get(0).getChilds().size(), is(2));
machine.sendEvent("E4");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("S3"));
assertThat(context.getChilds().size(), is(1));
assertThat(context.getChilds().get(0).getChilds().size(), is(2));
machine.sendEvent("E5");
persister.persist(machine, "xxx");
context = persist.contexts.get("xxx");
assertThat(context.getState(), is("END"));
assertThat(context.getChilds().size(), is(1));
assertThat(context.getChilds().get(0).getChilds().isEmpty(), is(true));
}
private StateMachine<String, String> buildSimpleFlat() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureStates()
@@ -170,7 +217,6 @@ public class DefaultStateMachinePersisterTests {
.source("S11I")
.target("S111")
.event("E3");
return builder.build();
}
@@ -218,6 +264,78 @@ public class DefaultStateMachinePersisterTests {
return builder.build();
}
private StateMachine<String, String> buildDeepNestedRegionsAndFork() throws Exception {
Builder<String, String> builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial("S1")
.and()
.withStates()
.parent("S1")
.initial("S2")
.state("S22")
.fork("F1")
.state("S3")
.join("J1")
.end("END")
.and()
.withStates()
.parent("S3")
.initial("S4")
.state("S41")
.end("S4E")
.and()
.withStates()
.parent("S3")
.initial("S5")
.state("S51")
.end("S5E");
builder.configureTransitions()
.withExternal()
.source("S2")
.target("S22")
.event("E1")
.and()
.withExternal()
.source("S22")
.target("F1")
.and()
.withFork()
.source("F1")
.target("S3")
.and()
.withExternal()
.source("S4")
.target("S41")
.event("E2")
.and()
.withExternal()
.source("S41")
.target("S4E")
.event("E3")
.and()
.withExternal()
.source("S5")
.target("S51")
.event("E4")
.and()
.withExternal()
.source("S51")
.target("S5E")
.event("E5")
.and()
.withJoin()
.source("S3")
.target("J1")
.and()
.withExternal()
.source("J1")
.target("END");
return builder.build();
}
static class InMemoryStateMachinePersist1 implements StateMachinePersist<String, String, String> {
public final HashMap<String, StateMachineContext<String, String>> contexts = new HashMap<>();