Add configuration model for submachines

- some cleaning
- refactoring some state concepts to have a better
  support for submachines
This commit is contained in:
Janne Valkealahti
2015-02-15 15:36:33 +00:00
parent d9b72b2e9a
commit cd93fb69cf
32 changed files with 1188 additions and 214 deletions

View File

@@ -19,26 +19,35 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.builders.StateMachineStates.StateMachineStatesData;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
import org.springframework.statemachine.state.DefaultPseudoState;
import org.springframework.statemachine.state.EnumState;
import org.springframework.statemachine.state.PseudoState;
import org.springframework.statemachine.state.PseudoStateKind;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.state.StateMachineState;
import org.springframework.statemachine.support.LifecycleObjectSupport;
import org.springframework.statemachine.support.tree.Tree;
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.Transition;
import org.springframework.statemachine.transition.TransitionKind;
/**
*
* {@link StateMachineFactory} implementation using enums to build {@link StateMachine}s.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -46,7 +55,7 @@ import org.springframework.statemachine.transition.TransitionKind;
*/
public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> extends LifecycleObjectSupport implements
StateMachineFactory<S, E> {
private final StateMachineTransitions<S, E> stateMachineTransitions;
private final StateMachineStates<S, E> stateMachineStates;
@@ -65,16 +74,68 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
@Override
public StateMachine<S, E> getStateMachine() {
return stateMachine();
// we store mappings from state id's to states which gets
// created during the process. This is needed for transitions to
// find a correct mappings because they use state id's, not actual
// states.
final Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
// we eventually return this machine which is a last one build
StateMachine<S, E> machine = null;
Map<Object, StateMachineStatesData<S, E>> states = stateMachineStates.getStates();
Tree<StateMachineStatesData<S, E>> tree = new Tree<StateMachineStatesData<S, E>>();
for (Entry<Object, StateMachineStatesData<S, E>> e : states.entrySet()) {
Object id = e.getKey();
StateMachineStatesData<S, E> value = e.getValue();
Object parent = value.getParent();
tree.add(value, id, parent);
}
TreeTraverser<Node<StateMachineStatesData<S, E>>> traverser = new TreeTraverser<Node<StateMachineStatesData<S, E>>>() {
@Override
public Iterable<Node<StateMachineStatesData<S, E>>> children(Node<StateMachineStatesData<S, E>> root) {
return root.getChildren();
}
};
Stack<StackItem<S, E>> stack = new Stack<StackItem<S, E>>();
for (Node<StateMachineStatesData<S, E>> node : traverser.postOrderTraversal(tree.getRoot())) {
System.out.println(node.getData());
Object parent = node.getData().getParent();
if (!stack.empty()) {
StackItem<S, E> pop = stack.pop();
machine = buildSubMachine(stateMap, pop.machine, node.getData(), stateMachineTransitions, getBeanFactory());
stack.push(new StackItem<S, E>(machine, parent));
} else {
machine = buildSimpleMachine(stateMap, node.getData(), stateMachineTransitions, getBeanFactory());
stack.push(new StackItem<S, E>(machine, parent));
}
}
return machine;
}
public StateMachine<S, E> stateMachine() {
Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
for (StateData<S, E> stateData : stateMachineStates.getStates()) {
private static class StackItem<S, E> {
StateMachine<S, E> machine;
Object id;
public StackItem(StateMachine<S, E> machine, Object id) {
this.machine = machine;
this.id = id;
}
}
private static <S extends Enum<S>, E extends Enum<E>> StateMachine<S, E> buildSimpleMachine(Map<S, State<S, E>> stateMap, StateMachineStatesData<S, E> statesData, StateMachineTransitions<S, E> transitionsData, BeanFactory beanFactory) {
for (StateData<S, E> stateData : statesData.getStates()) {
// TODO: doesn't feel right to tweak initial kind like this
PseudoState pseudoState = null;
if (stateData.getState() == stateMachineStates.getInitialState()) {
if (stateData.getState() == statesData.getInitialState()) {
pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL);
}
stateMap.put(stateData.getState(), new EnumState<S, E>(stateData.getState(), stateData.getDeferred(),
@@ -82,7 +143,7 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
}
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
for (TransitionData<S, E> transitionData : stateMachineTransitions.getTransitions()) {
for (TransitionData<S, E> transitionData : transitionsData.getTransitions()) {
S source = transitionData.getSource();
S target = transitionData.getTarget();
E event = transitionData.getEvent();
@@ -90,22 +151,57 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
transitions.add(transition);
}
}
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(stateMap.values(), transitions,
stateMap.get(stateMachineStates.getInitialState()), stateMap.get(stateMachineStates.getEndState()));
stateMap.get(statesData.getInitialState()), stateMap.get(statesData.getEndState()));
machine.afterPropertiesSet();
if (getBeanFactory() != null) {
machine.setBeanFactory(getBeanFactory());
if (beanFactory != null) {
machine.setBeanFactory(beanFactory);
}
return machine;
}
private static <S extends Enum<S>, E extends Enum<E>> StateMachine<S, E> buildSubMachine(Map<S, State<S, E>> stateMap, StateMachine<S, E> submachine, StateMachineStatesData<S, E> statesData, StateMachineTransitions<S, E> transitionsData, BeanFactory beanFactory) {
Collection<State<S, E>> states = new ArrayList<State<S,E>>();
State<S, E> state = null;
for (StateData<S, E> stateData : statesData.getStates()) {
state = new StateMachineState<S, E>(stateData.getState(), submachine, stateData.getDeferred(),
stateData.getEntryActions(), stateData.getExitActions(), new DefaultPseudoState(
PseudoStateKind.INITIAL));
states.add(state);
stateMap.put(stateData.getState(), state);
}
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
for (TransitionData<S, E> transitionData : transitionsData.getTransitions()) {
S source = transitionData.getSource();
S target = transitionData.getTarget();
E event = transitionData.getEvent();
if (transitionData.getKind() == TransitionKind.EXTERNAL) {
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
transitionData.getActions(), event, transitionData.getGuard());
transitions.add(transition);
}
}
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(states, transitions, state, null);
machine.afterPropertiesSet();
if (beanFactory != null) {
machine.setBeanFactory(beanFactory);
}
machine.setAutoStartup(isAutoStartup());
machine.start();
return machine;
}

View File

@@ -15,22 +15,33 @@
*/
package org.springframework.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.builders.StateMachineStates.StateMachineStatesData;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.statemachine.config.configurers.DefaultStateConfigurer;
import org.springframework.statemachine.config.configurers.DefaultSubStateConfigurer;
import org.springframework.statemachine.config.configurers.StateConfigurer;
import org.springframework.statemachine.config.configurers.SubStateConfigurer;
/**
* {@link AnnotationBuilder} for {@link StateMachineStates}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class StateMachineStateBuilder<S, E>
extends AbstractConfiguredAnnotationBuilder<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
implements StateMachineStateConfigurer<S, E> {
private Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
private S initialState;
private S endState;
private final Map<Object, StateMachineStatesData<S, E>> data = new HashMap<Object, StateMachineStatesData<S,E>>();
public StateMachineStateBuilder() {
super();
@@ -47,25 +58,26 @@ public class StateMachineStateBuilder<S, E>
@Override
protected StateMachineStates<S, E> performBuild() throws Exception {
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, endState, states);
return bean;
return new StateMachineStates<S, E>(data);
}
@Override
public StateConfigurer<S, E> withStates() throws Exception {
return apply(new DefaultStateConfigurer<S, E>());
return apply(new DefaultStateConfigurer<S, E>(null, null));
}
public void add(Collection<StateData<S, E>> states) {
this.states.addAll(states);
@Override
public StateConfigurer<S, E> withStates(Object parent) throws Exception {
return apply(new DefaultStateConfigurer<S, E>(null, parent));
}
public void setInitialState(S state) {
this.initialState = state;
@Override
public SubStateConfigurer<S, E> withSubStates(S state, Object parent) throws Exception {
return apply(new DefaultSubStateConfigurer<S, E>(state, state, parent));
}
public void setEndState(S endState) {
this.endState = endState;
public void add(Object id, Object parent, Collection<StateData<S, E>> states, S initialState, S endState) {
data.put(id, new StateMachineStatesData<S, E>(states, initialState, endState, parent));
}
}

View File

@@ -16,9 +16,14 @@
package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.StateConfigurer;
import org.springframework.statemachine.config.configurers.SubStateConfigurer;
public interface StateMachineStateConfigurer<S, E> {
StateConfigurer<S, E> withStates() throws Exception;
StateConfigurer<S, E> withStates(Object parent) throws Exception;
SubStateConfigurer<S, E> withSubStates(S state, Object parent) throws Exception;
}

View File

@@ -18,35 +18,62 @@ package org.springframework.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import org.springframework.statemachine.action.Action;
public class StateMachineStates<S, E> {
private Collection<StateData<S, E>> states;
private final Map<Object, StateMachineStatesData<S, E>> states;
private final S initialState;
private final S endState;
public StateMachineStates(S initialState, S endState, Collection<StateData<S, E>> states) {
public StateMachineStates(Map<Object, StateMachineStatesData<S, E>> states) {
this.states = states;
this.initialState = initialState;
this.endState = endState;
}
public Collection<StateData<S, E>> getStates() {
public Map<Object, StateMachineStatesData<S, E>> getStates() {
return states;
}
public S getInitialState() {
return initialState;
public static class StateMachineStatesData<S, E> {
private Collection<StateData<S, E>> states;
private final S initialState;
private final S endState;
private final Object parent;
public StateMachineStatesData(Collection<StateData<S, E>> states, S initialState, S endState, Object parent) {
this.states = states;
this.initialState = initialState;
this.endState = endState;
this.parent = parent;
}
public Collection<StateData<S, E>> getStates() {
return states;
}
public S getInitialState() {
return initialState;
}
public S getEndState() {
return endState;
}
public Object getParent() {
return parent;
}
@Override
public String toString() {
return "StateMachineStatesData [states=" + states + ", initialState=" + initialState + ", endState="
+ endState + ", parent=" + parent + "]";
}
}
public S getEndState() {
return endState;
}
public static class StateData<S, E> {
private S state;
private Collection<E> deferred;
@@ -76,6 +103,12 @@ public class StateMachineStates<S, E> {
public Collection<Action<S, E>> getExitActions() {
return exitActions;
}
@Override
public String toString() {
return "StateData [state=" + state + ", deferred=" + deferred + ", entryActions=" + entryActions
+ ", exitActions=" + exitActions + "]";
}
}
}

View File

@@ -30,17 +30,24 @@ public class DefaultStateConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
implements StateConfigurer<S, E> {
private final Object id;
private final Object parent;
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
private S initial;
private S end;
public DefaultStateConfigurer(Object id, Object parent) {
this.id = id;
this.parent = parent;
}
@Override
public void configure(StateMachineStateBuilder<S, E> builder) throws Exception {
builder.add(states);
builder.setInitialState(initial);
builder.setEndState(end);
builder.add(id, parent, states, initial, end);
}
@Override
@@ -48,7 +55,7 @@ public class DefaultStateConfigurer<S, E>
this.initial = initial;
return this;
}
@Override
public StateConfigurer<S, E> end(S end) {
this.end = end;
@@ -59,7 +66,7 @@ public class DefaultStateConfigurer<S, E>
public StateConfigurer<S, E> state(S state) {
return state(state, (E[])null);
}
@Override
public StateConfigurer<S, E> state(S state, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
states.add(new StateData<S, E>(state, null, entryActions, exitActions));

View File

@@ -0,0 +1,76 @@
/*
* 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.config.configurers;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineStateBuilder;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStates;
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
public class DefaultSubStateConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineStates<S, E>, StateMachineStateConfigurer<S, E>, StateMachineStateBuilder<S, E>>
implements SubStateConfigurer<S, E> {
private S state;
private final Object id;
private final Object parent;
private S initial;
private Collection<Action<S, E>> entryActions;
private Collection<Action<S, E>> exitActions;
private final Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
public DefaultSubStateConfigurer(S state, Object id) {
this(state, id, null);
}
public DefaultSubStateConfigurer(S state, Object id, Object parent) {
this.state = state;
this.id = id;
this.parent = parent;
}
@Override
public void configure(StateMachineStateBuilder<S, E> builder) throws Exception {
states.add(new StateData<S, E>(state, null, entryActions, exitActions));
builder.add(id, parent, states, initial, null);
}
@Override
public SubStateConfigurer<S, E> initial(S initial) {
this.initial = initial;
return this;
}
@Override
public SubStateConfigurer<S, E> entry(Collection<Action<S, E>> entryActions) {
this.entryActions = entryActions;
return this;
}
@Override
public SubStateConfigurer<S, E> exit(Collection<Action<S, E>> exitActions) {
this.exitActions = exitActions;
return this;
}
}

View File

@@ -30,7 +30,7 @@ public interface StateConfigurer<S, E> extends
StateConfigurer<S, E> state(S state);
StateConfigurer<S, E> state(S state, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions);
StateConfigurer<S, E> state(S state, E... deferred);
StateConfigurer<S, E> states(Set<S> states);

View File

@@ -0,0 +1,33 @@
/*
* 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.config.configurers;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerBuilder;
public interface SubStateConfigurer<S, E> extends
AnnotationConfigurerBuilder<StateMachineStateConfigurer<S, E>> {
SubStateConfigurer<S, E> initial(S initial);
SubStateConfigurer<S, E> entry(Collection<Action<S, E>> entryActions);
SubStateConfigurer<S, E> exit(Collection<Action<S, E>> exitActions);
}

View File

@@ -25,20 +25,20 @@ import org.springframework.statemachine.region.Region;
/**
* Base implementation of a {@link State} having a single state identifier.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
private final Collection<S> ids;
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
*/
public AbstractSimpleState(S id) {
this(id, null, null, null, null);
@@ -47,7 +47,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
@@ -59,7 +59,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param deferred the deferred
*/
public AbstractSimpleState(S id, Collection<E> deferred) {
@@ -69,17 +69,17 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param pseudoState the pseudo state
*/
public AbstractSimpleState(S id, PseudoState pseudoState) {
this(id, null, null, null, pseudoState);
}
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
@@ -88,7 +88,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, Collection<Region<S, E>> regions) {
super(deferred, entryActions, exitActions, pseudoState, regions);
super(id, deferred, entryActions, exitActions, pseudoState, regions);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
@@ -96,7 +96,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
@@ -105,7 +105,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, StateMachine<S, E> submachine) {
super(deferred, entryActions, exitActions, pseudoState, submachine);
super(id, deferred, entryActions, exitActions, pseudoState, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
@@ -113,7 +113,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new abstract simple state.
*
* @param id the id
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
@@ -121,7 +121,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
super(deferred, entryActions, exitActions, pseudoState);
super(id, deferred, entryActions, exitActions, pseudoState);
this.ids = new ArrayList<S>();
this.ids.add(id);
}

View File

@@ -23,7 +23,6 @@ import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.region.Region;
import org.springframework.util.StringUtils;
/**
* Base implementation of a {@link State}.
@@ -35,6 +34,7 @@ import org.springframework.util.StringUtils;
*/
public abstract class AbstractState<S, E> implements State<S, E> {
private final S id;
private final PseudoState pseudoState;
private final Collection<E> deferred;
private final Collection<Action<S, E>> entryActions;
@@ -45,76 +45,83 @@ public abstract class AbstractState<S, E> implements State<S, E> {
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param pseudoState the pseudo state
*/
public AbstractState(PseudoState pseudoState) {
this(null, null, null, pseudoState);
public AbstractState(S id, PseudoState pseudoState) {
this(id, null, null, null, pseudoState);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
*/
public AbstractState(Collection<E> deferred) {
this(deferred, null, null);
public AbstractState(S id, Collection<E> deferred) {
this(id, deferred, null, null);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
this(deferred, entryActions, exitActions, null);
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
this(id, deferred, entryActions, exitActions, null);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
this(deferred, entryActions, exitActions, pseudoState, null, null);
this(id, deferred, entryActions, exitActions, pseudoState, null, null);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
* @param pseudoState the pseudo state
* @param submachine the submachine
*/
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, StateMachine<S, E> submachine) {
this(deferred, entryActions, exitActions, pseudoState, null, submachine);
this(id, deferred, entryActions, exitActions, pseudoState, null, submachine);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
* @param pseudoState the pseudo state
* @param regions the regions
*/
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
public AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, Collection<Region<S, E>> regions) {
this(deferred, entryActions, exitActions, pseudoState, regions, null);
this(id, deferred, entryActions, exitActions, pseudoState, regions, null);
}
/**
* Instantiates a new abstract state.
*
* @param id the state identifier
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
@@ -122,8 +129,9 @@ public abstract class AbstractState<S, E> implements State<S, E> {
* @param regions the regions
* @param submachine the submachine
*/
private AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
private AbstractState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, Collection<Region<S, E>> regions, StateMachine<S, E> submachine) {
this.id = id;
this.deferred = deferred;
this.entryActions = entryActions;
this.exitActions = exitActions;
@@ -147,6 +155,11 @@ public abstract class AbstractState<S, E> implements State<S, E> {
@Override
public abstract void entry(E event, StateContext<S, E> context);
@Override
public S getId() {
return id;
}
@Override
public abstract Collection<S> getIds();
@@ -200,8 +213,9 @@ public abstract class AbstractState<S, E> implements State<S, E> {
@Override
public String toString() {
return "AbstractState [ids=" + StringUtils.collectionToCommaDelimitedString(getIds()) + ", pseudoState=" + pseudoState + ", deferred=" + deferred
+ ", entryActions=" + entryActions + ", exitActions=" + exitActions + "]";
return "AbstractState [id=" + id + ", pseudoState=" + pseudoState + ", deferred=" + deferred
+ ", entryActions=" + entryActions + ", exitActions=" + exitActions + ", regions=" + regions
+ ", submachine=" + submachine + "]";
}
}

View File

@@ -36,56 +36,61 @@ public class RegionState<S, E> extends AbstractState<S, E> {
/**
* Instantiates a new region state.
*
* @param id the state identifier
* @param regions the regions
*/
public RegionState(Collection<Region<S, E>> regions) {
super(null, null, null, null, regions);
public RegionState(S id, Collection<Region<S, E>> regions) {
super(id, null, null, null, null, regions);
}
/**
* Instantiates a new region state.
*
* @param id the state identifier
* @param regions the regions
* @param deferred the deferred
*/
public RegionState(Collection<Region<S, E>> regions, Collection<E> deferred) {
super(deferred, null, null, null, regions);
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> deferred) {
super(id, deferred, null, null, null, regions);
}
/**
* Instantiates a new region state.
*
* @param id the state identifier
* @param regions the regions
* @param pseudoState the pseudo state
*/
public RegionState(Collection<Region<S, E>> regions, PseudoState pseudoState) {
super(null, null, null, pseudoState, regions);
public RegionState(S id, Collection<Region<S, E>> regions, PseudoState pseudoState) {
super(id, null, null, null, pseudoState, regions);
}
/**
* Instantiates a new region state.
*
* @param id the state identifier
* @param regions the regions
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public RegionState(Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
super(deferred, entryActions, exitActions, pseudoState, regions);
super(id, deferred, entryActions, exitActions, pseudoState, regions);
}
/**
* Instantiates a new region state.
*
* @param id the state identifier
* @param regions the regions
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public RegionState(Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
super(deferred, entryActions, exitActions, null, regions);
public RegionState(S id, Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
super(id, deferred, entryActions, exitActions, null, regions);
}
@Override

View File

@@ -54,6 +54,13 @@ public interface State<S, E> {
*/
void entry(E event, StateContext<S, E> context);
/**
* Gets the state identifier.
*
* @return the state identifiers
*/
S getId();
/**
* Gets the state identifiers. Usually returned collection contains only one
* identifier except in a case where state is an orthogonal.

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.statemachine.state;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
@@ -34,69 +34,87 @@ import org.springframework.statemachine.transition.TransitionKind;
*/
public class StateMachineState<S, E> extends AbstractState<S, E> {
private final Collection<S> ids;
/**
* Instantiates a new state machine state.
*
* @param id the state identifier
* @param submachine the submachine
*/
public StateMachineState(StateMachine<S, E> submachine) {
super(null, null, null, null, submachine);
public StateMachineState(S id, StateMachine<S, E> submachine) {
super(id, null, null, null, null, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
/**
* Instantiates a new state machine state.
*
* @param id the state identifier
* @param submachine the submachine
* @param deferred the deferred
*/
public StateMachineState(StateMachine<S, E> submachine, Collection<E> deferred) {
super(deferred, null, null, null, submachine);
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred) {
super(id, deferred, null, null, null, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
/**
* Instantiates a new state machine state.
*
* @param id the state identifier
* @param submachine the submachine
* @param pseudoState the pseudo state
*/
public StateMachineState(StateMachine<S, E> submachine, PseudoState pseudoState) {
super(null, null, null, pseudoState, submachine);
public StateMachineState(S id, StateMachine<S, E> submachine, PseudoState pseudoState) {
super(id, null, null, null, pseudoState, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
/**
* Instantiates a new state machine state.
*
* @param id the state identifier
* @param submachine the submachine
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public StateMachineState(StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
super(deferred, entryActions, exitActions, pseudoState, submachine);
super(id, deferred, entryActions, exitActions, pseudoState, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
/**
* Instantiates a new state machine state.
*
* @param id the state identifier
* @param submachine the submachine
* @param deferred the deferred
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public StateMachineState(StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
super(deferred, entryActions, exitActions, null, submachine);
public StateMachineState(S id, StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
super(id, deferred, entryActions, exitActions, null, submachine);
this.ids = new ArrayList<S>();
this.ids.add(id);
}
@Override
public Collection<S> getIds() {
Collection<S> ret = new ArrayList<S>(ids);
State<S, E> state = getSubmachine().getState();
if (state != null) {
return state.getIds();
} else {
return Collections.emptyList();
ret.addAll(state.getIds());
}
return ret;
}
@Override
@@ -137,8 +155,8 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
@Override
public String toString() {
return "StateMachineState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ ", toString()=" + super.toString() + "]";
return "StateMachineState [getIds()=" + getIds() + ", toString()=" + super.toString() + ", getClass()="
+ getClass() + "]";
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.tree;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.springframework.util.Assert;
public abstract class AbstractIterator<T> implements Iterator<T> {
private State state = State.NOT_READY;
private enum State {
READY, NOT_READY, DONE, FAILED,
}
private T next;
protected abstract T computeNext();
protected final T endOfData() {
state = State.DONE;
return null;
}
@Override
public final boolean hasNext() {
Assert.state(state != State.FAILED);
switch (state) {
case DONE:
return false;
case READY:
return true;
default:
}
return tryToComputeNext();
}
private boolean tryToComputeNext() {
state = State.FAILED;
next = computeNext();
if (state != State.DONE) {
state = State.READY;
return true;
}
return false;
}
@Override
public final T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
state = State.NOT_READY;
T result = next;
next = null;
return result;
}
public final T peek() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return next;
}
public final void remove() {
throw new UnsupportedOperationException("remove");
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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.tree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Utility class which can be used to represent a tree based data structure.
*
* @author Janne Valkealahti
*
* @param <T> the type of node data
*/
public class Tree<T> {
private Node<T> root;
private final Map<Object, Node<T>> map = new HashMap<Object, Node<T>>();
private final List<DataWrap<T>> notMapped = new ArrayList<DataWrap<T>>();
public Tree() {
}
public Node<T> getRoot() {
return root;
}
public void add(T data, Object id, Object parent) {
notMapped.add(new DataWrap<T>(data, id, parent));
tryMapping();
}
private void tryMapping() {
int size = notMapped.size();
Iterator<DataWrap<T>> iter = notMapped.iterator();
while(iter.hasNext()) {
DataWrap<T> next = iter.next();
if (next.parent == null) {
Node<T> n = new Node<T>(next.data);
map.put(next.id, n);
root = n;
iter.remove();
} else {
if (map.containsKey(next.parent)) {
Node<T> n = new Node<T>(next.data);
Node<T> node = map.get(next.parent);
map.put(next.id, n);
node.getChildren().add(n);
iter.remove();
}
}
}
if (notMapped.size() < size) {
tryMapping();
}
}
public static class Node<T> {
private final T data;
private final List<Node<T>> children;
public Node(T data) {
this(data, null);
}
public Node(T data, List<Node<T>> children) {
this.data = data;
this.children = children != null ? children : new ArrayList<Node<T>>();
}
public T getData() {
return data;
}
public List<Node<T>> getChildren() {
return children;
}
}
private static class DataWrap<T> {
final T data;
final Object id;
final Object parent;
public DataWrap(T data, Object id, Object parent) {
this.data = data;
this.id = id;
this.parent = parent;
}
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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.tree;
import java.util.ArrayDeque;
import java.util.Iterator;
import org.springframework.util.Assert;
public abstract class TreeTraverser<T> {
/**
* Returns the children of the specified node. Must not contain null.
*/
public abstract Iterable<T> children(T root);
public final Iterable<T> postOrderTraversal(final T root) {
Assert.notNull(root);
return new Iterable<T>() {
@Override
public Iterator<T> iterator() {
return postOrderIterator(root);
}
};
}
Iterator<T> postOrderIterator(T root) {
return new PostOrderIterator(root);
}
private static final class PostOrderNode<T> {
final T root;
final Iterator<T> childIterator;
PostOrderNode(T root, Iterator<T> childIterator) {
Assert.notNull(root);
Assert.notNull(childIterator);
this.root = root;
this.childIterator = childIterator;
}
}
private final class PostOrderIterator extends AbstractIterator<T> {
private final ArrayDeque<PostOrderNode<T>> stack;
PostOrderIterator(T root) {
this.stack = new ArrayDeque<PostOrderNode<T>>();
stack.addLast(expand(root));
}
@Override
protected T computeNext() {
while (!stack.isEmpty()) {
PostOrderNode<T> top = stack.getLast();
if (top.childIterator.hasNext()) {
T child = top.childIterator.next();
stack.addLast(expand(child));
} else {
stack.removeLast();
return top.root;
}
}
return endOfData();
}
private PostOrderNode<T> expand(T t) {
return new PostOrderNode<T>(t, children(t).iterator());
}
}
}

View File

@@ -91,7 +91,7 @@ public class RegionMachineTests {
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
regions.add(machine);
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(regions);
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(TestStates.S11, regions);
assertThat(state.isSimple(), is(false));
assertThat(state.isComposite(), is(true));
@@ -165,7 +165,7 @@ public class RegionMachineTests {
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
regions.add(machine11);
regions.add(machine12);
RegionState<TestStates,TestEvents> stateR = new RegionState<TestStates,TestEvents>(regions, null, null, null, pseudoState);
RegionState<TestStates,TestEvents> stateR = new RegionState<TestStates,TestEvents>(TestStates.S11, regions, null, null, null, pseudoState);
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
states.add(stateR);

View File

@@ -41,7 +41,8 @@ public class StateMachineFactoryTests extends AbstractStateMachineTests {
EnumStateMachineFactory<TestStates, TestEvents> stateMachineFactory =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINEFACTORY, EnumStateMachineFactory.class);
StateMachine<TestStates,TestEvents> machine = stateMachineFactory.getStateMachine();
machine.start();
assertThat(machine.getState().getIds(), contains(TestStates.S1));
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
assertThat(machine.getState().getIds(), contains(TestStates.S2));

View File

@@ -44,6 +44,7 @@ public class StateMachineTests extends AbstractStateMachineTests {
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
assertThat(machine, notNullValue());
machine.start();
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).setHeader("foo", "jee1").build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).setHeader("foo", "jee2").build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E4).setHeader("foo", "jee2").build());

View File

@@ -17,14 +17,23 @@ package org.springframework.statemachine;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
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.core.task.SyncTaskExecutor;
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.state.DefaultPseudoState;
import org.springframework.statemachine.state.EnumState;
import org.springframework.statemachine.state.PseudoState;
@@ -37,6 +46,11 @@ import org.springframework.statemachine.transition.Transition;
public class SubStateMachineTests extends AbstractStateMachineTests {
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
@Test
public void testExternalTransition() throws Exception {
/**
@@ -85,7 +99,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
entryActionsS11.add(entryActionS11);
Collection<Action<TestStates, TestEvents>> exitActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS11.add(exitActionS11);
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
substates11.add(stateS11);
@@ -100,13 +114,13 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS1.add(exitActionS1);
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
states.add(stateS1);
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
DefaultExternalTransition<TestStates,TestEvents> transitionFromS11ToS1 =
DefaultExternalTransition<TestStates,TestEvents> transitionFromS111ToS1 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS1, null, TestEvents.E1, null);
transitions.add(transitionFromS11ToS1);
transitions.add(transitionFromS111ToS1);
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateS1, null);
@@ -134,6 +148,96 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
assertThat(exitActionS1.stateContexts.size(), is(1));
}
// @Test
public void testExternalTransition2() throws Exception {
/**
* +---------------------------------------------------+
* *-init->| S1 |
* +---------------------------------------------------+
* | entry/ |
* | exit/ |
* | +-----------+ +-----------+ |
* | *-->| S111 | | S112 | |
* | +-----------+ +-----------+ |
* | | entry/ | | entry/ | |
* | | exit/ |----E1--->| exit/ | |
* | | | | | |
* | | |<---E2----| | |
* | | | | | |
* | +-----------+ +-----------+ |
* | |
* +---------------------------------------------------+
*/
PseudoState pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL);
TestEntryAction entryActionS111 = new TestEntryAction("S111");
TestExitAction exitActionS111 = new TestExitAction("S111");
Collection<Action<TestStates, TestEvents>> entryActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
entryActionsS111.add(entryActionS111);
Collection<Action<TestStates, TestEvents>> exitActionsS111 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS111.add(exitActionS111);
State<TestStates,TestEvents> stateS111 = new EnumState<TestStates,TestEvents>(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState);
TestEntryAction entryActionS112 = new TestEntryAction("S112");
TestExitAction exitActionS112 = new TestExitAction("S112");
Collection<Action<TestStates, TestEvents>> entryActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
entryActionsS112.add(entryActionS112);
Collection<Action<TestStates, TestEvents>> exitActionsS112 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS111.add(exitActionS112);
State<TestStates,TestEvents> stateS112 = new EnumState<TestStates,TestEvents>(TestStates.S112, null, entryActionsS112, exitActionsS112, null);
// submachine 1
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
substates11.add(stateS111);
substates11.add(stateS112);
Collection<Transition<TestStates,TestEvents>> subtransitions11 = new ArrayList<Transition<TestStates,TestEvents>>();
EnumStateMachine<TestStates, TestEvents> submachine11 = new EnumStateMachine<TestStates, TestEvents>(substates11, subtransitions11, stateS111, null);
// machine
TestEntryAction entryActionS1 = new TestEntryAction("S1");
TestExitAction exitActionS1 = new TestExitAction("S1");
Collection<Action<TestStates, TestEvents>> entryActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
entryActionsS1.add(entryActionS1);
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS1.add(exitActionS1);
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine11, null, entryActionsS1, exitActionsS1, pseudoState);
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
states.add(stateS1);
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
DefaultExternalTransition<TestStates,TestEvents> transitionFromS111ToS112 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS112, null, TestEvents.E1, null);
transitions.add(transitionFromS111ToS112);
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateS1, null);
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
machine.setTaskExecutor(taskExecutor);
machine.afterPropertiesSet();
machine.start();
submachine11.setTaskExecutor(taskExecutor);
machine.sendEvent(TestEvents.E1);
assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(entryActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS112.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(entryActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(entryActionS111.stateContexts.size(), is(1));
assertThat(exitActionS111.stateContexts.size(), is(1));
assertThat(entryActionS112.stateContexts.size(), is(1));
assertThat(exitActionS112.stateContexts.size(), is(0));
assertThat(entryActionS1.stateContexts.size(), is(1));
assertThat(exitActionS1.stateContexts.size(), is(0));
}
@Test
public void testLocalTransition() throws Exception {
/**
@@ -182,7 +286,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
entryActionsS11.add(entryActionS11);
Collection<Action<TestStates, TestEvents>> exitActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS11.add(exitActionS11);
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
StateMachineState<TestStates,TestEvents> stateS11 = new StateMachineState<TestStates,TestEvents>(TestStates.S11, submachine11, null, entryActionsS11, exitActionsS11, pseudoState);
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
substates11.add(stateS11);
@@ -197,7 +301,7 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
Collection<Action<TestStates, TestEvents>> exitActionsS1 = new ArrayList<Action<TestStates, TestEvents>>();
exitActionsS1.add(exitActionS1);
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
StateMachineState<TestStates,TestEvents> stateS1 = new StateMachineState<TestStates,TestEvents>(TestStates.S1, submachine1, null, entryActionsS1, exitActionsS1, pseudoState);
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
states.add(stateS1);
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
@@ -231,4 +335,192 @@ public class SubStateMachineTests extends AbstractStateMachineTests {
assertThat(exitActionS1.stateContexts.size(), is(0));
}
@Test
public void testExternalTransition3() throws Exception {
context.register(BaseConfig.class, Config1.class);
context.refresh();
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
@SuppressWarnings("unchecked")
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
machine.sendEvent(TestEvents.E1);
TestEntryAction entryActionS111 = context.getBean("entryActionS111", TestEntryAction.class);
TestExitAction exitActionS111 = context.getBean("exitActionS111", TestExitAction.class);
TestEntryAction entryActionS11 = context.getBean("entryActionS11", TestEntryAction.class);
TestExitAction exitActionS11 = context.getBean("exitActionS11", TestExitAction.class);
TestEntryAction entryActionS1 = context.getBean("entryActionS1", TestEntryAction.class);
TestExitAction exitActionS1 = context.getBean("exitActionS1", TestExitAction.class);
assertThat(entryActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS111.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(entryActionS11.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS11.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(entryActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(exitActionS1.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(entryActionS11.stateContexts.size(), is(2));
assertThat(exitActionS11.stateContexts.size(), is(1));
assertThat(entryActionS11.stateContexts.size(), is(2));
assertThat(exitActionS11.stateContexts.size(), is(1));
assertThat(entryActionS1.stateContexts.size(), is(2));
assertThat(exitActionS1.stateContexts.size(), is(1));
}
@Configuration
@EnableStateMachine
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@SuppressWarnings("unchecked")
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withSubStates(TestStates.S1, null)
// .initial(TestStates.S11)
.entry(Arrays.asList(entryActionS1()))
.exit(Arrays.asList(exitActionS1()))
.and()
.withSubStates(TestStates.S11, TestStates.S1)
// .initial(TestStates.S111)
.entry(Arrays.asList(entryActionS11()))
.exit(Arrays.asList(exitActionS11()))
.and()
.withStates(TestStates.S11)
.initial(TestStates.S111)
.state(TestStates.S111, Arrays.asList(entryActionS111()), Arrays.asList(exitActionS111()));
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
.withExternal()
.source(TestStates.S111)
.target(TestStates.S1)
.event(TestEvents.E1);
}
@Bean(name = "entryActionS111")
public Action<TestStates, TestEvents> entryActionS111() {
return new TestEntryAction("S111");
}
@Bean(name = "exitActionS111")
public Action<TestStates, TestEvents> exitActionS111() {
return new TestExitAction("S111");
}
@Bean(name = "entryActionS11")
public Action<TestStates, TestEvents> entryActionS11() {
return new TestEntryAction("S11");
}
@Bean(name = "exitActionS11")
public Action<TestStates, TestEvents> exitActionS11() {
return new TestExitAction("S11");
}
@Bean(name = "entryActionS1")
public Action<TestStates, TestEvents> entryActionS1() {
return new TestEntryAction("S1");
}
@Bean(name = "exitActionS1")
public Action<TestStates, TestEvents> exitActionS1() {
return new TestExitAction("S1");
}
}
// @Configuration
// @EnableStateMachine(name = "submachine11Config")
// public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
//
// @Override
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
// states
// .withStates()
// .initial(TestStates.S111)
// .state(TestStates.S111, Arrays.asList(testEntryAction()), Arrays.asList(testExitAction()));
// }
//
// @Bean(name = "entryActionS111")
// public Action<TestStates, TestEvents> testEntryAction() {
// return new TestEntryAction();
// }
//
// @Bean(name = "exitActionS111")
// public Action<TestStates, TestEvents> testExitAction() {
// return new TestExitAction();
// }
//
// }
// @Configuration
// @EnableStateMachine(name = "submachine1Config")
// public static class Config2 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
//
// @Autowired
// @Qualifier("submachine11Config")
// public StateMachine<TestStates,TestEvents> submachine;
//
// @Override
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
// states
// .withSubmachine()
// .submachine(submachine);
// }
//
// @Bean(name = "entryActionS11")
// public Action<TestStates, TestEvents> testEntryAction() {
// return new TestEntryAction();
// }
//
// @Bean(name = "exitActionS11")
// public Action<TestStates, TestEvents> testExitAction() {
// return new TestExitAction();
// }
//
// }
// @Configuration
// @EnableStateMachine(name = "submachineConfig")
// public static class Config3 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
//
// @Autowired
// @Qualifier("submachine1Config")
// public StateMachine<TestStates,TestEvents> submachine;
//
// @Override
// public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
// states
// .withSubmachine()
// .submachine(submachine);
// }
//
// @Override
// public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
// transitions
// .withExternal()
// .source(TestStates.S111)
// .target(TestStates.S1)
// .event(TestEvents.E1);
// }
//
// @Bean(name = "entryActionS1")
// public TestEntryAction testEntryAction() {
// return new TestEntryAction();
// }
//
// @Bean(name = "exitActionS1")
// public TestExitAction testExitAction() {
// return new TestExitAction();
// }
//
// }
}

View File

@@ -37,7 +37,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
/**
* Tests for state machine actions.
*
*
* @author Janne Valkealahti
*
*/
@@ -50,6 +50,7 @@ public class ActionTests extends AbstractStateMachineTests {
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
StateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
machine.start();
TestCountAction testAction1 = ctx.getBean("testAction1", TestCountAction.class);
TestCountAction testAction2 = ctx.getBean("testAction2", TestCountAction.class);
@@ -71,7 +72,7 @@ public class ActionTests extends AbstractStateMachineTests {
private static class TestCountAction implements Action<TestStates, TestEvents> {
int count = 0;
public TestCountAction() {
count = 0;
}

View File

@@ -37,7 +37,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
import org.springframework.statemachine.processor.StateMachineAnnotationPostProcessor;
public class MethodAnnotationTests extends AbstractStateMachineTests {
@Test
@SuppressWarnings("unchecked")
public void testMethodAnnotations() throws Exception {
@@ -46,24 +46,25 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
assertThat(context.containsBean("fooMachine"), is(true));
machine.start();
Bean1 bean1 = context.getBean(Bean1.class);
// this event should cause 'method1' to get called
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
assertThat(bean1.onMethod1Latch.await(2, TimeUnit.SECONDS), is(true));
assertThat(bean1.onOnTransitionFromS2ToS3Latch.await(2, TimeUnit.SECONDS), is(false));
context.close();
}
@WithStateMachine(name = StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE)
static class Bean1 {
CountDownLatch onMethod1Latch = new CountDownLatch(1);
CountDownLatch onOnTransitionFromS2ToS3Latch = new CountDownLatch(1);
@OnTransition(source = "S1", target = "S2")
public void method1() {
onMethod1Latch.countDown();
@@ -73,8 +74,8 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
public void onTransitionFromS2ToS3() {
onOnTransitionFromS2ToS3Latch.countDown();
}
}
@Configuration
@@ -84,20 +85,20 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
public Bean1 bean1() {
return new Bean1();
}
}
@Configuration
static class AnnoConfig {
@Bean(name="org.springframework.statemachine.internal.springStateMachineAnnotationPostProcessor")
public StateMachineAnnotationPostProcessor springStateMachineAnnotationPostProcessor() {
return new StateMachineAnnotationPostProcessor();
}
}
@Configuration
@EnableStateMachine(name = {StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, "fooMachine"})
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@@ -125,12 +126,12 @@ public class MethodAnnotationTests extends AbstractStateMachineTests {
.target(TestStates.S3)
.event(TestEvents.E2);
}
@Bean
public TestGuard testGuard() {
return new TestGuard();
}
@Bean
public TestAction testAction() {
return new TestAction();

View File

@@ -31,48 +31,62 @@ import org.springframework.statemachine.AbstractStateMachineTests;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateMachineSystemConstants;
import org.springframework.statemachine.TestUtils;
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;
/**
* Tests for state machine configuration.
*
*
* @author Janne Valkealahti
*
*/
public class ConfigurationTests extends AbstractStateMachineTests {
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testStates() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config1.class);
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
context.register(Config1.class);
context.refresh();
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
assertThat(machine, notNullValue());
TestAction testAction = ctx.getBean("testAction", TestAction.class);
TestGuard testGuard = ctx.getBean("testGuard", TestGuard.class);
TestAction testAction = context.getBean("testAction", TestAction.class);
TestGuard testGuard = context.getBean("testGuard", TestGuard.class);
assertThat(testAction, notNullValue());
assertThat(testGuard, notNullValue());
ctx.close();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testEndState() throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config3.class);
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
context.register(Config3.class);
context.refresh();
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
assertThat(machine, notNullValue());
Object endState = TestUtils.readField("endState", machine);
assertThat(endState, notNullValue());
ctx.close();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testSimpleSubmachine() throws Exception {
context.register(Config4.class);
context.refresh();
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
assertThat(machine, notNullValue());
}
@Configuration
@EnableStateMachine
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@@ -103,7 +117,7 @@ public class ConfigurationTests extends AbstractStateMachineTests {
public TestAction testAction() {
return new TestAction();
}
@Bean
public TestGuard testGuard() {
return new TestGuard();
@@ -152,12 +166,15 @@ public class ConfigurationTests extends AbstractStateMachineTests {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withStates()
.withSubStates(TestStates.S1, null)
.initial(TestStates.S1)
.and()
.withStates(TestStates.S1)
.initial(TestStates.S1)
.end(TestStates.SF)
.states(EnumSet.allOf(TestStates.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
@@ -169,9 +186,14 @@ public class ConfigurationTests extends AbstractStateMachineTests {
.withLocal()
.source(TestStates.S1)
.target(TestStates.S2)
.event(TestEvents.E2);
.event(TestEvents.E2)
.and()
.withInternal()
.source(TestStates.S2)
.event(TestEvents.E3);
}
}
}

View File

@@ -40,7 +40,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
/**
* Tests from state machine app context events.
*
*
* @author Janne Valkealahti
*
*/
@@ -50,7 +50,7 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
@Test
public void testContextEvents() throws Exception {
context.register(BaseConfig.class, StateMachineEventPublisherConfiguration.class, Config1.class);
@@ -60,13 +60,14 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
@SuppressWarnings("unchecked")
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine, notNullValue());
machine.sendEvent(TestEvents.E1);
machine.sendEvent(TestEvents.E2);
machine.sendEvent(TestEvents.E3);
machine.sendEvent(TestEvents.E4);
machine.sendEvent(TestEvents.EF);
// 6 events instead of 5, first one is initial transition
// to SI where source state is null
assertThat(listener.onEventLatch.await(5, TimeUnit.SECONDS), is(true));
@@ -119,9 +120,9 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
public TestEventListener testEventListener() {
return new TestEventListener();
}
}
static class TestEventListener implements ApplicationListener<AbstractStateMachineEvent> {
CountDownLatch onEventLatch = new CountDownLatch(6);
@@ -134,6 +135,6 @@ public class StateMachineEventTests extends AbstractStateMachineTests {
onEventLatch.countDown();
}
}
}
}

View File

@@ -44,7 +44,7 @@ import org.springframework.statemachine.support.DefaultStateContext;
/**
* Tests for using spel expressions in guards.
*
*
* @author Janne Valkealahti
*
*/
@@ -60,10 +60,10 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
map.put("foo", "bar");
MessageHeaders headers = new MessageHeaders(map);
DefaultStateContext<TestStates, TestEvents> stateContext = new DefaultStateContext<TestStates, TestEvents>(headers, null, null);
assertThat(guard.evaluate(stateContext), is(true));
}
@SuppressWarnings({ "unchecked" })
@Test
public void testGuardDenyStateChange() throws Exception {
@@ -71,13 +71,14 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine.getState().getIds(), contains(TestStates.S1));
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
assertThat(machine.getState().getIds(), contains(TestStates.S1));
ctx.close();
}
@Configuration
@EnableStateMachine
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@@ -99,7 +100,7 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
.event(TestEvents.E1)
.guardExpression("false");
}
}
}

View File

@@ -45,7 +45,7 @@ import org.springframework.statemachine.state.State;
/**
* Tests for state machine listener functionality.
*
*
* @author Janne Valkealahti
*
*/
@@ -58,6 +58,7 @@ public class ListenerTests extends AbstractStateMachineTests {
@SuppressWarnings("unchecked")
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
TestStateMachineListener listener = new TestStateMachineListener();
machine.addStateListener(listener);

View File

@@ -42,7 +42,7 @@ public class EndStateTests extends AbstractStateMachineTests {
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
@Test
public void testEndStateCompletes() {
context.register(Config1.class);
@@ -51,6 +51,7 @@ public class EndStateTests extends AbstractStateMachineTests {
@SuppressWarnings("unchecked")
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine, notNullValue());
assertThat(machine.isComplete(), is(false));
machine.sendEvent(TestEvents.E1);

View File

@@ -34,7 +34,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
/**
* Tests for functionality around state machine initial state.
*
*
* @author Janne Valkealahti
*
*/
@@ -48,10 +48,10 @@ public class InitialStateTests extends AbstractStateMachineTests {
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine.getState().getIds(), contains(TestStates.S1));
}
@Test(expected = Exception.class)
public void testInitialStateMissingFailure() throws Exception {
context.register(BaseConfig.class, Config2.class);
@@ -110,10 +110,10 @@ public class InitialStateTests extends AbstractStateMachineTests {
}
}
@Override
protected AnnotationConfigApplicationContext buildContext() {
return new AnnotationConfigApplicationContext();
}
}

View File

@@ -15,8 +15,8 @@
*/
package org.springframework.statemachine.state;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
@@ -32,7 +32,7 @@ import org.springframework.statemachine.transition.Transition;
/**
* Tests for states using a submachine.
*
*
* @author Janne Valkealahti
*
*/
@@ -40,7 +40,7 @@ public class RegionStateTests extends AbstractStateMachineTests {
@Test
public void testSimpleRegionState() {
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
@@ -71,20 +71,20 @@ public class RegionStateTests extends AbstractStateMachineTests {
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
machine.setTaskExecutor(taskExecutor);
machine.start();
Collection<Region<TestStates,TestEvents>> regions = new ArrayList<Region<TestStates,TestEvents>>();
regions.add(machine);
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(regions);
RegionState<TestStates,TestEvents> state = new RegionState<TestStates,TestEvents>(TestStates.S11, regions);
assertThat(state.isSimple(), is(false));
assertThat(state.isComposite(), is(true));
assertThat(state.isOrthogonal(), is(false));
assertThat(state.isSubmachineState(), is(false));
assertThat(state.getIds(), contains(TestStates.SI));
}
}

View File

@@ -31,7 +31,7 @@ import org.springframework.statemachine.transition.Transition;
/**
* Tests for states using a submachine.
*
*
* @author Janne Valkealahti
*
*/
@@ -39,7 +39,7 @@ public class SubmachineStateTests extends AbstractStateMachineTests {
@Test
public void testSimpleSubmachineState() {
State<TestStates,TestEvents> stateSI = new EnumState<TestStates,TestEvents>(TestStates.SI);
State<TestStates,TestEvents> stateS1 = new EnumState<TestStates,TestEvents>(TestStates.S1);
State<TestStates,TestEvents> stateS2 = new EnumState<TestStates,TestEvents>(TestStates.S2);
@@ -70,18 +70,18 @@ public class SubmachineStateTests extends AbstractStateMachineTests {
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
machine.setTaskExecutor(taskExecutor);
machine.start();
StateMachineState<TestStates,TestEvents> state = new StateMachineState<TestStates,TestEvents>(machine);
StateMachineState<TestStates,TestEvents> state = new StateMachineState<TestStates,TestEvents>(TestStates.S4, machine);
assertThat(state.isSimple(), is(false));
assertThat(state.isComposite(), is(false));
assertThat(state.isOrthogonal(), is(false));
assertThat(state.isSubmachineState(), is(true));
assertThat(state.getIds(), contains(TestStates.SI));
assertThat(state.getIds(), contains(TestStates.S4, TestStates.SI));
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.tree;
import org.junit.Test;
import org.springframework.statemachine.support.tree.Tree.Node;
public class TreeTests {
@Test
public void testTree1() {
Tree<String> tree = new Tree<String>();
tree.add("S", "S", null);
tree.add("S1", "S1", "S");
tree.add("S2", "S2", "S");
tree.add("S11", "S11", "S1");
tree.add("S12", "S12", "S1");
tree.add("S13", "S13", "S1");
tree.add("S21", "S21", "S2");
TreeTraverser<Node<String>> traverser = new TreeTraverser<Node<String>>() {
@Override
public Iterable<Node<String>> children(Node<String> root) {
return root.getChildren();
}
};
for (Node<String> node : traverser.postOrderTraversal(tree.getRoot())) {
System.out.println(node.getData());
}
}
@Test
public void testTree2() {
Tree<String> tree = new Tree<String>();
tree.add("S2", "S2", "S");
tree.add("S13", "S13", "S1");
tree.add("S11", "S11", "S1");
tree.add("S", "S", null);
tree.add("S12", "S12", "S1");
tree.add("S21", "S21", "S2");
tree.add("S1", "S1", "S");
TreeTraverser<Node<String>> traverser = new TreeTraverser<Node<String>>() {
@Override
public Iterable<Node<String>> children(Node<String> root) {
return root.getChildren();
}
};
for (Node<String> node : traverser.postOrderTraversal(tree.getRoot())) {
System.out.println(node.getData());
}
}
}

View File

@@ -41,7 +41,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
/**
* Tests for state machine transitions.
*
*
* @author Janne Valkealahti
*
*/
@@ -54,14 +54,14 @@ public class TransitionTests extends AbstractStateMachineTests {
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
assertThat(machine.getState().getIds(), contains(TestStates.S1));
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
assertThat(machine.getState().getIds(), contains(TestStates.S3));
ctx.close();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testInternalTransition() throws Exception {
@@ -69,28 +69,28 @@ public class TransitionTests extends AbstractStateMachineTests {
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
EnumStateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
machine.start();
TestExitAction testExitAction = ctx.getBean("testExitAction", TestExitAction.class);
TestEntryAction testEntryAction = ctx.getBean("testEntryAction", TestEntryAction.class);
TestAction externalTestAction = ctx.getBean("externalTestAction", TestAction.class);
TestAction internalTestAction = ctx.getBean("internalTestAction", TestAction.class);
assertThat(machine.getState().getIds(), contains(TestStates.S1));
assertThat(machine.getState().getIds(), contains(TestStates.S1));
assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
machine.sendEvent(TestEvents.E1);
assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat(internalTestAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
machine.sendEvent(TestEvents.E2);
assertThat(testExitAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(testEntryAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(externalTestAction.onExecuteLatch.await(1, TimeUnit.SECONDS), is(true));
assertThat(machine.getState().getIds(), contains(TestStates.S2));
ctx.close();
ctx.close();
}
@Configuration
@@ -151,7 +151,7 @@ public class TransitionTests extends AbstractStateMachineTests {
.event(TestEvents.E2)
.action(externalTestAction());
}
@Bean
public Action<TestStates, TestEvents> testEntryAction() {
return new TestEntryAction();
@@ -161,7 +161,7 @@ public class TransitionTests extends AbstractStateMachineTests {
public Action<TestStates, TestEvents> testExitAction() {
return new TestExitAction();
}
@Bean
public Action<TestStates, TestEvents> externalTestAction() {
return new TestAction();
@@ -171,7 +171,7 @@ public class TransitionTests extends AbstractStateMachineTests {
public Action<TestStates, TestEvents> internalTestAction() {
return new TestAction();
}
}
}