diff --git a/build.gradle b/build.gradle index c2ab7a4a..e9b55cbe 100644 --- a/build.gradle +++ b/build.gradle @@ -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") } } diff --git a/gradle.properties b/gradle.properties index a7b02616..a0694db1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 + diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateContext.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateContext.java index a07c77c9..fea23026 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateContext.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateContext.java @@ -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 { /** * Gets the event message headers. @@ -44,4 +44,11 @@ public interface StateContext { */ ExtendedState getExtendedState(); + /** + * Gets the transition. + * + * @return the transition + */ + Transition getTransition(); + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java index 7efe869f..1982fb6a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/StateMachine.java @@ -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 the type of state @@ -43,17 +43,22 @@ public interface StateMachine extends Region { */ 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 event); /** * Send an event {@code E} to the state machine. - * + * * @param event the event to send */ void sendEvent(E event); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java index 41a10ee3..c63aad28 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java @@ -22,15 +22,17 @@ import org.springframework.statemachine.StateContext; * events by executing an {@code Action} with a {@link StateContext}. * * @author Janne Valkealahti - * + * + * @param the type of state + * @param the type of event */ -public interface Action { +public interface Action { /** * Execute action with a {@link StateContext}. * * @param context the state context */ - void execute(StateContext context); + void execute(StateContext context); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java index 39633e80..e83488d9 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineStates.java @@ -50,12 +50,12 @@ public class StateMachineStates { public static class StateData { private S state; private Collection deferred; - private Collection entryActions; - private Collection exitActions; + private Collection> entryActions; + private Collection> exitActions; public StateData(S state, Collection deferred) { this(state, deferred, null, null); } - public StateData(S state, Collection deferred, Collection entryActions, Collection exitActions) { + public StateData(S state, Collection deferred, Collection> entryActions, Collection> exitActions) { this.state = state; this.deferred = deferred; this.entryActions = entryActions; @@ -70,10 +70,10 @@ public class StateMachineStates { public Collection getDeferred() { return deferred; } - public Collection getEntryActions() { + public Collection> getEntryActions() { return entryActions; } - public Collection getExitActions() { + public Collection> getExitActions() { return exitActions; } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java index ec59f9fb..971ad6c2 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java @@ -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 public InternalTransitionConfigurer withInternal() throws Exception { return apply(new DefaultInternalTransitionConfigurer()); } + + @Override + public LocalTransitionConfigurer withLocal() throws Exception { + return apply(new DefaultLocalTransitionConfigurer()); + } - public void add(S source, S target, E event, Collection actions, Guard guard, TransitionKind kind) { + public void add(S source, S target, E event, Collection> actions, Guard guard, TransitionKind kind) { transitionData.add(new TransitionData(source, target, event, actions, guard, kind)); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionConfigurer.java index a1cc39a4..a5d776c0 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionConfigurer.java @@ -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 { ExternalTransitionConfigurer withExternal() throws Exception; InternalTransitionConfigurer withInternal() throws Exception; + + LocalTransitionConfigurer withLocal() throws Exception; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java index f71a8a15..eb08cfd1 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitions.java @@ -37,10 +37,10 @@ public class StateMachineTransitions { S source; S target; E event; - Collection actions; - Guard guard; + Collection> actions; + Guard guard; TransitionKind kind; - public TransitionData(S source, S target, E event, Collection actions, Guard guard, TransitionKind kind) { + public TransitionData(S source, S target, E event, Collection> actions, Guard guard, TransitionKind kind) { this.source = source; this.target = target; this.event = event; @@ -57,10 +57,10 @@ public class StateMachineTransitions { public E getEvent() { return event; } - public Collection getActions() { + public Collection> getActions() { return actions; } - public Guard getGuard() { + public Guard getGuard() { return guard; } public TransitionKind getKind() { diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java index baaa93fd..a47cd53d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java @@ -48,9 +48,9 @@ public class DefaultExternalTransitionConfigurer private E event; - private Collection actions = new ArrayList(); + private Collection> actions = new ArrayList>(); - private Guard guard; + private Guard guard; @Override public void configure(StateMachineTransitionBuilder builder) throws Exception { @@ -76,13 +76,13 @@ public class DefaultExternalTransitionConfigurer } @Override - public ExternalTransitionConfigurer action(Action action) { + public ExternalTransitionConfigurer action(Action action) { actions.add(action); return this; } @Override - public ExternalTransitionConfigurer guard(Guard guard) { + public ExternalTransitionConfigurer guard(Guard guard) { this.guard = guard; return this; } @@ -91,7 +91,7 @@ public class DefaultExternalTransitionConfigurer public ExternalTransitionConfigurer guardExpression(String expression) { SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); - this.guard = new SpelExpressionGuard(parser.parseExpression(expression)); + this.guard = new SpelExpressionGuard(parser.parseExpression(expression)); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java index c976f825..a00a38a8 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java @@ -48,9 +48,9 @@ public class DefaultInternalTransitionConfigurer private E event; - private Collection actions = new ArrayList(); + private Collection> actions = new ArrayList>(); - private Guard guard; + private Guard guard; @Override public void configure(StateMachineTransitionBuilder builder) throws Exception { @@ -70,13 +70,13 @@ public class DefaultInternalTransitionConfigurer } @Override - public InternalTransitionConfigurer action(Action action) { + public InternalTransitionConfigurer action(Action action) { actions.add(action); return this; } @Override - public InternalTransitionConfigurer guard(Guard guard) { + public InternalTransitionConfigurer guard(Guard guard) { this.guard = guard; return this; } @@ -85,7 +85,7 @@ public class DefaultInternalTransitionConfigurer public InternalTransitionConfigurer guardExpression(String expression) { SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); - this.guard = new SpelExpressionGuard(parser.parseExpression(expression)); + this.guard = new SpelExpressionGuard(parser.parseExpression(expression)); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java new file mode 100644 index 00000000..ebbcd239 --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java @@ -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 the type of state + * @param the type of event + */ +public class DefaultLocalTransitionConfigurer + extends AnnotationConfigurerAdapter, StateMachineTransitionConfigurer, StateMachineTransitionBuilder> + implements LocalTransitionConfigurer { + + private S source; + + private S target; + + private E event; + + private Collection> actions = new ArrayList>(); + + private Guard guard; + + @Override + public void configure(StateMachineTransitionBuilder builder) throws Exception { + builder.add(source, target, event, actions, guard, TransitionKind.LOCAL); + } + + @Override + public LocalTransitionConfigurer source(S source) { + this.source = source; + return this; + } + + @Override + public LocalTransitionConfigurer target(S target) { + this.target = target; + return this; + } + + @Override + public LocalTransitionConfigurer event(E event) { + this.event = event; + return this; + } + + @Override + public LocalTransitionConfigurer action(Action action) { + actions.add(action); + return this; + } + + @Override + public LocalTransitionConfigurer guard(Guard guard) { + this.guard = guard; + return this; + } + + @Override + public LocalTransitionConfigurer guardExpression(String expression) { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); + this.guard = new SpelExpressionGuard(parser.parseExpression(expression)); + return this; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java index aaf97fac..fb107a2e 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultStateConfigurer.java @@ -61,7 +61,7 @@ public class DefaultStateConfigurer } @Override - public StateConfigurer state(S state, Collection entryActions, Collection exitActions) { + public StateConfigurer state(S state, Collection> entryActions, Collection> exitActions) { states.add(new StateData(state, null, entryActions, exitActions)); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/LocalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/LocalTransitionConfigurer.java new file mode 100644 index 00000000..582b14ed --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/LocalTransitionConfigurer.java @@ -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 the type of state + * @param the type of event + */ +public interface LocalTransitionConfigurer extends + TransitionConfigurer, S, E> { + + /** + * Specify a target state {@code S} for this {@link Transition}. + * + * @param target the target state {@code S} + * @return configurer for chaining + */ + LocalTransitionConfigurer target(S target); + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java index c2aa3856..bc64e4fc 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/StateConfigurer.java @@ -29,7 +29,7 @@ public interface StateConfigurer extends StateConfigurer state(S state); - StateConfigurer state(S state, Collection entryActions, Collection exitActions); + StateConfigurer state(S state, Collection> entryActions, Collection> exitActions); StateConfigurer state(S state, E... deferred); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java index 01690fbd..fead6e7d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java @@ -55,7 +55,7 @@ public interface TransitionConfigurer extends * @param action the action * @return configurer for chaining */ - T action(Action action); + T action(Action action); /** * Specify a {@link Guard} for this {@link Transition}. @@ -63,7 +63,7 @@ public interface TransitionConfigurer extends * @param guard the guard * @return configurer for chaining */ - T guard(Guard guard); + T guard(Guard guard); /** * Specify a {@link Guard} backed by a SpEL expression for this {@link Transition}. diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/Guard.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/Guard.java index b0477733..440f2a2a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/Guard.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/Guard.java @@ -24,9 +24,11 @@ import org.springframework.statemachine.StateContext; * {@code FALSE}. * * @author Janne Valkealahti - * + * + * @param the type of state + * @param the type of event */ -public interface Guard { +public interface Guard { /** * 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 context); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/SpelExpressionGuard.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/SpelExpressionGuard.java index 7b731ab3..1a83718d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/SpelExpressionGuard.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/guard/SpelExpressionGuard.java @@ -26,7 +26,7 @@ import org.springframework.util.Assert; * @author Janne Valkealahti * */ -public class SpelExpressionGuard implements Guard { +public class SpelExpressionGuard implements Guard { private final Expression expression; @@ -41,7 +41,7 @@ public class SpelExpressionGuard implements Guard { } @Override - public boolean evaluate(StateContext context) { + public boolean evaluate(StateContext context) { StandardEvaluationContext evaluationContext = new StandardEvaluationContext(context); return expression.getValue(evaluationContext, Boolean.class); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java index 07b22e52..30408e72 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractSimpleState.java @@ -52,7 +52,7 @@ public abstract class AbstractSimpleState extends AbstractState { * @param entryActions the entry actions * @param exitActions the exit actions */ - public AbstractSimpleState(S id, Collection deferred, Collection entryActions, Collection exitActions) { + public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions) { this(id, deferred, entryActions, exitActions, null); } @@ -86,7 +86,7 @@ public abstract class AbstractSimpleState extends AbstractState { * @param pseudoState the pseudo state * @param regions the regions */ - public AbstractSimpleState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions) { super(deferred, entryActions, exitActions, pseudoState, regions); this.ids = new ArrayList(); @@ -103,7 +103,7 @@ public abstract class AbstractSimpleState extends AbstractState { * @param pseudoState the pseudo state * @param submachine the submachine */ - public AbstractSimpleState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, StateMachine submachine) { super(deferred, entryActions, exitActions, pseudoState, submachine); this.ids = new ArrayList(); @@ -119,7 +119,7 @@ public abstract class AbstractSimpleState extends AbstractState { * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public AbstractSimpleState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public AbstractSimpleState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { super(deferred, entryActions, exitActions, pseudoState); this.ids = new ArrayList(); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java index 3fcb0c1b..9cd0e30d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/AbstractState.java @@ -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 the type of state @@ -35,8 +36,8 @@ public abstract class AbstractState implements State { private final PseudoState pseudoState; private final Collection deferred; - private final Collection entryActions; - private final Collection exitActions; + private final Collection> entryActions; + private final Collection> exitActions; private final Collection> regions = new ArrayList>(); private final StateMachine submachine; @@ -48,7 +49,7 @@ public abstract class AbstractState implements State { public AbstractState(PseudoState pseudoState) { this(null, null, null, pseudoState); } - + /** * Instantiates a new abstract state. * @@ -65,10 +66,10 @@ public abstract class AbstractState implements State { * @param entryActions the entry actions * @param exitActions the exit actions */ - public AbstractState(Collection deferred, Collection entryActions, Collection exitActions) { + public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions) { this(deferred, entryActions, exitActions, null); } - + /** * Instantiates a new abstract state. * @@ -77,7 +78,7 @@ public abstract class AbstractState implements State { * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public AbstractState(Collection deferred, Collection entryActions, Collection exitActions, + public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { this(deferred, entryActions, exitActions, pseudoState, null, null); } @@ -91,7 +92,7 @@ public abstract class AbstractState implements State { * @param pseudoState the pseudo state * @param submachine the submachine */ - public AbstractState(Collection deferred, Collection entryActions, Collection exitActions, + public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, StateMachine submachine) { this(deferred, entryActions, exitActions, pseudoState, null, submachine); } @@ -105,11 +106,11 @@ public abstract class AbstractState implements State { * @param pseudoState the pseudo state * @param regions the regions */ - public AbstractState(Collection deferred, Collection entryActions, Collection exitActions, + public AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions) { this(deferred, entryActions, exitActions, pseudoState, regions, null); } - + /** * Instantiates a new abstract state. * @@ -120,13 +121,13 @@ public abstract class AbstractState implements State { * @param regions the regions * @param submachine the submachine */ - private AbstractState(Collection deferred, Collection entryActions, Collection exitActions, + private AbstractState(Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions, StateMachine 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 implements State { } this.submachine = submachine; } - + + @Override + public abstract void exit(E event, StateContext context); + + @Override + public abstract void entry(E event, StateContext context); + @Override public abstract Collection getIds(); - + @Override public PseudoState getPseudoState() { return pseudoState; @@ -149,12 +156,12 @@ public abstract class AbstractState implements State { } @Override - public Collection getEntryActions() { + public Collection> getEntryActions() { return entryActions; } @Override - public Collection getExitActions() { + public Collection> getExitActions() { return exitActions; } @@ -177,11 +184,11 @@ public abstract class AbstractState implements State { public boolean isSubmachineState() { return submachine != null; } - + protected StateMachine getSubmachine() { return submachine; } - + protected Collection> getRegions() { return regions; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java index 4f898b03..9c5adea0 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/EnumState.java @@ -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, E extends Enum> extends AbstractSim * @param entryActions the entry actions * @param exitActions the exit actions */ - public EnumState(S id, Collection deferred, Collection entryActions, Collection exitActions) { + public EnumState(S id, Collection deferred, Collection> entryActions, Collection> exitActions) { super(id, deferred, entryActions, exitActions); } @@ -81,7 +82,7 @@ public class EnumState, E extends Enum> extends AbstractSim * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public EnumState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public EnumState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { super(id, deferred, entryActions, exitActions, pseudoState); } @@ -96,7 +97,7 @@ public class EnumState, E extends Enum> extends AbstractSim * @param pseudoState the pseudo state * @param regions the regions */ - public EnumState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public EnumState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, Collection> regions) { super(id, deferred, entryActions, exitActions, pseudoState, regions); } @@ -111,11 +112,31 @@ public class EnumState, E extends Enum> extends AbstractSim * @param pseudoState the pseudo state * @param submachine the submachine */ - public EnumState(S id, Collection deferred, Collection entryActions, Collection exitActions, + public EnumState(S id, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState, StateMachine submachine) { super(id, deferred, entryActions, exitActions, pseudoState, submachine); } + @Override + public void exit(E event, StateContext context) { + Collection> actions = getExitActions(); + if (actions != null) { + for (Action action : actions) { + action.execute(context); + } + } + } + + @Override + public void entry(E event, StateContext context) { + Collection> actions = getEntryActions(); + if (actions != null) { + for (Action action : actions) { + action.execute(context); + } + } + } + @Override public String toString() { return "EnumState [getIds()=" + getIds() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java index 1c54d033..08757c7f 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/RegionState.java @@ -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 the type of state @@ -59,7 +60,7 @@ public class RegionState extends AbstractState { public RegionState(Collection> regions, PseudoState pseudoState) { super(null, null, null, pseudoState, regions); } - + /** * Instantiates a new region state. * @@ -69,7 +70,7 @@ public class RegionState extends AbstractState { * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public RegionState(Collection> regions, Collection deferred, Collection entryActions, Collection exitActions, + public RegionState(Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { super(deferred, entryActions, exitActions, pseudoState, regions); } @@ -82,10 +83,22 @@ public class RegionState extends AbstractState { * @param entryActions the entry actions * @param exitActions the exit actions */ - public RegionState(Collection> regions, Collection deferred, Collection entryActions, Collection exitActions) { + public RegionState(Collection> regions, Collection deferred, Collection> entryActions, Collection> exitActions) { super(deferred, entryActions, exitActions, null, regions); } + @Override + public void exit(E event, StateContext context) { + // TODO Auto-generated method stub + + } + @Override + public void entry(E event, StateContext context) { + // TODO Auto-generated method stub + + } + + @Override public Collection getIds() { ArrayList ids = new ArrayList(); @@ -94,5 +107,5 @@ public class RegionState extends AbstractState { } return ids; } - + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java index 3082890a..dc7dd349 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/State.java @@ -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 the type of state @@ -29,6 +30,22 @@ import org.springframework.statemachine.action.Action; */ public interface State { + /** + * Initiate an exit sequence for the state. + * + * @param event the event + * @param context the context + */ + void exit(E event, StateContext context); + + /** + * Initiate an entry sequence for the state. + * + * @param event the event + * @param context the context + */ + void entry(E event, StateContext 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 { * 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 { * @return the state deferred events */ Collection getDeferredEvents(); - + /** * Gets {@link Action}s executed entering in this state. * * @return the state entry actions */ - Collection getEntryActions(); + Collection> getEntryActions(); /** * Gets {@link Action}s executed exiting from this state. * * @return the state exit actions */ - Collection getExitActions(); + Collection> getExitActions(); /** * Checks if state is a simple state. A simple state does not have any diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java index e9f38552..e106f2a6 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/StateMachineState.java @@ -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 the type of state @@ -58,7 +62,7 @@ public class StateMachineState extends AbstractState { public StateMachineState(StateMachine submachine, PseudoState pseudoState) { super(null, null, null, pseudoState, submachine); } - + /** * Instantiates a new state machine state. * @@ -68,7 +72,7 @@ public class StateMachineState extends AbstractState { * @param exitActions the exit actions * @param pseudoState the pseudo state */ - public StateMachineState(StateMachine submachine, Collection deferred, Collection entryActions, Collection exitActions, + public StateMachineState(StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions, PseudoState pseudoState) { super(deferred, entryActions, exitActions, pseudoState, submachine); } @@ -81,13 +85,60 @@ public class StateMachineState extends AbstractState { * @param entryActions the entry actions * @param exitActions the exit actions */ - public StateMachineState(StateMachine submachine, Collection deferred, Collection entryActions, Collection exitActions) { + public StateMachineState(StateMachine submachine, Collection deferred, Collection> entryActions, Collection> exitActions) { super(deferred, entryActions, exitActions, null, submachine); } @Override public Collection getIds() { - return getSubmachine().getState().getIds(); + State state = getSubmachine().getState(); + if (state != null) { + return state.getIds(); + } else { + return Collections.emptyList(); + } } - + + @Override + public void exit(E event, StateContext context) { + getSubmachine().getState().exit(event, context); + getSubmachine().stop(); + Collection> actions = getExitActions(); + if (actions != null && !isLocal(context)) { + for (Action action : actions) { + action.execute(context); + } + } + } + + @Override + public void entry(E event, StateContext context) { + Collection> actions = getEntryActions(); + if (actions != null && !isLocal(context)) { + for (Action action : actions) { + action.execute(context); + } + } + if (getPseudoState() != null && getPseudoState().getKind() == PseudoStateKind.INITIAL) { + getSubmachine().start(); + } else { + getSubmachine().getState().entry(event, context); + } + } + + private boolean isLocal(StateContext context) { + Transition 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() + "]"; + } + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 989bf1a5..fa82a3ca 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -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 the type of state @@ -73,7 +72,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport private final State initialState; private final State endState; - + private final Message initialEvent; private final ExtendedState extendedState; @@ -112,7 +111,7 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport State initialState, State endState) { this(states, transitions, initialState, endState, null, null); } - + /** * Instantiates a new abstract state machine. * @@ -179,7 +178,7 @@ public abstract class AbstractStateMachine 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 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, E> listener) { stateListener.register(listener); } - + @Override public boolean isComplete() { return (endState != null && endState.equals(currentState)); @@ -206,62 +211,55 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport * an unmodifiable copy because states in a state machine are immutable. * * @return immutable copy of existing states - */ + */ @Override public Collection> getStates() { return Collections.unmodifiableCollection(states); } - + @Override public Collection> getTransitions() { return transitions; } - - private void switchToState(State state, Message event) { - log.info("Moving into state=" + state + " from " + currentState); - - exitFromState(currentState, event); + + private void switchToState(State state, Message event, Transition transition) { + exitFromState(currentState, event, transition); notifyStateChanged(currentState, state); - + callHandlers(currentState, state, event); - + currentState = state; - entryToState(state, event); + entryToState(state, event, transition); - for (Transition transition : transitions) { - State source = transition.getSource(); - State target = transition.getTarget(); - if (transition.getTrigger() == null && source.equals(currentState)) { - switchToState(target, event); + // TODO: should handle triggerles transition some how differently + for (Transition t : transitions) { + State source = t.getSource(); + State target = t.getTarget(); + if (t.getTrigger() == null && source.equals(currentState)) { + switchToState(target, event, t); } } } - private void exitFromState(State state, Message event) { + private void exitFromState(State state, Message event, Transition transition) { if (state != null) { + log.trace("Exit state=[" + state + "]"); MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( new HashMap()); - Collection actions = state.getExitActions(); - if (actions != null) { - for (Action action : actions) { - action.execute(new DefaultStateContext(messageHeaders, extendedState)); - } - } + StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState, transition); + state.exit(event != null ? event.getPayload() : null, stateContext); } } - private void entryToState(State state, Message event) { + private void entryToState(State state, Message event, Transition transition) { if (state != null) { + log.trace("Enter state=[" + state + "]"); MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( new HashMap()); - Collection actions = state.getEntryActions(); - if (actions != null) { - for (Action action : actions) { - action.execute(new DefaultStateContext(messageHeaders, extendedState)); - } - } + StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState, transition); + state.entry(event != null ? event.getPayload() : null, stateContext); } } @@ -274,11 +272,13 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport State source = transition.getSource(); State target = transition.getTarget(); Trigger 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 stateContext = new DefaultStateContext(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 extends LifecycleObjectSupport Trigger trigger = transition.getTrigger(); if (source.equals(currentState)) { if (trigger != null && trigger.evaluate(event.getPayload())) { - boolean transit = transition.transit(new DefaultStateContext(event.getHeaders(), extendedState)); + StateContext stateContext = new DefaultStateContext(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 extends LifecycleObjectSupport if (sourceState != null && targetState != null) { MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders( new HashMap()); - StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState); + StateContext stateContext = new DefaultStateContext(messageHeaders, extendedState, null); getStateMachineHandlerResults(getStateMachineHandlers(sourceState, targetState), stateContext); } - + } - private List getStateMachineHandlerResults(List stateMachineHandlers, final StateContext stateContext) { - StateMachineRuntime runtime = new StateMachineRuntime() { + private List getStateMachineHandlerResults(List stateMachineHandlers, final StateContext stateContext) { + StateMachineRuntime runtime = new StateMachineRuntime() { @Override - public StateContext getStateContext() { + public StateContext getStateContext() { return stateContext; } }; @@ -353,9 +354,9 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport return results; } - private List getStateMachineHandlers(State sourceState, State targetState) { + private List getStateMachineHandlers(State sourceState, State 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 extends LifecycleObjectSupport handlersList.add(entry.getValue()); } } - + OrderComparator comparator = new OrderComparator(); Collections.sort(handlersList, comparator); return handlersList; - } + } private void notifyStateChanged(State source, State target) { stateListener.stateChanged(source, target); @@ -389,5 +390,5 @@ public abstract class AbstractStateMachine extends LifecycleObjectSupport eventPublisher.publishStateChanged(this, source, target); } } - + } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateContext.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateContext.java index db0e7949..c10d7847 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateContext.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateContext.java @@ -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 implements StateContext { private final MessageHeaders messageHeaders; private final ExtendedState extendedState; + + private final Transition transition; - public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState) { + public DefaultStateContext(MessageHeaders messageHeaders, ExtendedState extendedState, Transition 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 getTransition() { + return transition; + } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java new file mode 100644 index 00000000..a9a4d946 --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/StateMachineUtils.java @@ -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 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 boolean containsAtleastOne(Collection left, Collection right) { + if (left == null || right == null) { + return false; + } + for (S id : left) { + if (right.contains(id)) { + return true; + } + } + return false; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java index 23189c03..b2478400 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java @@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State; public abstract class AbstractExternalTransition extends AbstractTransition implements Transition { - public AbstractExternalTransition(State source, State target, Collection actions, E event, Guard guard) { + public AbstractExternalTransition(State source, State target, Collection> actions, E event, Guard guard) { super(source, target, actions, event, TransitionKind.EXTERNAL, guard); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java index d278e67f..247808d8 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java @@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State; public class AbstractInternalTransition extends AbstractTransition implements Transition { - public AbstractInternalTransition(State source,Collection actions, E event, Guard guard) { + public AbstractInternalTransition(State source,Collection> actions, E event, Guard guard) { super(source, source, actions, event, TransitionKind.INTERNAL, guard); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java index de101a39..a76acc20 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java @@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State; public class AbstractLocalTransition extends AbstractTransition implements Transition { - public AbstractLocalTransition(State source, State target,Collection actions, E event, Guard guard) { + public AbstractLocalTransition(State source, State target,Collection> actions, E event, Guard guard) { super(source, target, actions, event, TransitionKind.LOCAL, guard); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java index e3845ca3..aa55cf2d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java @@ -39,16 +39,16 @@ public abstract class AbstractTransition implements Transition { private final State target; - private final Collection actions; + private final Collection> actions; private final TransitionKind kind; - private final Guard guard; + private final Guard guard; private Trigger trigger; - public AbstractTransition(State source, State target, Collection actions, E event, - TransitionKind kind, Guard guard) { + public AbstractTransition(State source, State target, Collection> actions, E event, + TransitionKind kind, Guard 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 implements Transition { } @Override - public Collection getActions() { + public Collection> getActions() { return actions; } @@ -83,14 +83,14 @@ public abstract class AbstractTransition implements Transition { } @Override - public boolean transit(StateContext context) { + public boolean transit(StateContext context) { if (guard != null) { if (!guard.evaluate(context)) { return false; } } if (actions != null) { - for (Action action : actions) { + for (Action action : actions) { action.execute(context); } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java index a167a2e0..9134fe94 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java @@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State; public class DefaultExternalTransition extends AbstractExternalTransition { - public DefaultExternalTransition(State source, State target, Collection actions, E event, Guard guard) { + public DefaultExternalTransition(State source, State target, Collection> actions, E event, Guard guard) { super(source, target, actions, event, guard); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java index 08d428de..4a23bf91 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java @@ -23,7 +23,7 @@ import org.springframework.statemachine.state.State; public class DefaultInternalTransition extends AbstractInternalTransition { - public DefaultInternalTransition(State source, Collection actions, E event, Guard guard) { + public DefaultInternalTransition(State source, Collection> actions, E event, Guard guard) { super(source, actions, event, guard); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java new file mode 100644 index 00000000..f3f4cede --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java @@ -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 extends AbstractLocalTransition { + + public DefaultLocalTransition(State source, State target, Collection> actions, E event, Guard guard) { + super(source, target, actions, event, guard); + } + + @Override + public String toString() { + return "DefaultLocalTransition [getSource()=" + getSource() + ", getTarget()=" + getTarget() + "]"; + } + +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java index 1a4cabfe..37051ac3 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java @@ -39,7 +39,7 @@ public interface Transition { * @param context the state context * @return true, if transition happened, false otherwise */ - boolean transit(StateContext context); + boolean transit(StateContext context); /** * Gets the source state of this transition. @@ -60,7 +60,7 @@ public interface Transition { * * @return the transition actions */ - Collection getActions(); + Collection> getActions(); /** * Gets the transition trigger. diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/AbstractStateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/AbstractStateMachineTests.java index cbf6183d..c04e8de9 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/AbstractStateMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/AbstractStateMachineTests.java @@ -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 { + 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 context) { onEvaluateLatch.countDown(); return evaluationResult; } - } + } - protected static class AbstractTestAction implements Action { + protected static class AbstractTestAction implements Action { + protected String message = null; public CountDownLatch onExecuteLatch = new CountDownLatch(1); - + public List> stateContexts = new ArrayList>(); + + public AbstractTestAction() { + } + + public AbstractTestAction(String message) { + this.message = message; + } + @Override - public void execute(StateContext context) { + public void execute(StateContext context) { + if (message != null) { + log.info(this); + } onExecuteLatch.countDown(); + stateContexts.add(context); } } - + } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/EnumStateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/EnumStateMachineTests.java index 1b00d57b..4af99b5b 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/EnumStateMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/EnumStateMachineTests.java @@ -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> transitions = new ArrayList>(); - Collection actionsFromSIToS1 = new ArrayList(); + Collection> actionsFromSIToS1 = new ArrayList>(); actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1")); DefaultExternalTransition transitionFromSIToS1 = new DefaultExternalTransition(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null); - Collection actionsFromS1ToS2 = new ArrayList(); + Collection> actionsFromS1ToS2 = new ArrayList>(); actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2")); DefaultExternalTransition transitionFromS1ToS2 = new DefaultExternalTransition(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null); - Collection actionsFromS2ToS3 = new ArrayList(); + Collection> actionsFromS2ToS3 = new ArrayList>(); actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3")); DefaultExternalTransition transitionFromS2ToS3 = new DefaultExternalTransition(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null); @@ -123,17 +121,17 @@ public class EnumStateMachineTests extends AbstractStateMachineTests { // transitions Collection> transitions = new ArrayList>(); - Collection actionsFromSIToS1 = new ArrayList(); + Collection> actionsFromSIToS1 = new ArrayList>(); actionsFromSIToS1.add(new LoggingAction("actionsFromSIToS1")); DefaultExternalTransition transitionFromSIToS1 = new DefaultExternalTransition(stateSI, stateS1, actionsFromSIToS1, TestEvents.E1, null); - Collection actionsFromS1ToS2 = new ArrayList(); + Collection> actionsFromS1ToS2 = new ArrayList>(); actionsFromS1ToS2.add(new LoggingAction("actionsFromS1ToS2")); DefaultExternalTransition transitionFromS1ToS2 = new DefaultExternalTransition(stateS1, stateS2, actionsFromS1ToS2, TestEvents.E2, null); - Collection actionsFromS2ToS3 = new ArrayList(); + Collection> actionsFromS2ToS3 = new ArrayList>(); actionsFromS1ToS2.add(new LoggingAction("actionsFromS2ToS3")); DefaultExternalTransition transitionFromS2ToS3 = new DefaultExternalTransition(stateS2, stateS3, actionsFromS2ToS3, TestEvents.E3, null); @@ -174,7 +172,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests { Collection> states = new ArrayList>(); states.add(stateSI); - Collection actionsInSI = new ArrayList(); + Collection> actionsInSI = new ArrayList>(); actionsInSI.add(new LoggingAction("actionsInSI")); DefaultInternalTransition transitionInternalSI = new DefaultInternalTransition(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 { 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 context) { log.info("Hello from LoggingAction " + message); } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java index f111fa79..85b136b0 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/StateMachineTests.java @@ -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 { 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 context) { log.info("Hello from LoggingAction " + message + " foo=" + context.getMessageHeaders().get("foo")); } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java new file mode 100644 index 00000000..46a17d0e --- /dev/null +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/SubStateMachineTests.java @@ -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> entryActionsS111 = new ArrayList>(); + entryActionsS111.add(entryActionS111); + Collection> exitActionsS111 = new ArrayList>(); + exitActionsS111.add(exitActionS111); + State stateS111 = new EnumState(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState); + + // submachine 11 + Collection> substates111 = new ArrayList>(); + substates111.add(stateS111); + Collection> subtransitions111 = new ArrayList>(); + EnumStateMachine submachine11 = new EnumStateMachine(substates111, subtransitions111, stateS111, null); + + // submachine 1 + TestEntryAction entryActionS11 = new TestEntryAction("S11"); + TestExitAction exitActionS11 = new TestExitAction("S11"); + Collection> entryActionsS11 = new ArrayList>(); + entryActionsS11.add(entryActionS11); + Collection> exitActionsS11 = new ArrayList>(); + exitActionsS11.add(exitActionS11); + StateMachineState stateS11 = new StateMachineState(submachine11, null, entryActionsS11, exitActionsS11, pseudoState); + + Collection> substates11 = new ArrayList>(); + substates11.add(stateS11); + Collection> subtransitions11 = new ArrayList>(); + EnumStateMachine submachine1 = new EnumStateMachine(substates11, subtransitions11, stateS11, null); + + // machine + TestEntryAction entryActionS1 = new TestEntryAction("S1"); + TestExitAction exitActionS1 = new TestExitAction("S1"); + Collection> entryActionsS1 = new ArrayList>(); + entryActionsS1.add(entryActionS1); + Collection> exitActionsS1 = new ArrayList>(); + exitActionsS1.add(exitActionS1); + + StateMachineState stateS1 = new StateMachineState(submachine1, null, entryActionsS1, exitActionsS1, pseudoState); + Collection> states = new ArrayList>(); + states.add(stateS1); + Collection> transitions = new ArrayList>(); + DefaultExternalTransition transitionFromS11ToS1 = + new DefaultExternalTransition(stateS111, stateS1, null, TestEvents.E1, null); + transitions.add(transitionFromS11ToS1); + EnumStateMachine machine = new EnumStateMachine(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> entryActionsS111 = new ArrayList>(); + entryActionsS111.add(entryActionS111); + Collection> exitActionsS111 = new ArrayList>(); + exitActionsS111.add(exitActionS111); + State stateS111 = new EnumState(TestStates.S111, null, entryActionsS111, exitActionsS111, pseudoState); + + // submachine 11 + Collection> substates111 = new ArrayList>(); + substates111.add(stateS111); + Collection> subtransitions111 = new ArrayList>(); + EnumStateMachine submachine11 = new EnumStateMachine(substates111, subtransitions111, stateS111, null); + + // submachine 1 + TestEntryAction entryActionS11 = new TestEntryAction("S11"); + TestExitAction exitActionS11 = new TestExitAction("S11"); + Collection> entryActionsS11 = new ArrayList>(); + entryActionsS11.add(entryActionS11); + Collection> exitActionsS11 = new ArrayList>(); + exitActionsS11.add(exitActionS11); + StateMachineState stateS11 = new StateMachineState(submachine11, null, entryActionsS11, exitActionsS11, pseudoState); + + Collection> substates11 = new ArrayList>(); + substates11.add(stateS11); + Collection> subtransitions11 = new ArrayList>(); + EnumStateMachine submachine1 = new EnumStateMachine(substates11, subtransitions11, stateS11, null); + + // machine + TestEntryAction entryActionS1 = new TestEntryAction("S1"); + TestExitAction exitActionS1 = new TestExitAction("S1"); + Collection> entryActionsS1 = new ArrayList>(); + entryActionsS1.add(entryActionS1); + Collection> exitActionsS1 = new ArrayList>(); + exitActionsS1.add(exitActionS1); + + StateMachineState stateS1 = new StateMachineState(submachine1, null, entryActionsS1, exitActionsS1, pseudoState); + Collection> states = new ArrayList>(); + states.add(stateS1); + Collection> transitions = new ArrayList>(); + DefaultLocalTransition transitionFromS11ToS1 = + new DefaultLocalTransition(stateS111, stateS1, null, TestEvents.E1, null); + transitions.add(transitionFromS11ToS1); + EnumStateMachine machine = new EnumStateMachine(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)); + } + +} diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java index d0f1119c..d94afc11 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java @@ -68,7 +68,7 @@ public class ActionTests extends AbstractStateMachineTests { } - private static class TestCountAction implements Action { + private static class TestCountAction implements Action { int count = 0; @@ -77,7 +77,7 @@ public class ActionTests extends AbstractStateMachineTests { } @Override - public void execute(StateContext context) { + public void execute(StateContext context) { count++; } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java index 6936a5ad..c31ea492 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/ConfigurationTests.java @@ -144,5 +144,34 @@ public class ConfigurationTests extends AbstractStateMachineTests { } } + + @Configuration + @EnableStateMachine + public static class Config4 extends EnumStateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(TestStates.S1) + .end(TestStates.SF) + .states(EnumSet.allOf(TestStates.class)); + } + + @Override + public void configure(StateMachineTransitionConfigurer 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); + } + + } } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/common/annotation/SimpleAnnotationConfiguration2Tests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/common/annotation/SimpleAnnotationConfiguration2Tests.java index 359ca809..ba542141 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/common/annotation/SimpleAnnotationConfiguration2Tests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/common/annotation/SimpleAnnotationConfiguration2Tests.java @@ -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; diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java index 9ac26461..82b0342e 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/guard/SpelExpressionGuardTests.java @@ -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 guard = new SpelExpressionGuard(expression); Map map = new HashMap(); map.put("foo", "bar"); MessageHeaders headers = new MessageHeaders(map); - DefaultStateContext stateContext = new DefaultStateContext(headers, null); + DefaultStateContext stateContext = new DefaultStateContext(headers, null, null); assertThat(guard.evaluate(stateContext), is(true)); } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java index cb04b19c..1574d9e0 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/listener/ListenerTests.java @@ -77,7 +77,7 @@ public class ListenerTests extends AbstractStateMachineTests { ctx.close(); } - private static class LoggingAction implements Action { + private static class LoggingAction implements Action { 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 context) { log.info("Hello from LoggingAction " + message + " foo=" + context.getMessageHeaders().get("foo")); } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/StateActionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/StateActionTests.java index ba37a50c..b3332d9a 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/StateActionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/state/StateActionTests.java @@ -71,8 +71,10 @@ public class StateActionTests extends AbstractStateMachineTests { @Override public void configure(StateMachineStateConfigurer states) throws Exception { - Collection entryActions = Arrays.asList(testEntryAction()); - Collection exitActions = Arrays.asList(testExitAction()); + @SuppressWarnings("unchecked") + Collection> entryActions = Arrays.asList(testEntryAction()); + @SuppressWarnings("unchecked") + Collection> exitActions = Arrays.asList(testExitAction()); states .withStates() .initial(TestStates.S1) @@ -90,12 +92,12 @@ public class StateActionTests extends AbstractStateMachineTests { } @Bean - public Action testEntryAction() { + public Action testEntryAction() { return new TestEntryAction(); } @Bean - public Action testExitAction() { + public Action testExitAction() { return new TestExitAction(); } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java index e1d2fece..4084adfc 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/transition/TransitionTests.java @@ -126,8 +126,10 @@ public class TransitionTests extends AbstractStateMachineTests { @Override public void configure(StateMachineStateConfigurer states) throws Exception { - Collection entryActions = Arrays.asList(testEntryAction()); - Collection exitActions = Arrays.asList(testExitAction()); + @SuppressWarnings("unchecked") + Collection> entryActions = Arrays.asList(testEntryAction()); + @SuppressWarnings("unchecked") + Collection> exitActions = Arrays.asList(testExitAction()); states .withStates() .initial(TestStates.S1) @@ -151,22 +153,22 @@ public class TransitionTests extends AbstractStateMachineTests { } @Bean - public Action testEntryAction() { + public Action testEntryAction() { return new TestEntryAction(); } @Bean - public Action testExitAction() { + public Action testExitAction() { return new TestExitAction(); } @Bean - public Action externalTestAction() { + public Action externalTestAction() { return new TestAction(); } @Bean - public Action internalTestAction() { + public Action internalTestAction() { return new TestAction(); } diff --git a/spring-statemachine-core/src/test/resources/log4j.properties b/spring-statemachine-core/src/test/resources/log4j.properties new file mode 100644 index 00000000..4589b1e9 --- /dev/null +++ b/spring-statemachine-core/src/test/resources/log4j.properties @@ -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 +