Transition error action not used per defined action

- Revert some previous changes order to define transition
  error action logic in a different place. Now basically
  wrapping try/catch in a dedicated action order to have
  separate error action per action as is expected by a config
  SPI.
- Added some tests.
- Added docs for transition error actions.
- Relates to #238
- Fixes #259
This commit is contained in:
Janne Valkealahti
2016-09-27 09:21:30 +01:00
parent 8fc7592404
commit 0f3ffa1876
24 changed files with 452 additions and 211 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2016 the original author or authors.
* Copyright 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,9 +17,13 @@
package org.springframework.statemachine.action;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.support.DefaultStateContext;
/**
* Action Utilities.
*
* @author Janne Valkealahti
*
*/
public final class Actions {
@@ -28,9 +32,10 @@ public final class Actions {
}
/**
* Builds a noop {@link Action}.
*
* @param <S> represents states Class
* @param <E> represents event Class
* @param <S> the type of state
* @param <E> the type of event
* @return an empty (Noop) Action.
*/
public static <S, E> Action<S, E> emptyAction() {
@@ -41,4 +46,35 @@ public final class Actions {
}
};
}
/**
* Builds a erro calling action {@link Action}.
*
* @param <S> the type of state
* @param <E> the type of event
* @param action the action
* @param errorAction the error action
* @return the error calling action
*/
public static <S, E> Action<S, E> errorCallingAction(final Action<S, E> action, final Action<S, E> errorAction) {
return new Action<S, E>() {
@Override
public void execute(final StateContext<S, E> context) {
try {
action.execute(context);
}
catch (Exception exception) {
// notify something wrong is happening in actions execution.
try {
errorAction.execute(new DefaultStateContext<>(context.getStage(), context.getMessage(), context.getMessageHeaders(),
context.getExtendedState(), context.getTransition(), context.getStateMachine(), context.getSource(),
context.getTarget(), context.getSources(), context.getTargets(), exception));
} catch (Exception e) {
// not interested
}
throw exception;
}
}
};
}
}

View File

@@ -735,7 +735,7 @@ 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.getErrorAction());
transitionData.getSecurityRule());
transitions.add(transition);
} else if (transitionData.getKind() == TransitionKind.LOCAL) {
@@ -745,12 +745,12 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
}
DefaultLocalTransition<S, E> transition = new DefaultLocalTransition<S, E>(stateMap.get(source),
stateMap.get(target), transitionData.getActions(), event, transitionData.getGuard(), trigger,
transitionData.getSecurityRule(), transitionData.getErrorAction());
transitionData.getSecurityRule());
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.getErrorAction());
transitionData.getSecurityRule());
transitions.add(transition);
}
}
@@ -764,7 +764,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, null);
source, target, null, null, null, null, null);
transitions.add(transition);
}
}

View File

@@ -174,17 +174,16 @@ 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, Action<S, E> errorAction) {
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule) {
// 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<>(source, target, state, event, period, count, actions, guard, kind, securityRule, errorAction));
transitionData.add(new TransitionData<>(source, target, state, event, period, count, actions, guard, kind, securityRule));
}
/**

View File

@@ -16,6 +16,7 @@
package org.springframework.statemachine.config.configurers;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.Actions;
import org.springframework.statemachine.config.builders.StateMachineTransitionBuilder;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.config.common.annotation.AnnotationConfigurerAdapter;
@@ -47,7 +48,6 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
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;
@@ -118,29 +118,17 @@ public abstract class AbstractTransitionConfigurer<S, E> extends
}
protected void addAction(Action<S, E> action) {
this.actions.add(action);
addAction(action, null);
}
protected void addAction(Action<S, E> action, Action<S, E> error) {
this.actions.add(error != null ? Actions.errorCallingAction(action, error) : action);
}
protected void setGuard(Guard<S, E> guard) {
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(), getErrorAction());
getSecurityRule());
}
@Override
@@ -86,8 +86,7 @@ public class DefaultExternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public ExternalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
addAction(action, 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(), getErrorAction());
getSecurityRule());
}
@Override
@@ -80,8 +80,7 @@ public class DefaultInternalTransitionConfigurer<S, E> extends AbstractTransitio
@Override
public InternalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
addAction(action, 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(), getErrorAction());
getSecurityRule());
}
@Override
@@ -85,8 +85,7 @@ public class DefaultLocalTransitionConfigurer<S, E> extends AbstractTransitionCo
@Override
public LocalTransitionConfigurer<S, E> action(Action<S, E> action, Action<S, E> error) {
addAction(action);
setErrorAction(error);
addAction(action, error);
return this;
}

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.
@@ -87,7 +87,7 @@ public interface TransitionConfigurer<T, S, E> extends
* 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.
* @param error action 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);

View File

@@ -39,7 +39,6 @@ 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.
@@ -49,7 +48,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, null);
this(source, target, null, event, null, null, null, null, TransitionKind.EXTERNAL, null);
}
/**
@@ -64,7 +63,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, null);
this(source, target, null, event, null, null, actions, guard, kind, null);
}
/**
@@ -80,7 +79,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, null);
this(source, target, null, null, period, count, actions, guard, kind, null);
}
/**
@@ -96,10 +95,9 @@ 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, Action<S, E> errorAction) {
Guard<S, E> guard, TransitionKind kind, SecurityRule securityRule) {
this.source = source;
this.target = target;
this.state = state;
@@ -110,7 +108,6 @@ public class TransitionData<S, E> {
this.guard = guard;
this.kind = kind;
this.securityRule = securityRule;
this.errorAction = errorAction;
}
/**
@@ -202,12 +199,4 @@ public class TransitionData<S, E> {
public SecurityRule getSecurityRule() {
return securityRule;
}
/**
*
* @return the error {@link Action}
*/
public Action<S, E> getErrorAction() {
return errorAction;
}
}
}

