Add capabilities to provide ActionError in Transition

- Add classes directory to be ignored by git
- Doesn't yet handle actions defined for entry,
  exit or do behariour.
- Docs to come when features around error actions
  are fully implemented.
- Fixes #240
This commit is contained in:
Christophe
2016-08-24 14:07:40 +02:00
committed by Janne Valkealahti
parent 062bc9aa0e
commit 4c34e46ccf
22 changed files with 329 additions and 183 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@ metastore_db
.DS_Store
/out/
target
classes

View File

@@ -34,5 +34,4 @@ public interface Action<S, E> {
* @param context the state context
*/
void execute(StateContext<S, E> context);
}

View File

@@ -0,0 +1,44 @@
/*
* 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.
* 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.action;
import org.springframework.statemachine.StateContext;
/**
* Action Utilities.
*/
public final class Actions {
private Actions() {
// This helper class should not be instantiated.
}
/**
*
* @param <S> represents states Class
* @param <E> represents event Class
* @return an empty (Noop) Action.
*/
public static <S, E> Action<S, E> emptyAction() {
return new Action<S, E>() {
@Override
public void execute(final StateContext<S, E> context) {
// Nothing to do;
}
};
}
}

View File

@@ -15,15 +15,6 @@
*/
package org.springframework.statemachine.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
@@ -86,6 +77,15 @@ import org.springframework.statemachine.trigger.Trigger;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Stack;
/**
* Base {@link StateMachineFactory} implementation building {@link StateMachine}s.
*
@@ -694,13 +694,13 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
}
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(stateMap.get(source),
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard(), trigger,
transitionData.getSecurityRule());
transitionData.getSecurityRule(), transitionData.getErrorAction());
transitions.add(transition);
} else if (transitionData.getKind() == TransitionKind.INTERNAL) {
DefaultInternalTransition<S, E> transition = new DefaultInternalTransition<S, E>(stateMap.get(source),
transitionData.getActions(), event, transitionData.getGuard(), trigger,
transitionData.getSecurityRule());
transitionData.getSecurityRule(), transitionData.getErrorAction());
transitions.add(transition);
}
}
@@ -714,7 +714,7 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
if (source != null && !source.isOrthogonal()) {
State<S, E> target = stateMap.get(entry.getKey());
DefaultExternalTransition<S, E> transition = new DefaultExternalTransition<S, E>(
source, target, null, null, null, null, null);
source, target, null, null, null, null, null, null);
transitions.add(transition);
}
}

View File

@@ -15,12 +15,6 @@
*/
package org.springframework.statemachine.config.builders;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder;
import org.springframework.statemachine.config.common.annotation.AnnotationBuilder;
@@ -57,6 +51,12 @@ import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.transition.TransitionKind;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* {@link AnnotationBuilder} for {@link TransitionsData}.
*
@@ -108,7 +108,7 @@ public class StateMachineTransitionBuilder<S, E>
@Override
protected TransitionsData<S, E> performBuild() throws Exception {
return new TransitionsData<S, E>(transitionData, choices, junctions, forks, joins, entryData, exitData, historyData);
return new TransitionsData<>(transitionData, choices, junctions, forks, joins, entryData, exitData, historyData);
}
@Override
@@ -174,16 +174,17 @@ public class StateMachineTransitionBuilder<S, E>
* @param guard the guard
* @param kind the kind
* @param securityRule the security rule
* @param errorAction the {@link Action} that will be called each time an action is gonna throw an exception.
*/
public void addTransition(S source, S target, S state, E event, Long period, Integer count, Collection<Action<S, E>> actions,
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule) {
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule, Action<S, E> errorAction) {
// if rule not given, get it from global
if (securityRule == null) {
@SuppressWarnings("unchecked")
ConfigurationData<S, E> config = getSharedObject(ConfigurationData.class);
securityRule = config.getTransitionSecurityRule();
}
transitionData.add(new TransitionData<S, E>(source, target, state, event, period, count, actions, guard, kind, securityRule));
transitionData.add(new TransitionData<>(source, target, state, event, period, count, actions, guard, kind, securityRule, errorAction));
}
/**

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.statemachine.config.configurers;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
@@ -27,6 +24,9 @@ import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.security.SecurityRule.ComparisonType;
import java.util.ArrayList;
import java.util.Collection;
/**
* Base class for transition configurers.
*
@@ -44,9 +44,10 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
private E event;
private Long period;
private Integer count;
private final Collection<Action<S, E>> actions = new ArrayList<Action<S, E>>();
private final Collection<Action<S, E>> actions = new ArrayList<>();
private Guard<S, E> guard;
private SecurityRule securityRule;
private Action<S, E> errorAction;
protected S getSource() {
return source;
@@ -68,6 +69,10 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
return period;
}
/**
*
* @return trigger count
*/
public Integer getCount() {
return count;
}
@@ -104,6 +109,10 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
this.period = period;
}
/**
*
* @param count to set how many time the trigger will be called.
*/
public void setCount(Integer count) {
this.count = count;
}
@@ -116,6 +125,22 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
this.guard = guard;
}
/**
*
* @return the Error {@link Action}
*/
public Action<S, E> getErrorAction() {
return errorAction;
}
/**
*
* @param errorAction the {@link Action} that will be called each time an action is gonna throw an exception.
*/
public void setErrorAction(Action<S, E> errorAction) {
this.errorAction = errorAction;
}
protected void setSecurityRule(String attributes, ComparisonType match) {
if (securityRule == null) {
securityRule = new SecurityRule();

View File

@@ -39,7 +39,7 @@ public class DefaultExternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.addTransition(getSource(), getTarget(), getState(), getEvent(), getPeriod(), getCount(), getActions(), getGuard(), TransitionKind.EXTERNAL,
getSecurityRule());
getSecurityRule(), getErrorAction());
}
@Override
@@ -81,7 +81,13 @@ public class DefaultExternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public ExternalTransitionConfigurer<S, E> action(Action<S, E> action) {
return action(action, null);
}
@Override
public ExternalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
return this;
}

