Base support for local transitions

- resolves #5
- big conceptual changes to how sub-machines are handled
  order to get events working with a multi-level sub states.
- added more typing throughout few interfaces to properly
  pass in state context via action axecution chain.
  this type change effectively caused change to most of the
  classes but not that much functional change of behaviour.
- cleaning and tidy up
This commit is contained in:
Janne Valkealahti
2015-02-11 13:36:52 +00:00
parent 250f99a1ae
commit b8f5320e38
47 changed files with 882 additions and 205 deletions

View File

@@ -100,6 +100,7 @@ project('spring-statemachine-core') {
testCompile "org.hamcrest:hamcrest-core:$hamcrestVersion"
testCompile "org.hamcrest:hamcrest-library:$hamcrestVersion"
testCompile "junit:junit:$junitVersion"
testRuntime("log4j:log4j:$log4jVersion")
}
}

View File

@@ -2,3 +2,5 @@ version=1.0.0.BUILD-SNAPSHOT
springVersion = 4.1.4.RELEASE
hamcrestVersion = 1.3
junitVersion = 4.11
log4jVersion = 1.2.17

View File

@@ -24,11 +24,11 @@ import org.springframework.statemachine.transition.Transition;
* {@code StateContext} is representing a current context used in
* {@link Transition}s, {@link Action}s and {@link Guard}s order to get access
* to event headers and {@link ExtendedState}.
*
*
* @author Janne Valkealahti
*
*/
public interface StateContext {
public interface StateContext<S, E> {
/**
* Gets the event message headers.
@@ -44,4 +44,11 @@ public interface StateContext {
*/
ExtendedState getExtendedState();
/**
* Gets the transition.
*
* @return the transition
*/
Transition<S, E> getTransition();
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
/**
* {@code StateMachine} provides an APIs for generic finite state machine needed
* for basic operations like working with states, events and a lifecycle.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -43,17 +43,22 @@ public interface StateMachine<S, E> extends Region<S, E> {
*/
void start();
/**
* Stop the state machine.
*/
void stop();
/**
* Send an event {@code E} wrapped with a {@link Message} to the state
* machine.
*
*
* @param event the wrapped event to send
*/
void sendEvent(Message<E> event);
/**
* Send an event {@code E} to the state machine.
*
*
* @param event the event to send
*/
void sendEvent(E event);

View File

@@ -22,15 +22,17 @@ import org.springframework.statemachine.StateContext;
* events by executing an {@code Action} with a {@link StateContext}.
*
* @author Janne Valkealahti
*
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface Action {
public interface Action<S, E> {
/**
* Execute action with a {@link StateContext}.
*
* @param context the state context
*/
void execute(StateContext context);
void execute(StateContext<S, E> context);
}

View File

@@ -50,12 +50,12 @@ public class StateMachineStates<S, E> {
public static class StateData<S, E> {
private S state;
private Collection<E> deferred;
private Collection<Action> entryActions;
private Collection<Action> exitActions;
private Collection<Action<S, E>> entryActions;
private Collection<Action<S, E>> exitActions;
public StateData(S state, Collection<E> deferred) {
this(state, deferred, null, null);
}
public StateData(S state, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
public StateData(S state, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
this.state = state;
this.deferred = deferred;
this.entryActions = entryActions;
@@ -70,10 +70,10 @@ public class StateMachineStates<S, E> {
public Collection<E> getDeferred() {
return deferred;
}
public Collection<Action> getEntryActions() {
public Collection<Action<S, E>> getEntryActions() {
return entryActions;
}
public Collection<Action> getExitActions() {
public Collection<Action<S, E>> getExitActions() {
return exitActions;
}
}

View File

@@ -24,8 +24,10 @@ import org.springframework.statemachine.config.common.annotation.AbstractConfigu
import org.springframework.statemachine.config.common.annotation.ObjectPostProcessor;
import org.springframework.statemachine.config.configurers.DefaultExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.DefaultInternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.DefaultLocalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.LocalTransitionConfigurer;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.transition.TransitionKind;
@@ -64,8 +66,13 @@ public class StateMachineTransitionBuilder<S, E>
public InternalTransitionConfigurer<S, E> withInternal() throws Exception {
return apply(new DefaultInternalTransitionConfigurer<S, E>());
}
@Override
public LocalTransitionConfigurer<S, E> withLocal() throws Exception {
return apply(new DefaultLocalTransitionConfigurer<S, E>());
}
public void add(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
public void add(S source, S target, E event, Collection<Action<S, E>> actions, Guard<S, E> guard, TransitionKind kind) {
transitionData.add(new TransitionData<S, E>(source, target, event, actions, guard, kind));
}

View File

@@ -17,11 +17,14 @@ package org.springframework.statemachine.config.builders;
import org.springframework.statemachine.config.configurers.ExternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.InternalTransitionConfigurer;
import org.springframework.statemachine.config.configurers.LocalTransitionConfigurer;
public interface StateMachineTransitionConfigurer<S, E> {
ExternalTransitionConfigurer<S, E> withExternal() throws Exception;
InternalTransitionConfigurer<S, E> withInternal() throws Exception;
LocalTransitionConfigurer<S, E> withLocal() throws Exception;
}

View File

@@ -37,10 +37,10 @@ public class StateMachineTransitions<S, E> {
S source;
S target;
E event;
Collection<Action> actions;
Guard guard;
Collection<Action<S, E>> actions;
Guard<S, E> guard;
TransitionKind kind;
public TransitionData(S source, S target, E event, Collection<Action> actions, Guard guard, TransitionKind kind) {
public TransitionData(S source, S target, E event, Collection<Action<S, E>> actions, Guard<S, E> guard, TransitionKind kind) {
this.source = source;
this.target = target;
this.event = event;
@@ -57,10 +57,10 @@ public class StateMachineTransitions<S, E> {
public E getEvent() {
return event;
}
public Collection<Action> getActions() {
public Collection<Action<S, E>> getActions() {
return actions;
}
public Guard getGuard() {
public Guard<S, E> getGuard() {
return guard;
}
public TransitionKind getKind() {

View File

@@ -48,9 +48,9 @@ public class DefaultExternalTransitionConfigurer<S, E>
private E event;
private Collection<Action> actions = new ArrayList<Action>();
private Collection<Action<S, E>> actions = new ArrayList<Action<S, E>>();
private Guard guard;
private Guard<S, E> guard;
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
@@ -76,13 +76,13 @@ public class DefaultExternalTransitionConfigurer<S, E>
}
@Override
public ExternalTransitionConfigurer<S, E> action(Action action) {
public ExternalTransitionConfigurer<S, E> action(Action<S, E> action) {
actions.add(action);
return this;
}
@Override
public ExternalTransitionConfigurer<S, E> guard(Guard guard) {
public ExternalTransitionConfigurer<S, E> guard(Guard<S, E> guard) {
this.guard = guard;
return this;
}
@@ -91,7 +91,7 @@ public class DefaultExternalTransitionConfigurer<S, E>
public ExternalTransitionConfigurer<S, E> guardExpression(String expression) {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
this.guard = new SpelExpressionGuard<S, E>(parser.parseExpression(expression));
return this;
}

View File

@@ -48,9 +48,9 @@ public class DefaultInternalTransitionConfigurer<S, E>
private E event;
private Collection<Action> actions = new ArrayList<Action>();
private Collection<Action<S, E>> actions = new ArrayList<Action<S, E>>();
private Guard guard;
private Guard<S, E> guard;
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
@@ -70,13 +70,13 @@ public class DefaultInternalTransitionConfigurer<S, E>
}
@Override
public InternalTransitionConfigurer<S, E> action(Action action) {
public InternalTransitionConfigurer<S, E> action(Action<S, E> action) {
actions.add(action);
return this;
}
@Override
public InternalTransitionConfigurer<S, E> guard(Guard guard) {
public InternalTransitionConfigurer<S, E> guard(Guard<S, E> guard) {
this.guard = guard;
return this;
}
@@ -85,7 +85,7 @@ public class DefaultInternalTransitionConfigurer<S, E>
public InternalTransitionConfigurer<S, E> guardExpression(String expression) {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
this.guard = new SpelExpressionGuard(parser.parseExpression(expression));
this.guard = new SpelExpressionGuard<S, E>(parser.parseExpression(expression));
return this;
}

View File

@@ -0,0 +1,98 @@
/*
* 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.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitions;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.guard.SpelExpressionGuard;
import org.springframework.statemachine.transition.TransitionKind;
/**
* Default implementation of a {@link LocalTransitionConfigurer}.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public class DefaultLocalTransitionConfigurer<S, E>
extends AnnotationConfigurerAdapter<StateMachineTransitions<S, E>, StateMachineTransitionConfigurer<S, E>, StateMachineTransitionBuilder<S, E>>
implements LocalTransitionConfigurer<S, E> {
private S source;
private S target;
private E event;
private Collection<Action<S, E>> actions = new ArrayList<Action<S, E>>();
private Guard<S, E> guard;
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.add(source, target, event, actions, guard, TransitionKind.LOCAL);
}
@Override
public LocalTransitionConfigurer<S, E> source(S source) {
this.source = source;
return this;
}
@Override
public LocalTransitionConfigurer<S, E> target(S target) {
this.target = target;
return this;
}
@Override
public LocalTransitionConfigurer<S, E> event(E event) {
this.event = event;
return this;
}
@Override
public LocalTransitionConfigurer<S, E> action(Action<S, E> action) {
actions.add(action);
return this;
}
@Override
public LocalTransitionConfigurer<S, E> guard(Guard<S, E> guard) {
this.guard = guard;
return this;
}
@Override
public LocalTransitionConfigurer<S, E> guardExpression(String expression) {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
this.guard = new SpelExpressionGuard<S, E>(parser.parseExpression(expression));
return this;
}
}

View File

@@ -61,7 +61,7 @@ public class DefaultStateConfigurer<S, E>
}
@Override
public StateConfigurer<S, E> state(S state, Collection<Action> entryActions, Collection<Action> exitActions) {
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));
return this;
}

View File

@@ -0,0 +1,39 @@
/*
* 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 org.springframework.statemachine.transition.Transition;
/**
* {@code TransitionConfigurer} interface for configuring local {@link Transition}s.
*
* @author Janne Valkealahti
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface LocalTransitionConfigurer<S, E> extends
TransitionConfigurer<LocalTransitionConfigurer<S, E>, S, E> {
/**
* Specify a target state {@code S} for this {@link Transition}.
*
* @param target the target state {@code S}
* @return configurer for chaining
*/
LocalTransitionConfigurer<S, E> target(S target);
}

View File

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

View File

@@ -55,7 +55,7 @@ public interface TransitionConfigurer<T, S, E> extends
* @param action the action
* @return configurer for chaining
*/
T action(Action action);
T action(Action<S, E> action);
/**
* Specify a {@link Guard} for this {@link Transition}.
@@ -63,7 +63,7 @@ public interface TransitionConfigurer<T, S, E> extends
* @param guard the guard
* @return configurer for chaining
*/
T guard(Guard guard);
T guard(Guard<S, E> guard);
/**
* Specify a {@link Guard} backed by a SpEL expression for this {@link Transition}.

View File

@@ -24,9 +24,11 @@ import org.springframework.statemachine.StateContext;
* {@code FALSE}.
*
* @author Janne Valkealahti
*
*
* @param <S> the type of state
* @param <E> the type of event
*/
public interface Guard {
public interface Guard<S, E> {
/**
* Evaluate a guard condition.
@@ -34,6 +36,6 @@ public interface Guard {
* @param context the state context
* @return true, if guard evaluation is successful, false otherwise.
*/
boolean evaluate(StateContext context);
boolean evaluate(StateContext<S, E> context);
}

View File

@@ -26,7 +26,7 @@ import org.springframework.util.Assert;
* @author Janne Valkealahti
*
*/
public class SpelExpressionGuard implements Guard {
public class SpelExpressionGuard<S, E> implements Guard<S, E> {
private final Expression expression;
@@ -41,7 +41,7 @@ public class SpelExpressionGuard implements Guard {
}
@Override
public boolean evaluate(StateContext context) {
public boolean evaluate(StateContext<S, E> context) {
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(context);
return expression.getValue(evaluationContext, Boolean.class);
}

View File

@@ -52,7 +52,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
this(id, deferred, entryActions, exitActions, null);
}
@@ -86,7 +86,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
* @param pseudoState the pseudo state
* @param regions the regions
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
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);
this.ids = new ArrayList<S>();
@@ -103,7 +103,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
* @param pseudoState the pseudo state
* @param submachine the submachine
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
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);
this.ids = new ArrayList<S>();
@@ -119,7 +119,7 @@ public abstract class AbstractSimpleState<S, E> extends AbstractState<S, E> {
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public AbstractSimpleState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
super(deferred, entryActions, exitActions, pseudoState);
this.ids = new ArrayList<S>();

View File

@@ -18,6 +18,7 @@ package org.springframework.statemachine.state;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.region.Region;
@@ -25,7 +26,7 @@ import org.springframework.util.StringUtils;
/**
* Base implementation of a {@link State}.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -35,8 +36,8 @@ public abstract class AbstractState<S, E> implements State<S, E> {
private final PseudoState pseudoState;
private final Collection<E> deferred;
private final Collection<Action> entryActions;
private final Collection<Action> exitActions;
private final Collection<Action<S, E>> entryActions;
private final Collection<Action<S, E>> exitActions;
private final Collection<Region<S, E>> regions = new ArrayList<Region<S, E>>();
private final StateMachine<S, E> submachine;
@@ -48,7 +49,7 @@ public abstract class AbstractState<S, E> implements State<S, E> {
public AbstractState(PseudoState pseudoState) {
this(null, null, null, pseudoState);
}
/**
* Instantiates a new abstract state.
*
@@ -65,10 +66,10 @@ public abstract class AbstractState<S, E> implements State<S, E> {
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public AbstractState(Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
this(deferred, entryActions, exitActions, null);
}
/**
* Instantiates a new abstract state.
*
@@ -77,7 +78,7 @@ public abstract class AbstractState<S, E> implements State<S, E> {
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public AbstractState(Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public AbstractState(Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
this(deferred, entryActions, exitActions, pseudoState, null, null);
}
@@ -91,7 +92,7 @@ public abstract class AbstractState<S, E> implements State<S, E> {
* @param pseudoState the pseudo state
* @param submachine the submachine
*/
public AbstractState(Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public AbstractState(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);
}
@@ -105,11 +106,11 @@ public abstract class AbstractState<S, E> implements State<S, E> {
* @param pseudoState the pseudo state
* @param regions the regions
*/
public AbstractState(Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public AbstractState(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);
}
/**
* Instantiates a new abstract state.
*
@@ -120,13 +121,13 @@ 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> entryActions, Collection<Action> exitActions,
private AbstractState(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.deferred = deferred;
this.entryActions = entryActions;
this.exitActions = exitActions;
this.pseudoState = pseudoState;
// use of private ctor should prevent user to
// add regions and a submachine which is not allowed.
if (regions != null) {
@@ -134,10 +135,16 @@ public abstract class AbstractState<S, E> implements State<S, E> {
}
this.submachine = submachine;
}
@Override
public abstract void exit(E event, StateContext<S, E> context);
@Override
public abstract void entry(E event, StateContext<S, E> context);
@Override
public abstract Collection<S> getIds();
@Override
public PseudoState getPseudoState() {
return pseudoState;
@@ -149,12 +156,12 @@ public abstract class AbstractState<S, E> implements State<S, E> {
}
@Override
public Collection<Action> getEntryActions() {
public Collection<Action<S, E>> getEntryActions() {
return entryActions;
}
@Override
public Collection<Action> getExitActions() {
public Collection<Action<S, E>> getExitActions() {
return exitActions;
}
@@ -177,11 +184,11 @@ public abstract class AbstractState<S, E> implements State<S, E> {
public boolean isSubmachineState() {
return submachine != null;
}
protected StateMachine<S, E> getSubmachine() {
return submachine;
}
protected Collection<Region<S, E>> getRegions() {
return regions;
}

View File

@@ -17,6 +17,7 @@ package org.springframework.statemachine.state;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.region.Region;
@@ -68,7 +69,7 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
public EnumState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions) {
super(id, deferred, entryActions, exitActions);
}
@@ -81,7 +82,7 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public EnumState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState) {
super(id, deferred, entryActions, exitActions, pseudoState);
}
@@ -96,7 +97,7 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
* @param pseudoState the pseudo state
* @param regions the regions
*/
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public EnumState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, Collection<Region<S, E>> regions) {
super(id, deferred, entryActions, exitActions, pseudoState, regions);
}
@@ -111,11 +112,31 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSim
* @param pseudoState the pseudo state
* @param submachine the submachine
*/
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public EnumState(S id, Collection<E> deferred, Collection<Action<S, E>> entryActions, Collection<Action<S, E>> exitActions,
PseudoState pseudoState, StateMachine<S, E> submachine) {
super(id, deferred, entryActions, exitActions, pseudoState, submachine);
}
@Override
public void exit(E event, StateContext<S, E> context) {
Collection<Action<S, E>> actions = getExitActions();
if (actions != null) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
}
@Override
public void entry(E event, StateContext<S, E> context) {
Collection<Action<S, E>> actions = getEntryActions();
if (actions != null) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
}
@Override
public String toString() {
return "EnumState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()

View File

@@ -18,12 +18,13 @@ package org.springframework.statemachine.state;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.region.Region;
/**
* A {@link State} implementation where states are wrapped in a regions..
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -59,7 +60,7 @@ public class RegionState<S, E> extends AbstractState<S, E> {
public RegionState(Collection<Region<S, E>> regions, PseudoState pseudoState) {
super(null, null, null, pseudoState, regions);
}
/**
* Instantiates a new region state.
*
@@ -69,7 +70,7 @@ public class RegionState<S, E> extends AbstractState<S, E> {
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public RegionState(Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public RegionState(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);
}
@@ -82,10 +83,22 @@ public class RegionState<S, E> extends AbstractState<S, E> {
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public RegionState(Collection<Region<S, E>> regions, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
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);
}
@Override
public void exit(E event, StateContext<S, E> context) {
// TODO Auto-generated method stub
}
@Override
public void entry(E event, StateContext<S, E> context) {
// TODO Auto-generated method stub
}
@Override
public Collection<S> getIds() {
ArrayList<S> ids = new ArrayList<S>();
@@ -94,5 +107,5 @@ public class RegionState<S, E> extends AbstractState<S, E> {
}
return ids;
}
}

View File

@@ -17,11 +17,12 @@ package org.springframework.statemachine.state;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
/**
* {@code State} is an interface representing possible state in a state machine.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -29,6 +30,22 @@ import org.springframework.statemachine.action.Action;
*/
public interface State<S, E> {
/**
* Initiate an exit sequence for the state.
*
* @param event the event
* @param context the context
*/
void exit(E event, StateContext<S, E> context);
/**
* Initiate an entry sequence for the state.
*
* @param event the event
* @param context the context
*/
void entry(E event, StateContext<S, E> context);
/**
* Gets the state identifiers. Usually returned collection contains only one
* identifier except in a case where state is an orthogonal.
@@ -41,7 +58,7 @@ public interface State<S, E> {
* Gets a {@link PseudoState} attached to a {@code State}.
* {@link PseudoState} is not required and thus this method return
* {@code NULL} if it's not set.
*
*
* @return pseudostate or null if state doesn't have one
*/
PseudoState getPseudoState();
@@ -52,20 +69,20 @@ public interface State<S, E> {
* @return the state deferred events
*/
Collection<E> getDeferredEvents();
/**
* Gets {@link Action}s executed entering in this state.
*
* @return the state entry actions
*/
Collection<Action> getEntryActions();
Collection<Action<S, E>> getEntryActions();
/**
* Gets {@link Action}s executed exiting from this state.
*
* @return the state exit actions
*/
Collection<Action> getExitActions();
Collection<Action<S, E>> getExitActions();
/**
* Checks if state is a simple state. A simple state does not have any

View File

@@ -16,13 +16,17 @@
package org.springframework.statemachine.state;
import java.util.Collection;
import java.util.Collections;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.transition.Transition;
import org.springframework.statemachine.transition.TransitionKind;
/**
* A {@link State} implementation where state is wrapped in a substatemachine.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -58,7 +62,7 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
public StateMachineState(StateMachine<S, E> submachine, PseudoState pseudoState) {
super(null, null, null, pseudoState, submachine);
}
/**
* Instantiates a new state machine state.
*
@@ -68,7 +72,7 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
* @param exitActions the exit actions
* @param pseudoState the pseudo state
*/
public StateMachineState(StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
public StateMachineState(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);
}
@@ -81,13 +85,60 @@ public class StateMachineState<S, E> extends AbstractState<S, E> {
* @param entryActions the entry actions
* @param exitActions the exit actions
*/
public StateMachineState(StateMachine<S, E> submachine, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
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);
}
@Override
public Collection<S> getIds() {
return getSubmachine().getState().getIds();
State<S, E> state = getSubmachine().getState();
if (state != null) {
return state.getIds();
} else {
return Collections.emptyList();
}
}
@Override
public void exit(E event, StateContext<S, E> context) {
getSubmachine().getState().exit(event, context);
getSubmachine().stop();
Collection<Action<S, E>> actions = getExitActions();
if (actions != null && !isLocal(context)) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
}
@Override
public void entry(E event, StateContext<S, E> context) {
Collection<Action<S, E>> actions = getEntryActions();
if (actions != null && !isLocal(context)) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
if (getPseudoState() != null && getPseudoState().getKind() == PseudoStateKind.INITIAL) {
getSubmachine().start();
} else {
getSubmachine().getState().entry(event, context);
}
}
private boolean isLocal(StateContext<S, E> context) {
Transition<S, E> transition = context.getTransition();
if (transition != null && TransitionKind.LOCAL == transition.getKind() && this == transition.getTarget()) {
return true;
} else {
return false;
}
}
@Override
public String toString() {
return "StateMachineState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
+ ", toString()=" + super.toString() + "]";
}
}

View File

@@ -38,7 +38,6 @@ import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.annotation.OnTransition;
import org.springframework.statemachine.event.StateMachineEventPublisher;
import org.springframework.statemachine.listener.CompositeStateMachineListener;
@@ -56,7 +55,7 @@ import org.springframework.util.Assert;
/**
* Base implementation of a {@link StateMachine} loosely modelled from UML state
* machine.
*
*
* @author Janne Valkealahti
*
* @param <S> the type of state
@@ -73,7 +72,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
private final State<S,E> initialState;
private final State<S,E> endState;
private final Message<E> initialEvent;
private final ExtendedState extendedState;
@@ -112,7 +111,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
State<S, E> initialState, State<S, E> endState) {
this(states, transitions, initialState, endState, null, null);
}
/**
* Instantiates a new abstract state machine.
*
@@ -179,7 +178,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
@Override
protected void onInit() throws Exception {
super.onInit();
Assert.notNull(initialState, "Initial state must be set");
Assert.notNull(initialState, "Initial state must be set");
Assert.state(initialState.getPseudoState() != null
&& initialState.getPseudoState().getKind() == PseudoStateKind.INITIAL,
"Initial state's pseudostate kind must be INITIAL");
@@ -188,14 +187,20 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
@Override
protected void doStart() {
super.doStart();
switchToState(initialState, initialEvent);
switchToState(initialState, initialEvent, null);
}
@Override
protected void doStop() {
super.doStop();
currentState = null;
}
@Override
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
stateListener.register(listener);
}
@Override
public boolean isComplete() {
return (endState != null && endState.equals(currentState));
@@ -206,62 +211,55 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
* an unmodifiable copy because states in a state machine are immutable.
*
* @return immutable copy of existing states
*/
*/
@Override
public Collection<State<S, E>> getStates() {
return Collections.unmodifiableCollection(states);
}
@Override
public Collection<Transition<S, E>> getTransitions() {
return transitions;
}
private void switchToState(State<S,E> state, Message<E> event) {
log.info("Moving into state=" + state + " from " + currentState);
exitFromState(currentState, event);
private void switchToState(State<S,E> state, Message<E> event, Transition<S,E> transition) {
exitFromState(currentState, event, transition);
notifyStateChanged(currentState, state);
callHandlers(currentState, state, event);
currentState = state;
entryToState(state, event);
entryToState(state, event, transition);
for (Transition<S,E> transition : transitions) {
State<S,E> source = transition.getSource();
State<S,E> target = transition.getTarget();
if (transition.getTrigger() == null && source.equals(currentState)) {
switchToState(target, event);
// TODO: should handle triggerles transition some how differently
for (Transition<S,E> t : transitions) {
State<S,E> source = t.getSource();
State<S,E> target = t.getTarget();
if (t.getTrigger() == null && source.equals(currentState)) {
switchToState(target, event, t);
}
}
}
private void exitFromState(State<S, E> state, Message<E> event) {
private void exitFromState(State<S, E> state, Message<E> event, Transition<S, E> transition) {
if (state != null) {
log.trace("Exit state=[" + state + "]");
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
Collection<Action> actions = state.getExitActions();
if (actions != null) {
for (Action action : actions) {
action.execute(new DefaultStateContext(messageHeaders, extendedState));
}
}
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(messageHeaders, extendedState, transition);
state.exit(event != null ? event.getPayload() : null, stateContext);
}
}
private void entryToState(State<S,E> state, Message<E> event) {
private void entryToState(State<S, E> state, Message<E> event, Transition<S, E> transition) {
if (state != null) {
log.trace("Enter state=[" + state + "]");
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
Collection<Action> actions = state.getEntryActions();
if (actions != null) {
for (Action action : actions) {
action.execute(new DefaultStateContext(messageHeaders, extendedState));
}
}
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(messageHeaders, extendedState, transition);
state.entry(event != null ? event.getPayload() : null, stateContext);
}
}
@@ -274,11 +272,13 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
State<S,E> source = transition.getSource();
State<S,E> target = transition.getTarget();
Trigger<S, E> trigger = transition.getTrigger();
if (source.equals(currentState)) {
if (StateMachineUtils.containsAtleastOne(source.getIds(), currentState.getIds())) {
if (trigger != null && trigger.evaluate(queuedEvent.getPayload())) {
boolean transit = transition.transit(new DefaultStateContext(queuedEvent.getHeaders(), extendedState));
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(queuedEvent.getHeaders(), extendedState, transition);
boolean transit = transition.transit(stateContext);
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
switchToState(target, queuedEvent);
switchToState(target, queuedEvent, transition);
}
break;
} else if (source.getDeferredEvents() != null && source.getDeferredEvents().contains(queuedEvent.getPayload())) {
@@ -304,9 +304,10 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
Trigger<S, E> trigger = transition.getTrigger();
if (source.equals(currentState)) {
if (trigger != null && trigger.evaluate(event.getPayload())) {
boolean transit = transition.transit(new DefaultStateContext(event.getHeaders(), extendedState));
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(event.getHeaders(), extendedState, transition);
boolean transit = transition.transit(stateContext);
if (transit && transition.getKind() != TransitionKind.INTERNAL) {
switchToState(target, event);
switchToState(target, event, transition);
}
iterator.remove();
}
@@ -333,16 +334,16 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
if (sourceState != null && targetState != null) {
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState);
StateContext<S, E> stateContext = new DefaultStateContext<S, E>(messageHeaders, extendedState, null);
getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext);
}
}
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext stateContext) {
StateMachineRuntime runtime = new StateMachineRuntime() {
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext<S, E> stateContext) {
StateMachineRuntime runtime = new StateMachineRuntime() {
@Override
public StateContext getStateContext() {
public StateContext<S, E> getStateContext() {
return stateContext;
}
};
@@ -353,9 +354,9 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
return results;
}
private List<StateMachineHandler> getStateMachineHandlers(State<S,E> sourceState, State<S,E> targetState) {
private List<StateMachineHandler> getStateMachineHandlers(State<S, E> sourceState, State<S, E> targetState) {
BeanFactory beanFactory = getBeanFactory();
// TODO think how to handle null bf
if (beanFactory == null) {
return Collections.emptyList();
@@ -376,11 +377,11 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
handlersList.add(entry.getValue());
}
}
OrderComparator comparator = new OrderComparator();
Collections.sort(handlersList, comparator);
return handlersList;
}
}
private void notifyStateChanged(State<S,E> source, State<S,E> target) {
stateListener.stateChanged(source, target);
@@ -389,5 +390,5 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
eventPublisher.publishStateChanged(this, source, target);
}
}
}

View File

@@ -18,16 +18,20 @@ package org.springframework.statemachine.support;
import org.springframework.messaging.MessageHeaders;
import org.springframework.statemachine.ExtendedState;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.transition.Transition;
public class DefaultStateContext implements StateContext {
public class DefaultStateContext<S, E> implements StateContext<S, E> {
private final MessageHeaders messageHeaders;
private final ExtendedState extendedState;
private final Transition<S,E> transition;
public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState) {
public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState, Transition<S,E> transition) {
this.messageHeaders = messageHeaders;
this.extendedState = extendedState;
this.transition = transition;
}
@Override
@@ -39,5 +43,10 @@ public class DefaultStateContext implements StateContext {
public ExtendedState getExtendedState() {
return extendedState;
}
@Override
public Transition<S, E> getTransition() {
return transition;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine.support;
import java.util.Collection;
/**
* Various utility methods for state machine.
*
* @author Janne Valkealahti
*
*/
public abstract class StateMachineUtils {
/**
* Checks if right hand collection has atleast one same item as left hand
* collection.
*
* @param <S> the generic type
* @param left the left collection
* @param right the right collection
* @return true, right contains at least one item from left, false otherwise.
*/
public static <S> boolean containsAtleastOne(Collection<S> left, Collection<S> right) {
if (left == null || right == null) {
return false;
}
for (S id : left) {
if (right.contains(id)) {
return true;
}
}
return false;
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
public abstract class AbstractExternalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
public AbstractExternalTransition(State<S,E> source, State<S,E> target, Collection<Action> actions, E event, Guard guard) {
public AbstractExternalTransition(State<S,E> source, State<S,E> target, Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard);
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
public class AbstractInternalTransition <S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
public AbstractInternalTransition(State<S, E> source,Collection<Action> actions, E event, Guard guard) {
public AbstractInternalTransition(State<S, E> source,Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard);
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
public class AbstractLocalTransition <S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
public AbstractLocalTransition(State<S, E> source, State<S,E> target,Collection<Action> actions, E event, Guard guard) {
public AbstractLocalTransition(State<S, E> source, State<S,E> target,Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, target, actions, event, TransitionKind.LOCAL, guard);
}

View File

@@ -39,16 +39,16 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
private final State<S,E> target;
private final Collection<Action> actions;
private final Collection<Action<S, E>> actions;
private final TransitionKind kind;
private final Guard guard;
private final Guard<S, E> guard;
private Trigger<S, E> trigger;
public AbstractTransition(State<S, E> source, State<S, E> target, Collection<Action> actions, E event,
TransitionKind kind, Guard guard) {
public AbstractTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
TransitionKind kind, Guard<S, E> guard) {
Assert.notNull(source, "Source must be set");
// Assert.notNull(target, "Target must be set");
Assert.notNull(kind, "Transition type must be set");
@@ -73,7 +73,7 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
}
@Override
public Collection<Action> getActions() {
public Collection<Action<S, E>> getActions() {
return actions;
}
@@ -83,14 +83,14 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
}
@Override
public boolean transit(StateContext context) {
public boolean transit(StateContext<S, E> context) {
if (guard != null) {
if (!guard.evaluate(context)) {
return false;
}
}
if (actions != null) {
for (Action action : actions) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
public class DefaultExternalTransition<S, E> extends AbstractExternalTransition<S, E> {
public DefaultExternalTransition(State<S,E> source, State<S,E> target, Collection<Action> actions, E event, Guard guard) {
public DefaultExternalTransition(State<S,E> source, State<S,E> target, Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, target, actions, event, guard);
}

View File

@@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State;
public class DefaultInternalTransition<S, E> extends AbstractInternalTransition<S, E> {
public DefaultInternalTransition(State<S, E> source, Collection<Action> actions, E event, Guard guard) {
public DefaultInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, actions, event, guard);
}

View File

@@ -0,0 +1,35 @@
/*
* 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.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.state.State;
public class DefaultLocalTransition<S, E> extends AbstractLocalTransition<S, E> {
public DefaultLocalTransition(State<S,E> source, State<S,E> target, Collection<Action<S, E>> actions, E event, Guard<S, E> guard) {
super(source, target, actions, event, guard);
}
@Override
public String toString() {
return "DefaultLocalTransition [getSource()=" + getSource() + ", getTarget()=" + getTarget() + "]";
}
}

View File

@@ -39,7 +39,7 @@ public interface Transition<S, E> {
* @param context the state context
* @return true, if transition happened, false otherwise
*/
boolean transit(StateContext context);
boolean transit(StateContext<S, E> context);
/**
* Gets the source state of this transition.
@@ -60,7 +60,7 @@ public interface Transition<S, E> {
*
* @return the transition actions
*/
Collection<Action> getActions();
Collection<Action<S, E>> getActions();
/**
* Gets the transition trigger.

View File

@@ -15,8 +15,12 @@
*/
package org.springframework.statemachine;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@@ -30,46 +34,41 @@ import org.springframework.statemachine.guard.Guard;
/**
* Base class for stace machine tests.
*
*
* @author Janne Valkealahti
*
*/
public abstract class AbstractStateMachineTests {
private final static Log log = LogFactory.getLog(AbstractStateMachineTests.class);
protected AnnotationConfigApplicationContext context;
@Before
public void setup() {
context = buildContext();
}
@After
public void clean() {
if (context != null) {
context.close();
}
}
protected AnnotationConfigApplicationContext buildContext() {
return null;
}
public enum TestStates {
SI,S1,S2,S3,S4,SF
SI,S1,S2,S3,S4,SF,
S11,S111,S21,S211
}
public enum TestSubStates {
SUBSI,SUBS1,SUBS2,SUBS3,SUBS4
}
public enum TestEvents {
E1,E2,E3,E4,EF
}
public enum TestSubEvents {
SUBE1,SUBE2,SUBE3,SUBE4
}
@Configuration
public static class BaseConfig {
@@ -77,46 +76,86 @@ public abstract class AbstractStateMachineTests {
public TaskExecutor taskExecutor() {
return new SyncTaskExecutor();
}
}
public static class TestEntryAction extends AbstractTestAction {
public TestEntryAction() {
super();
}
public TestEntryAction(String message) {
super(message);
}
@Override
public String toString() {
return "TestEntryAction [message=" + message + "]";
}
}
public static class TestExitAction extends AbstractTestAction {
public TestExitAction() {
super();
}
public TestExitAction(String message) {
super(message);
}
@Override
public String toString() {
return "TestExitAction [message=" + message + "]";
}
}
public static class TestAction extends AbstractTestAction {
}
public static class TestGuard implements Guard {
public static class TestGuard implements Guard<TestStates, TestEvents> {
public CountDownLatch onEvaluateLatch = new CountDownLatch(1);
boolean evaluationResult = true;
public TestGuard() {
}
public TestGuard(boolean evaluationResult) {
this.evaluationResult = evaluationResult;
}
@Override
public boolean evaluate(StateContext context) {
public boolean evaluate(StateContext<TestStates, TestEvents> context) {
onEvaluateLatch.countDown();
return evaluationResult;
}
}
}
protected static class AbstractTestAction implements Action {
protected static class AbstractTestAction implements Action<TestStates, TestEvents> {
protected String message = null;
public CountDownLatch onExecuteLatch = new CountDownLatch(1);
public List<StateContext<TestStates, TestEvents>> stateContexts = new ArrayList<StateContext<TestStates, TestEvents>>();
public AbstractTestAction() {
}
public AbstractTestAction(String message) {
this.message = message;
}
@Override
public void execute(StateContext context) {
public void execute(StateContext<TestStates, TestEvents> context) {
if (message != null) {
log.info(this);
}
onExecuteLatch.countDown();
stateContexts.add(context);
}
}
}

View File

@@ -26,8 +26,6 @@ import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.state.EnumState;
import org.springframework.statemachine.state.State;
@@ -53,17 +51,17 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
Collection<Action> actionsFromSIToS1 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromSIToS1 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToS1 =
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null);
Collection<Action> actionsFromS1ToS2 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromS1ToS2 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromS1ToS2 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null);
Collection<Action> actionsFromS2ToS3 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromS2ToS3 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromS2ToS3 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null);
@@ -123,17 +121,17 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
// transitions
Collection<Transition<TestStates,TestEvents>> transitions = new ArrayList<Transition<TestStates,TestEvents>>();
Collection<Action> actionsFromSIToS1 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromSIToS1 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromSIToS1 =
new DefaultExternalTransition<TestStates,TestEvents>(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null);
Collection<Action> actionsFromS1ToS2 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromS1ToS2 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromS1ToS2 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null);
Collection<Action> actionsFromS2ToS3 = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsFromS2ToS3 = new ArrayList<Action<TestStates,TestEvents>>();
actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3"));
DefaultExternalTransition<TestStates,TestEvents> transitionFromS2ToS3 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null);
@@ -174,7 +172,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
Collection<State<TestStates,TestEvents>> states = new ArrayList<State<TestStates,TestEvents>>();
states.add(stateSI);
Collection<Action> actionsInSI = new ArrayList<Action>();
Collection<Action<TestStates,TestEvents>> actionsInSI = new ArrayList<Action<TestStates,TestEvents>>();
actionsInSI.add(new LoggingAction("actionsInSI"));
DefaultInternalTransition<TestStates,TestEvents> transitionInternalSI =
new DefaultInternalTransition<TestStates,TestEvents>(stateSI, actionsInSI, TestEvents.E1, null);
@@ -191,7 +189,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
}
private static class LoggingAction implements Action {
private static class LoggingAction implements Action<TestStates, TestEvents> {
private static final Log log = LogFactory.getLog(LoggingAction.class);
@@ -202,7 +200,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
}
@Override
public void execute(StateContext context) {
public void execute(StateContext<TestStates, TestEvents> context) {
log.info("Hello from LoggingAction " + message);
}

View File

@@ -28,9 +28,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.EnumStateMachine;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachineSystemConstants;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
@@ -53,7 +50,7 @@ public class StateMachineTests extends AbstractStateMachineTests {
ctx.close();
}
private static class LoggingAction implements Action {
private static class LoggingAction implements Action<TestStates, TestEvents> {
private static final Log log = LogFactory.getLog(StateMachineTests.LoggingAction.class);
@@ -64,7 +61,7 @@ public class StateMachineTests extends AbstractStateMachineTests {
}
@Override
public void execute(StateContext context) {
public void execute(StateContext<TestStates, TestEvents> context) {
log.info("Hello from LoggingAction " + message + " foo=" + context.getMessageHeaders().get("foo"));
}

View File

@@ -0,0 +1,234 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.statemachine;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.statemachine.action.Action;
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.transition.DefaultExternalTransition;
import org.springframework.statemachine.transition.DefaultLocalTransition;
import org.springframework.statemachine.transition.Transition;
public class SubStateMachineTests extends AbstractStateMachineTests {
@Test
public void testExternalTransition() throws Exception {
/**
* +-------------------------------------------+
* *-init->| S1 |
* +-------------------------------------------+
* | entry/ |
* | exit/ |
* | +--------------------------+ |
* | *-->| S11 | |
* | +--------------------------+ |
* | | entry/ | |
* | | exit/ | |
* | | +-----------+ | |
* | | *-->| S111 | | |
* | | +-----------+ | |
* | | | entry/ | | |
* |<----E1-----------| exit/ | | |
* | | | | | |
* | | +-----------+ | |
* | | | |
* | +--------------------------+ |
* | |
* +-------------------------------------------+
*/
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);
// submachine 11
Collection<State<TestStates,TestEvents>> substates111 = new ArrayList<State<TestStates,TestEvents>>();
substates111.add(stateS111);
Collection<Transition<TestStates,TestEvents>> subtransitions111 = new ArrayList<Transition<TestStates,TestEvents>>();
EnumStateMachine<TestStates, TestEvents> submachine11 = new EnumStateMachine<TestStates, TestEvents>(substates111, subtransitions111, stateS111, null);
// submachine 1
TestEntryAction entryActionS11 = new TestEntryAction("S11");
TestExitAction exitActionS11 = new TestExitAction("S11");
Collection<Action<TestStates, TestEvents>> entryActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
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);
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
substates11.add(stateS11);
Collection<Transition<TestStates,TestEvents>> subtransitions11 = new ArrayList<Transition<TestStates,TestEvents>>();
EnumStateMachine<TestStates, TestEvents> submachine1 = new EnumStateMachine<TestStates, TestEvents>(substates11, subtransitions11, stateS11, 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>(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 =
new DefaultExternalTransition<TestStates,TestEvents>(stateS111, stateS1, null, TestEvents.E1, null);
transitions.add(transitionFromS11ToS1);
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateS1, null);
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
machine.setTaskExecutor(taskExecutor);
machine.afterPropertiesSet();
machine.start();
submachine1.setTaskExecutor(taskExecutor);
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(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));
}
@Test
public void testLocalTransition() throws Exception {
/**
* +-------------------------------------------+
* *-init->| S1 |
* +-------------------------------------------+
* | entry/ |
* | exit/ |
* | +--------------------------+ |
* | *-->| S11 | |
* | +--------------------------+ |
* | | entry/ | |
* | | exit/ | |
* | | +-----------+ | |
* | | *-->| S111 | | |
* | | +-----------+ | |
* | | | entry/ | | |
* |<----E1-----------| exit/ | | |
* | | | | | |
* | | +-----------+ | |
* | | | |
* | +--------------------------+ |
* | |
* +-------------------------------------------+
*/
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);
// submachine 11
Collection<State<TestStates,TestEvents>> substates111 = new ArrayList<State<TestStates,TestEvents>>();
substates111.add(stateS111);
Collection<Transition<TestStates,TestEvents>> subtransitions111 = new ArrayList<Transition<TestStates,TestEvents>>();
EnumStateMachine<TestStates, TestEvents> submachine11 = new EnumStateMachine<TestStates, TestEvents>(substates111, subtransitions111, stateS111, null);
// submachine 1
TestEntryAction entryActionS11 = new TestEntryAction("S11");
TestExitAction exitActionS11 = new TestExitAction("S11");
Collection<Action<TestStates, TestEvents>> entryActionsS11 = new ArrayList<Action<TestStates, TestEvents>>();
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);
Collection<State<TestStates,TestEvents>> substates11 = new ArrayList<State<TestStates,TestEvents>>();
substates11.add(stateS11);
Collection<Transition<TestStates,TestEvents>> subtransitions11 = new ArrayList<Transition<TestStates,TestEvents>>();
EnumStateMachine<TestStates, TestEvents> submachine1 = new EnumStateMachine<TestStates, TestEvents>(substates11, subtransitions11, stateS11, 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>(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>>();
DefaultLocalTransition<TestStates,TestEvents> transitionFromS11ToS1 =
new DefaultLocalTransition<TestStates,TestEvents>(stateS111, stateS1, null, TestEvents.E1, null);
transitions.add(transitionFromS11ToS1);
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateS1, null);
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
machine.setTaskExecutor(taskExecutor);
machine.afterPropertiesSet();
machine.start();
submachine1.setTaskExecutor(taskExecutor);
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(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(false));
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(1));
assertThat(exitActionS1.stateContexts.size(), is(0));
}
}

View File

@@ -68,7 +68,7 @@ public class ActionTests extends AbstractStateMachineTests {
}
private static class TestCountAction implements Action {
private static class TestCountAction implements Action<TestStates, TestEvents> {
int count = 0;
@@ -77,7 +77,7 @@ public class ActionTests extends AbstractStateMachineTests {
}
@Override
public void execute(StateContext context) {
public void execute(StateContext<TestStates, TestEvents> context) {
count++;
}

View File

@@ -144,5 +144,34 @@ public class ConfigurationTests extends AbstractStateMachineTests {
}
}
@Configuration
@EnableStateMachine
public static class Config4 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withStates()
.initial(TestStates.S1)
.end(TestStates.SF)
.states(EnumSet.allOf(TestStates.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
.withExternal()
.source(TestStates.SI)
.target(TestStates.S1)
.event(TestEvents.E1)
.and()
.withLocal()
.source(TestStates.S1)
.target(TestStates.S2)
.event(TestEvents.E2);
}
}
}

View File

@@ -32,7 +32,6 @@ import org.springframework.core.io.Resource;
import org.springframework.statemachine.config.common.annotation.simple.EnableSimpleTest2;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfig;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBeanABuilder;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBeanB;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBeanBConfigurer;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigBuilder;
import org.springframework.statemachine.config.common.annotation.simple.SimpleTestConfigurerAdapter;

View File

@@ -55,11 +55,11 @@ public class SpelExpressionGuardTests extends AbstractStateMachineTests {
SpelExpressionParser parser = new SpelExpressionParser(
new SpelParserConfiguration(SpelCompilerMode.MIXED, null));
Expression expression = parser.parseExpression("messageHeaders.get('foo')=='bar'");
SpelExpressionGuard guard = new SpelExpressionGuard(expression);
SpelExpressionGuard<TestStates, TestEvents> guard = new SpelExpressionGuard<TestStates, TestEvents>(expression);
Map<String, Object> map = new HashMap<String, Object>();
map.put("foo", "bar");
MessageHeaders headers = new MessageHeaders(map);
DefaultStateContext stateContext = new DefaultStateContext(headers, null);
DefaultStateContext<TestStates, TestEvents> stateContext = new DefaultStateContext<TestStates, TestEvents>(headers, null, null);
assertThat(guard.evaluate(stateContext), is(true));
}

View File

@@ -77,7 +77,7 @@ public class ListenerTests extends AbstractStateMachineTests {
ctx.close();
}
private static class LoggingAction implements Action {
private static class LoggingAction implements Action<TestStates, TestEvents> {
private static final Log log = LogFactory.getLog(LoggingAction.class);
@@ -88,7 +88,7 @@ public class ListenerTests extends AbstractStateMachineTests {
}
@Override
public void execute(StateContext context) {
public void execute(StateContext<TestStates, TestEvents> context) {
log.info("Hello from LoggingAction " + message + " foo=" + context.getMessageHeaders().get("foo"));
}

View File

@@ -71,8 +71,10 @@ public class StateActionTests extends AbstractStateMachineTests {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
Collection<Action> entryActions = Arrays.asList(testEntryAction());
Collection<Action> exitActions = Arrays.asList(testExitAction());
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> entryActions = Arrays.asList(testEntryAction());
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> exitActions = Arrays.asList(testExitAction());
states
.withStates()
.initial(TestStates.S1)
@@ -90,12 +92,12 @@ public class StateActionTests extends AbstractStateMachineTests {
}
@Bean
public Action testEntryAction() {
public Action<TestStates, TestEvents> testEntryAction() {
return new TestEntryAction();
}
@Bean
public Action testExitAction() {
public Action<TestStates, TestEvents> testExitAction() {
return new TestExitAction();
}

View File

@@ -126,8 +126,10 @@ public class TransitionTests extends AbstractStateMachineTests {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
Collection<Action> entryActions = Arrays.asList(testEntryAction());
Collection<Action> exitActions = Arrays.asList(testExitAction());
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> entryActions = Arrays.asList(testEntryAction());
@SuppressWarnings("unchecked")
Collection<Action<TestStates, TestEvents>> exitActions = Arrays.asList(testExitAction());
states
.withStates()
.initial(TestStates.S1)
@@ -151,22 +153,22 @@ public class TransitionTests extends AbstractStateMachineTests {
}
@Bean
public Action testEntryAction() {
public Action<TestStates, TestEvents> testEntryAction() {
return new TestEntryAction();
}
@Bean
public Action testExitAction() {
public Action<TestStates, TestEvents> testExitAction() {
return new TestExitAction();
}
@Bean
public Action externalTestAction() {
public Action<TestStates, TestEvents> externalTestAction() {
return new TestAction();
}
@Bean
public Action internalTestAction() {
public Action<TestStates, TestEvents> internalTestAction() {
return new TestAction();
}

View File

@@ -0,0 +1,8 @@
log4j.rootCategory=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %t %c{2} [%t] - %m%n
log4j.category.org.springframework.statemachine=TRACE