View File

@@ -25,14 +25,34 @@ import java.util.Collection;
public abstract class AbstractExternalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
/**
* Instantiates a new abstract external transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
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, Action<S, E> errorAction) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule, errorAction);
E event, Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule);
}
/**
* Instantiates a new abstract external transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
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) {
super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger);
}
}

View File

@@ -25,14 +25,32 @@ import org.springframework.statemachine.trigger.Trigger;
public class AbstractInternalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
/**
* Instantiates a new abstract internal transition.
*
* @param source the source
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
public AbstractInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger);
}
/**
* Instantiates a new abstract internal transition.
*
* @param source the source
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
public AbstractInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule, errorAction);
Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule);
}
}

View File

@@ -25,14 +25,34 @@ import java.util.Collection;
public class AbstractLocalTransition<S, E> extends AbstractTransition<S, E> implements Transition<S, E> {
/**
* Instantiates a new abstract local transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
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) {
super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger);
}
/**
* Instantiates a new abstract local transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
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, Action<S, E> errorAction) {
super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule, errorAction);
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule);
}
}

View File

@@ -15,19 +15,18 @@
*/
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}.
*
@@ -41,64 +40,52 @@ 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 TransitionKind kind;
private final Guard<S, E> guard;
private final Trigger<S, E> trigger;
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, Actions.<S, E>emptyAction());
/**
* Instantiates a new abstract transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param kind the kind
* @param guard the guard
* @param trigger the trigger
*/
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,
SecurityRule securityRule,
Action<S, E> errorAction) {
Assert.notNull(source, "Source must be set");
/**
* Instantiates a new abstract transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param kind the kind
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
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) {
Assert.notNull(kind, "Transition type must be set");
this.source = source;
this.target = target;
this.actions = actions;
this.errorAction = errorAction == null
? Actions.<S, E> emptyAction()
: errorAction; this.kind = kind;
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() {
return source;
@@ -123,7 +110,6 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
}
}
executeAllActions(context);
return true;
}
@@ -137,44 +123,23 @@ public abstract class AbstractTransition<S, E> implements Transition<S, E> {
return securityRule;
}
/**
*
* @return the target {@link State}
*/
@Override
public State<S, E> getTarget() {
return target;
}
/**
*
* @return all {@link Action}
*/
@Override
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;
}
action.execute(context);
}
}
}

View File

