Events not send to regions
- refactor event processing around state interfaces order to pass event from a state machine into a regions.
This commit is contained in:
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package org.springframework.statemachine;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.region.Region;
|
||||
import org.springframework.statemachine.state.State;
|
||||
@@ -38,31 +37,6 @@ public interface StateMachine<S, E> extends Region<S, E> {
|
||||
*/
|
||||
State<S,E> getInitialState();
|
||||
|
||||
/**
|
||||
* Start the state machine.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop the state machine.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Send an event {@code E} wrapped with a {@link Message} to the state
|
||||
* machine.
|
||||
*
|
||||
* @param event the wrapped event to send
|
||||
*/
|
||||
void sendEvent(Message<E> event);
|
||||
|
||||
/**
|
||||
* Send an event {@code E} to the state machine.
|
||||
*
|
||||
* @param event the event to send
|
||||
*/
|
||||
void sendEvent(E event);
|
||||
|
||||
/**
|
||||
* Adds the state listener.
|
||||
*
|
||||
|
||||
@@ -17,13 +17,14 @@ package org.springframework.statemachine.region;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
* A region is an orthogonal part of either a composite state or a state
|
||||
* machine. It contains states and transitions.
|
||||
*
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
@@ -31,28 +32,52 @@ import org.springframework.statemachine.transition.Transition;
|
||||
*/
|
||||
public interface Region<S, E> {
|
||||
|
||||
/**
|
||||
* Start the region.
|
||||
*/
|
||||
void start();
|
||||
|
||||
/**
|
||||
* Stop the region.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Send an event {@code E} wrapped with a {@link Message} to the region.
|
||||
*
|
||||
* @param event the wrapped event to send
|
||||
*/
|
||||
void sendEvent(Message<E> event);
|
||||
|
||||
/**
|
||||
* Send an event {@code E} to the region.
|
||||
*
|
||||
* @param event the event to send
|
||||
*/
|
||||
void sendEvent(E event);
|
||||
|
||||
/**
|
||||
* Gets the current {@link State}.
|
||||
*
|
||||
* @return current state
|
||||
*/
|
||||
State<S,E> getState();
|
||||
|
||||
|
||||
/**
|
||||
* Gets the {@link State}s defined in this region. Returned collection is
|
||||
* an unmodifiable copy because states in a state machine are immutable.
|
||||
*
|
||||
* @return immutable copy of states
|
||||
*/
|
||||
*/
|
||||
Collection<State<S, E>> getStates();
|
||||
|
||||
|
||||
/**
|
||||
* Gets a {@link Transition}s for this region.
|
||||
*
|
||||
*
|
||||
* @return immutable copy of transitions
|
||||
*/
|
||||
Collection<Transition<S,E>> getTransitions();
|
||||
|
||||
|
||||
/**
|
||||
* Checks if region complete. Region is considered to be completed if it has
|
||||
* reached its end state and no further event processing is happening.
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.statemachine.state;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
@@ -136,6 +137,10 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
this.submachine = submachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvent(Message<E> event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract void exit(E event, StateContext<S, E> context);
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.statemachine.state;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.region.Region;
|
||||
@@ -87,8 +88,21 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
super(deferred, entryActions, exitActions, null, regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEvent(Message<E> event) {
|
||||
if (getRegions() != null) {
|
||||
for (Region<S, E> r : getRegions()) {
|
||||
r.sendEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(E event, StateContext<S, E> context) {
|
||||
for (Region<S, E> region : getRegions()) {
|
||||
region.getState().exit(event, context);
|
||||
region.stop();
|
||||
}
|
||||
Collection<Action<S, E>> actions = getExitActions();
|
||||
if (actions != null) {
|
||||
for (Action<S, E> action : actions) {
|
||||
@@ -105,13 +119,26 @@ public class RegionState<S, E> extends AbstractState<S, E> {
|
||||
action.execute(context);
|
||||
}
|
||||
}
|
||||
|
||||
if (getPseudoState() != null && getPseudoState().getKind() == PseudoStateKind.INITIAL) {
|
||||
for (Region<S, E> region : getRegions()) {
|
||||
region.start();
|
||||
}
|
||||
} else {
|
||||
for (Region<S, E> region : getRegions()) {
|
||||
region.getState().entry(event, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<S> getIds() {
|
||||
ArrayList<S> ids = new ArrayList<S>();
|
||||
for (Region<S, E> r : getRegions()) {
|
||||
ids.addAll(r.getState().getIds());
|
||||
State<S, E> s = r.getState();
|
||||
if (s != null) {
|
||||
ids.addAll(s.getIds());
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.springframework.statemachine.state;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
@@ -30,6 +31,13 @@ import org.springframework.statemachine.action.Action;
|
||||
*/
|
||||
public interface State<S, E> {
|
||||
|
||||
/**
|
||||
* Send an event {@code E} wrapped with a {@link Message} to the state.
|
||||
*
|
||||
* @param event the wrapped event to send
|
||||
*/
|
||||
void sendEvent(Message<E> event);
|
||||
|
||||
/**
|
||||
* Initiate an exit sequence for the state.
|
||||
*
|
||||
|
||||
@@ -166,6 +166,12 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Queue event " + event);
|
||||
}
|
||||
|
||||
// TODO: should not do here
|
||||
if (currentState != null) {
|
||||
currentState.sendEvent(event);
|
||||
}
|
||||
|
||||
eventQueue.add(event);
|
||||
scheduleEventQueueProcessing();
|
||||
}
|
||||
|
||||
@@ -62,7 +62,8 @@ public abstract class AbstractStateMachineTests {
|
||||
|
||||
public enum TestStates {
|
||||
SI,S1,S2,S3,S4,SF,
|
||||
S11,S111,S21,S211
|
||||
S11,S111,S112,S12,S121,S122,
|
||||
S21,S211
|
||||
}
|
||||
|
||||
public enum TestEvents {
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.springframework.statemachine;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -31,7 +31,10 @@ import org.springframework.statemachine.AbstractStateMachineTests.TestExitAction
|
||||
import org.springframework.statemachine.AbstractStateMachineTests.TestStates;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.region.Region;
|
||||
import org.springframework.statemachine.state.DefaultPseudoState;
|
||||
import org.springframework.statemachine.state.EnumState;
|
||||
import org.springframework.statemachine.state.PseudoState;
|
||||
import org.springframework.statemachine.state.PseudoStateKind;
|
||||
import org.springframework.statemachine.state.RegionState;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.DefaultExternalTransition;
|
||||
@@ -106,4 +109,97 @@ public class RegionMachineTests {
|
||||
assertThat(exitActionS1.stateContexts.size(), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiRegion() throws Exception {
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
PseudoState pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL);
|
||||
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
|
||||
|
||||
TestEntryAction entryActionS111 = new TestEntryAction("S111");
|
||||
TestExitAction exitActionS111 = new TestExitAction("S111");
|
||||
Collection<Action<TestStates, TestEvents>> entryActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS111.add(entryActionS111);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS111.add(exitActionS111);
|
||||
State<TestStates,TestEvents> stateS111 = new EnumState<TestStates,TestEvents>(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState);
|
||||
|
||||
TestEntryAction entryActionS112 = new TestEntryAction("S112");
|
||||
TestExitAction exitActionS112 = new TestExitAction("S112");
|
||||
Collection<Action<TestStates, TestEvents>> entryActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS112.add(entryActionS112);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS112.add(exitActionS112);
|
||||
State<TestStates,TestEvents> stateS112 = new EnumState<TestStates,TestEvents>(TestStates.S112, null, entryActionsS112, exitActionsS112);
|
||||
|
||||
TestEntryAction entryActionS121 = new TestEntryAction("S121");
|
||||
TestExitAction exitActionS121 = new TestExitAction("S121");
|
||||
Collection<Action<TestStates, TestEvents>> entryActionsS121 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
entryActionsS111.add(entryActionS121);
|
||||
Collection<Action<TestStates, TestEvents>> exitActionsS121 = new ArrayList<Action<TestStates, TestEvents>>();
|
||||
exitActionsS111.add(exitActionS121);
|
||||
State<TestStates,TestEvents> stateS121 = new EnumState<TestStates,TestEvents>(TestStates.S121, null, entryActionsS121, exitActionsS121, pseudoState);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states11 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states11.add(stateSI);
|
||||
states11.add(stateS111);
|
||||
states11.add(stateS112);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions11 = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromS111ToS112 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS112, null, TestEvents.E2, null);
|
||||
transitions11.add(transitionFromS111ToS112);
|
||||
EnumStateMachine<TestStates, TestEvents> machine11 = new EnumStateMachine<TestStates, TestEvents>(states11, transitions11, stateS111, null);
|
||||
machine11.setTaskExecutor(taskExecutor);
|
||||
machine11.afterPropertiesSet();
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states12 = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states12.add(stateSI);
|
||||
states12.add(stateS121);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions12 = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToS121 =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateS111, null, TestEvents.E3, null);
|
||||
transitions12.add(transitionFromSIToS121);
|
||||
EnumStateMachine<TestStates, TestEvents> machine12 = new EnumStateMachine<TestStates, TestEvents>(states12, transitions12, stateS121, null);
|
||||
machine12.setTaskExecutor(taskExecutor);
|
||||
machine12.afterPropertiesSet();
|
||||
|
||||
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
|
||||
regions.add(machine11);
|
||||
regions.add(machine12);
|
||||
RegionState<TestStates,TestEvents> stateR = new RegionState<TestStates,TestEvents>(regions, null, null, null, pseudoState);
|
||||
|
||||
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
|
||||
states.add(stateR);
|
||||
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
|
||||
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToRegionstate =
|
||||
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateR, null, TestEvents.E1, null);
|
||||
transitions.add(transitionFromSIToRegionstate);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateR, null);
|
||||
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.afterPropertiesSet();
|
||||
machine.start();
|
||||
|
||||
assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
assertThat(entryActionS121.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(exitActionS121.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
|
||||
assertThat(entryActionS111.stateContexts.size(), is(1));
|
||||
assertThat(exitActionS111.stateContexts.size(), is(0));
|
||||
assertThat(entryActionS121.stateContexts.size(), is(1));
|
||||
assertThat(exitActionS121.stateContexts.size(), is(0));
|
||||
|
||||
machine.sendEvent(TestEvents.E2);
|
||||
|
||||
assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(entryActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
|
||||
assertThat(exitActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
|
||||
|
||||
assertThat(entryActionS111.stateContexts.size(), is(1));
|
||||
assertThat(exitActionS111.stateContexts.size(), is(1));
|
||||
assertThat(entryActionS112.stateContexts.size(), is(1));
|
||||
assertThat(exitActionS112.stateContexts.size(), is(0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user