Add base support for pseudostates
- resolves #3 by adding base pseudostate support for initial state. - doesn't yet provide full support for any other pseudostate expect initial. - Also added initial event which is used when statemachine starts and activates initial state.
This commit is contained in:
@@ -93,6 +93,7 @@ project('spring-statemachine-core') {
|
||||
description = "Spring State Machine Core"
|
||||
|
||||
dependencies {
|
||||
compile "org.springframework:spring-tx:$springVersion"
|
||||
compile "org.springframework:spring-messaging:$springVersion"
|
||||
|
||||
testCompile "org.springframework:spring-test:$springVersion"
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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 java.io.IOException;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
|
||||
/**
|
||||
* General exception indicating a problem in interacting with statemachine.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class StateMachineException extends NonTransientDataAccessException {
|
||||
|
||||
private static final long serialVersionUID = 4485522802268496000L;
|
||||
|
||||
/**
|
||||
* Constructs a generic StateMachineException.
|
||||
*
|
||||
* @param e the {@link RemoteException}
|
||||
*/
|
||||
public StateMachineException(IOException e) {
|
||||
super(e.getMessage(), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a generic StateMachineException.
|
||||
*
|
||||
* @param message the message
|
||||
* @param e the exception
|
||||
*/
|
||||
public StateMachineException(String message, Exception e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a generic StateMachineException.
|
||||
*
|
||||
* @param message the message
|
||||
* @param cause the throwable cause
|
||||
*/
|
||||
public StateMachineException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a generic StateMachineException.
|
||||
*
|
||||
* @param message the message
|
||||
*/
|
||||
public StateMachineException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,10 @@ import org.springframework.statemachine.config.builders.StateMachineStates;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStates.StateData;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitions.TransitionData;
|
||||
import org.springframework.statemachine.state.DefaultPseudoState;
|
||||
import org.springframework.statemachine.state.EnumState;
|
||||
import org.springframework.statemachine.state.PseudoState;
|
||||
import org.springframework.statemachine.state.PseudoStateKind;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.support.LifecycleObjectSupport;
|
||||
import org.springframework.statemachine.transition.DefaultExternalTransition;
|
||||
@@ -68,8 +71,14 @@ public class EnumStateMachineFactory<S extends Enum<S>, E extends Enum<E>> exten
|
||||
public StateMachine<State<S, E>, E> stateMachine() {
|
||||
Map<S, State<S, E>> stateMap = new HashMap<S, State<S, E>>();
|
||||
for (StateData<S, E> stateData : stateMachineStates.getStates()) {
|
||||
|
||||
// TODO: doesn't feel right to tweak initial kind like this
|
||||
PseudoState pseudoState = null;
|
||||
if (stateData.getState() == stateMachineStates.getInitialState()) {
|
||||
pseudoState = new DefaultPseudoState(PseudoStateKind.INITIAL);
|
||||
}
|
||||
stateMap.put(stateData.getState(), new EnumState<S, E>(stateData.getState(), stateData.getDeferred(),
|
||||
stateData.getEntryActions(), stateData.getExitActions()));
|
||||
stateData.getEntryActions(), stateData.getExitActions(), pseudoState));
|
||||
}
|
||||
|
||||
Collection<Transition<S, E>> transitions = new ArrayList<Transition<S, E>>();
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link PseudoState}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractPseudoState implements PseudoState {
|
||||
|
||||
private final PseudoStateKind kind;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract pseudo state.
|
||||
*
|
||||
* @param kind the kind
|
||||
*/
|
||||
public AbstractPseudoState(PseudoStateKind kind) {
|
||||
this.kind = kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PseudoStateKind getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,32 +19,89 @@ import java.util.Collection;
|
||||
|
||||
import org.springframework.statemachine.action.Action;
|
||||
|
||||
/**
|
||||
* Base implementation of a {@link State}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
* @param <S> the type of state
|
||||
* @param <E> the type of event
|
||||
*/
|
||||
public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
|
||||
private S id;
|
||||
private Collection<E> deferred;
|
||||
private Collection<Action> entryActions;
|
||||
private Collection<Action> exitActions;
|
||||
private final S id;
|
||||
private final PseudoState pseudoState;
|
||||
private final Collection<E> deferred;
|
||||
private final Collection<Action> entryActions;
|
||||
private final Collection<Action> exitActions;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state.
|
||||
*
|
||||
* @param id the id
|
||||
*/
|
||||
public AbstractState(S id) {
|
||||
this(id, null);
|
||||
this(id, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state.
|
||||
*
|
||||
* @param id the id
|
||||
* @param pseudoState the pseudo state
|
||||
*/
|
||||
public AbstractState(S id, PseudoState pseudoState) {
|
||||
this(id, null, null, null, pseudoState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state.
|
||||
*
|
||||
* @param id the id
|
||||
* @param deferred the deferred
|
||||
*/
|
||||
public AbstractState(S id, Collection<E> deferred) {
|
||||
this(id, deferred, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state.
|
||||
*
|
||||
* @param id the id
|
||||
* @param deferred the deferred
|
||||
* @param entryActions the entry actions
|
||||
* @param exitActions the exit actions
|
||||
*/
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions) {
|
||||
this(id, deferred, entryActions, exitActions, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state.
|
||||
*
|
||||
* @param id the id
|
||||
* @param deferred the deferred
|
||||
* @param entryActions the entry actions
|
||||
* @param exitActions the exit actions
|
||||
* @param pseudoState the pseudo state
|
||||
*/
|
||||
public AbstractState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions, PseudoState pseudoState) {
|
||||
this.id = id;
|
||||
this.deferred = deferred;
|
||||
this.entryActions = entryActions;
|
||||
this.exitActions = exitActions;
|
||||
this.pseudoState = pseudoState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PseudoState getPseudoState() {
|
||||
return pseudoState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<E> getDeferredEvents() {
|
||||
@@ -63,8 +120,8 @@ public abstract class AbstractState<S, E> implements State<S, E> {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractState [id=" + id + ", deferred=" + deferred + ", entryActions=" + entryActions
|
||||
+ ", exitActions=" + exitActions + "]";
|
||||
return "AbstractState [id=" + id + ", pseudoState=" + pseudoState + ", deferred=" + deferred
|
||||
+ ", entryActions=" + entryActions + ", exitActions=" + exitActions + "]";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Default implementation of a {@link PseudoState} which is a simple passthrough
|
||||
* via {@link AbstractPseudoState}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class DefaultPseudoState extends AbstractPseudoState {
|
||||
|
||||
/**
|
||||
* Instantiates a new default pseudo state.
|
||||
*
|
||||
* @param kind the kind
|
||||
*/
|
||||
public DefaultPseudoState(PseudoStateKind kind) {
|
||||
super(kind);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,10 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSta
|
||||
super(id);
|
||||
}
|
||||
|
||||
public EnumState(S id, PseudoState pseudoState) {
|
||||
super(id, pseudoState);
|
||||
}
|
||||
|
||||
public EnumState(S id, Collection<E> deferred) {
|
||||
super(id, deferred);
|
||||
}
|
||||
@@ -33,6 +37,11 @@ public class EnumState<S extends Enum<S>, E extends Enum<E>> extends AbstractSta
|
||||
super(id, deferred, entryActions, exitActions);
|
||||
}
|
||||
|
||||
public EnumState(S id, Collection<E> deferred, Collection<Action> entryActions, Collection<Action> exitActions,
|
||||
PseudoState pseudoState) {
|
||||
super(id, deferred, entryActions, exitActions, pseudoState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EnumState [getId()=" + getId() + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A {@code PseudoState} is an abstraction that encompasses different types of
|
||||
* transient states or vertices in the state machine.
|
||||
*
|
||||
* <p>
|
||||
* Pseudostates are typically used to connect multiple transitions into more
|
||||
* complex state transitions paths. For example, by combining a transition
|
||||
* entering a fork pseudostate with a set of transitions exiting the fork
|
||||
* pseudostate, we get a compound transition that leads to a set of orthogonal
|
||||
* target states.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public interface PseudoState {
|
||||
|
||||
/**
|
||||
* Gets the pseudostate kind.
|
||||
*
|
||||
* @return the pseudostate kind
|
||||
*/
|
||||
PseudoStateKind getKind();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Defines enumeration of a {@link PseudoState} kind. This is uses within a
|
||||
* transitive states indicating its kind.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public enum PseudoStateKind {
|
||||
|
||||
/** Indicates an initial kind. */
|
||||
INITIAL
|
||||
|
||||
}
|
||||
@@ -36,6 +36,15 @@ public interface State<S, E> {
|
||||
*/
|
||||
S getId();
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Gets the deferred events for this state.
|
||||
*
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.springframework.statemachine.listener.StateMachineListener;
|
||||
import org.springframework.statemachine.processor.StateMachineHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineOnTransitionHandler;
|
||||
import org.springframework.statemachine.processor.StateMachineRuntime;
|
||||
import org.springframework.statemachine.state.PseudoStateKind;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
import org.springframework.statemachine.transition.TransitionKind;
|
||||
@@ -70,6 +71,8 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
|
||||
private final State<S,E> initialState;
|
||||
|
||||
private final Message<E> initialEvent;
|
||||
|
||||
private final ExtendedState extendedState;
|
||||
|
||||
private final Queue<Message<E>> eventQueue = new ConcurrentLinkedQueue<Message<E>>();
|
||||
@@ -79,7 +82,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
private final CompositeStateMachineListener<S, E> stateListener = new CompositeStateMachineListener<S, E>();
|
||||
|
||||
private volatile State<S,E> currentState;
|
||||
|
||||
|
||||
private volatile Runnable task;
|
||||
|
||||
/**
|
||||
@@ -93,7 +96,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
State<S, E> initialState) {
|
||||
this(states, transitions, initialState, new DefaultExtendedState());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
@@ -104,10 +107,25 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract state machine.
|
||||
*
|
||||
* @param states the states of this machine
|
||||
* @param transitions the transitions of this machine
|
||||
* @param initialState the initial 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) {
|
||||
super();
|
||||
this.states = states;
|
||||
this.transitions = transitions;
|
||||
this.initialState = initialState;
|
||||
this.initialEvent = initialEvent;
|
||||
this.extendedState = extendedState;
|
||||
}
|
||||
|
||||
@@ -137,10 +155,19 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
sendEvent(MessageBuilder.withPayload(event).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() throws Exception {
|
||||
super.onInit();
|
||||
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");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() {
|
||||
super.doStart();
|
||||
switchToState(initialState, null);
|
||||
switchToState(initialState, initialEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,7 +184,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
public Collection<State<S,E>> getStates() {
|
||||
return Collections.unmodifiableCollection(states);
|
||||
}
|
||||
|
||||
|
||||
private void switchToState(State<S,E> state, Message<E> event) {
|
||||
log.info("Moving into state=" + state + " from " + currentState);
|
||||
|
||||
@@ -205,7 +232,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processEventQueue() {
|
||||
log.debug("Process event queue");
|
||||
Message<E> queuedEvent = null;
|
||||
@@ -269,7 +296,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
getTaskExecutor().execute(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void callHandlers(State<S,E> sourceState, State<S,E> targetState, Message<E> event) {
|
||||
if (sourceState != null && targetState != null) {
|
||||
MessageHeaders messageHeaders = event != null ? event.getHeaders() : new MessageHeaders(
|
||||
@@ -279,8 +306,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private List<Object> getStateMachineHandlerResults(List<StateMachineHandler> stateMachineHandlers, final StateContext stateContext) {
|
||||
StateMachineRuntime runtime = new StateMachineRuntime() {
|
||||
@Override
|
||||
@@ -294,7 +320,7 @@ public abstract class AbstractStateMachine<S, E> extends LifecycleObjectSupport
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private List<StateMachineHandler> getStateMachineHandlers(State<S,E> sourceState, State<S,E> targetState) {
|
||||
BeanFactory beanFactory = getBeanFactory();
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class LifecycleObjectSupport implements InitializingBean, SmartL
|
||||
private static final Log log = LogFactory.getLog(LifecycleObjectSupport.class);
|
||||
|
||||
// fields for lifecycle
|
||||
private volatile boolean autoStartup = true;
|
||||
private volatile boolean autoStartup = false;
|
||||
private volatile int phase = 0;
|
||||
private volatile boolean running;
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ package org.springframework.statemachine;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
@@ -33,6 +36,24 @@ import org.springframework.statemachine.guard.Guard;
|
||||
*/
|
||||
public abstract class AbstractStateMachineTests {
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.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.Configuration;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Tests for functionality around state machine initial state.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*
|
||||
*/
|
||||
public class InitialStateTests extends AbstractStateMachineTests {
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
@Test
|
||||
public void testInitialStateTransition() throws Exception {
|
||||
context.register(BaseConfig.class, Config1.class);
|
||||
context.refresh();
|
||||
assertTrue(context.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
|
||||
EnumStateMachine<TestStates,TestEvents> machine =
|
||||
context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, EnumStateMachine.class);
|
||||
|
||||
assertThat(machine.getState().getId(), is(TestStates.S1));
|
||||
}
|
||||
|
||||
@Test(expected = Exception.class)
|
||||
public void testInitialStateMissingFailure() throws Exception {
|
||||
context.register(BaseConfig.class, Config2.class);
|
||||
context.refresh();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public 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));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.SI)
|
||||
.target(TestStates.S1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S1)
|
||||
.target(TestStates.S2)
|
||||
.event(TestEvents.E1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(TestStates.S2)
|
||||
.target(TestStates.S3)
|
||||
.event(TestEvents.E2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config2 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.states(EnumSet.allOf(TestStates.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(TestStates.SI)
|
||||
.target(TestStates.S1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
return new AnnotationConfigApplicationContext();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user