diff --git a/.gitignore b/.gitignore index 3411683f..418736c0 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ metastore_db .DS_Store /out/ target +classes diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java index c63aad28..36d54513 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Action.java @@ -34,5 +34,4 @@ public interface Action { * @param context the state context */ void execute(StateContext context); - } 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 new file mode 100644 index 00000000..3d25f11a --- /dev/null +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/action/Actions.java @@ -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 represents states Class + * @param represents event Class + * @return an empty (Noop) Action. + */ + public static Action emptyAction() { + return new Action() { + @Override + public void execute(final StateContext context) { + // Nothing to do; + } + }; + } +} 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 4068beb4..885fe327 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 @@ -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 extends LifecycleObjectS } DefaultExternalTransition transition = new DefaultExternalTransition(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 transition = new DefaultInternalTransition(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 extends LifecycleObjectS if (source != null && !source.isOrthogonal()) { State target = stateMap.get(entry.getKey()); DefaultExternalTransition transition = new DefaultExternalTransition( - source, target, null, null, null, null, null); + source, target, null, 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 8c05d31d..dc1f8d71 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 @@ -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 @Override protected TransitionsData performBuild() throws Exception { - return new TransitionsData(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 * @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) { + Guard guard, TransitionKind kind, SecurityRule securityRule, Action errorAction) { // 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)); + transitionData.add(new TransitionData<>(source, target, state, event, period, count, actions, guard, kind, securityRule, errorAction)); } /** 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 25365282..6fbdcb13 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 @@ -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 extends private E event; private Long period; private Integer count; - private final Collection> actions = new ArrayList>(); + private final Collection> actions = new ArrayList<>(); private Guard guard; private SecurityRule securityRule; + private Action errorAction; protected S getSource() { return source; @@ -68,6 +69,10 @@ public abstract class AbstractTransitionConfigurer extends return period; } + /** + * + * @return trigger count + */ public Integer getCount() { return count; } @@ -104,6 +109,10 @@ public abstract class AbstractTransitionConfigurer 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 extends 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 71d6d0bf..5969004d 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()); + getSecurityRule(), getErrorAction()); } @Override @@ -81,7 +81,13 @@ public class DefaultExternalTransitionConfigurer extends AbstractTransitio @Override public ExternalTransitionConfigurer action(Action action) { + return action(action, null); + } + + @Override + public ExternalTransitionConfigurer action(Action action, Action error) { addAction(action); + setErrorAction(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 22c8e7d3..888dd5ea 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()); + getSecurityRule(), getErrorAction()); } @Override @@ -75,7 +75,13 @@ public class DefaultInternalTransitionConfigurer extends AbstractTransitio @Override public InternalTransitionConfigurer action(Action action) { + return action(action, null); + } + + @Override + public InternalTransitionConfigurer action(Action action, Action error) { addAction(action); + setErrorAction(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 c3f2f3b0..2e7e6b3d 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()); + getSecurityRule(), getErrorAction()); } @Override @@ -80,7 +80,13 @@ public class DefaultLocalTransitionConfigurer extends AbstractTransitionCo @Override public LocalTransitionConfigurer action(Action action) { + return action(action, null); + } + + @Override + public LocalTransitionConfigurer action(Action action, Action error) { addAction(action); + setErrorAction(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 68c0e5c3..732404d8 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 @@ -83,6 +83,15 @@ public interface TransitionConfigurer extends */ T action(Action 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 action, Action error); + /** * Specify a {@link Guard} for this {@link Transition}. * 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 e6646fc7..4dd725af 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 @@ -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 { private final Guard guard; private final TransitionKind kind; private final SecurityRule securityRule; + private final Action errorAction; /** * Instantiates a new transition data. @@ -48,7 +49,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); + this(source, target, null, event, null, null, null, null, TransitionKind.EXTERNAL, null, null); } /** @@ -63,7 +64,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); + this(source, target, null, event, null, null, actions, guard, kind, null, null); } /** @@ -79,7 +80,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); + this(source, target, null, null, period, count, actions, guard, kind, null, null); } /** @@ -95,9 +96,10 @@ 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) { + Guard guard, TransitionKind kind, SecurityRule securityRule, Action errorAction) { this.source = source; this.target = target; this.state = state; @@ -108,6 +110,7 @@ public class TransitionData { this.guard = guard; this.kind = kind; this.securityRule = securityRule; + this.errorAction = errorAction; } /** @@ -199,4 +202,12 @@ 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 d7f01a81..41245004 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 @@ -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 extends AbstractTransition implements Transition { public AbstractExternalTransition(State source, State target, Collection> actions, - E event, Guard guard, Trigger trigger, SecurityRule securityRule) { - super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule); + E event, Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { + super(source, target, actions, event, TransitionKind.EXTERNAL, guard, trigger, securityRule, errorAction); } public AbstractExternalTransition(State source, State target, Collection> actions, 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 355a989c..3d8181d5 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 @@ -31,8 +31,8 @@ public class AbstractInternalTransition extends AbstractTransition i } public AbstractInternalTransition(State source, Collection> actions, E event, Guard guard, - Trigger trigger, SecurityRule securityRule) { - super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule); + Trigger trigger, SecurityRule securityRule, Action errorAction) { + super(source, source, actions, event, TransitionKind.INTERNAL, guard, trigger, securityRule, errorAction); } } 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 6082123a..c47bc0ba 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 @@ -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 extends AbstractTransition implements Transition { public AbstractLocalTransition(State source, State target, Collection> actions, E event, @@ -31,8 +31,8 @@ public class AbstractLocalTransition extends AbstractTransition impl } public AbstractLocalTransition(State source, State target, Collection> actions, E event, - Guard guard, Trigger trigger, SecurityRule securityRule) { - super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule); + Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { + super(source, target, actions, event, TransitionKind.LOCAL, guard, trigger, securityRule, errorAction); } } 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 517e90c9..57476fca 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,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 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 State target; - - private final Collection> actions; + private final State source; private final TransitionKind kind; @@ -53,39 +53,57 @@ public abstract class AbstractTransition implements Transition { 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); + 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()); } - public AbstractTransition(State source, State target, Collection> actions, E event, - TransitionKind kind, Guard guard, Trigger trigger, SecurityRule securityRule) { + 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"); 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. emptyAction() + : errorAction; 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() { + public State getSource() { return source; } - @Override - public State getTarget() { - return target; - } - - @Override - public Collection> getActions() { - return actions; - } - @Override public Trigger getTrigger() { return trigger; @@ -98,16 +116,14 @@ public abstract class AbstractTransition implements Transition { 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 action : actions) { - action.execute(context); - } - } + executeAllActions(context); + return true; } @@ -121,4 +137,44 @@ public abstract class AbstractTransition implements Transition { return securityRule; } + /** + * + * @return the target {@link State} + */ + public State getTarget() { + return target; + } + + /** + * + * @return all {@link Action} + */ + 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; + } + } + } } 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 71495a32..038914c7 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 @@ -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 extends AbstractExternalTransition { public DefaultExternalTransition(State source, State target, Collection> actions, E event, @@ -31,8 +31,8 @@ public class DefaultExternalTransition extends AbstractExternalTransition< } public DefaultExternalTransition(State source, State target, Collection> actions, E event, - Guard guard, Trigger trigger, SecurityRule securityRule) { - super(source, target, actions, event, guard, trigger, securityRule); + Guard guard, Trigger trigger, SecurityRule securityRule, Action errorAction) { + super(source, target, actions, event, guard, trigger, securityRule, errorAction); } } 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 5f9962dc..4bec8812 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 @@ -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 extends AbstractInternalTransition { public DefaultInternalTransition(State source, Collection> actions, E event, Guard guard, @@ -31,8 +31,8 @@ public class DefaultInternalTransition extends AbstractInternalTransition< } public DefaultInternalTransition(State source, Collection> actions, E event, Guard guard, - Trigger trigger, SecurityRule securityRule) { - super(source, actions, event, guard, trigger, securityRule); + Trigger trigger, SecurityRule securityRule, Action errorAction) { + super(source, actions, event, guard, trigger, securityRule, errorAction); } } 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 0fa72e9e..7f912ec7 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 @@ -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 the type of state * @param the type of event */ -public class InitialTransition implements Transition { - - private final State target; - - private final Collection> actions; +public class InitialTransition extends AbstractTransition + implements Transition { /** - * Instantiates a new initial transition. * - * @param target the initial target state + * @param target state. */ public InitialTransition(State 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 target, Action action) { - this.target = target; - ArrayList> list = new ArrayList>(); - if (action != null) { - list.add(action); - } - this.actions = list; + this(target, action == null ? Collections.>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 target, Collection> actions) { - this.target = target; - this.actions = actions; + public InitialTransition(State target, Collection> actions, Action errorAction) { + super(target, actions, TransitionKind.INITIAL, errorAction); } @Override public boolean transit(StateContext context) { - if (actions != null) { - for (Action action : actions) { - action.execute(context); - } - } + executeAllActions(context); return false; } - - @Override - public State getSource() { - return null; - } - - @Override - public State getTarget() { - return target; - } - - @Override - public Collection> getActions() { - return actions; - } - - @Override - public Trigger getTrigger() { - return null; - } - - @Override - public TransitionKind getKind() { - return TransitionKind.INITIAL; - } - - @Override - public SecurityRule getSecurityRule() { - // initial cannot have security - return null; - } - } 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 efacddb3..baaa24f6 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 @@ -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 { */ 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. * 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 00a86f28..243e6d40 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 @@ -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 context) { + throw new RuntimeException("Fake Error"); + } + }; + } + + @Bean + public TestCountAction testErrorAction() { return new TestCountAction(); } 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 080195b9..8ada99d6 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 @@ -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> transitions = new ArrayList<>(); - TransitionData transitionData1 = new TransitionData("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null); + TransitionData transitionData1 = new TransitionData("S1", "S2", null, "E1", null, null, null, null, TransitionKind.EXTERNAL, null, null); transitions.add(transitionData1); Map>> choices = new HashMap<>(); Map>> junctions = new HashMap<>(); 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 4ef4c4a6..c9385330 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,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 getErrorAction() { + return Actions.emptyAction(); + } } private static class MockStatemachine implements StateMachine {