From 66295d9ed2fbf1ea53d38e7dc2a4b9d1a18f61e5 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sun, 8 May 2016 08:58:50 +0100 Subject: [PATCH] State exists in regions - Related to work done in #221 where we got into trouble for properly exiting states when fork/join is used. --- .../buildtests/ForkJoinEntryExitTests.java | 35 ++++++++++++++++++- .../support/AbstractStateMachine.java | 20 +++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/ForkJoinEntryExitTests.java b/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/ForkJoinEntryExitTests.java index f723fc93..802874fc 100644 --- a/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/ForkJoinEntryExitTests.java +++ b/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/ForkJoinEntryExitTests.java @@ -84,8 +84,41 @@ public class ForkJoinEntryExitTests extends AbstractBuildTests { .expectStates("S2", "S210", "S220").and() .step() .sendEvent("E2") + .expectStateEntered(1) + .expectStateExited(1) + .expectStates("S2", "S211", "S220").and() + .step() .sendEvent("E3") - .expectStateEntered(3) + .expectStateEntered(2) + .expectStateExited(4) + .expectStates("S3").and() + .build(); + plan.test(); + } + + @Test + @SuppressWarnings("unchecked") + public void testJoinExitsViaChoices() throws Exception { + context.register(Config2.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectState("S1").and() + .step() + .sendEvent("E1") + .expectStates("S2", "S210", "S220").and() + .step() + .sendEvent("E2") + .expectStateEntered(1) + .expectStateExited(1) + .expectStates("S2", "S211", "S220").and() + .step() + .sendEvent("E3") + .expectStateEntered(2) + .expectStateExited(4) .expectStates("S3").and() .build(); plan.test(); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 30fd19ed..6357495c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -45,6 +45,7 @@ import org.springframework.statemachine.region.Region; import org.springframework.statemachine.state.AbstractState; import org.springframework.statemachine.state.ForkPseudoState; import org.springframework.statemachine.state.HistoryPseudoState; +import org.springframework.statemachine.state.JoinPseudoState; import org.springframework.statemachine.state.PseudoState; import org.springframework.statemachine.state.PseudoStateContext; import org.springframework.statemachine.state.PseudoStateKind; @@ -773,10 +774,18 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo @Override public void onContext(PseudoStateContext context) { PseudoState pseudoState = context.getPseudoState(); + if (pseudoState.getKind() == PseudoStateKind.JOIN) { + List> joins = ((JoinPseudoState)context.getPseudoState()).getJoins(); + for (State join : joins) { + exitFromState(join, null, null, getRelayStateMachine()); + } + } State toState = findStateWithPseudoState(pseudoState); StateContext stateContext = buildStateContext(Stage.STATE_EXIT, null, null, getRelayStateMachine()); pseudoState.exit(stateContext); toState = followLinkedPseudoStates(toState, stateContext); + // should figure out what transition to use as we pass null for now + // which is then expected in exitCurrentState switchToState(toState, null, null, getRelayStateMachine()); } }); @@ -953,6 +962,16 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo StateMachine submachine = ((AbstractState)currentState).getSubmachine(); ((AbstractStateMachine)submachine).exitCurrentState(state, message, transition, stateMachine); exitFromState(currentState, message, transition, stateMachine); + } else if (currentState.isOrthogonal()) { + Collection> regions = ((AbstractState)currentState).getRegions(); + for (Region r : regions) { + if (r.getStates().contains(state)) { + exitFromState(r.getState(), message, transition, stateMachine); + } + } + if (transition == null) { + exitFromState(currentState, message, transition, stateMachine); + } } else { exitFromState(currentState, message, transition, stateMachine); } @@ -983,6 +1002,7 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo } else if (!isSubOfSource && !isSubOfTarget && findDeep == null) { } else if (!isSubOfSource && !isSubOfTarget && (transition.getSource() == currentState && StateMachineUtils.isSubstate(currentState, transition.getTarget()))) { } else if (StateMachineUtils.isNormalPseudoState(transition.getTarget())) { + } else if (findDeep != null && findDeep != state && findDeep.getStates().contains(state)) { } else if (!isSubOfSource && !isSubOfTarget) { return; }