View File

@@ -39,7 +39,7 @@ public class DefaultInternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.addTransition(getSource(), getTarget(), getState(), getEvent(), getPeriod(), getCount(), getActions(), getGuard(), TransitionKind.INTERNAL,
getSecurityRule());
getSecurityRule(), getErrorAction());
}
@Override
@@ -75,7 +75,13 @@ public class DefaultInternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public InternalTransitionConfigurer<S, E> action(Action<S, E> action) {
return action(action, null);
}
@Override
public InternalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
return this;
}

View File

@@ -38,7 +38,7 @@ public class DefaultLocalTransitionConfigurer<S, E> extends AbstractTransitionCo
@Override
public void configure(StateMachineTransitionBuilder<S, E> builder) throws Exception {
builder.addTransition(getSource(), getTarget(), getState(), getEvent(), getPeriod(), getCount(), getActions(), getGuard(), TransitionKind.LOCAL,
getSecurityRule());
getSecurityRule(), getErrorAction());
}
@Override
@@ -80,7 +80,13 @@ public class DefaultLocalTransitionConfigurer<S, E> extends AbstractTransitionCo
@Override
public LocalTransitionConfigurer<S, E> action(Action<S, E> action) {
return action(action, null);
}
@Override
public LocalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
return this;
}

View File

@@ -83,6 +83,15 @@ public interface TransitionConfigurer<T, S, E> extends
*/
T action(Action<S, E> action);
/**
* Specify {@link Action} for this {@link Transition}.
*
* @param action the action
* @param error that will be called if any unexpected exception is thrown by the action.
* @return configurer for chaining
*/
T action(Action<S, E> action, Action<S, E> error);
/**
* Specify a {@link Guard} for this {@link Transition}.
*

View File

@@ -15,13 +15,13 @@
*/
package org.springframework.statemachine.config.model;
import java.util.Collection;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.transition.TransitionKind;
import java.util.Collection;
/**
* A simple data object keeping transition related configs in a same place.
*
@@ -39,6 +39,7 @@ public class TransitionData<S, E> {
private final Guard<S, E> guard;
private final TransitionKind kind;
private final SecurityRule securityRule;
private final Action<S, E> errorAction;
/**
* Instantiates a new transition data.
@@ -48,7 +49,7 @@ public class TransitionData<S, E> {
* @param event the event
*/
public TransitionData(S source, S target, E event) {
this(source, target, null, event, null, null, null, null, TransitionKind.EXTERNAL, null);
this(source, target, null, event, null, null, null, null, TransitionKind.EXTERNAL, null, null);
}
/**
@@ -63,7 +64,7 @@ public class TransitionData<S, E> {
*/
public TransitionData(S source, S target, E event, Collection<Action<S, E>> actions,
Guard<S, E> guard, TransitionKind kind) {
this(source, target, null, event, null, null, actions, guard, kind, null);
this(source, target, null, event, null, null, actions, guard, kind, null, null);
}
/**
@@ -79,7 +80,7 @@ public class TransitionData<S, E> {
*/
public TransitionData(S source, S target, Long period, Integer count, Collection<Action<S, E>> actions,
Guard<S, E> guard, TransitionKind kind) {
this(source, target, null, null, period, count, actions, guard, kind, null);
this(source, target, null, null, period, count, actions, guard, kind, null, null);
}
/**
@@ -95,9 +96,10 @@ public class TransitionData<S, E> {
* @param guard the guard
* @param kind the kind
* @param securityRule the security rule
* @param errorAction the {@link Action} that will be called each time an action is gonna throw an exception.
*/
public TransitionData(S source, S target, S state, E event, Long period, Integer count, Collection<Action<S, E>> actions,
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule) {
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule, Action<S, E> errorAction) {
this.source = source;
this.target = target;
this.state = state;
@@ -108,6 +110,7 @@ public class TransitionData<S, E> {
this.guard = guard;
this.kind = kind;
this.securityRule = securityRule;
this.errorAction = errorAction;
}
/**
@@ -199,4 +202,12 @@ public class TransitionData<S, E> {
public SecurityRule getSecurityRule() {
return securityRule;
}
/**
*
* @return the error {@link Action}
*/
public Action<S, E> getErrorAction() {
return errorAction;
}
}

