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:
Janne Valkealahti
2015-02-12 10:37:54 +00:00
parent 12e23debb4
commit 25c34a3f7b
8 changed files with 177 additions and 35 deletions

View File

@@ -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.
*

View File

@@ -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.

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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.
*

View File

@@ -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();
}

View File

@@ -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 {

View File

@@ -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));
}
}