Add StateContext into StateMachineListener
- new StateMachineListener method for listening all StateContext changes in a various stages. - StateContext now has a stage enum field telling at which stage it is. - Polish some classes and docs. - Prepare tests for StateContext. - Relates to #126, #138 and #150
This commit is contained in:
@@ -283,7 +283,7 @@ configure(rootProject) {
|
||||
idseparator: '-',
|
||||
doctype: 'book',
|
||||
numbered: '',
|
||||
'spring-hadoop-version' : project.version,
|
||||
'spring-statemachine-version' : project.version,
|
||||
'spring-version' : springVersion,
|
||||
revnumber : project.version
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
:core-dao-exceptions: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/dao.html#dao-exceptions
|
||||
:core-jdbc: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/jdbc.html
|
||||
:core-jdbc-JdbcTemplate: http://docs.spring.io/spring/docs/{spring-version}/spring-framework-reference/html/jdbc.html#jdbc-JdbcTemplate
|
||||
:sm-statecontext: http://docs.spring.io/spring-statemachine/docs/{spring-statemachine-version}/api/org/springframework/statemachine/StateContext.html
|
||||
|
||||
= Spring Statemachine - Reference Documentation
|
||||
|
||||
|
||||
@@ -633,7 +633,7 @@ include::samples/DocsConfigurationSampleTests.java[tags=snippet6]
|
||||
|
||||
[[sm-statecontext]]
|
||||
== Using StateContext
|
||||
_StateContext_ is a domain object representing a current status of a
|
||||
{sm-statecontext}[_StateContext_] is a domain object representing a current status of a
|
||||
state machine within a transition or an action. Context gives an
|
||||
access to a various information like event, message headers, extended
|
||||
state variables, current transition and a top-level state machine in
|
||||
|
||||
@@ -37,6 +37,13 @@ import org.springframework.statemachine.transition.Transition;
|
||||
*/
|
||||
public interface StateContext<S, E> {
|
||||
|
||||
/**
|
||||
* Gets the stage this context is attached.
|
||||
*
|
||||
* @return the stage
|
||||
*/
|
||||
Stage getStage();
|
||||
|
||||
/**
|
||||
* Gets the message associated with a context. Message may be null if transition
|
||||
* is not triggered by a signal.
|
||||
@@ -115,4 +122,21 @@ public interface StateContext<S, E> {
|
||||
*/
|
||||
Exception getException();
|
||||
|
||||
/**
|
||||
* Enumeration of possible stages context is attached.
|
||||
*/
|
||||
public static enum Stage {
|
||||
EVENT_NOT_ACCEPTED,
|
||||
EXTENDED_STATE_CHANGED,
|
||||
STATE_CHANGED,
|
||||
STATE_ENTRY,
|
||||
STATE_EXIT,
|
||||
STATEMACHINE_ERROR,
|
||||
STATEMACHINE_START,
|
||||
STATEMACHINE_STOP,
|
||||
TRANSITION,
|
||||
TRANSITION_START,
|
||||
TRANSITION_END;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public abstract class StateMachineEvent extends ApplicationEvent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractStateMachineEvent [source=" + source + "]";
|
||||
return "StateMachineEvent [source=" + source + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class TransitionEvent extends StateMachineEvent {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OnTransitionStartEvent [transition=" + transition + "]";
|
||||
return "TransitionEvent [transition=" + transition + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.statemachine.listener;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -30,8 +31,8 @@ import org.springframework.statemachine.transition.Transition;
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public class CompositeStateMachineListener<S,E> extends AbstractCompositeListener<StateMachineListener<S,E>> implements
|
||||
StateMachineListener<S, E> {
|
||||
public class CompositeStateMachineListener<S, E> extends AbstractCompositeListener<StateMachineListener<S, E>>
|
||||
implements StateMachineListener<S, E> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<S, E> from, State<S, E> to) {
|
||||
@@ -121,4 +122,12 @@ public class CompositeStateMachineListener<S,E> extends AbstractCompositeListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<S, E> stateContext) {
|
||||
for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
|
||||
StateMachineListener<S, E> listener = iterator.next();
|
||||
listener.stateContext(stateContext);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package org.springframework.statemachine.listener;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateContext.Stage;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -110,4 +112,11 @@ public interface StateMachineListener<S,E> {
|
||||
*/
|
||||
void extendedStateChanged(Object key, Object value);
|
||||
|
||||
/**
|
||||
* Notified on various {@link Stage}s about a {@link StateContext}.
|
||||
*
|
||||
* @param stateContext the state context
|
||||
*/
|
||||
void stateContext(StateContext<S, E> stateContext);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.statemachine.listener;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
@@ -75,4 +76,8 @@ public class StateMachineListenerAdapter<S, E> implements StateMachineListener<S
|
||||
public void extendedStateChanged(Object key, Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<S, E> stateContext) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.ExtendedState.ExtendedStateChangeListener;
|
||||
import org.springframework.statemachine.StateContext.Stage;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineContext;
|
||||
@@ -176,7 +177,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
public boolean sendEvent(Message<E> event) {
|
||||
if (hasStateMachineError()) {
|
||||
// TODO: should we throw exception?
|
||||
notifyEventNotAccepted(event, buildStateContext(event, null, getRelayStateMachine()));
|
||||
notifyEventNotAccepted(event, buildStateContext(Stage.EVENT_NOT_ACCEPTED, event, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,18 +185,18 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
event = getStateMachineInterceptors().preEvent(event, this);
|
||||
} catch (Exception e) {
|
||||
log.info("Event " + event + " threw exception in interceptors, not accepting event");
|
||||
notifyEventNotAccepted(event, buildStateContext(event, null, getRelayStateMachine()));
|
||||
notifyEventNotAccepted(event, buildStateContext(Stage.EVENT_NOT_ACCEPTED, event, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isComplete() || !isRunning()) {
|
||||
notifyEventNotAccepted(event, buildStateContext(event, null, getRelayStateMachine()));
|
||||
notifyEventNotAccepted(event, buildStateContext(Stage.EVENT_NOT_ACCEPTED, event, null, getRelayStateMachine()));
|
||||
return false;
|
||||
}
|
||||
boolean accepted = acceptEvent(event);
|
||||
stateMachineExecutor.execute();
|
||||
if (!accepted) {
|
||||
notifyEventNotAccepted(event, buildStateContext(event, null, getRelayStateMachine()));
|
||||
notifyEventNotAccepted(event, buildStateContext(Stage.EVENT_NOT_ACCEPTED, event, null, getRelayStateMachine()));
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
@@ -216,7 +217,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
extendedState.setExtendedStateChangeListener(new ExtendedStateChangeListener() {
|
||||
@Override
|
||||
public void changed(Object key, Object value) {
|
||||
notifyExtendedStateChanged(key, value, buildStateContext(null, null, getRelayStateMachine()));
|
||||
notifyExtendedStateChanged(key, value, buildStateContext(Stage.EXTENDED_STATE_CHANGED, null, null, getRelayStateMachine()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -261,17 +262,17 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
@Override
|
||||
public void transit(Transition<S, E> t, StateContext<S, E> stateContext, Message<E> queuedMessage) {
|
||||
StateContext<S, E> stateContext2 = buildStateContext(queuedMessage, null, getRelayStateMachine());
|
||||
notifyTransitionStart(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
notifyTransition(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
// TODO: fix above stateContext as it's not used
|
||||
notifyTransitionStart(t, queuedMessage, buildStateContext(Stage.TRANSITION_START, queuedMessage, null, getRelayStateMachine()));
|
||||
notifyTransition(t, queuedMessage, buildStateContext(Stage.TRANSITION, queuedMessage, null, getRelayStateMachine()));
|
||||
if (t.getKind() == TransitionKind.INITIAL) {
|
||||
switchToState(t.getTarget(), queuedMessage, t, getRelayStateMachine());
|
||||
notifyStateMachineStarted(getRelayStateMachine(), stateContext2);
|
||||
notifyStateMachineStarted(getRelayStateMachine(), buildStateContext(Stage.STATEMACHINE_START, queuedMessage, null, getRelayStateMachine()));
|
||||
} else if (t.getKind() != TransitionKind.INTERNAL) {
|
||||
switchToState(t.getTarget(), queuedMessage, t, getRelayStateMachine());
|
||||
}
|
||||
// TODO: looks like events should be called here and anno processing earlier
|
||||
notifyTransitionEnd(t, queuedMessage, buildStateContext(queuedMessage, null, getRelayStateMachine()));
|
||||
notifyTransitionEnd(t, queuedMessage, buildStateContext(Stage.TRANSITION_END, queuedMessage, null, getRelayStateMachine()));
|
||||
}
|
||||
});
|
||||
stateMachineExecutor = executor;
|
||||
@@ -303,7 +304,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
// assume that state was set/reseted so we need to
|
||||
// dispatch started event which would net getting
|
||||
// dispatched via executor
|
||||
StateContext<S, E> stateContext = buildStateContext(null, null, getRelayStateMachine());
|
||||
StateContext<S, E> stateContext = buildStateContext(Stage.STATEMACHINE_START, null, null, getRelayStateMachine());
|
||||
notifyStateMachineStarted(getRelayStateMachine(), stateContext);
|
||||
return;
|
||||
}
|
||||
@@ -325,7 +326,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
@Override
|
||||
protected void doStop() {
|
||||
stateMachineExecutor.stop();
|
||||
notifyStateMachineStopped(this, buildStateContext(null, null, this));
|
||||
notifyStateMachineStopped(this, buildStateContext(Stage.STATEMACHINE_STOP, null, null, this));
|
||||
currentState = null;
|
||||
initialEnabled = null;
|
||||
}
|
||||
@@ -346,7 +347,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
currentError = exception;
|
||||
}
|
||||
if (currentError != null) {
|
||||
notifyStateMachineError(this, currentError, buildStateContext(null, null, this, currentError));
|
||||
notifyStateMachineError(this, currentError, buildStateContext(Stage.STATEMACHINE_ERROR, null, null, this, currentError));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +625,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
PseudoStateKind kind = state.getPseudoState() != null ? state.getPseudoState().getKind() : null;
|
||||
if (kind == PseudoStateKind.CHOICE || kind == PseudoStateKind.HISTORY_SHALLOW
|
||||
|| kind == PseudoStateKind.HISTORY_DEEP) {
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_CHANGED, message, transition, stateMachine);
|
||||
State<S, E> toState = state.getPseudoState().entry(stateContext);
|
||||
|
||||
if (kind == PseudoStateKind.CHOICE) {
|
||||
@@ -659,7 +660,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
public void onContext(PseudoStateContext<S, E> context) {
|
||||
PseudoState<S, E> pseudoState = context.getPseudoState();
|
||||
State<S, E> toState = findStateWithPseudoState(pseudoState);
|
||||
StateContext<S, E> stateContext = buildStateContext(null, null, getRelayStateMachine());
|
||||
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_EXIT, null, null, getRelayStateMachine());
|
||||
pseudoState.exit(stateContext);
|
||||
switchToState(toState, null, null, getRelayStateMachine());
|
||||
}
|
||||
@@ -677,22 +678,22 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
return null;
|
||||
}
|
||||
|
||||
private StateContext<S, E> buildStateContext(Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
private StateContext<S, E> buildStateContext(Stage stage, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
return new DefaultStateContext<S, E>(message, messageHeaders, extendedState, transition, stateMachine);
|
||||
return new DefaultStateContext<S, E>(stage, message, messageHeaders, extendedState, transition, stateMachine, null, null, null);
|
||||
}
|
||||
|
||||
private StateContext<S, E> buildStateContext(Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, Exception exception) {
|
||||
private StateContext<S, E> buildStateContext(Stage stage, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, Exception exception) {
|
||||
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
return new DefaultStateContext<S, E>(message, messageHeaders, extendedState, transition, stateMachine, null, null, exception);
|
||||
return new DefaultStateContext<S, E>(stage, message, messageHeaders, extendedState, transition, stateMachine, null, null, exception);
|
||||
}
|
||||
|
||||
private StateContext<S, E> buildStateContext(Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target) {
|
||||
private StateContext<S, E> buildStateContext(Stage stage, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target) {
|
||||
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
|
||||
new HashMap<String, Object>());
|
||||
return new DefaultStateContext<S, E>(message, messageHeaders, extendedState, transition, stateMachine, source, target, null);
|
||||
return new DefaultStateContext<S, E>(stage, message, messageHeaders, extendedState, transition, stateMachine, source, target, null);
|
||||
}
|
||||
|
||||
private State<S, E> findDeepParent(State<S, E> state) {
|
||||
@@ -727,7 +728,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
start();
|
||||
}
|
||||
entryToState(state, message, transition, stateMachine);
|
||||
notifyStateChanged(notifyFrom, state, message, buildStateContext(message, null, getRelayStateMachine(), notifyFrom, state));
|
||||
notifyStateChanged(notifyFrom, state, message, buildStateContext(Stage.STATE_CHANGED, message, null, getRelayStateMachine(), notifyFrom, state));
|
||||
nonDeepStatePresent = true;
|
||||
} else if (currentState == null && StateMachineUtils.isSubstate(findDeep, state)) {
|
||||
if (exit) {
|
||||
@@ -739,7 +740,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
start();
|
||||
}
|
||||
entryToState(findDeep, message, transition, stateMachine);
|
||||
notifyStateChanged(notifyFrom, findDeep, message, buildStateContext(message, null, getRelayStateMachine(), notifyFrom, findDeep));
|
||||
notifyStateChanged(notifyFrom, findDeep, message, buildStateContext(Stage.STATE_CHANGED, message, null, getRelayStateMachine(), notifyFrom, findDeep));
|
||||
}
|
||||
|
||||
if (currentState != null && !nonDeepStatePresent) {
|
||||
@@ -826,7 +827,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
return;
|
||||
}
|
||||
log.trace("Trying Exit state=[" + state + "]");
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_EXIT, message, transition, stateMachine);
|
||||
|
||||
if (transition != null) {
|
||||
|
||||
@@ -853,7 +854,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
log.debug("Exit state=[" + state + "]");
|
||||
state.exit(stateContext);
|
||||
|
||||
notifyStateExited(state, message, buildStateContext(message, null, getRelayStateMachine(), state, null));
|
||||
notifyStateExited(state, message, buildStateContext(Stage.STATE_EXIT, message, null, getRelayStateMachine(), state, null));
|
||||
}
|
||||
|
||||
private void entryToState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
@@ -861,7 +862,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
return;
|
||||
}
|
||||
log.trace("Trying Enter state=[" + state + "]");
|
||||
StateContext<S, E> stateContext = buildStateContext(message, transition, stateMachine);
|
||||
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_ENTRY, message, transition, stateMachine);
|
||||
|
||||
if (transition != null) {
|
||||
State<S, E> findDeep1 = findDeepParent(transition.getTarget());
|
||||
@@ -881,7 +882,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
}
|
||||
}
|
||||
|
||||
notifyStateEntered(state, message, buildStateContext(message, null, getRelayStateMachine(), null, state));
|
||||
notifyStateEntered(state, message, buildStateContext(Stage.STATE_ENTRY, message, null, getRelayStateMachine(), null, state));
|
||||
log.debug("Enter state=[" + state + "]");
|
||||
state.entry(stateContext);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import org.springframework.statemachine.transition.Transition;
|
||||
*/
|
||||
public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
|
||||
private final Stage stage;
|
||||
private final Message<E> message;
|
||||
private final MessageHeaders messageHeaders;
|
||||
private final ExtendedState extendedState;
|
||||
@@ -42,13 +43,22 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
private final State<S, E> target;
|
||||
private final Exception exception;
|
||||
|
||||
public DefaultStateContext(Message<E> message, MessageHeaders messageHeaders, ExtendedState extendedState, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
this(message, messageHeaders, extendedState, transition, stateMachine, null, null, null);
|
||||
}
|
||||
|
||||
public DefaultStateContext(Message<E> message, MessageHeaders messageHeaders, ExtendedState extendedState, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target, Exception exception) {
|
||||
/**
|
||||
* Instantiates a new default state context.
|
||||
*
|
||||
* @param stage the stage
|
||||
* @param message the message
|
||||
* @param messageHeaders the message headers
|
||||
* @param extendedState the extended state
|
||||
* @param transition the transition
|
||||
* @param stateMachine the state machine
|
||||
* @param source the source
|
||||
* @param target the target
|
||||
* @param exception the exception
|
||||
*/
|
||||
public DefaultStateContext(Stage stage, Message<E> message, MessageHeaders messageHeaders, ExtendedState extendedState,
|
||||
Transition<S, E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target, Exception exception) {
|
||||
this.stage = stage;
|
||||
this.message = message;
|
||||
this.messageHeaders = messageHeaders;
|
||||
this.extendedState = extendedState;
|
||||
@@ -59,6 +69,11 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stage getStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E getEvent() {
|
||||
return message != null ? message.getPayload() : null;
|
||||
@@ -113,4 +128,12 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultStateContext [stage=" + stage + ", message=" + message + ", messageHeaders=" + messageHeaders + ", extendedState="
|
||||
+ extendedState + ", transition=" + transition + ", stateMachine=" + stateMachine + ", source=" + source + ", target="
|
||||
+ target + ", exception=" + exception + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.StateContext.Stage;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.statemachine.trigger.DefaultTriggerContext;
|
||||
@@ -405,7 +406,7 @@ public class DefaultStateMachineExecutor<S, E> extends LifecycleObjectSupport im
|
||||
// we want to keep the originating sm id
|
||||
map.put(StateMachineSystemConstants.STATEMACHINE_IDENTIFIER, stateMachine.getId());
|
||||
}
|
||||
return new DefaultStateContext<S, E>(message, new MessageHeaders(map), stateMachine.getExtendedState(), transition, stateMachine);
|
||||
return new DefaultStateContext<S, E>(Stage.TRANSITION, message, new MessageHeaders(map), stateMachine.getExtendedState(), transition, stateMachine, null, null, null);
|
||||
}
|
||||
|
||||
private void registerTriggerListener() {
|
||||
|
||||
@@ -131,6 +131,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateChanged(State<S,E> source, State<S,E> target, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateChanged(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateChanged(source, target);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -142,6 +143,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateEntered(State<S,E> state, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateEntry(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateEntered(state);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -153,6 +155,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateExited(State<S,E> state, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateExit(getBeanName(), null, message, stateContext);
|
||||
stateListener.stateExited(state);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -164,6 +167,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyEventNotAccepted(Message<E> event, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnEventNotAccepted(getBeanName(), stateContext);
|
||||
stateListener.eventNotAccepted(event);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -175,6 +179,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyTransitionStart(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransitionStart(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transitionStarted(transition);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -186,6 +191,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyTransition(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransition(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transition(transition);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -197,6 +203,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyTransitionEnd(Transition<S,E> transition, Message<E> message, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnTransitionEnd(getBeanName(), transition, message, stateContext);
|
||||
stateListener.transitionEnded(transition);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -208,6 +215,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateMachineStarted(StateMachine<S, E> stateMachine, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineStart(getBeanName(), stateContext);
|
||||
stateListener.stateMachineStarted(stateMachine);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -219,6 +227,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateMachineStopped(StateMachine<S, E> stateMachine, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineStop(getBeanName(), stateContext);
|
||||
stateListener.stateMachineStopped(stateMachine);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -230,6 +239,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyStateMachineError(StateMachine<S, E> stateMachine, Exception exception, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnStateMachineError(getBeanName(), stateContext);
|
||||
stateListener.stateMachineError(stateMachine, exception);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -241,6 +251,7 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
protected void notifyExtendedStateChanged(Object key, Object value, StateContext<S, E> stateContext) {
|
||||
stateMachineHandlerCallHelper.callOnExtendedStateChanged(getBeanName(), key, value, stateContext);
|
||||
stateListener.extendedStateChanged(key, value);
|
||||
stateListener.stateContext(stateContext);
|
||||
if (contextEventsEnabled) {
|
||||
StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher();
|
||||
if (eventPublisher != null) {
|
||||
@@ -331,6 +342,11 @@ public abstract class StateMachineObjectSupport<S, E> extends LifecycleObjectSup
|
||||
stateListener.extendedStateChanged(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<S, E> stateContext) {
|
||||
stateListener.stateContext(stateContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.statemachine;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
|
||||
|
||||
public class StateContextTests extends AbstractStateMachineTests {
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
return new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartCycles() throws Exception {
|
||||
context.register(Config1.class);
|
||||
context.refresh();
|
||||
@SuppressWarnings("unchecked")
|
||||
StateMachine<States, Events> machine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
|
||||
|
||||
TestStateMachineListener listener = new TestStateMachineListener();
|
||||
machine.addStateListener(listener);
|
||||
|
||||
machine.start();
|
||||
assertThat(machine.getState().getIds(), containsInAnyOrder(States.S0, States.S1, States.S11));
|
||||
assertThat(listener.contexts.size(), is(19));
|
||||
// TODO: continue with other tests to verify context fields
|
||||
}
|
||||
|
||||
static class TestStateMachineListener extends StateMachineListenerAdapter<States, Events> {
|
||||
|
||||
ArrayList<StateContext<States, Events>> contexts = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<States, Events> stateContext) {
|
||||
contexts.add(stateContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<States, Events> states)
|
||||
throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(States.S0, fooAction())
|
||||
.state(States.S0)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(States.S0)
|
||||
.initial(States.S1)
|
||||
.state(States.S1)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(States.S1)
|
||||
.initial(States.S11)
|
||||
.state(States.S11)
|
||||
.state(States.S12)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(States.S0)
|
||||
.state(States.S2)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(States.S2)
|
||||
.initial(States.S21)
|
||||
.state(States.S21)
|
||||
.and()
|
||||
.withStates()
|
||||
.parent(States.S21)
|
||||
.initial(States.S211)
|
||||
.state(States.S211)
|
||||
.state(States.S212);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
|
||||
throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S1).event(Events.A)
|
||||
.guard(foo1Guard())
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S11).event(Events.B)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S21).target(States.S211).event(Events.B)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S2).event(Events.C)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S2).target(States.S1).event(Events.C)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S0).event(Events.D)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S211).target(States.S21).event(Events.D)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S0).target(States.S211).event(Events.E)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S211).event(Events.F)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S2).target(States.S11).event(Events.F)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S11).target(States.S211).event(Events.G)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S211).target(States.S0).event(Events.G)
|
||||
.and()
|
||||
.withInternal()
|
||||
.source(States.S0).event(Events.H)
|
||||
.guard(foo0Guard())
|
||||
.action(fooAction())
|
||||
.and()
|
||||
.withInternal()
|
||||
.source(States.S2).event(Events.H)
|
||||
.guard(foo1Guard())
|
||||
.action(fooAction())
|
||||
.and()
|
||||
.withInternal()
|
||||
.source(States.S1).event(Events.H)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S11).target(States.S12).event(Events.I)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S211).target(States.S212).event(Events.I)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S12).target(States.S212).event(Events.I);
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FooGuard foo0Guard() {
|
||||
return new FooGuard(0);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FooGuard foo1Guard() {
|
||||
return new FooGuard(1);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FooAction fooAction() {
|
||||
return new FooAction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static enum States {
|
||||
S0, S1, S11, S12, S2, S21, S211, S212
|
||||
}
|
||||
|
||||
public static enum Events {
|
||||
A, B, C, D, E, F, G, H, I
|
||||
}
|
||||
|
||||
private static class FooAction implements Action<States, Events> {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Map<Object, Object> variables = context.getExtendedState().getVariables();
|
||||
Integer foo = context.getExtendedState().get("foo", Integer.class);
|
||||
if (foo == null) {
|
||||
variables.put("foo", 0);
|
||||
} else if (foo == 0) {
|
||||
variables.put("foo", 1);
|
||||
} else if (foo == 1) {
|
||||
variables.put("foo", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class FooGuard implements Guard<States, Events> {
|
||||
|
||||
private final int match;
|
||||
|
||||
public FooGuard(int match) {
|
||||
this.match = match;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean evaluate(StateContext<States, Events> context) {
|
||||
Object foo = context.getExtendedState().getVariables().get("foo");
|
||||
return !(foo == null || !foo.equals(match));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -59,8 +59,8 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
|
||||
Map<String, Object> map = new HashMap<String, Object>();
|
||||
map.put("foo", "bar");
|
||||
MessageHeaders headers = new MessageHeaders(map);
|
||||
DefaultStateContext<TestStates, TestEvents> stateContext = new DefaultStateContext<TestStates, TestEvents>(null, headers, null, null, null);
|
||||
|
||||
DefaultStateContext<TestStates, TestEvents> stateContext = new DefaultStateContext<TestStates, TestEvents>(null, null, headers,
|
||||
null, null, null, null, null, null);
|
||||
assertThat(guard.evaluate(stateContext), is(true));
|
||||
}
|
||||
|
||||
|
||||
@@ -219,6 +219,10 @@ public class ListenerTests extends AbstractStateMachineTests {
|
||||
extendedLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<TestStates, TestEvents> stateContext) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateContext.Stage;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.annotation.EventHeaders;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
@@ -62,7 +63,7 @@ public class MethodParameterTests {
|
||||
StateMachineRuntime<String, String> runtime = new StateMachineRuntime<String, String>() {
|
||||
@Override
|
||||
public StateContext<String, String> getStateContext() {
|
||||
return new DefaultStateContext<String, String>(message, messageHeaders, extendedState, transition, stateMachine, source,
|
||||
return new DefaultStateContext<String, String>(Stage.TRANSITION, message, messageHeaders, extendedState, transition, stateMachine, source,
|
||||
target, exception);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ public class StateContextExpressionMethodsTests {
|
||||
extendedState.getVariables().put("boolean1", true);
|
||||
extendedState.getVariables().put("boolean2", false);
|
||||
StateContext<SpelStates, SpelEvents> stateContext = new DefaultStateContext<SpelStates, SpelEvents>(
|
||||
MessageBuilder.withPayload(SpelEvents.E1).build(), messageHeaders, extendedState, new MockTransition(), stateMachine);
|
||||
null, MessageBuilder.withPayload(SpelEvents.E1).build(), messageHeaders, extendedState, new MockTransition(), stateMachine, null, null, null);
|
||||
return stateContext;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user