View File

@@ -15,19 +15,19 @@
*/
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.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
public abstract class AbstractExternalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
public AbstractExternalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions,
E event, Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule);
E event, Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule, errorAction);
}
public AbstractExternalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions,

View File

@@ -31,8 +31,8 @@ public class AbstractInternalTransition<S, E> extends AbstractTransition<S, E> i
}
public AbstractInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule);
Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule, errorAction);
}
}

View File

@@ -15,14 +15,14 @@
*/
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.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
public class AbstractLocalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
public AbstractLocalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
@@ -31,8 +31,8 @@ public class AbstractLocalTransition<S, E> extends AbstractTransition<S, E> impl
}
public AbstractLocalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule);
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule, errorAction);
}
}

View File

@@ -15,18 +15,19 @@
*/
package org.springframework.statemachine.transition;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.Actions;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import org.springframework.util.Assert;
import java.util.Collection;
/**
* Base implementation of a {@link Transition}.
*
@@ -38,12 +39,11 @@ import org.springframework.util.Assert;
public abstract class AbstractTransition<S, E> implements Transition<S, E> {
private final static Log log = LogFactory.getLog(AbstractTransition.class);
protected final State<S, E> target;
protected final Collection<Action<S, E>> actions;
protected final Action<S, E> errorAction;
private final State<S,E> source;
private final State<S,E> target;
private final Collection<Action<S, E>> actions;
private final State<S, E> source;
private final TransitionKind kind;
@@ -53,39 +53,57 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
private final SecurityRule securityRule;
public AbstractTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
TransitionKind kind, Guard<S, E> guard, Trigger<S, E> trigger) {
this(source, target, actions, event, kind, guard, trigger, null);
public AbstractTransition(State<S, E> source,
State<S, E> target,
Collection<Action<S, E>> actions,
E event,
TransitionKind kind,
Guard<S, E> guard,
Trigger<S, E> trigger) {
this(source, target, actions, event, kind, guard, trigger, null, Actions.<S, E>emptyAction());
}
public AbstractTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
TransitionKind kind, Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
public AbstractTransition(State<S, E> source,
State<S, E> target,
Collection<Action<S, E>> actions,
E event,
TransitionKind kind,
Guard<S, E> guard,
Trigger<S, E> trigger,
SecurityRule securityRule,
Action<S, E> errorAction) {
Assert.notNull(source, "Source must be set");
Assert.notNull(kind, "Transition type must be set");
this.source = source;
this.target = target;
this.actions = actions;
this.kind = kind;
this.errorAction = errorAction == null
? Actions.<S, E> emptyAction()
: errorAction; this.kind = kind;
this.guard = guard;
this.trigger = trigger;
this.securityRule = securityRule;
}
protected AbstractTransition(State<S, E> target, Collection<Action<S, E>> actions, TransitionKind kind, Action<S, E> errorAction) {
Assert.notNull(kind, "Transition type must be set");
this.source = null;
this.target = target;
this.actions = actions;
this.errorAction = errorAction == null
? Actions.<S, E> emptyAction()
: errorAction;
this.kind = kind;
this.guard = null;
this.trigger = null;
this.securityRule = null;
}
@Override
public State<S,E> getSource() {
public State<S, E> getSource() {
return source;
}
@Override
public State<S,E> getTarget() {
return target;
}
@Override
public Collection<Action<S, E>> getActions() {
return actions;
}
@Override
public Trigger<S, E> getTrigger() {
return trigger;
@@ -98,16 +116,14 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
if (!guard.evaluate(context)) {
return false;
}
} catch (Throwable t) {
}
catch (Throwable t) {
log.warn("Deny guard due to throw as GUARD should not error", t);
return false;
}
}
if (actions != null) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
executeAllActions(context);
return true;
}
@@ -121,4 +137,44 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
return securityRule;
}
/**
*
* @return the target {@link State}
*/
public State<S, E> getTarget() {
return target;
}
/**
*
* @return all {@link Action}
*/
public Collection<Action<S, E>> getActions() {
return actions;
}
/**
*
* @return the {@link Action} called of any error occurred while actions are executed.
*/
public Action<S, E> getErrorAction() {
return errorAction;
}
protected final void executeAllActions(StateContext<S, E> context) {
if (actions == null) {
return;
}
for (Action<S, E> action : actions) {
try {
action.execute(context);
}
catch (Exception exception) {
errorAction.execute(context); // notify something wrong is happening in
// Actions execution.
throw exception;
}
}
}
}

