Move post state interceptor call later

- This is fixing some issues where anonymous transitions
  are causing post state call to happen too early i.e.
  with a submachine which transits away from it.
- Fixes #734
This commit is contained in:
Janne Valkealahti
2019-04-24 07:48:37 +01:00
parent 6867b5dc9e
commit 8f6c89d65e
2 changed files with 98 additions and 3 deletions

View File

@@ -930,7 +930,6 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
if (isComplete()) {
stop();
}
callPostStateChangeInterceptors(toState, message, transition, stateMachine);
}
private State<S,E> followLinkedPseudoStates(State<S,E> state, StateContext<S, E> stateContext) {
@@ -1259,6 +1258,9 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
if (state == null) {
return;
}
// call post interceptors here instead end of switchToState
// as anonymous transition would cause post calls to happen on wrong order
callPostStateChangeInterceptors(state, message, transition, stateMachine);
log.debug("Trying Enter state=[" + state + "]");
if (log.isTraceEnabled()) {
log.trace("Trying Enter state=[" + state + "]");

View File

@@ -137,6 +137,17 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
assertThat(interceptor.preStateChangeCount1, is(1));
assertThat(interceptor.preStateChangeLatch2.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount2, is(1));
interceptor.reset(1);
listener.reset(1);
machine.sendEvent(Events.C);
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(1));
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S0));
assertThat(interceptor.preStateChangeLatch1.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount1, is(1));
assertThat(interceptor.preStateChangeLatch2.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount2, is(1));
}
@Test
@@ -310,6 +321,54 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
assertThat(interceptor.preStateChangeCount2, is(1));
}
@Test
public void testIntercept7() throws InterruptedException {
context.register(Config6.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(2);
listener.reset(2);
machine.sendEvent(Events.A);
assertThat(listener.stateChangedLatch.await(2, TimeUnit.SECONDS), is(true));
assertThat(listener.stateChangedCount, is(2));
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S2));
assertThat(interceptor.preStateChangeLatch1.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount1, is(2));
assertThat(interceptor.preStateChangeLatch2.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.preStateChangeCount2, is(2));
assertThat(interceptor.postStateChangeLatch1.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.postStateChangeCount1, is(2));
assertThat(interceptor.postStateChangeLatch2.await(2, TimeUnit.SECONDS), is(true));
assertThat(interceptor.postStateChangeCount2, is(2));
assertThat(interceptor.preStateChangeStates1.size(), is(2));
assertThat(interceptor.postStateChangeStates1.size(), is(2));
assertThat(interceptor.preStateChangeStates1.get(0).getId(), is(States.S1));
assertThat(interceptor.preStateChangeStates1.get(1).getId(), is(States.S2));
assertThat(interceptor.postStateChangeStates1.get(0).getId(), is(States.S1));
assertThat(interceptor.postStateChangeStates1.get(1).getId(), is(States.S2));
}
@Configuration
@EnableStateMachine
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
@@ -455,7 +514,11 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
.and()
.withExternal()
.source(States.S1).target(States.S2)
.event(Events.B);
.event(Events.B)
.and()
.withExternal()
.source(States.S2).target(States.S0)
.event(Events.C);
}
}
@@ -551,6 +614,37 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
}
@Configuration
@EnableStateMachine
static class Config6 extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.S0)
.state(States.S1)
.state(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)
.and()
.withExternal()
.source(States.S2).target(States.S0)
.event(Events.C);
}
}
public static enum States {
S0, S1, S11, S12, S2, S21, S211, S212, S3;
}
@@ -686,7 +780,6 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
return stateContext;
}
public void reset(int c1) {
preStateChangeLatch1 = new CountDownLatch(c1);
preStateChangeLatch2 = new CountDownLatch(c1);