@@ -40,8 +40,8 @@ public class EnumStateMachine<S extends Enum<S>, E extends Enum<E>> extends Abst
|
||||
* @param initialState the initial state
|
||||
*/
|
||||
public EnumStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState) {
|
||||
super(states, transitions, initialState);
|
||||
State<S, E> initialState, State<S, E> endState) {
|
||||
super(states, transitions, initialState, endState);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
|
||||
}
|
||||
|
||||
EnumStateMachine<S, E> machine = new EnumStateMachine<S, E>(stateMap.values(), transitions,
|
||||
stateMap.get(stateMachineStates.getInitialState()));
|
||||
stateMap.get(stateMachineStates.getInitialState()), stateMap.get(stateMachineStates.getEndState()));
|
||||
machine.afterPropertiesSet();
|
||||
if (getBeanFactory() != null) {
|
||||
machine.setBeanFactory(getBeanFactory());
|
||||
|
||||
@@ -30,6 +30,7 @@ public class StateMachineStateBuilder<S, E>
|
||||
|
||||
private Collection<StateData<S, E>> states = new ArrayList<StateData<S, E>>();
|
||||
private S initialState;
|
||||
private S endState;
|
||||
|
||||
public StateMachineStateBuilder() {
|
||||
super();
|
||||
@@ -46,7 +47,7 @@ public class StateMachineStateBuilder<S, E>
|
||||
|
||||
@Override
|
||||
protected StateMachineStates<S, E> performBuild() throws Exception {
|
||||
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, states);
|
||||
StateMachineStates<S, E> bean = new StateMachineStates<S, E>(initialState, endState, states);
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -62,5 +63,9 @@ public class StateMachineStateBuilder<S, E>
|
||||
public void setInitialState(S state) {
|
||||
this.initialState = state;
|
||||
}
|
||||
|
||||
public void setEndState(S endState) {
|
||||
this.endState = endState;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,9 +27,12 @@ public class StateMachineStates<S, E> {
|
||||
|
||||
private final S initialState;
|
||||
|
||||
public StateMachineStates(S initialState, Collection<StateData<S, E>> states) {
|
||||
private final S endState;
|
||||
|
||||
public StateMachineStates(S initialState, S endState, Collection<StateData<S, E>> states) {
|
||||
this.states = states;
|
||||
this.initialState = initialState;
|
||||
this.endState = endState;
|
||||
}
|
||||
|
||||
public Collection<StateData<S, E>> getStates() {
|
||||
@@ -40,6 +43,10 @@ public class StateMachineStates<S, E> {
|
||||
return initialState;
|
||||
}
|
||||
|
||||
public S getEndState() {
|
||||
return endState;
|
||||
}
|
||||
|
||||
public static class StateData<S, E> {
|
||||
private S state;
|
||||
private Collection<E> deferred;
|
||||
|
||||
@@ -34,10 +34,13 @@ public class DefaultStateConfigurer<S, E>
|
||||
|
||||
private S initial;
|
||||
|
||||
private S end;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateBuilder<S, E> builder) throws Exception {
|
||||
builder.add(states);
|
||||
builder.setInitialState(initial);
|
||||
builder.setEndState(end);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,6 +48,12 @@ public class DefaultStateConfigurer<S, E>
|
||||
this.initial = initial;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> end(S end) {
|
||||
this.end = end;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> state(S state) {
|
||||
|
||||
@@ -35,4 +35,6 @@ public interface StateConfigurer<S, E> extends
|
||||
|
||||
StateConfigurer<S, E> states(Set<S> states);
|
||||
|
||||
StateConfigurer<S, E> end(S end);
|
||||
|
||||
}
|
||||
|
||||
@@ -53,4 +53,11 @@ public interface Region<S, E> {
|
||||
*/
|
||||
Collection<Transition<S,E>> getTransitions();
|
||||
|
||||
/**
|
||||
* Checks if region complete. Region is considered to be completed if it has
|
||||
* reached its end state and no further event processing is happening.
|
||||
*
|
||||
* @return true, if complete
|
||||
*/
|
||||
boolean isComplete();
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ 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;
|
||||
@@ -97,6 +99,19 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
this(states, transitions, initialState, new DefaultExtendedState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
* @param states the states
|
||||
* @param transitions the transitions
|
||||
* @param initialState the initial state
|
||||
* @param endState the end state
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState, State<S, E> endState) {
|
||||
this(states, transitions, initialState, endState, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
@@ -107,7 +122,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState, ExtendedState extendedState) {
|
||||
this(states, transitions, initialState, null, extendedState);
|
||||
this(states, transitions, initialState, null, null, extendedState);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,15 +131,17 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
* @param states the states of this machine
|
||||
* @param transitions the transitions of this machine
|
||||
* @param initialState the initial state of this machine
|
||||
* @param endState the final state of this machine
|
||||
* @param initialEvent the initial event of this machine
|
||||
* @param extendedState the extended state of this machine
|
||||
*/
|
||||
public AbstractStateMachine(Collection<State<S, E>> states, Collection<Transition<S, E>> transitions,
|
||||
State<S, E> initialState, Message<E> initialEvent, ExtendedState extendedState) {
|
||||
State<S, E> initialState, State<S, E> endState, Message<E> initialEvent, ExtendedState extendedState) {
|
||||
super();
|
||||
this.states = states;
|
||||
this.transitions = transitions;
|
||||
this.initialState = initialState;
|
||||
this.endState = endState;
|
||||
this.initialEvent = initialEvent;
|
||||
this.extendedState = extendedState;
|
||||
}
|
||||
@@ -141,6 +158,9 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
@Override
|
||||
public void sendEvent(Message<E> event) {
|
||||
if (isComplete()) {
|
||||
return;
|
||||
}
|
||||
// TODO: machine header looks weird!
|
||||
event = MessageBuilder.fromMessage(event).setHeader("machine", this).build();
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -174,6 +194,11 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
public void addStateListener(StateMachineListener<State<S, E>, E> listener) {
|
||||
stateListener.register(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
return (endState != null && endState.equals(currentState));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link State}s defined in this machine. Returned collection is
|
||||
|
||||
@@ -55,7 +55,7 @@ public abstract class AbstractStateMachineTests {
|
||||
}
|
||||
|
||||
public enum TestStates {
|
||||
SI,S1,S2,S3,S4
|
||||
SI,S1,S2,S3,S4,SF
|
||||
}
|
||||
|
||||
public enum TestSubStates {
|
||||
@@ -63,7 +63,7 @@ public abstract class AbstractStateMachineTests {
|
||||
}
|
||||
|
||||
public enum TestEvents {
|
||||
E1,E2,E3,E4
|
||||
E1,E2,E3,E4,EF
|
||||
}
|
||||
|
||||
public enum TestSubEvents {
|
||||
|
||||
@@ -73,7 +73,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
@@ -144,7 +144,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
// create machine
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
// StateMachine<State<TestStates, TestEvents>, TestEvents> machine2 = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
@@ -184,7 +184,7 @@ public class EnumStateMachineTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionInternalSI);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import org.springframework.statemachine.config.builders.StateMachineTransitionCo
|
||||
public class StateMachineTests extends AbstractStateMachineTests {
|
||||
|
||||
@Test
|
||||
public void test1() {
|
||||
public void testLoggingEvents() {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);
|
||||
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.TestUtils;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
@@ -58,6 +59,20 @@ public class ConfigurationTests extends AbstractStateMachineTests {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testEndState() throws Exception {
|
||||
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config3.class);
|
||||
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
Object endState = TestUtils.readField("endState", machine);
|
||||
assertThat(endState, notNullValue());
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
@@ -114,5 +129,20 @@ public class ConfigurationTests extends AbstractStateMachineTests {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config3 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.state;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.EnumStateMachine;
|
||||
import org.springframework.statemachine.StateMachineSystemConstants;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
public class EndStateTests extends AbstractStateMachineTests {
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
return new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndStateCompletes() {
|
||||
context.register(Config1.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
@SuppressWarnings("unchecked")
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
assertThat(machine, notNullValue());
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.E1);
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.E2);
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.E3);
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.E4);
|
||||
assertThat(machine.isComplete(), is(false));
|
||||
machine.sendEvent(TestEvents.EF);
|
||||
assertThat(machine.isComplete(), is(true));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(TestStates.SI)
|
||||
.states(EnumSet.allOf(TestStates.class))
|
||||
.end(TestStates.SF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.SI)
|
||||
.target(TestStates.S1)
|
||||
.event(TestEvents.E1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E2)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E3)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S3)
|
||||
.target(TestStates.S4)
|
||||
.event(TestEvents.E4)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S4)
|
||||
.target(TestStates.SF)
|
||||
.event(TestEvents.EF);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TaskExecutor taskExecutor() {
|
||||
return new SyncTaskExecutor();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class RegionStateTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public class SubmachineStateTests extends AbstractStateMachineTests {
|
||||
transitions.add(transitionFromS2ToS3);
|
||||
|
||||
SyncTaskExecutor taskExecutor = new SyncTaskExecutor();
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI);
|
||||
EnumStateMachine<TestStates, TestEvents> machine = new EnumStateMachine<TestStates, TestEvents>(states, transitions, stateSI, null);
|
||||
machine.setTaskExecutor(taskExecutor);
|
||||
machine.start();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user