View File

@@ -15,14 +15,14 @@
*/
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.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
public class DefaultExternalTransition<S, E> extends AbstractExternalTransition<S, E> {
public DefaultExternalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
@@ -31,8 +31,8 @@ public class DefaultExternalTransition<S, E> extends AbstractExternalTransition<
}
public DefaultExternalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event,
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, guard, trigger, securityRule);
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, target, actions, event, guard, trigger, securityRule, errorAction);
}
}

View File

@@ -15,14 +15,14 @@
*/
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.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
public class DefaultInternalTransition<S, E> extends AbstractInternalTransition<S, E> {
public DefaultInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
@@ -31,8 +31,8 @@ public class DefaultInternalTransition<S, E> extends AbstractInternalTransition<
}
public DefaultInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, actions, event, guard, trigger, securityRule);
Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, actions, event, guard, trigger, securityRule, errorAction);
}
}

View File

@@ -15,14 +15,12 @@
*/
package org.springframework.statemachine.transition;
import java.util.ArrayList;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
import java.util.Collections;
/**
* {@link Transition} used during a state machine start.
@@ -32,87 +30,39 @@ import org.springframework.statemachine.trigger.Trigger;
* @param <S> the type of state
* @param <E> the type of event
*/
public class InitialTransition<S, E> implements Transition<S, E> {
private final State<S, E> target;
private final Collection<Action<S, E>> actions;
public class InitialTransition<S, E> extends AbstractTransition<S, E>
implements Transition<S, E> {
/**
* Instantiates a new initial transition.
*
* @param target the initial target state
* @param target state.
*/
public InitialTransition(State<S, E> target) {
this.target = target;
this.actions = null;
this(target, null);
}
/**
* Instantiates a new initial transition.
*
* @param target the initial target state
* @param action the initial action
* @param target state
* @param action to be executed to succeed for this transition
*/
public InitialTransition(State<S, E> target, Action<S, E> action) {
this.target = target;
ArrayList<Action<S,E>> list = new ArrayList<Action<S, E>>();
if (action != null) {
list.add(action);
}
this.actions = list;
this(target, action == null ? Collections.<Action<S,E>>emptyList(): Collections.singleton(action), null);
}
/**
* Instantiates a new initial transition.
*
* @param target the initial target state
* @param actions the initial actions
* @param target state
* @param actions to be executed to succeed for this transition
* @param errorAction to be executed if one of {@link #getActions()} throw an exception.
*/
public InitialTransition(State<S, E> target, Collection<Action<S, E>> actions) {
this.target = target;
this.actions = actions;
public InitialTransition(State<S, E> target, Collection<Action<S, E>> actions, Action<S, E> errorAction) {
super(target, actions, TransitionKind.INITIAL, errorAction);
}
@Override
public boolean transit(StateContext<S, E> context) {
if (actions != null) {
for (Action<S, E> action : actions) {
action.execute(context);
}
}
executeAllActions(context);
return false;
}
@Override
public State<S, E> getSource() {
return null;
}
@Override
public State<S, E> getTarget() {
return target;
}
@Override
public Collection<Action<S, E>> getActions() {
return actions;
}
@Override
public Trigger<S, E> getTrigger() {
return null;
}
@Override
public TransitionKind getKind() {
return TransitionKind.INITIAL;
}
@Override
public SecurityRule getSecurityRule() {
// initial cannot have security
return null;
}
}

View File

@@ -15,14 +15,14 @@
*/
package org.springframework.statemachine.transition;
import java.util.Collection;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.trigger.Trigger;
import java.util.Collection;
/**
* {@code Transition} is something what a state machine associates with a state
* changes.
@@ -63,6 +63,14 @@ public interface Transition<S, E> {
*/
Collection<Action<S, E>> getActions();
/**
* Get The action that will be execute if one of {@link #getActions()} throw an exception.
* This action may be usefull if you want to notify another sub system from unexpected technical error.
*
* @return the error {@link Action}
*/
Action<S, E> getErrorAction();
/**
* Gets the transition trigger.
*

View File

@@ -15,10 +15,6 @@
*/
package org.springframework.statemachine.action;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -35,6 +31,10 @@ import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Tests for state machine actions.
*
@@ -54,13 +54,18 @@ public class ActionTests extends AbstractStateMachineTests {
TestCountAction testAction1 = ctx.getBean("testAction1", TestCountAction.class);
TestCountAction testAction2 = ctx.getBean("testAction2", TestCountAction.class);
TestCountAction testAction3 = ctx.getBean("testAction3", TestCountAction.class);
TestCountAction testAction3 = ctx.getBean("testAction3", TestCountAction.class);
TestCountAction testAction4 = ctx.getBean("testAction4", TestCountAction.class);
TestCountAction testErrorAction = ctx.getBean("testErrorAction", TestCountAction.class);
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E2).build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E4).build());
assertThat(testAction1.count, is(1));
assertThat(testAction2.count, is(1));
assertThat(testAction3.count, is(1));
assertThat(testAction3.count, is(1));
assertThat(testAction4.count, is(0));
assertThat(testErrorAction.count, is(1));
ctx.close();
}
@@ -95,8 +100,9 @@ public class ActionTests extends AbstractStateMachineTests {
.initial(TestStates.S1)
.state(TestStates.S1)
.state(TestStates.S2)
.state(TestStates.S3)
.state(TestStates.S4);
.state(TestStates.S3)
.state(TestStates.S4)
.state(TestStates.S10);
}
@Override
@@ -118,7 +124,13 @@ public class ActionTests extends AbstractStateMachineTests {
.source(TestStates.S3)
.target(TestStates.S4)
.event(TestEvents.E3)
.action(testAction3());
.action(testAction3())
.and()
.withExternal()
.source(TestStates.S4)
.target(TestStates.S10)
.event(TestEvents.E4)
.action(testAction4(), testErrorAction());
}
@Bean
@@ -131,8 +143,23 @@ public class ActionTests extends AbstractStateMachineTests {
return new TestCountAction();
}
@Bean
public TestCountAction testAction3() {
@Bean
public TestCountAction testAction3() {
return new TestCountAction();
}
@Bean
public TestCountAction testAction4() {
return new TestCountAction() {
@Override
public void execute(StateContext<TestStates, TestEvents> context) {
throw new RuntimeException("Fake Error");
}
};
}
@Bean
public TestCountAction testErrorAction() {
return new TestCountAction();
}

View File

@@ -15,16 +15,6 @@
*/
package org.springframework.statemachine.config.model;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.task.SyncTaskExecutor;
@@ -40,6 +30,12 @@ import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.transition.TransitionKind;
import java.util.*;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
public class StateMachineModelTests {
@Test
@@ -72,7 +68,7 @@ public class StateMachineModelTests {
Collection<TransitionData<String, String>> transitions = new ArrayList<>();
TransitionData<String, String> transitionData1 = new TransitionData<String, String>("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null);
TransitionData<String, String> transitionData1 = new TransitionData<String, String>("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null, null);
transitions.add(transitionData1);
Map<String, List<ChoiceData<String, String>>> choices = new HashMap<>();
Map<String, List<JunctionData<String, String>>> junctions = new HashMap<>();

View File

@@ -15,15 +15,6 @@
*/
package org.springframework.statemachine.support;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.junit.Test;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
@@ -36,6 +27,7 @@ import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.access.StateMachineAccessor;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.Actions;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.security.SecurityRule;
import org.springframework.statemachine.state.EnumState;
@@ -44,6 +36,11 @@ import org.springframework.statemachine.transition.Transition;
import org.springframework.statemachine.transition.TransitionKind;
import org.springframework.statemachine.trigger.Trigger;
import java.util.*;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class StateContextExpressionMethodsTests {
@Test
@@ -133,6 +130,10 @@ public class StateContextExpressionMethodsTests {
return null;
}
@Override
public Action<SpelStates, SpelEvents> getErrorAction() {
return Actions.emptyAction();
}
}
private static class MockStatemachine implements StateMachine<SpelStates, SpelEvents> {