Fix PreStateChange interceptors being called twice for END states

- Backport #367
- Relates to #372
This commit is contained in:
Janne Valkealahti
2017-06-08 09:40:09 +01:00
parent 3eec94fe0c
commit 7fccb6db94
2 changed files with 73 additions and 1 deletions

View File

@@ -812,7 +812,8 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_CHANGED, message, transition, stateMachine);
State<S,E> toState = followLinkedPseudoStates(state, stateContext);
PseudoStateKind kind = state.getPseudoState() != null ? state.getPseudoState().getKind() : null;
if (kind != null && (kind != PseudoStateKind.INITIAL && kind != PseudoStateKind.JOIN && kind != PseudoStateKind.FORK)) {
if (kind != null && (kind != PseudoStateKind.INITIAL && kind != PseudoStateKind.JOIN
&& kind != PseudoStateKind.FORK && kind != PseudoStateKind.END)) {
callPreStateChangeInterceptors(toState, message, transition, stateMachine);
}

View File

@@ -242,6 +242,48 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
assertThat(interceptor.preStateChangeStates.get(0).getId(), is(interceptor.postStateChangeStates.get(0).getId()));
}
@Test
public void testIntercept6() throws InterruptedException {
context.register(Config5.class);
context.refresh();
@SuppressWarnings("unchecked")
StateMachine<States, Events> machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
TestListener listener = new TestListener();
machine.addStateListener(listener);
TestStateChangeInterceptor interceptor = new TestStateChangeInterceptor();
machine.getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<States, Events>>() {
@Override
public void apply(StateMachineAccess<States, Events> function) {
function.addStateMachineInterceptor(interceptor);
}
});
machine.start();
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S0));
interceptor.reset(1);
listener.reset(1);
machine.sendEvent(Events.A);
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S1));
assertThat(interceptor.preStateChangeLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount, is(1));
interceptor.reset(1);
listener.reset(1);
machine.sendEvent(Events.E);
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S2));
assertThat(interceptor.preStateChangeLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount, is(1));
}
@Configuration
@EnableStateMachine
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
@@ -454,6 +496,35 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
}
}
@Configuration
@EnableStateMachine
static class Config5 extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.S0)
.state(States.S1)
.end(States.S2);
}
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.S0).target(States.S1)
.event(Events.A)
.and()
.withExternal()
.source(States.S1).target(States.S2)
.event(Events.E);
}
}
public static enum States {
S0, S1, S11, S12, S2, S21, S211, S212, S3;
}