From fd6fcb6ee20065aaef4c6aa0897c0050da39f146 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sat, 5 Nov 2016 10:04:20 +0000 Subject: [PATCH] Add redis repository config support - Change machine id as empty string instead of null if it's not set. - Implement domain classes for redis. - Shared tests. - Fixes #267 --- build.gradle | 13 + settings.gradle | 1 + spring-statemachine-data/build.gradle | 14 + .../data/jpa/JpaRepositoryState.java | 5 +- .../data/jpa/JpaRepositoryTransition.java | 2 +- .../data/redis/RedisActionRepository.java | 27 + .../data/redis/RedisGuardRepository.java | 27 + .../data/redis/RedisRepositoryAction.java | 63 ++ .../data/redis/RedisRepositoryGuard.java | 63 ++ .../data/redis/RedisRepositoryState.java | 249 +++++++ .../data/redis/RedisRepositoryTransition.java | 184 +++++ .../data/redis/RedisStateRepository.java | 27 + .../data/redis/RedisTransitionRepository.java | 28 + .../data/redis/RedisRepositoryTests.java | 124 ++++ .../statemachine/data/redis/RedisRule.java | 52 ++ .../redis/src/test/resources/data10.json | 71 ++ .../redis/src/test/resources/data11.json | 93 +++ .../redis/src/test/resources/data12.json | 60 ++ .../redis/src/test/resources/data13.json | 50 ++ .../redis/src/test/resources/data2.json | 46 ++ .../redis/src/test/resources/data3.json | 52 ++ .../redis/src/test/resources/data4.json | 64 ++ .../redis/src/test/resources/data5.json | 58 ++ .../redis/src/test/resources/data6.json | 84 +++ .../redis/src/test/resources/data7.json | 38 ++ .../redis/src/test/resources/data8.json | 84 +++ .../redis/src/test/resources/data9.json | 131 ++++ .../RepositoryStateMachineModelFactory.java | 5 +- .../data/AbstractRepositoryTests.java | 638 ++++++++++++++++++ 29 files changed, 2347 insertions(+), 6 deletions(-) create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisActionRepository.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisGuardRepository.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryAction.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryGuard.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryTransition.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisStateRepository.java create mode 100644 spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisTransitionRepository.java create mode 100644 spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRepositoryTests.java create mode 100644 spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRule.java create mode 100644 spring-statemachine-data/redis/src/test/resources/data10.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data11.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data12.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data13.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data2.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data3.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data4.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data5.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data6.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data7.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data8.json create mode 100644 spring-statemachine-data/redis/src/test/resources/data9.json create mode 100644 spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java diff --git a/build.gradle b/build.gradle index fec836e3..464a9b05 100644 --- a/build.gradle +++ b/build.gradle @@ -210,12 +210,25 @@ project('spring-statemachine-redis') { } project('spring-statemachine-data-common') { + configurations { + testArtifacts.extendsFrom testRuntime + } dependencies { compile project(":spring-statemachine-core") compile "org.springframework.data:spring-data-commons:$springDataCommonsVersion" optional "org.springframework.security:spring-security-core:$springSecurityVersion" compile "com.fasterxml.jackson.core:jackson-core:$jackson2Version" compile "com.fasterxml.jackson.core:jackson-databind:$jackson2Version" + testCompile project(":spring-statemachine-test") + testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" + testRuntime "org.springframework.boot:spring-boot-starter-web:$springBootVersion" + } + task testJar(type: Jar) { + classifier = 'tests' + from sourceSets.test.output + } + artifacts { + testArtifacts testJar } } diff --git a/settings.gradle b/settings.gradle index 04e11360..f5dfa326 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include 'spring-statemachine-samples:monitoring' include 'spring-statemachine-data' include 'spring-statemachine-data:jpa' +include 'spring-statemachine-data:redis' rootProject.children.find { if (it.name == 'spring-statemachine-recipes') { diff --git a/spring-statemachine-data/build.gradle b/spring-statemachine-data/build.gradle index 0df257a8..9b2797be 100644 --- a/spring-statemachine-data/build.gradle +++ b/spring-statemachine-data/build.gradle @@ -13,3 +13,17 @@ project('spring-statemachine-data-jpa') { testRuntime "org.springframework.boot:spring-boot-starter-web:$springBootVersion" } } + +project('spring-statemachine-data-redis') { + description = "Spring State Machine Data Redis" + dependencies { + compile project(":spring-statemachine-data-common") + compile "org.springframework.data:spring-data-redis:$springDataRedisVersion" + testCompile project(":spring-statemachine-test") + optional "org.eclipse.persistence:javax.persistence:$eclipsePersistenceVersion" + testCompile project(path:":spring-statemachine-data-common", configuration:"testArtifacts") + testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" + testRuntime "org.springframework.boot:spring-boot-starter-data-redis:$springBootVersion" + testRuntime "org.springframework.boot:spring-boot-starter-web:$springBootVersion" + } +} 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 bc39571d..8dc19e35 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 @@ -72,6 +72,7 @@ public class JpaRepositoryState extends RepositoryState { * Instantiates a new jpa repository state. */ public JpaRepositoryState() { + this(null); } /** @@ -80,7 +81,7 @@ public class JpaRepositoryState extends RepositoryState { * @param state the state */ public JpaRepositoryState(String state) { - this.state = state; + this(state, false); } /** @@ -129,7 +130,7 @@ public class JpaRepositoryState extends RepositoryState { */ public JpaRepositoryState(String machineId, JpaRepositoryState parentState, String state, boolean initial, Set stateActions, Set entryActions, Set exitActions) { - this.machineId = machineId; + this.machineId = machineId == null ? "" : machineId; this.parentState = parentState; this.state = state; this.initial = initial; diff --git a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java index 5d3a4655..9347cc58 100644 --- a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java @@ -102,7 +102,7 @@ public class JpaRepositoryTransition extends RepositoryTransition { * @param actions the actions */ public JpaRepositoryTransition(String machineId, JpaRepositoryState source, JpaRepositoryState target, String event, Set actions) { - this.machineId = machineId; + this.machineId = machineId == null ? "" : machineId; this.source = source; this.target = target; this.event = event; diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisActionRepository.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisActionRepository.java new file mode 100644 index 00000000..6004d936 --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisActionRepository.java @@ -0,0 +1,27 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.statemachine.data.ActionRepository; + +/** + * A {@link ActionRepository} interface for Redis used for actions. + * + * @author Janne Valkealahti + * + */ +public interface RedisActionRepository extends ActionRepository { +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisGuardRepository.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisGuardRepository.java new file mode 100644 index 00000000..01d244b9 --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisGuardRepository.java @@ -0,0 +1,27 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.statemachine.data.GuardRepository; + +/** + * A {@link GuardRepository} interface for Redis used for guards. + * + * @author Janne Valkealahti + * + */ +public interface RedisGuardRepository extends GuardRepository { +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryAction.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryAction.java new file mode 100644 index 00000000..82fe393c --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryAction.java @@ -0,0 +1,63 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.data.annotation.Id; + +//import javax.persistence.GeneratedValue; +//import javax.persistence.GenerationType; +//import javax.persistence.Id; + +import org.springframework.data.redis.core.RedisHash; +import org.springframework.statemachine.data.RepositoryAction; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +/** + * Redis entity for actions. + * + * @author Janne Valkealahti + * + */ +@RedisHash("RedisRepositoryAction") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) +public class RedisRepositoryAction extends RepositoryAction { + + @Id + private String id; + + private String name; + private String spel; + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getSpel() { + return spel; + } + + public void setSpel(String spel) { + this.spel = spel; + } +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryGuard.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryGuard.java new file mode 100644 index 00000000..54ac6f9e --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryGuard.java @@ -0,0 +1,63 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.data.annotation.Id; + +//import javax.persistence.GeneratedValue; +//import javax.persistence.GenerationType; +//import javax.persistence.Id; + +import org.springframework.data.redis.core.RedisHash; +import org.springframework.statemachine.data.RepositoryGuard; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +/** + * Redis entity for actions. + * + * @author Janne Valkealahti + * + */ +@RedisHash("RedisRepositoryGuard") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) +public class RedisRepositoryGuard extends RepositoryGuard { + + @Id + String id; + + private String name; + private String spel; + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getSpel() { + return spel; + } + + public void setSpel(String spel) { + this.spel = spel; + } +} 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 new file mode 100644 index 00000000..4b4cebfd --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryState.java @@ -0,0 +1,249 @@ +/* + * 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. + * 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.data.redis; + +import java.util.Set; + +//import javax.persistence.ElementCollection; +//import javax.persistence.FetchType; +//import javax.persistence.GeneratedValue; +//import javax.persistence.GenerationType; +//import javax.persistence.Id; +//import javax.persistence.OneToMany; +//import javax.persistence.OneToOne; + +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.RepositoryState; +import org.springframework.statemachine.state.PseudoStateKind; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +/** + * Redis entity for states. + * + * @author Janne Valkealahti + * + */ +@RedisHash("RedisRepositoryState") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) +public class RedisRepositoryState extends RepositoryState { + + @Id + private String id; + + @Indexed + private String machineId = ""; + private String state; + private String region; + private boolean initial; + private PseudoStateKind kind; + private String submachineId; + + @Reference + private RedisRepositoryState parentState; + + @Reference + private Set stateActions; + + @Reference + private Set entryActions; + + @Reference + private Set exitActions; + + private Set deferredEvents; + + /** + * Instantiates a new redis repository state. + */ + public RedisRepositoryState() { + } + + /** + * Instantiates a new redis repository state. + * + * @param state the state + */ + public RedisRepositoryState(String state) { + this.state = state; + } + + /** + * Instantiates a new redis repository state. + * + * @param state the state + * @param initial the initial + */ + public RedisRepositoryState(String state, boolean initial) { + this(null, state, initial); + } + + /** + * Instantiates a new redis repository state. + * + * @param machineId the machine id + * @param state the state + * @param initial the initial + */ + public RedisRepositoryState(String machineId, String state, boolean initial) { + this(machineId, null, state, initial); + } + + /** + * Instantiates a new redis repository state. + * + * @param machineId the machine id + * @param parentState the parent state + * @param state the state + * @param initial the initial + */ + public RedisRepositoryState(String machineId, RedisRepositoryState parentState, String state, boolean initial) { + this(machineId, parentState, state, initial, null, null, null); + } + + /** + * Instantiates a new redis repository state. + * + * @param machineId the machine id + * @param parentState the parent state + * @param state the state + * @param initial the initial + * @param stateActions the state actions + * @param entryActions the entry actions + * @param exitActions the exit actions + */ + public RedisRepositoryState(String machineId, RedisRepositoryState parentState, String state, boolean initial, Set stateActions, + Set entryActions, Set exitActions) { + this.machineId = machineId; + this.parentState = parentState; + this.state = state; + this.initial = initial; + this.stateActions = stateActions; + this.entryActions = entryActions; + this.exitActions = exitActions; + } + + @Override + public String getMachineId() { + return machineId; + } + + public void setMachineId(String machineId) { + this.machineId = machineId; + } + + @Override + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + @Override + public RedisRepositoryState getParentState() { + return parentState; + } + + public void setParentState(RedisRepositoryState parentState) { + this.parentState = parentState; + } + + @Override + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + @Override + public PseudoStateKind getKind() { + return kind; + } + + public void setKind(PseudoStateKind kind) { + this.kind = kind; + } + + @Override + public boolean isInitial() { + return initial; + } + + public void setInitial(boolean initial) { + this.initial = initial; + } + + @Override + public Set getStateActions() { + return stateActions; + } + + public void setStateActions(Set stateActions) { + this.stateActions = stateActions; + } + + @Override + public Set getEntryActions() { + return entryActions; + } + + public void setEntryActions(Set entryActions) { + this.entryActions = entryActions; + } + + @Override + public Set getExitActions() { + return exitActions; + } + + public void setExitActions(Set exitActions) { + this.exitActions = exitActions; + } + + @Override + public Set getDeferredEvents() { + return deferredEvents; + } + + public void setDeferredEvents(Set deferredEvents) { + this.deferredEvents = deferredEvents; + } + + @Override + public String getSubmachineId() { + return submachineId; + } + + public void setSubmachineId(String submachineId) { + this.submachineId = submachineId; + } + + @Override + public String toString() { + return "RedisRepositoryState [id=" + id + ", machineId=" + machineId + ", state=" + state + ", region=" + region + + ", initial=" + initial + ", kind=" + kind + ", submachineId=" + submachineId + ", parentState=" + + parentState + ", stateActions=" + stateActions + ", entryActions=" + entryActions + ", exitActions=" + + exitActions + ", deferredEvents=" + deferredEvents + "]"; + } +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryTransition.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryTransition.java new file mode 100644 index 00000000..7474ab29 --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisRepositoryTransition.java @@ -0,0 +1,184 @@ +/* + * 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. + * 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.data.redis; + +import java.util.Set; + +import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Reference; + +//import javax.persistence.FetchType; +//import javax.persistence.GeneratedValue; +//import javax.persistence.GenerationType; +//import javax.persistence.Id; +//import javax.persistence.OneToMany; +//import javax.persistence.OneToOne; + +import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.index.Indexed; +import org.springframework.statemachine.data.RepositoryTransition; +import org.springframework.statemachine.transition.TransitionKind; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +/** + * Redis entity for transitions. + * + * @author Janne Valkealahti + * + */ +@RedisHash("RedisRepositoryTransition") +@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class) +public class RedisRepositoryTransition extends RepositoryTransition { + + @Id + private String id; + + @Indexed + private String machineId = ""; + + @Reference + private RedisRepositoryState source; + + @Reference + private RedisRepositoryState target; + + private String event; + private TransitionKind kind; + + @Reference + private Set actions; + + @Reference + private RedisRepositoryGuard guard; + + /** + * Instantiates a new redis repository transition. + */ + public RedisRepositoryTransition() { + this(null, null, null); + } + + /** + * Instantiates a new redis repository transition. + * + * @param source the source + * @param target the target + * @param event the event + */ + public RedisRepositoryTransition(RedisRepositoryState source, RedisRepositoryState target, String event) { + this(null, source, target, event); + } + + /** + * Instantiates a new redis repository transition. + * + * @param machineId the machine id + * @param source the source + * @param target the target + * @param event the event + */ + public RedisRepositoryTransition(String machineId, RedisRepositoryState source, RedisRepositoryState target, String event) { + this(machineId, source, target, event, null); + } + + /** + * Instantiates a new redis repository transition. + * + * @param machineId the machine id + * @param source the source + * @param target the target + * @param event the event + * @param actions the actions + */ + public RedisRepositoryTransition(String machineId, RedisRepositoryState source, RedisRepositoryState target, String event, Set actions) { + this.machineId = machineId == null ? "" : machineId; + this.source = source; + this.target = target; + this.event = event; + this.actions = actions; + } + + @Override + public String getMachineId() { + return machineId; + } + + public void setMachineId(String machineId) { + this.machineId = machineId; + } + + @Override + public RedisRepositoryState getSource() { + return source; + } + + public void setSource(RedisRepositoryState source) { + this.source = source; + } + + @Override + public RedisRepositoryState getTarget() { + return target; + } + + public void setTarget(RedisRepositoryState target) { + this.target = target; + } + + @Override + public String getEvent() { + return event; + } + + public void setEvent(String event) { + this.event = event; + } + + @Override + public Set getActions() { + return actions; + } + + public void setActions(Set actions) { + this.actions = actions; + } + + @Override + public RedisRepositoryGuard getGuard() { + return guard; + } + + public void setGuard(RedisRepositoryGuard guard) { + this.guard = guard; + } + + @Override + public TransitionKind getKind() { + return kind; + } + + public void setKind(TransitionKind kind) { + this.kind = kind; + } + + @Override + public String toString() { + return "RedisRepositoryTransition [id=" + id + ", machineId=" + machineId + ", source=" + source + ", target=" + target + ", event=" + + event + ", kind=" + kind + ", actions=" + actions + ", guard=" + guard + "]"; + } +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisStateRepository.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisStateRepository.java new file mode 100644 index 00000000..421ed103 --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisStateRepository.java @@ -0,0 +1,27 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.statemachine.data.StateRepository; + +/** + * A {@link StateRepository} interface for Redis used for states. + * + * @author Janne Valkealahti + * + */ +public interface RedisStateRepository extends StateRepository { +} diff --git a/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisTransitionRepository.java b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisTransitionRepository.java new file mode 100644 index 00000000..b61eb266 --- /dev/null +++ b/spring-statemachine-data/redis/src/main/java/org/springframework/statemachine/data/redis/RedisTransitionRepository.java @@ -0,0 +1,28 @@ +/* + * 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. + * 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.data.redis; + +import org.springframework.statemachine.data.StateRepository; +import org.springframework.statemachine.data.TransitionRepository; + +/** + * A {@link StateRepository} interface for Redis used for transitions. + * + * @author Janne Valkealahti + * + */ +public interface RedisTransitionRepository extends TransitionRepository { +} diff --git a/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRepositoryTests.java b/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRepositoryTests.java new file mode 100644 index 00000000..46adb7fb --- /dev/null +++ b/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRepositoryTests.java @@ -0,0 +1,124 @@ +/* + * 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. + * 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.data.redis; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Rule; +import org.junit.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.data.keyvalue.core.KeyValueTemplate; +import org.springframework.statemachine.data.AbstractRepositoryTests; +import org.springframework.statemachine.transition.TransitionKind; + +/** + * Redis repository config tests. + * + * @author Janne Valkealahti + */ +public class RedisRepositoryTests extends AbstractRepositoryTests { + + @Rule + public RedisRule redisAvailableRule = new RedisRule(); + + @Override + protected void cleanInternal() { + AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext(); + c.register(TestConfig.class); + c.refresh(); + KeyValueTemplate kvTemplate = c.getBean(KeyValueTemplate.class); + kvTemplate.delete(RedisRepositoryAction.class); + kvTemplate.delete(RedisRepositoryGuard.class); + kvTemplate.delete(RedisRepositoryState.class); + kvTemplate.delete(RedisRepositoryTransition.class); + c.close(); + } + + @Test + public void testPopulate1() { + context.register(getRegisteredClasses()); + context.register(Config2.class); + context.refresh(); + + RedisStateRepository stateRepository = context.getBean(RedisStateRepository.class); + RedisTransitionRepository transitionRepository = context.getBean(RedisTransitionRepository.class); + assertThat(stateRepository.count(), is(3l)); + assertThat(transitionRepository.count(), is(3l)); + + List states = new ArrayList<>(); + stateRepository.findAll().iterator().forEachRemaining(states::add); + List transitions = new ArrayList<>(); + transitionRepository.findAll().iterator().forEachRemaining(transitions::add); + assertThat(states.size(), is(3)); + assertThat(transitions.size(), is(3)); + RedisRepositoryTransition transition1 = transitions.get(0); + assertThat(transition1.getSource(), notNullValue()); + assertThat(transition1.getTarget(), notNullValue()); + } + + @Test + public void testRepository1() { + context.register(getRegisteredClasses()); + context.refresh(); + + RedisStateRepository statesRepository = context.getBean(RedisStateRepository.class); + RedisRepositoryState stateS1 = new RedisRepositoryState("S1"); + RedisRepositoryState stateS2 = new RedisRepositoryState("S2"); + assertThat(statesRepository.count(), is(0l)); + + statesRepository.save(stateS1); + statesRepository.save(stateS2); + assertThat(statesRepository.count(), is(2l)); + + RedisTransitionRepository transitionsRepository = context.getBean(RedisTransitionRepository.class); + RedisRepositoryTransition transition = new RedisRepositoryTransition(stateS1, stateS2, "E1"); + transition.setKind(TransitionKind.EXTERNAL); + transitionsRepository.save(transition); + + assertThat(statesRepository.count(), is(2l)); + + RedisRepositoryTransition transition2 = transitionsRepository.findAll().iterator().next(); + assertThat(transition2.getSource().getState(), is("S1")); + assertThat(transition2.getTarget().getState(), is("S2")); + assertThat(transition2.getEvent(), is("E1")); + assertThat(transition2.getKind(), is(TransitionKind.EXTERNAL)); + + List findByMachineId = statesRepository.findByMachineId(""); + assertThat(findByMachineId.size(), is(2)); + + context.close(); + } + + @Override + protected Class[] getRegisteredClasses() { + return new Class[] { TestConfig.class }; + } + + @Override + protected AnnotationConfigApplicationContext buildContext() { + return new AnnotationConfigApplicationContext(); + } + + @EnableAutoConfiguration + static class TestConfig { + } +} diff --git a/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRule.java b/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRule.java new file mode 100644 index 00000000..ff575a74 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/java/org/springframework/statemachine/data/redis/RedisRule.java @@ -0,0 +1,52 @@ +/* + * 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. + * 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.data.redis; + +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; + +/** + * Rule skipping tests if redis is not available from localhost with default settings. + * + * @author Janne Valkealahti + * + */ +public class RedisRule extends TestWatcher implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + JedisConnectionFactory connectionFactory = null; + try { + connectionFactory = new JedisConnectionFactory(); + connectionFactory.afterPropertiesSet(); + connectionFactory.getConnection().close(); + } catch (Exception e) { + return super.apply(new Statement() { + @Override + public void evaluate() throws Throwable { + } + }, Description.EMPTY); + } finally { + if (connectionFactory != null) { + connectionFactory.destroy(); + } + } + return super.apply(base, description); + } +} diff --git a/spring-statemachine-data/redis/src/test/resources/data10.json b/spring-statemachine-data/redis/src/test/resources/data10.json new file mode 100644 index 00000000..a775a5af --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data10.json @@ -0,0 +1,71 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "SI" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S1", + "kind": "FORK" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "SF", + "kind": "END" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "region": "r2", + "parentState": "3", + "state": "S20" + }, + { + "@id": "6", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r2", + "parentState": "3", + "state": "S21" + }, + { + "@id": "7", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "region": "r3", + "parentState": "3", + "state": "S30" + }, + { + "@id": "8", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r3", + "parentState": "3", + "state": "S31" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "6" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "8" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data11.json b/spring-statemachine-data/redis/src/test/resources/data11.json new file mode 100644 index 00000000..5e3142f5 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data11.json @@ -0,0 +1,93 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "SI" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S3", + "kind": "JOIN" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S4" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "region": "r2", + "parentState": "2", + "state": "S20" + }, + { + "@id": "6", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r2", + "parentState": "2", + "state": "S21" + }, + { + "@id": "7", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "region": "r3", + "parentState": "2", + "state": "S30" + }, + { + "@id": "8", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r3", + "parentState": "2", + "state": "S31" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "5", + "target": "6", + "event": "E2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "7", + "target": "8", + "event": "E3" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "7", + "target": "3" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "8", + "target": "3" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "3", + "target": "4" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "1", + "event": "E4" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data12.json b/spring-statemachine-data/redis/src/test/resources/data12.json new file mode 100644 index 00000000..afe671cc --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data12.json @@ -0,0 +1,60 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "READY" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S1" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S3", + "deferredEvents": ["E1", "E2"] + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "3", + "event": "E2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "4", + "event": "E3" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "2", + "event": "E4" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "3", + "event": "E5" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "1", + "event": "E6" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data13.json b/spring-statemachine-data/redis/src/test/resources/data13.json new file mode 100644 index 00000000..8c75b096 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data13.json @@ -0,0 +1,50 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "submachineId": "machineS2", + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S3" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "machineId": "machineS2", + "initial": true, + "state": "S21" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "machineId": "machineS2", + "state": "S22" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "event": "E2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "5", + "event": "E3" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data2.json b/spring-statemachine-data/redis/src/test/resources/data2.json new file mode 100644 index 00000000..8d89120a --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data2.json @@ -0,0 +1,46 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S3" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "spel": "true" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "event": "E2", + "guard": "4" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "event": "E3", + "kind": "LOCAL" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data3.json b/spring-statemachine-data/redis/src/test/resources/data3.json new file mode 100644 index 00000000..c2b13d7f --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data3.json @@ -0,0 +1,52 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "parentState": "2", + "state": "S20" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "parentState": "2", + "state": "S21" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "3", + "target": "4", + "event": "E2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "1", + "event": "E3" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "4", + "event": "E4" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data4.json b/spring-statemachine-data/redis/src/test/resources/data4.json new file mode 100644 index 00000000..9dc87f80 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data4.json @@ -0,0 +1,64 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "parentState": "2", + "state": "S20" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "parentState": "2", + "state": "S21" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "parentState": "2", + "state": "SH", + "kind": "HISTORY_SHALLOW" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "3", + "target": "4", + "event": "E2", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "1", + "event": "E3", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "5", + "event": "E4", + "kind": "EXTERNAL" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data5.json b/spring-statemachine-data/redis/src/test/resources/data5.json new file mode 100644 index 00000000..e082e9bf --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data5.json @@ -0,0 +1,58 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r1", + "initial": true, + "state": "S10" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r1", + "initial": false, + "state": "S11" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r2", + "initial": true, + "state": "S20" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "region": "r2", + "initial": false, + "state": "S21" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "3", + "target": "4", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "1", + "event": "E2", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "4", + "target": "3", + "event": "E3", + "kind": "EXTERNAL" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data6.json b/spring-statemachine-data/redis/src/test/resources/data6.json new file mode 100644 index 00000000..8e7a28f7 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data6.json @@ -0,0 +1,84 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "SI" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S3", + "kind": "CHOICE" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S30" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S31" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S32" + }, + { + "@id": "6", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S33" + }, + { + "@id": "7", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s30Guard" + }, + { + "@id": "8", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s31Guard" + }, + { + "@id": "9", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s32Guard" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "guard": "7" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "4", + "guard": "8" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "5", + "guard": "9" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "6" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data7.json b/spring-statemachine-data/redis/src/test/resources/data7.json new file mode 100644 index 00000000..78e47335 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data7.json @@ -0,0 +1,38 @@ +[ + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "spel": "true" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryAction", + "spel": "true" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryAction", + "spel": "false" + }, + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL", + "guard": "3", + "actions": ["4", "5"] + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data8.json b/spring-statemachine-data/redis/src/test/resources/data8.json new file mode 100644 index 00000000..38016127 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data8.json @@ -0,0 +1,84 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "SI" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S3", + "kind": "JUNCTION" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S30" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S31" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S32" + }, + { + "@id": "6", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": false, + "state": "S33" + }, + { + "@id": "7", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s30Guard" + }, + { + "@id": "8", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s31Guard" + }, + { + "@id": "9", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryGuard", + "name": "s32Guard" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1", + "kind": "EXTERNAL" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "guard": "7" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "4", + "guard": "8" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "5", + "guard": "9" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "6" + } +] diff --git a/spring-statemachine-data/redis/src/test/resources/data9.json b/spring-statemachine-data/redis/src/test/resources/data9.json new file mode 100644 index 00000000..dcf15c56 --- /dev/null +++ b/spring-statemachine-data/redis/src/test/resources/data9.json @@ -0,0 +1,131 @@ +[ + { + "@id": "1", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S1" + }, + { + "@id": "2", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2" + }, + { + "@id": "3", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S3" + }, + { + "@id": "4", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S4" + }, + { + "@id": "5", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S5" + }, + { + "@id": "6", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "initial": true, + "state": "S21", + "parentState": "2" + }, + { + "@id": "7", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S22", + "parentState": "2" + }, + { + "@id": "8", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S23", + "parentState": "2" + }, + { + "@id": "9", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2ENTRY1", + "parentState": "2", + "kind": "ENTRY" + }, + { + "@id": "10", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2ENTRY2", + "parentState": "2", + "kind": "ENTRY" + }, + { + "@id": "11", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2EXIT1", + "parentState": "2", + "kind": "EXIT" + }, + { + "@id": "12", + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryState", + "state": "S2EXIT2", + "parentState": "2", + "kind": "EXIT" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "2", + "target": "3", + "event": "E2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "9", + "event": "ENTRY1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "1", + "target": "10", + "event": "ENTRY2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "7", + "target": "11", + "event": "EXIT1" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "7", + "target": "12", + "event": "EXIT2" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "9", + "target": "7" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "10", + "target": "9" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "11", + "target": "4" + }, + { + "_class": "org.springframework.statemachine.data.redis.RedisRepositoryTransition", + "source": "12", + "target": "5" + } +] 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 f5570496..0c7b0eee 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 @@ -83,7 +83,7 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode ConfigurationData configurationData = new ConfigurationData<>(); Collection> stateDatas = new ArrayList<>(); - for (RepositoryState s : stateRepository.findByMachineId(machineId)) { + for (RepositoryState s : stateRepository.findByMachineId(machineId == null ? "" : machineId)) { // do recursive build to get states for a submachine StateMachineModel subStateMachineModel = null; @@ -187,8 +187,7 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode Map> forks = new HashMap>(); Map> joins = new HashMap>(); - - for (RepositoryTransition t : transitionRepository.findByMachineId(machineId)) { + for (RepositoryTransition t : transitionRepository.findByMachineId(machineId == null ? "" : machineId)) { Collection> actions = new ArrayList>(); Set repositoryActions = t.getActions(); 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 new file mode 100644 index 00000000..b24dc76d --- /dev/null +++ b/spring-statemachine-data/src/test/java/org/springframework/statemachine/data/AbstractRepositoryTests.java @@ -0,0 +1,638 @@ +/* + * 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. + * 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.data; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +import java.util.Iterator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.statemachine.StateContext; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.EnableStateMachineFactory; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.StateMachineFactory; +import org.springframework.statemachine.config.builders.StateMachineModelConfigurer; +import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.data.support.StateMachineJackson2RepositoryPopulatorFactoryBean; +import org.springframework.statemachine.guard.Guard; +import org.springframework.statemachine.state.PseudoStateKind; +import org.springframework.statemachine.state.State; +import org.springframework.statemachine.test.StateMachineTestPlan; +import org.springframework.statemachine.test.StateMachineTestPlanBuilder; +import org.springframework.util.ObjectUtils; + +/** + * Base repository config tests. + * + * @author Janne Valkealahti + */ +public abstract class AbstractRepositoryTests { + + protected AnnotationConfigApplicationContext context; + + @Before + public void setup() { + cleanInternal(); + context = buildContext(); + } + + @After + public void clean() { + if (context != null) { + context.close(); + } + context = null; + } + + protected void cleanInternal() { + } + + protected Class[] getRegisteredClasses() { + return new Class[0]; + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine2() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config2.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").and() + .step().sendEvent("E1").expectStates("S2").and() + .step().sendEvent("E2").expectStates("S3").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine3() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config3.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").and() + .step().sendEvent("E1").expectStates("S2", "S20").and() + .step().sendEvent("E2").expectStates("S2", "S21").and() + .step().sendEvent("E3").expectStates("S1").and() + .step().sendEvent("E4").expectStates("S2", "S21").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine4() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config4.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").and() + .step().sendEvent("E1").expectStates("S2", "S20").and() + .step().sendEvent("E2").expectStates("S2", "S21").and() + .step().sendEvent("E3").expectStates("S1").and() + .step().sendEvent("E4").expectStates("S2", "S21").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine5() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config5.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S10", "S20").and() + .step().sendEvent("E1").expectStates("S11", "S21").and() + .step().sendEvent("E2").expectStates("S10", "S21").and() + .step().sendEvent("E3").expectStates("S10", "S20").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine6First() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config6.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s30").build()).expectStates("S30").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine6Then1() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config6.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s31").build()).expectStates("S31").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine6Then2() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config6.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s32").build()).expectStates("S32").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine6Last() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config6.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").build()).expectStates("S33").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine8First() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config8.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("junction", "s30").build()).expectStates("S30").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine8Then1() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config8.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("junction", "s31").build()).expectStates("S31").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine8Then2() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config8.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").setHeader("junction", "s32").build()).expectStates("S32").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine8Last() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config8.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent(MessageBuilder.withPayload("E1").build()).expectStates("S33").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine9() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config9.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").and() + .step().sendEvent("ENTRY1").expectStates("S2", "S22").and() + .step().sendEvent("EXIT1").expectStates("S4").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine10() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config10.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent("E1").expectStates("S2", "S21", "S31").and() + .build(); + plan.test(); + + State endState = null; + Iterator> iterator = stateMachine.getStates().iterator(); + while (iterator.hasNext()) { + State next = iterator.next(); + if (next.getId().equals("SF")) { + endState = next; + break; + } + } + assertThat(endState, notNullValue()); + assertThat(endState.getPseudoState(), notNullValue()); + assertThat(endState.getPseudoState().getKind(), is(PseudoStateKind.END)); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine11() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config11.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("SI").and() + .step().sendEvent("E1").expectStates("S2", "S20", "S30").and() + .step().sendEvent("E2").expectStates("S2", "S21", "S30").and() + .step().sendEvent("E3").expectStates("S4").and() + .step().sendEvent("E4").expectStates("SI").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine12() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config12.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("READY").and() + .step().sendEvent("E3").expectStates("S3").and() + .step().sendEvent("E1").expectStates("S3").and() + .step().sendEvent("E6").expectStates("S1").and() + .build(); + plan.test(); + } + + @SuppressWarnings("unchecked") + @Test + public void testMachine13() throws Exception { + context.register(getRegisteredClasses()); + context.register(Config13.class, FactoryConfig.class); + context.refresh(); + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStates("S1").and() + .step().sendEvent("E1").expectStates("S2", "S21").and() + .step().sendEvent("E3").expectStates("S2", "S22").and() + .step().sendEvent("E2").expectStates("S3").and() + .build(); + plan.test(); + } + + @Configuration + public static class Config2 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data2.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config3 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data3.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config4 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data4.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config5 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data5.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config6 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data6.json")}); + return factoryBean; + } + + @Bean + public Guard s30Guard() { + return new ChoiceGuard("s30"); + } + + @Bean + public Guard s31Guard() { + return new ChoiceGuard("s31"); + } + + @Bean + public Guard s32Guard() { + return new ChoiceGuard("s32"); + } + + } + + @Configuration + public static class Config7 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data7.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config8 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data8.json")}); + return factoryBean; + } + + @Bean + public Guard s30Guard() { + return new JunctionGuard("s30"); + } + + @Bean + public Guard s31Guard() { + return new JunctionGuard("s31"); + } + + @Bean + public Guard s32Guard() { + return new JunctionGuard("s32"); + } + + } + + @Configuration + public static class Config9 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data9.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config10 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data10.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config11 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data11.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config12 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data12.json")}); + return factoryBean; + } + } + + @Configuration + public static class Config13 { + + @Bean + public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data13.json")}); + return factoryBean; + } + } + + @Configuration + @EnableStateMachineFactory + public static class FactoryConfig extends StateMachineConfigurerAdapter { + + @Autowired + private StateRepository stateRepository; + + @Autowired + private TransitionRepository transitionRepository; + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + return new RepositoryStateMachineModelFactory(stateRepository, transitionRepository); + } + } + + private static class ChoiceGuard implements Guard { + + private final String match; + + public ChoiceGuard(String match) { + this.match = match; + } + + @Override + public boolean evaluate(StateContext context) { + return ObjectUtils.nullSafeEquals(match, context.getMessageHeaders().get("choice", String.class)); + } + } + + private static class JunctionGuard implements Guard { + + private final String match; + + public JunctionGuard(String match) { + this.match = match; + } + + @Override + public boolean evaluate(StateContext context) { + return ObjectUtils.nullSafeEquals(match, context.getMessageHeaders().get("junction", String.class)); + } + } + + /** + * Builds the context. + * + * @return the annotation config application context + */ + protected AnnotationConfigApplicationContext buildContext() { + return null; + } +}