diff --git a/docs/src/reference/asciidoc/sm.adoc b/docs/src/reference/asciidoc/sm.adoc index 73b55387..59c84665 100644 --- a/docs/src/reference/asciidoc/sm.adoc +++ b/docs/src/reference/asciidoc/sm.adoc @@ -160,6 +160,7 @@ expression based guard is a _SpelExpressionGuard_. This was attached to transition between states `S2` and `S3`. Both guard in above sample always evaluate to true. +[[statemachine-config-actions]] === Configuring Actions Actions can be defined to be executed with transitions and states itself. Action is always executed as a result of a transition which @@ -220,6 +221,27 @@ wrapped within a `Runnable` which may get cancelled via action, you need to be able to catch `InterruptedException` which is raised if task is cancelled. +[[statemachine-config-actions-errorhandling]] +==== Transition Action Error Handling + +User can always catch exceptions manually but with actions defined for +transitions it is possible to define error action which is called if +exception is reased. Exception is then available from a `StateContext` +passed to that action. + +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippetEC] +---- + +Similar logic can be done manually for every action if needed. + +[source,java,indent=0] +---- +include::samples/DocsConfigurationSampleTests.java[tags=snippetED] +---- + + === Configuring Pseudo States _Pseudo state_ configuration is usually done by configuring states and @@ -1583,6 +1605,12 @@ recognize only `StateMachineEvent` instances. include::samples/DocsConfigurationSampleTests.java[tags=snippet4] ---- +[TIP] +==== +Actions defined for transitions also have their own error handling +logic <>. +==== + [[sm-persist]] == Persisting State Machine Traditionally an instance of a state machine is used as is within a diff --git a/docs/src/reference/asciidoc/whatsnew.adoc b/docs/src/reference/asciidoc/whatsnew.adoc index 0a64f7cd..cadbde5c 100644 --- a/docs/src/reference/asciidoc/whatsnew.adoc +++ b/docs/src/reference/asciidoc/whatsnew.adoc @@ -30,5 +30,5 @@ _UML_ support and integrations with external config repositories. * New _Repository_ abstraction keeping machine configuration in an external repository <> * New support for state actions. <> -* New error action concepts. +* New transition error action concepts. <> diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Actions.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Actions.java index 3d25f11a..52e99c7a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Actions.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Actions.java @@ -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 represents states Class - * @param represents event Class + * @param the type of state + * @param the type of event * @return an empty (Noop) Action. */ public static Action emptyAction() { @@ -41,4 +46,35 @@ public final class Actions { } }; } + + /** + * Builds a erro calling action {@link Action}. + * + * @param the type of state + * @param the type of event + * @param action the action + * @param errorAction the error action + * @return the error calling action + */ + public static Action errorCallingAction(final Action action, final Action errorAction) { + return new Action() { + @Override + public void execute(final StateContext 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; + } + } + }; + } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java index bdbcea66..9881be18 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java @@ -735,7 +735,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } DefaultExternalTransition transition = new DefaultExternalTransition(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 extends LifecycleObjectS } DefaultLocalTransition transition = new DefaultLocalTransition(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 transition = new DefaultInternalTransition(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 extends LifecycleObjectS if (source != null && !source.isOrthogonal()) { State target = stateMap.get(entry.getKey()); DefaultExternalTransition transition = new DefaultExternalTransition( - source, target, null, null, null, null, null, null); + source, target, null, null, null, null, null); transitions.add(transition); } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java index dc1f8d71..2bddb623 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineTransitionBuilder.java @@ -174,17 +174,16 @@ public class StateMachineTransitionBuilder * @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> actions, - Guard guard, TransitionKind kind, SecurityRule securityRule, Action errorAction) { + Guard guard, TransitionKind kind, SecurityRule securityRule) { // if rule not given, get it from global if (securityRule == null) { @SuppressWarnings("unchecked") ConfigurationData 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)); } /** diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/AbstractTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/AbstractTransitionConfigurer.java index 6fbdcb13..311864d0 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/AbstractTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/AbstractTransitionConfigurer.java @@ -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 extends private final Collection> actions = new ArrayList<>(); private Guard guard; private SecurityRule securityRule; - private Action errorAction; protected S getSource() { return source; @@ -118,29 +118,17 @@ public abstract class AbstractTransitionConfigurer extends } protected void addAction(Action action) { - this.actions.add(action); + addAction(action, null); + } + + protected void addAction(Action action, Action error) { + this.actions.add(error != null ? Actions.errorCallingAction(action, error) : action); } protected void setGuard(Guard guard) { this.guard = guard; } - /** - * - * @return the Error {@link Action} - */ - public Action 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 errorAction) { - this.errorAction = errorAction; - } - protected void setSecurityRule(String attributes, ComparisonType match) { if (securityRule == null) { securityRule = new SecurityRule(); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java index 5969004d..761cb89b 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultExternalTransitionConfigurer.java @@ -39,7 +39,7 @@ public class DefaultExternalTransitionConfigurer extends AbstractTransitio @Override public void configure(StateMachineTransitionBuilder 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 extends AbstractTransitio @Override public ExternalTransitionConfigurer action(Action action, Action error) { - addAction(action); - setErrorAction(error); + addAction(action, error); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java index 888dd5ea..31d537f6 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultInternalTransitionConfigurer.java @@ -39,7 +39,7 @@ public class DefaultInternalTransitionConfigurer extends AbstractTransitio @Override public void configure(StateMachineTransitionBuilder 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 extends AbstractTransitio @Override public InternalTransitionConfigurer action(Action action, Action error) { - addAction(action); - setErrorAction(error); + addAction(action, error); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java index 2e7e6b3d..9c59f4b9 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/DefaultLocalTransitionConfigurer.java @@ -38,7 +38,7 @@ public class DefaultLocalTransitionConfigurer extends AbstractTransitionCo @Override public void configure(StateMachineTransitionBuilder 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 extends AbstractTransitionCo @Override public LocalTransitionConfigurer action(Action action, Action error) { - addAction(action); - setErrorAction(error); + addAction(action, error); return this; } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java index 732404d8..da8a0c0c 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configurers/TransitionConfigurer.java @@ -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 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 action, Action error); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java index 4dd725af..57ffe35d 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/TransitionData.java @@ -39,7 +39,6 @@ public class TransitionData { private final Guard guard; private final TransitionKind kind; private final SecurityRule securityRule; - private final Action errorAction; /** * Instantiates a new transition data. @@ -49,7 +48,7 @@ public class TransitionData { * @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 { */ public TransitionData(S source, S target, E event, Collection> actions, Guard 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 { */ public TransitionData(S source, S target, Long period, Integer count, Collection> actions, Guard 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 { * @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> actions, - Guard guard, TransitionKind kind, SecurityRule securityRule, Action errorAction) { + Guard guard, TransitionKind kind, SecurityRule securityRule) { this.source = source; this.target = target; this.state = state; @@ -110,7 +108,6 @@ public class TransitionData { this.guard = guard; this.kind = kind; this.securityRule = securityRule; - this.errorAction = errorAction; } /** @@ -202,12 +199,4 @@ public class TransitionData { public SecurityRule getSecurityRule() { return securityRule; } - - /** - * - * @return the error {@link Action} - */ - public Action getErrorAction() { - return errorAction; - } -} \ No newline at end of file +} diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java index 41245004..6167e763 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractExternalTransition.java @@ -25,14 +25,34 @@ import java.util.Collection; public abstract class AbstractExternalTransition extends AbstractTransition implements Transition { + /** + * 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 source, State target, Collection> actions, - E event, Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule, errorAction); + E event, Guard guard, Trigger 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 source, State target, Collection> actions, E event, Guard guard, Trigger trigger) { super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger); } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java index 3d8181d5..ed55c794 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractInternalTransition.java @@ -25,14 +25,32 @@ import org.springframework.statemachine.trigger.Trigger; public class AbstractInternalTransition extends AbstractTransition implements Transition { + /** + * 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 source, Collection> actions, E event, Guard guard, Trigger 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 source, Collection> actions, E event, Guard guard, - Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule, errorAction); + Trigger trigger, SecurityRule securityRule) { + super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule); } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java index c47bc0ba..a35a2434 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractLocalTransition.java @@ -25,14 +25,34 @@ import java.util.Collection; public class AbstractLocalTransition extends AbstractTransition implements Transition { + /** + * 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 source, State target, Collection> actions, E event, Guard guard, Trigger 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 source, State target, Collection> actions, E event, - Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule, errorAction); + Guard guard, Trigger trigger, SecurityRule securityRule) { + super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule); } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java index 57476fca..87c32838 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/AbstractTransition.java @@ -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 implements Transition { private final static Log log = LogFactory.getLog(AbstractTransition.class); protected final State target; protected final Collection> actions; - protected final Action errorAction; - private final State source; - private final TransitionKind kind; - private final Guard guard; - private final Trigger trigger; - private final SecurityRule securityRule; - public AbstractTransition(State source, - State target, - Collection> actions, - E event, - TransitionKind kind, - Guard guard, - Trigger trigger) { - this(source, target, actions, event, kind, guard, trigger, null, Actions.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 source, State target, Collection> actions, E event, TransitionKind kind, + Guard guard, Trigger trigger) { + this(source, target, actions, event, kind, guard, trigger, null); } - public AbstractTransition(State source, - State target, - Collection> actions, - E event, - TransitionKind kind, - Guard guard, - Trigger trigger, - SecurityRule securityRule, - Action 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 source, State target, Collection> actions, E event, TransitionKind kind, + Guard guard, Trigger 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. emptyAction() - : errorAction; this.kind = kind; + this.kind = kind; this.guard = guard; this.trigger = trigger; this.securityRule = securityRule; } - protected AbstractTransition(State target, Collection> actions, TransitionKind kind, Action errorAction) { - Assert.notNull(kind, "Transition type must be set"); - this.source = null; - this.target = target; - this.actions = actions; - this.errorAction = errorAction == null - ? Actions. emptyAction() - : errorAction; - this.kind = kind; - this.guard = null; - this.trigger = null; - this.securityRule = null; - } - @Override public State getSource() { return source; @@ -123,7 +110,6 @@ public abstract class AbstractTransition implements Transition { } } executeAllActions(context); - return true; } @@ -137,44 +123,23 @@ public abstract class AbstractTransition implements Transition { return securityRule; } - /** - * - * @return the target {@link State} - */ + @Override public State getTarget() { return target; } - /** - * - * @return all {@link Action} - */ + @Override public Collection> getActions() { return actions; } - /** - * - * @return the {@link Action} called of any error occurred while actions are executed. - */ - public Action getErrorAction() { - return errorAction; - } - protected final void executeAllActions(StateContext context) { if (actions == null) { return; } for (Action 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); } } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java index 038914c7..42f5352a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultExternalTransition.java @@ -25,14 +25,34 @@ import java.util.Collection; public class DefaultExternalTransition extends AbstractExternalTransition { + /** + * 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 source, State target, Collection> actions, E event, Guard guard, Trigger 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 source, State target, Collection> actions, E event, - Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, target, actions, event, guard, trigger, securityRule, errorAction); + Guard guard, Trigger trigger, SecurityRule securityRule) { + super(source, target, actions, event, guard, trigger, securityRule); } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java index 4bec8812..d5179318 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultInternalTransition.java @@ -25,14 +25,32 @@ import java.util.Collection; public class DefaultInternalTransition extends AbstractInternalTransition { + /** + * 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 source, Collection> actions, E event, Guard guard, Trigger 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 source, Collection> actions, E event, Guard guard, - Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, actions, event, guard, trigger, securityRule, errorAction); + Trigger trigger, SecurityRule securityRule) { + super(source, actions, event, guard, trigger, securityRule); } - } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java index ccb136e2..acf5ca2e 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/DefaultLocalTransition.java @@ -25,14 +25,35 @@ import org.springframework.statemachine.trigger.Trigger; public class DefaultLocalTransition extends AbstractLocalTransition { + /** + * 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 source, State target, Collection> actions, E event, Guard guard, Trigger 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 source, State target, Collection> actions, E event, - Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { - super(source, target, actions, event, guard, trigger, securityRule, errorAction); + Guard guard, Trigger trigger, SecurityRule securityRule) { + super(source, target, actions, event, guard, trigger, securityRule); } @Override diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/InitialTransition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/InitialTransition.java index 7f912ec7..3d2c122b 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/InitialTransition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/InitialTransition.java @@ -34,30 +34,32 @@ public class InitialTransition extends AbstractTransition implements Transition { /** + * Instantiates a new initial transition. * - * @param target state. + * @param target the target */ public InitialTransition(State 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 target, Action action) { - this(target, action == null ? Collections.>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 target, Collection> actions, Action errorAction) { - super(target, actions, TransitionKind.INITIAL, errorAction); + public InitialTransition(State target, Collection> actions) { + super(null, target, actions, null, TransitionKind.INITIAL, null, null, null); } @Override diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java index baaa24f6..5e48b68f 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/transition/Transition.java @@ -63,14 +63,6 @@ public interface Transition { */ Collection> 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 getErrorAction(); - /** * Gets the transition trigger. * @@ -91,5 +83,4 @@ public interface Transition { * @return the security rule */ SecurityRule getSecurityRule(); - } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java index 243e6d40..04e9bcf5 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/action/ActionTests.java @@ -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 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 { int count = 0; + StateContext context; public TestCountAction() { count = 0; @@ -84,6 +103,7 @@ public class ActionTests extends AbstractStateMachineTests { @Override public void execute(StateContext 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 context) { - throw new RuntimeException("Fake Error"); - } - }; - } + @Bean + public TaskExecutor taskExecutor() { + return new SyncTaskExecutor(); + } + } - @Bean + @Configuration + @EnableStateMachine + static class Config2 extends EnumStateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineStateConfigurer states) throws Exception { + states + .withStates() + .initial(TestStates.S1) + .state(TestStates.S1) + .state(TestStates.S2); + } + + @Override + public void configure(StateMachineTransitionConfigurer 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 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 { } } - } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelTests.java index 8ada99d6..16c6db23 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelTests.java @@ -68,7 +68,7 @@ public class StateMachineModelTests { Collection> transitions = new ArrayList<>(); - TransitionData transitionData1 = new TransitionData("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null, null); + TransitionData transitionData1 = new TransitionData("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null); transitions.add(transitionData1); Map>> choices = new HashMap<>(); Map>> junctions = new HashMap<>(); diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java index 9a239745..cd26a0c8 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/docs/DocsConfigurationSampleTests.java @@ -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 { + + @Override + public void configure(StateMachineTransitionConfigurer transitions) + throws Exception { + transitions + .withExternal() + .source(States.S1) + .target(States.S2) + .event(Events.E1) + .action(action(), errorAction()); + } + + @Bean + public Action action() { + return new Action() { + + @Override + public void execute(StateContext context) { + throw new RuntimeException("MyError"); + } + }; + } + + @Bean + public Action errorAction() { + return new Action() { + + @Override + public void execute(StateContext context) { + // RuntimeException("MyError") added to context + Exception exception = context.getException(); + exception.getMessage(); + } + }; + } + + } +// end::snippetEC[] + + @Configuration + @EnableStateMachine + public class Config54 + extends EnumStateMachineConfigurerAdapter { + +// tag::snippetED[] + @Override + public void configure(StateMachineTransitionConfigurer transitions) + throws Exception { + transitions + .withExternal() + .source(States.S1) + .target(States.S2) + .event(Events.E1) + .action(Actions.errorCallingAction(action(), errorAction())); + } +// end::snippetED[] + + @Bean + public Action action() { + return new Action() { + + @Override + public void execute(StateContext context) { + throw new RuntimeException("MyError"); + } + }; + } + + @Bean + public Action errorAction() { + return new Action() { + + @Override + public void execute(StateContext context) { + // RuntimeException("MyError") added to context + Exception exception = context.getException(); + exception.getMessage(); + } + }; + } + } + + // tag::snippetFA[] @Configuration @EnableStateMachineFactory diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java index c9385330..f155dc10 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/StateContextExpressionMethodsTests.java @@ -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 getErrorAction() { - return Actions.emptyAction(); - } } private static class MockStatemachine implements StateMachine {