Direct region entry doesn't bypass initial state entry

- Enhance state context to have more info about target
  states which is needed to make a proper decision if
  region needs to be started.
- Fix RegionState to not to start if it looks like
  direct entry will be done later.
- Relates to #221
This commit is contained in:
Janne Valkealahti
2016-05-07 15:52:12 +01:00
parent 12137222a8
commit 2eb8788ce3
8 changed files with 606 additions and 21 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -15,6 +15,8 @@
*/
package org.springframework.statemachine;
import java.util.Collection;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.statemachine.action.Action;
@@ -104,7 +106,17 @@ public interface StateContext<S, E> {
*
* @return the source state
*/
State<S,E> getSource();
State<S, E> getSource();
/**
* Gets the source states of this context. Multiple sources are
* only valid during a context when machine is joining from multiple
* orthogonal regions.
*
* @return the source state
* @see #getSource()
*/
Collection<State<S, E>> getSources();
/**
* Gets the target state of this context. Generally target
@@ -113,7 +125,17 @@ public interface StateContext<S, E> {
*
* @return the target state
*/
State<S,E> getTarget();
State<S, E> getTarget();
/**
* Gets the target states of this context. Multiple targets are
* only valid during a context when machine is forking into multiple
* orthogonal regions.
*
* @return the target states
* @see #getTarget()
*/
Collection<State<S, E>> getTargets();
/**
* Gets the exception associated with a context.
@@ -138,5 +160,4 @@ public interface StateContext<S, E> {
TRANSITION_START,
TRANSITION_END;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -17,7 +17,6 @@ package org.springframework.statemachine.state;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
@@ -159,14 +158,8 @@ public class RegionState<S, E> extends AbstractState<S, E> {
if (getPseudoState() != null && getPseudoState().getKind() == PseudoStateKind.INITIAL) {
for (Region<S, E> region : getRegions()) {
boolean start = true;
PseudoState<S, E> ps = context.getTransition().getTarget().getPseudoState();
if (ps != null && ps.getKind() == PseudoStateKind.FORK) {
List<State<S, E>> forks = ((ForkPseudoState<S, E>)ps).getForks();
if (StateMachineUtils.containsAtleastOne(region.getStates(), forks)) {
// it looks like fork will take directly into a state so don't start
// as we want to bypass initial entry logic.
start = false;
}
if (StateMachineUtils.containsAtleastOne(region.getStates(), context.getTargets())) {
start = false;
}
if (start) {
region.start();

View File

@@ -735,7 +735,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
ForkPseudoState<S, E> fps = (ForkPseudoState<S, E>) toState.getPseudoState();
for (State<S, E> ss : fps.getForks()) {
callPreStateChangeInterceptors(ss, message, transition, stateMachine);
setCurrentState(ss, message, transition, false, stateMachine);
setCurrentState(ss, message, transition, false, stateMachine, null, fps.getForks());
}
} else {
setCurrentState(toState, message, transition, true, stateMachine);
@@ -811,6 +811,12 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
return new DefaultStateContext<S, E>(stage, message, messageHeaders, extendedState, transition, stateMachine, source, target, null);
}
private StateContext<S, E> buildStateContext(Stage stage, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine, Collection<State<S, E>> sources, Collection<State<S, E>> targets) {
MessageHeaders messageHeaders = message != null ? message.getHeaders() : new MessageHeaders(
new HashMap<String, Object>());
return new DefaultStateContext<S, E>(stage, message, messageHeaders, extendedState, transition, stateMachine, null, null, sources, targets, null);
}
private State<S, E> findDeepParent(State<S, E> state) {
for (State<S, E> s : states) {
if (s.getStates().contains(state)) {
@@ -821,6 +827,11 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
}
synchronized void setCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, boolean exit, StateMachine<S, E> stateMachine) {
setCurrentState(state, message, transition, exit, stateMachine, null, null);
}
synchronized void setCurrentState(State<S, E> state, Message<E> message, Transition<S, E> transition, boolean exit,
StateMachine<S, E> stateMachine, Collection<State<S, E>> sources, Collection<State<S, E>> targets) {
State<S, E> findDeep = findDeepParent(state);
boolean isTargetSubOf = false;
@@ -901,7 +912,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
}
currentState = findDeep;
if (shouldTryEntry) {
entryToState(currentState, message, transition, stateMachine);
entryToState(currentState, message, transition, stateMachine, sources, targets);
}
if (currentState.isSubmachineState()) {
@@ -985,11 +996,16 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
}
private void entryToState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
entryToState(state, message, transition, stateMachine, null, null);
}
private void entryToState(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine,
Collection<State<S, E>> sources, Collection<State<S, E>> targets) {
if (state == null) {
return;
}
log.trace("Trying Enter state=[" + state + "]");
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_ENTRY, message, transition, stateMachine);
StateContext<S, E> stateContext = buildStateContext(Stage.STATE_ENTRY, message, transition, stateMachine, sources, targets);
if (transition != null) {
State<S, E> findDeep1 = findDeepParent(transition.getTarget());
@@ -1026,5 +1042,4 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
return false;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015 the original author or authors.
* Copyright 2015-2016 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.
@@ -15,6 +15,8 @@
*/
package org.springframework.statemachine.support;
import java.util.Collection;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.statemachine.ExtendedState;
@@ -41,6 +43,8 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
private final StateMachine<S, E> stateMachine;
private final State<S, E> source;
private final State<S, E> target;
private final Collection<State<S, E>> sources;
private final Collection<State<S, E>> targets;
private final Exception exception;
/**
@@ -67,6 +71,39 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
this.source = source;
this.target = target;
this.exception = exception;
this.sources = null;
this.targets = null;
}
/**
* Instantiates a new default state context.
*
* @param stage the stage
* @param message the message
* @param messageHeaders the message headers
* @param extendedState the extended state
* @param transition the transition
* @param stateMachine the state machine
* @param source the source
* @param target the target
* @param sources the sources
* @param targets the targets
* @param exception the exception
*/
public DefaultStateContext(Stage stage, Message<E> message, MessageHeaders messageHeaders, ExtendedState extendedState,
Transition<S, E> transition, StateMachine<S, E> stateMachine, State<S, E> source, State<S, E> target,
Collection<State<S, E>> sources, Collection<State<S, E>> targets, Exception exception) {
this.stage = stage;
this.message = message;
this.messageHeaders = messageHeaders;
this.extendedState = extendedState;
this.transition = transition;
this.stateMachine = stateMachine;
this.source = source;
this.target = target;
this.sources = sources;
this.targets = targets;
this.exception = exception;
}
@Override
@@ -119,11 +156,21 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
return source != null ? source : (transition != null ? transition.getSource() : null);
}
@Override
public Collection<State<S, E>> getSources() {
return sources;
}
@Override
public State<S, E> getTarget() {
return target != null ? target : (transition != null ? transition.getTarget() : null);
}
@Override
public Collection<State<S, E>> getTargets() {
return targets;
}
@Override
public Exception getException() {
return exception;
@@ -133,7 +180,6 @@ public class DefaultStateContext<S, E> implements StateContext<S, E> {
public String toString() {
return "DefaultStateContext [stage=" + stage + ", message=" + message + ", messageHeaders=" + messageHeaders + ", extendedState="
+ extendedState + ", transition=" + transition + ", stateMachine=" + stateMachine + ", source=" + source + ", target="
+ target + ", exception=" + exception + "]";
+ target + ", sources=" + sources + ", targets=" + targets + ", exception=" + exception + "]";
}
}