Remove some EventNotAccepted events
- Now only trying to notify those from a root machine as same events from childs are kinda pointless and just create noise. - Fixes #287
This commit is contained in:
@@ -76,4 +76,10 @@ public interface StateMachineAccess<S, E> {
|
||||
*/
|
||||
void setForwardedInitialEvent(Message<E> message);
|
||||
|
||||
/**
|
||||
* Sets the parent machine.
|
||||
*
|
||||
* @param stateMachine the state machine
|
||||
*/
|
||||
void setParentMachine(StateMachine<S, E> stateMachine);
|
||||
}
|
||||
|
||||
@@ -322,6 +322,28 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
holder.getValue().setState(stateMap.get(holder.getKey()));
|
||||
}
|
||||
|
||||
// set parent machines for each built machine
|
||||
for (Entry<Object, StateMachine<S, E>> mme : machineMap.entrySet()) {
|
||||
StateMachine<S, E> m = null;
|
||||
if (mme.getKey() != null) {
|
||||
Object sParent = null;
|
||||
for (StateData<S, E> sd : stateMachineModel.getStatesData().getStateData()) {
|
||||
if (ObjectUtils.nullSafeEquals(sd.getState(), mme.getKey())) {
|
||||
sParent = sd.getParent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
m = machineMap.get(sParent);
|
||||
}
|
||||
final StateMachine<S, E> mm = m;
|
||||
mme.getValue().getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<S ,E>>(){
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.setParentMachine(mm);
|
||||
}
|
||||
});
|
||||
}
|
||||
return delegateAutoStartup(machine);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
private final Object lock = new Object();
|
||||
|
||||
private StateMachine<S, E> parentMachine;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
@@ -234,6 +236,13 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
return accepted;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void notifyEventNotAccepted(StateContext<S, E> stateContext) {
|
||||
if (parentMachine == null) {
|
||||
super.notifyEventNotAccepted(stateContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendEvent(E event) {
|
||||
return sendEvent(MessageBuilder.withPayload(event).build());
|
||||
@@ -521,6 +530,11 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
this.relay = stateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentMachine(StateMachine<S, E> parentMachine) {
|
||||
this.parentMachine = parentMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stateChangedInRelay() {
|
||||
// TODO: temp tweak, see super
|
||||
|
||||
@@ -23,7 +23,6 @@ import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.sameInstance;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
@@ -168,36 +167,15 @@ public class StateContextTests extends AbstractStateMachineTests {
|
||||
|
||||
// all nested machines sends these
|
||||
assertThat(listener.contexts, contains(
|
||||
hasStage(Stage.EVENT_NOT_ACCEPTED),
|
||||
hasStage(Stage.EVENT_NOT_ACCEPTED),
|
||||
hasStage(Stage.EVENT_NOT_ACCEPTED)
|
||||
));
|
||||
|
||||
|
||||
assertThat(listener.contexts.get(0).getStage(), is(Stage.EVENT_NOT_ACCEPTED));
|
||||
assertThat(listener.contexts.get(0).getTransition(), nullValue());
|
||||
assertThat(listener.contexts.get(0).getEvent(), is(Events.J));
|
||||
assertThat(listener.contexts.get(0).getSource(), notNullValue());
|
||||
assertThat(listener.contexts.get(0).getSource().getId(), is(States.S11));
|
||||
assertThat(listener.contexts.get(0).getSource().getId(), is(States.S0));
|
||||
assertThat(listener.contexts.get(0).getTarget(), nullValue());
|
||||
|
||||
assertThat(listener.contexts.get(1).getStage(), is(Stage.EVENT_NOT_ACCEPTED));
|
||||
assertThat(listener.contexts.get(1).getTransition(), nullValue());
|
||||
assertThat(listener.contexts.get(1).getEvent(), is(Events.J));
|
||||
assertThat(listener.contexts.get(1).getSource(), notNullValue());
|
||||
assertThat(listener.contexts.get(1).getSource().getId(), is(States.S1));
|
||||
assertThat(listener.contexts.get(1).getTarget(), nullValue());
|
||||
|
||||
assertThat(listener.contexts.get(2).getStage(), is(Stage.EVENT_NOT_ACCEPTED));
|
||||
assertThat(listener.contexts.get(2).getTransition(), nullValue());
|
||||
assertThat(listener.contexts.get(2).getEvent(), is(Events.J));
|
||||
assertThat(listener.contexts.get(2).getSource(), notNullValue());
|
||||
assertThat(listener.contexts.get(2).getSource().getId(), is(States.S0));
|
||||
assertThat(listener.contexts.get(2).getTarget(), nullValue());
|
||||
|
||||
// TODO: I wonder if these should be different machines
|
||||
assertThat(listener.contexts.get(0).getStateMachine(), sameInstance(listener.contexts.get(1).getStateMachine()));
|
||||
assertThat(listener.contexts.get(0).getStateMachine(), sameInstance(listener.contexts.get(2).getStateMachine()));
|
||||
}
|
||||
|
||||
static class TestStateMachineListener extends StateMachineListenerAdapter<States, Events> {
|
||||
|
||||
@@ -106,6 +106,10 @@ public class StateMachineAccessTests {
|
||||
this.relay = stateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentMachine(StateMachine<String, String> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetStateMachine(StateMachineContext<String, String> stateMachineContext) {
|
||||
}
|
||||
|
||||
@@ -124,6 +124,141 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventNotAcceptedS1() throws Exception {
|
||||
context.register(BaseConfig.class, Config3.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E2);
|
||||
assertThat(accepted, is(false));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(listener.eventNotAccepted.size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventAcceptedS1() throws Exception {
|
||||
context.register(BaseConfig.class, Config3.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E1);
|
||||
assertThat(accepted, is(true));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(listener.eventNotAccepted.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventAcceptedS1NoS1Transition() throws Exception {
|
||||
context.register(BaseConfig.class, Config4.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E1);
|
||||
assertThat(accepted, is(true));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(listener.eventNotAccepted.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventAcceptedS1GuardAllow() throws Exception {
|
||||
context.register(BaseConfig.class, Config3.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
machine.getExtendedState().getVariables().put("S1E1", true);
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E1);
|
||||
assertThat(accepted, is(true));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S2));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(listener.eventNotAccepted.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventAcceptedS11GuardAllow() throws Exception {
|
||||
context.register(BaseConfig.class, Config3.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
machine.getExtendedState().getVariables().put("S11E1", true);
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E1);
|
||||
assertThat(accepted, is(true));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S12));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(listener.eventNotAccepted.size(), is(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventAcceptedS111GuardAllow() throws Exception {
|
||||
context.register(BaseConfig.class, Config3.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
ObjectStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, ObjectStateMachine.class);
|
||||
TestListener listener = new TestListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S111));
|
||||
machine.getExtendedState().getVariables().put("S111E1", true);
|
||||
|
||||
listener.reset(1);
|
||||
boolean accepted = machine.sendEvent(TestEvents.E1);
|
||||
assertThat(accepted, is(true));
|
||||
assertThat(machine.getState().getIds(), contains(TestStates.S1, TestStates.S11, TestStates.S112));
|
||||
assertThat(listener.eventNotAcceptedLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(listener.eventNotAccepted.size(), is(0));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -216,6 +351,90 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config3 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S2)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(TestStates.S1)
|
||||
.initial(TestStates.S11)
|
||||
.state(TestStates.S12)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(TestStates.S11)
|
||||
.initial(TestStates.S111)
|
||||
.state(TestStates.S112);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("extendedState.variables.containsKey('S1E1')")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S11)
|
||||
.target(TestStates.S12)
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("extendedState.variables.containsKey('S11E1')")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S111)
|
||||
.target(TestStates.S112)
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("extendedState.variables.containsKey('S111E1')");
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config4 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.S1)
|
||||
.state(TestStates.S2)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(TestStates.S1)
|
||||
.initial(TestStates.S11)
|
||||
.state(TestStates.S12)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(TestStates.S11)
|
||||
.initial(TestStates.S111)
|
||||
.state(TestStates.S112);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.S11)
|
||||
.target(TestStates.S12)
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("extendedState.variables.containsKey('S11E1')")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S111)
|
||||
.target(TestStates.S112)
|
||||
.event(TestEvents.E1)
|
||||
.guardExpression("extendedState.variables.containsKey('S111E1')");
|
||||
}
|
||||
}
|
||||
|
||||
static class TestEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
volatile CountDownLatch onEventLatch = new CountDownLatch(6);
|
||||
@@ -254,6 +473,10 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
|
||||
eventNotAcceptedLatch.countDown();
|
||||
}
|
||||
|
||||
public void reset(int c1) {
|
||||
eventNotAcceptedLatch = new CountDownLatch(c1);
|
||||
eventNotAccepted.clear();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user