Add initial action and fix internal transition event

- New InitialTransition with initial action to get
  a change to modify variables when sm is started.
- Fixes a case where internal transition did't
  send transition event.
This commit is contained in:
Janne Valkealahti
2015-03-27 12:59:50 +00:00
parent ac13ef7fa6
commit 4bf49654b9
7 changed files with 141 additions and 14 deletions

View File

@@ -17,6 +17,7 @@ package org.springframework.statemachine;
import java.util.Collection;
import org.springframework.messaging.Message;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.support.AbstractStateMachine;
import org.springframework.statemachine.transition.Transition;
@@ -24,7 +25,7 @@ import org.springframework.statemachine.transition.Transition;
/**
* Specialisation of a {@link StateMachine} using enums
* as its {@link State} and event types.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -44,4 +45,10 @@ public class EnumStateMachine<S extends Enum<S>, E extends Enum<E>> extends Abst
super(states, transitions, initialState, endState);
}
public EnumStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, Transition<S, E> initialTransition, State<S, E> endState,
Message<E> initialEvent, ExtendedState extendedState) {
super(states, transitions, initialState, initialTransition, endState, initialEvent, extendedState);
}
}

View File

@@ -25,6 +25,7 @@ import java.util.Stack;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
@@ -42,6 +43,7 @@ import org.springframework.statemachine.support.tree.Tree.Node;
import org.springframework.statemachine.support.tree.TreeTraverser;
import org.springframework.statemachine.transition.DefaultExternalTransition;
import org.springframework.statemachine.transition.DefaultInternalTransition;
import org.springframework.statemachine.transition.InitialTransition;
import org.springframework.statemachine.transition.Transition;
import org.springframework.statemachine.transition.TransitionKind;
import org.springframework.statemachine.trigger.EventTrigger;
@@ -258,6 +260,7 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
Collection<TransitionData<S, E>> transitionsData, BeanFactory beanFactory) {
State<S, E> state = null;
State<S, E> initialState = null;
Action<S, E> initialAction = null;
State<S, E> endState = null;
Collection<State<S, E>> states = new ArrayList<State<S,E>>();
for (StateData<S, E> stateData : stateDatas) {
@@ -269,8 +272,10 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
// TODO: below if/else doesn't feel right
if (stateDatas.size() > 1 && stateData.isInitial()) {
initialState = state;
initialAction = stateData.getInitialAction();
} else if (stateDatas.size() == 1) {
initialState = state;
initialAction = stateData.getInitialAction();
}
states.add(state);
} else {
@@ -282,6 +287,7 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
stateData.getEntryActions(), stateData.getExitActions(), pseudoState);
if (stateData.isInitial()) {
initialState = state;
initialAction = stateData.getInitialAction();
}
if (stateData.isEnd()) {
endState = state;
@@ -325,8 +331,14 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
}
}
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(/*stateMap.values()*/ states, transitions,
initialState, endState);
// TODO: should make a proper transition
Transition<S, E> initialTransition = null;
if (initialAction != null) {
initialTransition = new InitialTransition<S, E>(initialState, initialAction);
}
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(states, transitions, initialState,
initialTransition, endState, null, null);
machine.afterPropertiesSet();
if (beanFactory != null) {
machine.setBeanFactory(beanFactory);

View File

@@ -38,6 +38,7 @@ public class StateData<S, E> {
private Collection<? extends Action<S, E>> entryActions;
private Collection<? extends Action<S, E>> exitActions;
private boolean initial = false;
private Action<S, E> initialAction;
private boolean end = false;
public StateData(Object parent, S state, Collection<E> deferred,
@@ -81,6 +82,14 @@ public class StateData<S, E> {
this.initial = initial;
}
public void setInitialAction(Action<S, E> action) {
this.initialAction = action;
}
public Action<S, E> getInitialAction() {
return initialAction;
}
public boolean isEnd() {
return end;
}

View File

@@ -35,7 +35,9 @@ public class DefaultStateConfigurer<S, E>
private final Collection<StateData<S, E>> incomplete = new ArrayList<StateData<S, E>>();
private S initial;
private S initialState;
private Action<S, E> initialAction;
private S end;
@@ -47,8 +49,9 @@ public class DefaultStateConfigurer<S, E>
for (StateData<S, E> s : incomplete) {
s.setParent(parent);
stateDatas.add(s);
if (s.getState() == initial) {
if (s.getState() == initialState) {
s.setInitial(true);
s.setInitialAction(initialAction);
}
if (s.getState() == end) {
s.setEnd(true);
@@ -59,10 +62,16 @@ public class DefaultStateConfigurer<S, E>
@Override
public StateConfigurer<S, E> initial(S initial) {
this.initial = initial;
this.initialState = initial;
return this;
}
@Override
public StateConfigurer<S, E> initial(S initial, Action<S, E> action) {
this.initialAction = action;
return initial(initial);
}
@Override
public StateConfigurer<S, E> parent(S state) {
this.parent = state;

View File

@@ -27,6 +27,8 @@ public interface StateConfigurer<S, E> extends
StateConfigurer<S, E> initial(S initial);
StateConfigurer<S, E> initial(S initial, Action<S, E> action);
StateConfigurer<S, E> parent(S state);
StateConfigurer<S, E> state(S state);

View File

@@ -79,6 +79,8 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
private final State<S,E> initialState;
private final Transition<S, E> initialTransition;
private final State<S,E> endState;
private final Message<E> initialEvent;
@@ -125,7 +127,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
*/
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, State<S, E> endState) {
this(states, transitions, initialState, endState, null, new DefaultExtendedState());
this(states, transitions, initialState, null, endState, null, new DefaultExtendedState());
}
/**
@@ -138,7 +140,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
*/
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, ExtendedState extendedState) {
this(states, transitions, initialState, null, null, extendedState);
this(states, transitions, initialState, null, null, null, extendedState);
}
/**
@@ -152,14 +154,15 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
* @param extendedState the extended state of this machine
*/
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
State<S, E> initialState, State<S, E> endState, Message<E> initialEvent, ExtendedState extendedState) {
State<S, E> initialState, Transition<S, E> initialTransition, State<S, E> endState, Message<E> initialEvent, ExtendedState extendedState) {
super();
this.states = states;
this.transitions = transitions;
this.initialState = initialState;
this.initialTransition = initialTransition;
this.endState = endState;
this.initialEvent = initialEvent;
this.extendedState = extendedState;
this.extendedState = extendedState != null ? extendedState : new DefaultExtendedState();
}
@Override
@@ -213,6 +216,12 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
super.doStart();
registerTriggerListener();
switchToState(initialState, initialEvent, null);
// TODO: for now execute outside of switchToState
if (initialTransition != null) {
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(
initialEvent != null ? initialEvent.getHeaders() : null, extendedState, initialTransition, this);
initialTransition.transit(stateContext);
}
}
@Override
@@ -444,13 +453,13 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
notifyTransitionStart(transition);
callHandlers(transition.getSource(), transition.getTarget(), queuedEvent);
boolean transit = transition.transit(stateContext);
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
switchToState(transition.getTarget(), queuedEvent, transition);
if (transit) {
if (transition.getKind() != TransitionKind.INTERNAL) {
switchToState(transition.getTarget(), queuedEvent, transition);
}
notifyTransition(transition);
}
notifyTransitionEnd(transition);
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.transition;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
public class InitialTransition<S, E> implements Transition<S, E> {
private final State<S, E> target;
private final Collection<Action<S, E>> actions;
public InitialTransition(State<S, E> target, Action<S, E> action) {
this.target = target;
ArrayList<Action<S,E>> list = new ArrayList<Action<S, E>>();
list.add(action);
this.actions = list;
}
public InitialTransition(State<S, E> target, Collection<Action<S, E>> actions) {
this.target = target;
this.actions = actions;
}
@Override
public boolean transit(StateContext<S, E> context) {
if (actions != null) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
return false;
}
@Override
public State<S, E> getSource() {
return null;
}
@Override
public State<S, E> getTarget() {
return target;
}
@Override
public Collection<Action<S, E>> getActions() {
return actions;
}
@Override
public Trigger<S, E> getTrigger() {
return null;
}
@Override
public TransitionKind getKind() {
return null;
}
}