diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java index 80a8a1de..07efc239 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java @@ -153,10 +153,10 @@ public abstract class AbstractState implements State { } @Override - public abstract void exit(E event, StateContext context); + public abstract void exit(StateContext context); @Override - public abstract void entry(E event, StateContext context); + public abstract void entry(StateContext context); @Override public S getId() { diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java index e7aea2e6..35430101 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java @@ -118,7 +118,7 @@ public class EnumState, E extends Enum> extends AbstractSim } @Override - public void exit(E event, StateContext context) { + public void exit(StateContext context) { Collection> actions = getExitActions(); if (actions != null) { for (Action action : actions) { @@ -128,7 +128,7 @@ public class EnumState, E extends Enum> extends AbstractSim } @Override - public void entry(E event, StateContext context) { + public void entry(StateContext context) { Collection> actions = getEntryActions(); if (actions != null) { for (Action action : actions) { diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java index 721178d3..6d0369a6 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java @@ -106,9 +106,9 @@ public class RegionState extends AbstractState { } @Override - public void exit(E event, StateContext context) { + public void exit(StateContext context) { for (Region region : getRegions()) { - region.getState().exit(event, context); + region.getState().exit(context); region.stop(); } Collection> actions = getExitActions(); @@ -120,7 +120,7 @@ public class RegionState extends AbstractState { } @Override - public void entry(E event, StateContext context) { + public void entry(StateContext context) { Collection> actions = getEntryActions(); if (actions != null) { for (Action action : actions) { @@ -135,7 +135,7 @@ public class RegionState extends AbstractState { } else { for (Region region : getRegions()) { if (region.getState() != null) { - region.getState().entry(event, context); + region.getState().entry(context); } } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java index fe0a50c5..728de99e 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java @@ -42,18 +42,16 @@ public interface State { /** * Initiate an exit sequence for the state. * - * @param event the event - * @param context the context + * @param context the state context */ - void exit(E event, StateContext context); + void exit(StateContext context); /** * Initiate an entry sequence for the state. * - * @param event the event - * @param context the context + * @param context the state context */ - void entry(E event, StateContext context); + void entry(StateContext context); /** * Gets the state identifier. diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java index ee55c36e..0dad7038 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java @@ -22,6 +22,7 @@ import org.springframework.messaging.Message; import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.action.Action; +import org.springframework.statemachine.support.StateMachineUtils; import org.springframework.statemachine.transition.Transition; import org.springframework.statemachine.transition.TransitionKind; @@ -131,13 +132,15 @@ public class StateMachineState extends AbstractState { } @Override - public void exit(E event, StateContext context) { + public void exit(StateContext context) { // don't stop if it looks like we're coming back // stop would cause start with entry which would // enable default transition and state - if (getSubmachine().getState() != null && context.getTransition().getSource().getId() != getSubmachine().getState().getId()) { + if (getSubmachine().getState() != null + && context.getTransition().getSource().getId() != getSubmachine().getState().getId()) { getSubmachine().stop(); - } else if (!isSubstate(context.getTransition().getTarget(), context.getTransition().getSource())) { + } else if (!StateMachineUtils.isSubstate(context.getTransition().getTarget(), context.getTransition() + .getSource())) { getSubmachine().stop(); } Collection> actions = getExitActions(); @@ -149,7 +152,7 @@ public class StateMachineState extends AbstractState { } @Override - public void entry(E event, StateContext context) { + public void entry(StateContext context) { Collection> actions = getEntryActions(); if (actions != null && !isLocal(context)) { for (Action action : actions) { @@ -180,12 +183,6 @@ public class StateMachineState extends AbstractState { } } - private boolean isSubstate(State left, State right) { - Collection> c = left.getStates(); - c.remove(left); - return c.contains(right); - } - @Override public String toString() { return "StateMachineState [getIds()=" + getIds() + ", toString()=" + super.toString() + ", getClass()=" diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 938f8a3c..cfe8c29e 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -43,8 +43,6 @@ import org.springframework.statemachine.ExtendedState; import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.annotation.OnTransition; -import org.springframework.statemachine.event.StateMachineEventPublisher; -import org.springframework.statemachine.listener.CompositeStateMachineListener; import org.springframework.statemachine.listener.StateMachineListener; import org.springframework.statemachine.processor.StateMachineHandler; import org.springframework.statemachine.processor.StateMachineOnTransitionHandler; @@ -72,7 +70,7 @@ import org.springframework.util.Assert; * @param the type of state * @param the type of event */ -public abstract class AbstractStateMachine extends LifecycleObjectSupport implements StateMachine { +public abstract class AbstractStateMachine extends StateMachineObjectSupport implements StateMachine { private static final Log log = LogFactory.getLog(AbstractStateMachine.class); @@ -92,8 +90,6 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport private final LinkedList> deferList = new LinkedList>(); - private final CompositeStateMachineListener stateListener = new CompositeStateMachineListener(); - private volatile State currentState; private volatile PseudoState history; @@ -108,8 +104,6 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport private final Map, Transition> triggerToTransitionMap = new HashMap, Transition>(); - private boolean contextEventsEnabled = true; - /** * Instantiates a new abstract state machine. * @@ -231,8 +225,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport switchToState(initialState, initialEvent, null, this); // TODO: for now execute outside of switchToState if (initialTransition != null) { - StateContext stateContext = new DefaultStateContext(initialEvent != null ? initialEvent.getPayload() : null, - initialEvent != null ? initialEvent.getHeaders() : null, extendedState, initialTransition, this); + StateContext stateContext = buildStateContext(initialEvent, initialTransition, this); initialTransition.transit(stateContext); } notifyStateMachineStarted(this); @@ -246,7 +239,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport @Override public void addStateListener(StateMachineListener listener) { - stateListener.register(listener); + getStateListener().register(listener); } @Override @@ -288,26 +281,15 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport return buf.toString(); } - /** - * Set if context application events are enabled. Events - * are enabled by default. Set this to false if you don't - * want state machine to send application context events. - * - * @param contextEventsEnabled the enabled flag - */ - public void setContextEventsEnabled(boolean contextEventsEnabled) { - this.contextEventsEnabled = contextEventsEnabled; - } + protected boolean acceptEvent(Message message) { - protected boolean acceptEvent(Message event) { - - boolean accepted = currentState.sendEvent(event); + boolean accepted = currentState.sendEvent(message); if (accepted) { return true; } if (log.isDebugEnabled()) { - log.debug("Queue event " + event); + log.debug("Queue event " + message); } Message defer = null; @@ -316,11 +298,11 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport Trigger trigger = transition.getTrigger(); if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) { - if (trigger != null && trigger.evaluate(new DefaultTriggerContext(event.getPayload()))) { - triggerQueue.add(new TriggerQueueItem(trigger, event)); + if (trigger != null && trigger.evaluate(new DefaultTriggerContext(message.getPayload()))) { + triggerQueue.add(new TriggerQueueItem(trigger, message)); return true; - } else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(event.getPayload())) { - defer = event; + } else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(message.getPayload())) { + defer = message; } } } @@ -333,32 +315,17 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport return false; } - private void switchToState(State state, Message event, Transition transition, StateMachine stateMachine) { + private void switchToState(State state, Message message, Transition transition, StateMachine stateMachine) { // TODO: need to make below more clear when // we figure out rest of a pseudostates - if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.CHOICE) { - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, - messageHeaders, extendedState, transition, stateMachine); - State entry = state.getPseudoState().entry(event.getPayload(), stateContext); - setCurrentState(entry, event, transition, true, stateMachine); - } else if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.HISTORY_SHALLOW) { - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, - messageHeaders, extendedState, transition, stateMachine); - State entry = state.getPseudoState().entry(event.getPayload(), stateContext); - setCurrentState(entry, event, transition, true, stateMachine); - } else if (state.getPseudoState() != null && state.getPseudoState().getKind() == PseudoStateKind.HISTORY_DEEP) { - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, - messageHeaders, extendedState, transition, stateMachine); - State entry = state.getPseudoState().entry(event.getPayload(), stateContext); - setCurrentState(entry, event, transition, true, stateMachine); + PseudoStateKind kind = state.getPseudoState() != null ? state.getPseudoState().getKind() : null; + if (kind == PseudoStateKind.CHOICE || kind == PseudoStateKind.HISTORY_SHALLOW + || kind == PseudoStateKind.HISTORY_DEEP) { + StateContext stateContext = buildStateContext(message, transition, stateMachine); + State toState = state.getPseudoState().entry(message.getPayload(), stateContext); + setCurrentState(toState, message, transition, true, stateMachine); } else { - setCurrentState(state, event, transition, true, stateMachine); + setCurrentState(state, message, transition, true, stateMachine); } // TODO: should handle triggerles transition some how differently @@ -366,7 +333,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport State source = t.getSource(); State target = t.getTarget(); if (t.getTrigger() == null && source.equals(currentState)) { - switchToState(target, event, t, stateMachine); + switchToState(target, message, t, stateMachine); } } if (isComplete()) { @@ -374,6 +341,13 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } } + private StateContext buildStateContext(Message message, Transition transition, StateMachine stateMachine) { + E event = message != null ? message.getPayload() : null; + MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders( + new HashMap()); + return new DefaultStateContext(event, messageHeaders, extendedState, transition, stateMachine); + } + private State findDeepParent(State state) { for (State s : states) { if (s.getStates().contains(state)) { @@ -383,11 +357,11 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport return null; } - void setCurrentState(State state, Message event, Transition transition, boolean exit, StateMachine stateMachine) { + void setCurrentState(State state, Message message, Transition transition, boolean exit, StateMachine stateMachine) { State findDeep = findDeepParent(state); boolean isTargetSubOf = false; if (transition != null) { - isTargetSubOf = isSubstate(state, transition.getSource()); + isTargetSubOf = StateMachineUtils.isSubstate(state, transition.getSource()); if (isTargetSubOf && currentState == transition.getTarget()) { state = transition.getSource(); } @@ -395,37 +369,37 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport if (states.contains(state)) { if (exit) { - exitCurrentState(state, event, transition, stateMachine); + exitCurrentState(state, message, transition, stateMachine); } State notifyFrom = currentState; currentState = state; if (!isRunning()) { start(); } - entryToState(state, event, transition, stateMachine); + entryToState(state, message, transition, stateMachine); notifyStateChanged(notifyFrom, state); } else if (currentState != null) { if (findDeep != null) { if (exit) { - exitCurrentState(state, event, transition, stateMachine); + exitCurrentState(state, message, transition, stateMachine); } if (currentState == findDeep) { StateMachine submachine = ((AbstractState)currentState).getSubmachine(); if (submachine.getState() == state) { if (currentState == findDeep) { if (isTargetSubOf) { - entryToState(currentState, event, transition, stateMachine); + entryToState(currentState, message, transition, stateMachine); } currentState = findDeep; - ((AbstractStateMachine)submachine).setCurrentState(state, event, transition, false, stateMachine); + ((AbstractStateMachine)submachine).setCurrentState(state, message, transition, false, stateMachine); return; } } } currentState = findDeep; - entryToState(currentState, event, transition, stateMachine); + entryToState(currentState, message, transition, stateMachine); StateMachine submachine = ((AbstractState)currentState).getSubmachine(); - ((AbstractStateMachine)submachine).setCurrentState(state, event, transition, false, stateMachine); + ((AbstractStateMachine)submachine).setCurrentState(state, message, transition, false, stateMachine); } } if (history != null) { @@ -437,86 +411,81 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } } - void exitCurrentState(State state, Message event, Transition transition, StateMachine stateMachine) { + void exitCurrentState(State state, Message message, Transition transition, StateMachine stateMachine) { if (currentState == null) { return; } if (currentState.isSubmachineState()) { StateMachine submachine = ((AbstractState)currentState).getSubmachine(); - ((AbstractStateMachine)submachine).exitCurrentState(state, event, transition, stateMachine); - exitFromState(currentState, event, transition, stateMachine); + ((AbstractStateMachine)submachine).exitCurrentState(state, message, transition, stateMachine); + exitFromState(currentState, message, transition, stateMachine); } else { - exitFromState(currentState, event, transition, stateMachine); + exitFromState(currentState, message, transition, stateMachine); } } - private boolean isSubstate(State left, State right) { - Collection> c = left.getStates(); - c.remove(left); - return c.contains(right); + private void exitFromState(State state, Message message, Transition transition, StateMachine stateMachine) { + if (state == null) { + return; + } + log.trace("Trying Exit state=[" + state + "]"); + StateContext stateContext = buildStateContext(message, transition, stateMachine); + + State findDeep = findDeepParent(transition.getTarget()); + boolean isTargetSubOfOtherState = findDeep != null && findDeep != currentState; + boolean isTargetSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), transition.getTarget()); + boolean isSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), currentState); + boolean isSubOfTarget = StateMachineUtils.isSubstate(transition.getTarget(), currentState); + + // TODO: this and entry below should be done via a separate + // voter of some sort which would reveal transition path + // we could make a choice on. + if (currentState == transition.getSource() && currentState == transition.getTarget()) { + } else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getSource()) { + } else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) { + } else if (isTargetSubOfOtherState) { + } else if (!isSubOfSource && !isSubOfTarget && findDeep == null) { + } else if (!isSubOfSource && !isSubOfTarget) { + return; + } + + if (transition.getSource() == currentState && isTargetSubOfSource) { + return; + } + + log.debug("Exit state=[" + state + "]"); + state.exit(stateContext); + notifyStateExited(state); } - private void exitFromState(State state, Message event, Transition transition, StateMachine stateMachine) { - if (state != null) { - log.trace("Exit state=[" + state + "]"); - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, - messageHeaders, extendedState, transition, stateMachine); + private void entryToState(State state, Message message, Transition transition, StateMachine stateMachine) { + if (state == null) { + return; + } + log.trace("Trying Enter state=[" + state + "]"); + StateContext stateContext = buildStateContext(message, transition, stateMachine); - State findDeep = findDeepParent(transition.getTarget()); - boolean isTargetSubOfOtherState = findDeep != null && findDeep != currentState; + if (transition != null) { + State findDeep1 = findDeepParent(transition.getTarget()); + State findDeep2 = findDeepParent(transition.getSource()); + boolean isComingFromOtherSubmachine = findDeep1 != null && findDeep2 != null && findDeep2 != currentState; - boolean isSubOfSource = isSubstate(transition.getSource(), currentState); - boolean isSubOfTarget = isSubstate(transition.getTarget(), currentState); + boolean isSubOfSource = StateMachineUtils.isSubstate(transition.getSource(), currentState); + boolean isSubOfTarget = StateMachineUtils.isSubstate(transition.getTarget(), currentState); if (currentState == transition.getSource() && currentState == transition.getTarget()) { - } else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getSource()) { } else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) { - } else if (isTargetSubOfOtherState) { - } else if (!isSubOfSource && !isSubOfTarget && findDeep == null) { + } else if (isComingFromOtherSubmachine) { + } else if (!isSubOfSource && !isSubOfTarget && findDeep2 == null) { + } else if (isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) { + return; } else if (!isSubOfSource && !isSubOfTarget) { return; } - - boolean isTargetSubOfSource = isSubstate(transition.getSource(), transition.getTarget()); - if (transition.getSource() == currentState && isTargetSubOfSource) { - return; - } - - state.exit(event != null ? event.getPayload() : null, stateContext); - notifyStateExited(state); } - } - private void entryToState(State state, Message event, Transition transition, StateMachine stateMachine) { - if (state != null) { - log.trace("Enter state=[" + state + "]"); - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, - messageHeaders, extendedState, transition, stateMachine); - - if (transition != null) { - State findDeep1 = findDeepParent(transition.getTarget()); - State findDeep2 = findDeepParent(transition.getSource()); - boolean isComingFromOtherSubmachine = findDeep1 != null && findDeep2 != null && findDeep2 != currentState; - - boolean isSubOfSource = isSubstate(transition.getSource(), currentState); - boolean isSubOfTarget = isSubstate(transition.getTarget(), currentState); - if (currentState == transition.getSource() && currentState == transition.getTarget()) { - } else if (!isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) { - } else if (isComingFromOtherSubmachine) { - } else if (!isSubOfSource && !isSubOfTarget && findDeep2 == null) { - } else if (isSubOfSource && !isSubOfTarget && currentState == transition.getTarget()) { - return; - } else if (!isSubOfSource && !isSubOfTarget) { - return; - } - } - - notifyStateEntered(state); - state.entry(event != null ? event.getPayload() : null, stateContext); - } + notifyStateEntered(state); + log.debug("Enter state=[" + state + "]"); + state.entry(stateContext); } private void processEventQueue() { @@ -590,8 +559,8 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport continue; } - Message queuedEvent = queueItem.message; - E event = queuedEvent != null ? queuedEvent.getPayload() : null; + Message queuedMessage = queueItem.message; + E event = queuedMessage != null ? queuedMessage.getPayload() : null; // need all transitions trigger could match, event trigger may match multiple // need to go up from substates and ask if trigger transit, if not check super @@ -622,8 +591,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport // go through candidates and transit max one for (Transition t : trans) { - StateContext stateContext = new DefaultStateContext(queuedEvent != null ? queuedEvent.getPayload() : null, - queuedEvent != null ? queuedEvent.getHeaders() : null, extendedState, t, this); + StateContext stateContext = buildStateContext(queuedMessage, t, this); if (t == null) { continue; } @@ -640,9 +608,9 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport // if transition will transit so that we can post // accurate notifyTransitionStart notifyTransitionStart(t); - callHandlers(t.getSource(), t.getTarget(), queuedEvent); + callHandlers(t.getSource(), t.getTarget(), queuedMessage); if (t.getKind() != TransitionKind.INTERNAL) { - switchToState(t.getTarget(), queuedEvent, t, this); + switchToState(t.getTarget(), queuedMessage, t, this); } notifyTransition(t); notifyTransitionEnd(t); @@ -654,16 +622,15 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } - private void callHandlers(State sourceState, State targetState, Message event) { + private void callHandlers(State sourceState, State targetState, Message message) { if (sourceState != null && targetState != null) { - MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( - new HashMap()); - StateContext stateContext = new DefaultStateContext(event != null ? event.getPayload() : null, messageHeaders, extendedState, null, this); + StateContext stateContext = buildStateContext(message, null, this); getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext); } } - private List getStateMachineHandlerResults(List> stateMachineHandlers, final StateContext stateContext) { + private List getStateMachineHandlerResults(List> stateMachineHandlers, + final StateContext stateContext) { StateMachineRuntime runtime = new StateMachineRuntime() { @Override public StateContext getStateContext() { @@ -677,7 +644,8 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport return results; } - private synchronized List> getStateMachineHandlers(State sourceState, State targetState) { + private synchronized List> getStateMachineHandlers(State sourceState, + State targetState) { BeanFactory beanFactory = getBeanFactory(); // TODO think how to handle null bf @@ -766,86 +734,6 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } } - private void notifyStateChanged(State source, State target) { - stateListener.stateChanged(source, target); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishStateChanged(this, source, target); - } - } - } - - private void notifyStateEntered(State state) { - stateListener.stateEntered(state); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishStateEntered(this, state); - } - } - } - - private void notifyStateExited(State state) { - stateListener.stateExited(state); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishStateExited(this, state); - } - } - } - - private void notifyTransitionStart(Transition transition) { - stateListener.transitionStarted(transition); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishTransitionStart(this, transition); - } - } - } - - private void notifyTransition(Transition transition) { - stateListener.transition(transition); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishTransitionEnd(this, transition); - } - } - } - - private void notifyTransitionEnd(Transition transition) { - stateListener.transitionEnded(transition); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishTransition(this, transition); - } - } - } - - private void notifyStateMachineStarted(StateMachine stateMachine) { - stateListener.stateMachineStarted(stateMachine); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishStateMachineStart(this, stateMachine); - } - } - } - - private void notifyStateMachineStopped(StateMachine stateMachine) { - stateListener.stateMachineStopped(stateMachine); - if (contextEventsEnabled) { - StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); - if (eventPublisher != null) { - eventPublisher.publishStateMachineStop(this, stateMachine); - } - } - } - private class TriggerQueueItem { Trigger trigger; Message message; @@ -855,53 +743,4 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport } } - /** - * This class is used to relay listener events from a submachines which works - * as its own listener context. User only connects to main root machine and - * expects to get events for all machines from there. - */ - private class StateMachineListenerRelay implements StateMachineListener { - - @Override - public void stateChanged(State from, State to) { - stateListener.stateChanged(from, to); - } - - @Override - public void stateEntered(State state) { - stateListener.stateEntered(state); - } - - @Override - public void stateExited(State state) { - stateListener.stateExited(state); - } - - @Override - public void transition(Transition transition) { - stateListener.transition(transition); - } - - @Override - public void transitionStarted(Transition transition) { - stateListener.transitionStarted(transition); - } - - @Override - public void transitionEnded(Transition transition) { - stateListener.transitionEnded(transition); - } - - @Override - public void stateMachineStarted(StateMachine stateMachine) { - stateListener.stateMachineStarted(stateMachine); - } - - @Override - public void stateMachineStopped(StateMachine stateMachine) { - stateListener.stateMachineStopped(stateMachine); - } - - } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/LifecycleObjectSupport.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/LifecycleObjectSupport.java index 8ba895d0..ea4987c6 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/LifecycleObjectSupport.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/LifecycleObjectSupport.java @@ -27,7 +27,6 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.context.SmartLifecycle; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.TaskScheduler; -import org.springframework.statemachine.event.StateMachineEventPublisher; import org.springframework.util.Assert; /** @@ -56,9 +55,6 @@ public abstract class LifecycleObjectSupport implements InitializingBean, SmartL // to access bean factory private volatile BeanFactory beanFactory; - /** Context application event publisher if exist */ - private volatile StateMachineEventPublisher stateMachineEventPublisher; - @Override public final void afterPropertiesSet() { try { @@ -230,31 +226,6 @@ public abstract class LifecycleObjectSupport implements InitializingBean, SmartL return taskExecutor; } - /** - * Gets the state machine event publisher. - * - * @return the state machine event publisher - */ - protected StateMachineEventPublisher getStateMachineEventPublisher() { - if(stateMachineEventPublisher == null && getBeanFactory() != null) { - if(log.isDebugEnabled()) { - log.debug("getting stateMachineEventPublisher service from bean factory " + getBeanFactory()); - } - stateMachineEventPublisher = StateMachineContextUtils.getEventPublisher(getBeanFactory()); - } - return stateMachineEventPublisher; - } - - /** - * Sets the state machine event publisher. - * - * @param stateMachineEventPublisher the new state machine event publisher - */ - public void setStateMachineEventPublisher(StateMachineEventPublisher stateMachineEventPublisher) { - Assert.notNull(stateMachineEventPublisher, "StateMachineEventPublisher cannot be null"); - this.stateMachineEventPublisher = stateMachineEventPublisher; - } - /** * Subclasses may implement this for initialization logic. Called * during the {@link InitializingBean} phase. Implementor should diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java new file mode 100644 index 00000000..c9086dab --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineObjectSupport.java @@ -0,0 +1,217 @@ +/* + * 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.support; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.event.StateMachineEventPublisher; +import org.springframework.statemachine.listener.CompositeStateMachineListener; +import org.springframework.statemachine.listener.StateMachineListener; +import org.springframework.statemachine.state.State; +import org.springframework.statemachine.transition.Transition; +import org.springframework.util.Assert; + +/** + * Support and helper class for base state machine implementation. + * + * @author Janne Valkealahti + * + * @param the type of state + * @param the type of event + */ +public abstract class StateMachineObjectSupport extends LifecycleObjectSupport { + + private static final Log log = LogFactory.getLog(StateMachineObjectSupport.class); + + private final CompositeStateMachineListener stateListener = new CompositeStateMachineListener(); + + /** Context application event publisher if exist */ + private volatile StateMachineEventPublisher stateMachineEventPublisher; + + /** Flag for application context events */ + private boolean contextEventsEnabled = true; + + /** + * Gets the state machine event publisher. + * + * @return the state machine event publisher + */ + protected StateMachineEventPublisher getStateMachineEventPublisher() { + if(stateMachineEventPublisher == null && getBeanFactory() != null) { + if(log.isDebugEnabled()) { + log.debug("getting stateMachineEventPublisher service from bean factory " + getBeanFactory()); + } + stateMachineEventPublisher = StateMachineContextUtils.getEventPublisher(getBeanFactory()); + } + return stateMachineEventPublisher; + } + + /** + * Sets the state machine event publisher. + * + * @param stateMachineEventPublisher the new state machine event publisher + */ + public void setStateMachineEventPublisher(StateMachineEventPublisher stateMachineEventPublisher) { + Assert.notNull(stateMachineEventPublisher, "StateMachineEventPublisher cannot be null"); + this.stateMachineEventPublisher = stateMachineEventPublisher; + } + + /** + * Set if context application events are enabled. Events + * are enabled by default. Set this to false if you don't + * want state machine to send application context events. + * + * @param contextEventsEnabled the enabled flag + */ + public void setContextEventsEnabled(boolean contextEventsEnabled) { + this.contextEventsEnabled = contextEventsEnabled; + } + + protected CompositeStateMachineListener getStateListener() { + return stateListener; + } + + protected void notifyStateChanged(State source, State target) { + stateListener.stateChanged(source, target); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateChanged(this, source, target); + } + } + } + + protected void notifyStateEntered(State state) { + stateListener.stateEntered(state); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateEntered(this, state); + } + } + } + + protected void notifyStateExited(State state) { + stateListener.stateExited(state); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateExited(this, state); + } + } + } + + protected void notifyTransitionStart(Transition transition) { + stateListener.transitionStarted(transition); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransitionStart(this, transition); + } + } + } + + protected void notifyTransition(Transition transition) { + stateListener.transition(transition); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransitionEnd(this, transition); + } + } + } + + protected void notifyTransitionEnd(Transition transition) { + stateListener.transitionEnded(transition); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishTransition(this, transition); + } + } + } + + protected void notifyStateMachineStarted(StateMachine stateMachine) { + stateListener.stateMachineStarted(stateMachine); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateMachineStart(this, stateMachine); + } + } + } + + protected void notifyStateMachineStopped(StateMachine stateMachine) { + stateListener.stateMachineStopped(stateMachine); + if (contextEventsEnabled) { + StateMachineEventPublisher eventPublisher = getStateMachineEventPublisher(); + if (eventPublisher != null) { + eventPublisher.publishStateMachineStop(this, stateMachine); + } + } + } + + /** + * This class is used to relay listener events from a submachines which works + * as its own listener context. User only connects to main root machine and + * expects to get events for all machines from there. + */ + protected class StateMachineListenerRelay implements StateMachineListener { + + @Override + public void stateChanged(State from, State to) { + stateListener.stateChanged(from, to); + } + + @Override + public void stateEntered(State state) { + stateListener.stateEntered(state); + } + + @Override + public void stateExited(State state) { + stateListener.stateExited(state); + } + + @Override + public void transition(Transition transition) { + stateListener.transition(transition); + } + + @Override + public void transitionStarted(Transition transition) { + stateListener.transitionStarted(transition); + } + + @Override + public void transitionEnded(Transition transition) { + stateListener.transitionEnded(transition); + } + + @Override + public void stateMachineStarted(StateMachine stateMachine) { + stateListener.stateMachineStarted(stateMachine); + } + + @Override + public void stateMachineStopped(StateMachine stateMachine) { + stateListener.stateMachineStopped(stateMachine); + } + + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java index 4a8b0eda..c73444b9 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java @@ -18,6 +18,7 @@ package org.springframework.statemachine.support; import java.util.ArrayList; import java.util.Collection; +import org.springframework.statemachine.state.State; import org.springframework.util.ObjectUtils; /** @@ -28,6 +29,19 @@ import org.springframework.util.ObjectUtils; */ public abstract class StateMachineUtils { + /** + * Checks if right hand side is a substate of a left hand side. + * + * @param left the super state + * @param right the sub state + * @return if sub is child of super + */ + public static boolean isSubstate(State left, State right) { + Collection> c = left.getStates(); + c.remove(left); + return c.contains(right); + } + /** * Checks if right hand collection has atleast one same item as left hand * collection.