From 0c3ea7fb2d9d50835e59b3ad809ae8edf9e9641d Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Wed, 23 Nov 2016 13:55:52 +0000 Subject: [PATCH] Add initial action to repository config - Now RepositoryState can have initial action if it is an initial state. - Implement all needed tests. - Fixes #281 --- .../statemachine/config/model/StateData.java | 18 ++++++++++++ .../data/jpa/JpaRepositoryState.java | 13 +++++++++ .../jpa/src/test/resources/data14.json | 27 +++++++++++++++++ .../data/mongodb/MongoDbRepositoryState.java | 13 +++++++++ .../mongodb/src/test/resources/data14.json | 27 +++++++++++++++++ .../data/redis/RedisRepositoryState.java | 13 +++++++++ .../redis/src/test/resources/data14.json | 27 +++++++++++++++++ .../statemachine/data/RepositoryState.java | 8 +++++ .../RepositoryStateMachineModelFactory.java | 14 ++++++++- .../data/AbstractRepositoryTests.java | 29 +++++++++++++++++++ 10 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 spring-statemachine-data/jpa/src/test/resources/data14.json create mode 100644 spring-statemachine-data/mongodb/src/test/resources/data14.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data14.json diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/StateData.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/StateData.java index cdac5fd2..ca9e5a7b 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/StateData.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/StateData.java @@ -109,6 +109,23 @@ public class StateData { */ public StateData(Object parent, Object region, S state, Collection deferred, Collection> entryActions, Collection> exitActions, boolean initial) { + this(parent, region, state, deferred, entryActions, exitActions, initial, null); + } + + /** + * Instantiates a new state data. + * + * @param parent the parent + * @param region the region + * @param state the state + * @param deferred the deferred + * @param entryActions the entry actions + * @param exitActions the exit actions + * @param initial the initial + * @param initialAction the initial action + */ + public StateData(Object parent, Object region, S state, Collection deferred, + Collection> entryActions, Collection> exitActions, boolean initial, Action initialAction) { this.state = state; this.deferred = deferred; this.entryActions = entryActions; @@ -116,6 +133,7 @@ public class StateData { this.parent = parent; this.region = region; this.initial = initial; + this.initialAction = initialAction; } /** diff --git a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java index 2047f5c3..96fa0537 100644 --- a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java @@ -26,6 +26,7 @@ import javax.persistence.Id; import javax.persistence.ManyToMany; import javax.persistence.OneToOne; +import org.springframework.statemachine.data.RepositoryAction; import org.springframework.statemachine.data.RepositoryState; import org.springframework.statemachine.state.PseudoStateKind; @@ -53,6 +54,9 @@ public class JpaRepositoryState extends RepositoryState { private PseudoStateKind kind; private String submachineId; + @OneToOne(fetch = FetchType.EAGER) + private JpaRepositoryAction initialAction; + @OneToOne(fetch = FetchType.EAGER) private JpaRepositoryState parentState; @@ -193,6 +197,15 @@ public class JpaRepositoryState extends RepositoryState { this.initial = initial; } + @Override + public RepositoryAction getInitialAction() { + return initialAction; + } + + public void setInitialAction(JpaRepositoryAction initialAction) { + this.initialAction = initialAction; + } + @Override public Set getStateActions() { return stateActions; diff --git a/spring-statemachine-data/jpa/src/test/resources/data14.json b/spring-statemachine-data/jpa/src/test/resources/data14.json new file mode 100644 index 00000000..517ea703 --- /dev/null +++ b/spring-statemachine-data/jpa/src/test/resources/data14.json @@ -0,0 +1,27 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryAction", + "spel": "extendedState.variables.put('foo', 0)" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState", + "initial": true, + "initialAction" : "1", + "state": "S1" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState", + "initial": false, + "state": "S2" + }, + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition", + "source": "2", + "target": "3", + "event": "E1", + "kind": "EXTERNAL" + } +] diff --git a/spring-statemachine-data/mongodb/src/main/java/org/springframework/statemachine/data/mongodb/MongoDbRepositoryState.java b/spring-statemachine-data/mongodb/src/main/java/org/springframework/statemachine/data/mongodb/MongoDbRepositoryState.java index f1385134..a267d5e3 100644 --- a/spring-statemachine-data/mongodb/src/main/java/org/springframework/statemachine/data/mongodb/MongoDbRepositoryState.java +++ b/spring-statemachine-data/mongodb/src/main/java/org/springframework/statemachine/data/mongodb/MongoDbRepositoryState.java @@ -20,6 +20,7 @@ import java.util.Set; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Reference; import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.statemachine.data.RepositoryAction; import org.springframework.statemachine.data.RepositoryState; import org.springframework.statemachine.state.PseudoStateKind; @@ -49,6 +50,9 @@ public class MongoDbRepositoryState extends RepositoryState { @Reference private MongoDbRepositoryState parentState; + @Reference + private MongoDbRepositoryAction initialAction; + @Reference private Set stateActions; @@ -184,6 +188,15 @@ public class MongoDbRepositoryState extends RepositoryState { this.initial = initial; } + @Override + public RepositoryAction getInitialAction() { + return initialAction; + } + + public void setInitialAction(MongoDbRepositoryAction initialAction) { + this.initialAction = initialAction; + } + @Override public Set getStateActions() { return stateActions; diff --git a/spring-statemachine-data/mongodb/src/test/resources/data14.json b/spring-statemachine-data/mongodb/src/test/resources/data14.json new file mode 100644 index 00000000..1bded115 --- /dev/null +++ b/spring-statemachine-data/mongodb/src/test/resources/data14.json @@ -0,0 +1,27 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.mongodb.MongoDbRepositoryAction", + "spel": "extendedState.variables.put('foo', 0)" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.mongodb.MongoDbRepositoryState", + "initial": true, + "initialAction" : "1", + "state": "S1" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.mongodb.MongoDbRepositoryState", + "initial": false, + "state": "S2" + }, + { + "_class": "org.springframework.statemachine.data.mongodb.MongoDbRepositoryTransition", + "source": "2", + "target": "3", + "event": "E1", + "kind": "EXTERNAL" + } +] diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java index d7157006..1aefd9d6 100644 --- a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java @@ -29,6 +29,7 @@ import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Reference; import org.springframework.data.redis.core.RedisHash; import org.springframework.data.redis.core.index.Indexed; +import org.springframework.statemachine.data.RepositoryAction; import org.springframework.statemachine.data.RepositoryState; import org.springframework.statemachine.state.PseudoStateKind; @@ -59,6 +60,9 @@ public class RedisRepositoryState extends RepositoryState { @Reference private RedisRepositoryState parentState; + @Reference + private RedisRepositoryAction initialAction; + @Reference private Set stateActions; @@ -194,6 +198,15 @@ public class RedisRepositoryState extends RepositoryState { this.initial = initial; } + @Override + public RepositoryAction getInitialAction() { + return initialAction; + } + + public void setInitialAction(RedisRepositoryAction initialAction) { + this.initialAction = initialAction; + } + @Override public Set getStateActions() { return stateActions; diff --git a/spring-statemachine-data/redis/src/test/resources/data14.json b/spring-statemachine-data/redis/src/test/resources/data14.json new file mode 100644 index 00000000..a12d2b95 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data14.json @@ -0,0 +1,27 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryAction", + "spel": "extendedState.variables.put('foo', 0)" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "initialAction" : "1", + "state": "S1" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "event": "E1", + "kind": "EXTERNAL" + } +] diff --git a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java index f12752ac..e2117a04 100644 --- a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java @@ -62,6 +62,14 @@ public abstract class RepositoryState extends BaseRepositoryEntity { */ public abstract boolean isInitial(); + /** + * Gets the initial action. This is any meaningful if + * state is initial state. + * + * @return the initial action + */ + public abstract RepositoryAction getInitialAction(); + /** * Gets the state actions. * diff --git a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java index 0c7b0eee..6d90ba5a 100644 --- a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java @@ -153,6 +153,18 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode Object region = s.getRegion(); StateData stateData = new StateData(parentState != null ? parentState.getState() : null, region, s.getState(), s.isInitial()); + Action initialAction = null; + if (s.getInitialAction() != null) { + if (StringUtils.hasText(s.getInitialAction().getName())) { + initialAction = resolveAction(s.getInitialAction().getName()); + } else if (StringUtils.hasText(s.getInitialAction().getSpel())) { + SpelExpressionParser parser = new SpelExpressionParser( + new SpelParserConfiguration(SpelCompilerMode.MIXED, null)); + + initialAction = new SpelExpressionAction(parser.parseExpression(s.getInitialAction().getSpel())); + } + } + stateData.setInitialAction(initialAction); stateData.setStateActions(stateActions); stateData.setEntryActions(entryActions); stateData.setExitActions(exitActions); @@ -170,7 +182,7 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode Collection> submachineStateDataOrig = subStateMachineModel.getStatesData().getStateData(); for (StateData sd : submachineStateDataOrig) { submachineStateData.add(new StateData(s.getState(), sd.getRegion(), sd.getState(), - sd.getDeferred(), sd.getEntryActions(), sd.getExitActions(), sd.isInitial())); + sd.getDeferred(), sd.getEntryActions(), sd.getExitActions(), sd.isInitial(), sd.getInitialAction())); } stateData.setSubmachineStateData(submachineStateData); } diff --git a/spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java b/spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java index b24dc76d..96ad951d 100644 --- a/spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java +++ b/spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java @@ -412,6 +412,24 @@ public abstract class AbstractRepositoryTests { plan.test(); } + @SuppressWarnings("unchecked") + @Test + public void testMachine14() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config14.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").expectVariable("foo", 0).and() + .step().sendEvent("E1").expectStates("S2").and() + .build(); + plan.test(); + } + @Configuration public static class Config2 { @@ -576,6 +594,17 @@ public abstract class AbstractRepositoryTests { } } + @Configuration + public static class Config14 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data14.json")}); + return factoryBean; + } + } + @Configuration @EnableStateMachineFactory public static class FactoryConfig extends StateMachineConfigurerAdapter {