@@ -25,14 +25,34 @@ import java.util.Collection;
public class DefaultExternalTransition<S, E> extends AbstractExternalTransition<S, E> {
/**
* Instantiates a new default external transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
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) {
super(source, target, actions, event, guard, trigger);
}
/**
* Instantiates a new default external transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
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, Action<S, E> errorAction) {
super(source, target, actions, event, guard, trigger, securityRule, errorAction);
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, guard, trigger, securityRule);
}
}

View File

@@ -25,14 +25,32 @@ import java.util.Collection;
public class DefaultInternalTransition<S, E> extends AbstractInternalTransition<S, E> {
/**
* Instantiates a new default internal transition.
*
* @param source the source
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
public DefaultInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger) {
super(source, actions, event, guard, trigger);
}
/**
* Instantiates a new default internal transition.
*
* @param source the source
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
public DefaultInternalTransition(State<S, E> source, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger, SecurityRule securityRule, Action<S, E> errorAction) {
super(source, actions, event, guard, trigger, securityRule, errorAction);
Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, actions, event, guard, trigger, securityRule);
}
}

View File

@@ -25,14 +25,35 @@ import org.springframework.statemachine.trigger.Trigger;
public class DefaultLocalTransition<S, E> extends AbstractLocalTransition<S, E> {
/**
* Instantiates a new default local transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
*/
public DefaultLocalTransition(State<S, E> source, State<S, E> target, Collection<Action<S, E>> actions, E event, Guard<S, E> guard,
Trigger<S, E> trigger) {
super(source, target, actions, event, guard, trigger);
}
/**
* Instantiates a new default local transition.
*
* @param source the source
* @param target the target
* @param actions the actions
* @param event the event
* @param guard the guard
* @param trigger the trigger
* @param securityRule the security rule
*/
public DefaultLocalTransition(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, Action<S, E> errorAction) {
super(source, target, actions, event, guard, trigger, securityRule, errorAction);
Guard<S, E> guard, Trigger<S, E> trigger, SecurityRule securityRule) {
super(source, target, actions, event, guard, trigger, securityRule);
}
@Override

View File

@@ -34,30 +34,32 @@ public class InitialTransition<S, E> extends AbstractTransition<S, E>
implements Transition<S, E> {
/**
* Instantiates a new initial transition.
*
* @param target state.
* @param target the target
*/
public InitialTransition(State<S, E> target) {
this(target, null);
super(null, target, null, null, TransitionKind.INITIAL, null, null, null);
}
/**
* Instantiates a new initial transition.
*
* @param target state
* @param action to be executed to succeed for this transition
* @param target the target
* @param action the action
*/
public InitialTransition(State<S, E> target, Action<S, E> action) {
this(target, action == null ? Collections.<Action<S,E>>emptyList(): Collections.singleton(action), null);
super(null, target, action != null ? Collections.singleton(action) : null, null, TransitionKind.INITIAL, null, null, null);
}
/**
* Instantiates a new initial transition.
*
* @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.
* @param target the target
* @param actions the actions
*/
public InitialTransition(State<S, E> target, Collection<Action<S, E>> actions, Action<S, E> errorAction) {
super(target, actions, TransitionKind.INITIAL, errorAction);
public InitialTransition(State<S, E> target, Collection<Action<S, E>> actions) {
super(null, target, actions, null, TransitionKind.INITIAL, null, null, null);
}
@Override

View File

@@ -63,14 +63,6 @@ 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.
*
@@ -91,5 +83,4 @@ public interface Transition<S, E> {
* @return the security rule
*/
SecurityRule getSecurityRule();
}

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,12 @@
*/
package org.springframework.statemachine.action;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.notNullValue;
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;
@@ -31,10 +37,6 @@ 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,18 +56,34 @@ 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 testAction4 = ctx.getBean("testAction4", TestCountAction.class);
TestCountAction testErrorAction = ctx.getBean("testErrorAction", TestCountAction.class);
TestCountAction testAction3 = ctx.getBean("testAction3", 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.E4).build());
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E3).build());
assertThat(testAction1.count, is(1));
assertThat(testAction2.count, is(1));
assertThat(testAction3.count, is(1));
assertThat(testAction4.count, is(0));
assertThat(testErrorAction.count, is(1));
assertThat(testAction3.count, is(1));
ctx.close();
}
@SuppressWarnings({ "unchecked" })
@Test
public void testTransitionActionErrors() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config2.class);
assertTrue(ctx.containsBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE));
StateMachine<TestStates,TestEvents> machine =
ctx.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
machine.start();
TestCountAction testAction1 = ctx.getBean("testAction1", TestCountAction.class);
TestCountAction testErrorAction = ctx.getBean("testErrorAction", TestCountAction.class);
machine.sendEvent(MessageBuilder.withPayload(TestEvents.E1).build());
assertThat(testAction1.count, is(1));
assertThat(testErrorAction.count, is(1));
assertThat(testErrorAction.context, notNullValue());
assertThat(testErrorAction.context.getException(), notNullValue());
assertThat(testErrorAction.context.getException(), instanceOf(RuntimeException.class));
assertThat(testErrorAction.context.getException().getMessage(), is("Fake Error"));
ctx.close();
}
@@ -77,6 +95,7 @@ public class ActionTests extends AbstractStateMachineTests {
private static class TestCountAction implements Action<TestStates, TestEvents> {
int count = 0;
StateContext<TestStates, TestEvents> context;
public TestCountAction() {
count = 0;
@@ -84,6 +103,7 @@ public class ActionTests extends AbstractStateMachineTests {
@Override
public void execute(StateContext<TestStates, TestEvents> context) {
this.context = context;
count++;
}
@@ -100,9 +120,8 @@ public class ActionTests extends AbstractStateMachineTests {
.initial(TestStates.S1)
.state(TestStates.S1)
.state(TestStates.S2)
.state(TestStates.S3)
.state(TestStates.S4)
.state(TestStates.S10);
.state(TestStates.S3)
.state(TestStates.S4);
}
@Override
@@ -124,13 +143,7 @@ public class ActionTests extends AbstractStateMachineTests {
.source(TestStates.S3)
.target(TestStates.S4)
.event(TestEvents.E3)
.action(testAction3())
.and()
.withExternal()
.source(TestStates.S4)
.target(TestStates.S10)
.event(TestEvents.E4)
.action(testAction4(), testErrorAction());
.action(testAction3());
}
@Bean
@@ -143,22 +156,52 @@ public class ActionTests extends AbstractStateMachineTests {
return new TestCountAction();
}
@Bean
public TestCountAction testAction3() {
return new TestCountAction();
}
@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 TaskExecutor taskExecutor() {
return new SyncTaskExecutor();
}
}
@Bean
@Configuration
@EnableStateMachine
static class Config2 extends EnumStateMachineConfigurerAdapter<TestStates, TestEvents> {
@Override
public void configure(StateMachineStateConfigurer<TestStates, TestEvents> states) throws Exception {
states
.withStates()
.initial(TestStates.S1)
.state(TestStates.S1)
.state(TestStates.S2);
}
@Override
public void configure(StateMachineTransitionConfigurer<TestStates, TestEvents> transitions) throws Exception {
transitions
.withExternal()
.source(TestStates.S1)
.target(TestStates.S2)
.event(TestEvents.E1)
.action(testAction1(), testErrorAction());
}
@Bean
public TestCountAction testAction1() {
return new TestCountAction() {
@Override
public void execute(StateContext<TestStates, TestEvents> context) {
super.execute(context);
throw new RuntimeException("Fake Error");
}
};
}
@Bean
public TestCountAction testErrorAction() {
return new TestCountAction();
}
@@ -169,5 +212,4 @@ public class ActionTests extends AbstractStateMachineTests {
}
}
}

View File

@@ -68,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, null);
TransitionData<String, String> transitionData1 = new TransitionData<String, String>("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null);
transitions.add(transitionData1);
Map<String, List<ChoiceData<String, String>>> choices = new HashMap<>();
Map<String, List<JunctionData<String, String>>> junctions = new HashMap<>();

View File

@@ -37,6 +37,7 @@ import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.access.StateMachineAccess;
import org.springframework.statemachine.access.StateMachineFunction;
import org.springframework.statemachine.action.Action;
import org.springframework.statemachine.action.Actions;
import org.springframework.statemachine.action.SpelExpressionAction;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
@@ -265,6 +266,94 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
}
// end::snippetEB[]
// tag::snippetEC[]
@Configuration
@EnableStateMachine
public class Config53
extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
}
@Bean
public Action<States, Events> action() {
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> context) {
throw new RuntimeException("MyError");
}
};
}
@Bean
public Action<States, Events> errorAction() {
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> context) {
// RuntimeException("MyError") added to context
Exception exception = context.getException();
exception.getMessage();
}
};
}
}
// end::snippetEC[]
@Configuration
@EnableStateMachine
public class Config54
extends EnumStateMachineConfigurerAdapter<States, Events> {
// tag::snippetED[]
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(Actions.errorCallingAction(action(), errorAction()));
}
// end::snippetED[]
@Bean
public Action<States, Events> action() {
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> context) {
throw new RuntimeException("MyError");
}
};
}
@Bean
public Action<States, Events> errorAction() {
return new Action<States, Events>() {
@Override
public void execute(StateContext<States, Events> context) {
// RuntimeException("MyError") added to context
Exception exception = context.getException();
exception.getMessage();
}
};
}
}
// tag::snippetFA[]
@Configuration
@EnableStateMachineFactory

View File

@@ -15,6 +15,15 @@
*/
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;
@@ -27,7 +36,6 @@ 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;
@@ -36,11 +44,6 @@ 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
@@ -129,11 +132,6 @@ public class StateContextExpressionMethodsTests {
public SecurityRule getSecurityRule() {
return null;
}
@Override
public Action<SpelStates, SpelEvents> getErrorAction() {
return Actions.emptyAction();
}
}
private static class MockStatemachine implements StateMachine<SpelStates, SpelEvents> {