From 0d329d9e57ba68222473d5fc1c68d47069cbdd7b Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Fri, 23 Sep 2016 17:26:39 +0100 Subject: [PATCH] Add initial infra for config repository - Initial infra for config abstraction with Spring Data Repositories via RepositoryStateMachineModelFactory. - Initial jpa impl. - Initial jpa sample atop of H2 DB. - Relates to #250 --- build.gradle | 8 ++ gradle.properties | 3 + settings.gradle | 10 ++ spring-statemachine-data/build.gradle | 11 ++ .../data/jpa/JpaRepositoryState.java | 82 +++++++++++++ .../data/jpa/JpaRepositoryTransition.java | 90 ++++++++++++++ .../data/jpa/JpaStateRepository.java | 27 +++++ .../data/jpa/JpaTransitionRepository.java | 28 +++++ .../data/jpa/JpaRepositoryTests.java | 114 ++++++++++++++++++ .../statemachine/data/RepositoryState.java | 33 +++++ .../RepositoryStateMachineModelFactory.java | 73 +++++++++++ .../data/RepositoryTransition.java | 37 ++++++ .../statemachine/data/StateRepository.java | 31 +++++ .../data/TransitionRepository.java | 31 +++++ spring-statemachine-samples/build.gradle | 11 ++ .../main/java/demo/datajpa/Application.java | 31 +++++ .../java/demo/datajpa/StateMachineConfig.java | 66 ++++++++++ .../demo/datajpa/StateMachineController.java | 80 ++++++++++++ .../demo/datajpa/StateMachineLogListener.java | 49 ++++++++ .../src/main/resources/application.yml | 6 + .../datajpa/src/main/resources/data.json | 29 +++++ .../src/main/resources/templates/states.html | 32 +++++ 22 files changed, 882 insertions(+) create mode 100644 spring-statemachine-data/build.gradle create mode 100644 spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java create mode 100644 spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java create mode 100644 spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaStateRepository.java create mode 100644 spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaTransitionRepository.java create mode 100644 spring-statemachine-data/jpa/src/test/java/org/springframework/statemachine/data/jpa/JpaRepositoryTests.java create mode 100644 spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java create mode 100644 spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java create mode 100644 spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryTransition.java create mode 100644 spring-statemachine-data/src/main/java/org/springframework/statemachine/data/StateRepository.java create mode 100644 spring-statemachine-data/src/main/java/org/springframework/statemachine/data/TransitionRepository.java create mode 100644 spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/Application.java create mode 100644 spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineConfig.java create mode 100644 spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineController.java create mode 100644 spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineLogListener.java create mode 100644 spring-statemachine-samples/datajpa/src/main/resources/application.yml create mode 100644 spring-statemachine-samples/datajpa/src/main/resources/data.json create mode 100644 spring-statemachine-samples/datajpa/src/main/resources/templates/states.html diff --git a/build.gradle b/build.gradle index 607c39b5..d72a936d 100644 --- a/build.gradle +++ b/build.gradle @@ -195,6 +195,14 @@ project('spring-statemachine-redis') { } } +project('spring-statemachine-data-common') { + dependencies { + compile project(":spring-statemachine-core") + compile "org.springframework.data:spring-data-commons:$springDataCommonsVersion" + optional "org.springframework.security:spring-security-core:$springSecurityVersion" + } +} + project('spring-statemachine-cluster') { description = "Spring State Machine Cluster" diff --git a/gradle.properties b/gradle.properties index 58221fa1..090ce587 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,8 @@ springShellVersion=1.1.0.RELEASE springSecurityVersion=4.0.3.RELEASE springDataRedisVersion=1.6.2.RELEASE +springDataCommonsVersion=1.12.2.RELEASE +springDataJpaVersion=1.10.2.RELEASE springCloudClusterVersion=1.0.0.RELEASE jedisVersion=2.7.3 junitVersion=4.12 @@ -17,6 +19,7 @@ tomcatEmbedVersion=8.0.30 servletApiVersion=3.0.1 commonsPool2Version=2.4.2 hsqlVersion=2.3.1 +h2Version=1.4.190 eclipseUml2UmlVersion=5.0.0-v20140602-0749 eclipseUml2TypesVersion=2.0.0-v20140602-0749 eclipseUml2CommonVersion=2.0.0-v20140602-0749 diff --git a/settings.gradle b/settings.gradle index fe030488..96555abb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -24,6 +24,10 @@ include 'spring-statemachine-samples:security' include 'spring-statemachine-samples:eventservice' include 'spring-statemachine-samples:deploy' include 'spring-statemachine-samples:ordershipping' +include 'spring-statemachine-samples:datajpa' + +include 'spring-statemachine-data' +include 'spring-statemachine-data:jpa' rootProject.children.find { if (it.name == 'spring-statemachine-recipes') { @@ -35,5 +39,11 @@ rootProject.children.find { it.name = 'spring-statemachine-samples-' + it.name } } + if (it.name == 'spring-statemachine-data') { + it.name = 'spring-statemachine-data-common' + it.children.each { + it.name = 'spring-statemachine-data-' + it.name + } + } } diff --git a/spring-statemachine-data/build.gradle b/spring-statemachine-data/build.gradle new file mode 100644 index 00000000..79af8a89 --- /dev/null +++ b/spring-statemachine-data/build.gradle @@ -0,0 +1,11 @@ +description = "Spring State Machine Data Common" + +project('spring-statemachine-data-jpa') { + description = "Spring State Machine Data Jpa" + dependencies { + compile project(":spring-statemachine-data-common") + optional "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" + testCompile "org.hsqldb:hsqldb:$hsqlVersion" + testCompile "org.springframework.boot:spring-boot-starter-test:$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 new file mode 100644 index 00000000..43bdb152 --- /dev/null +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryState.java @@ -0,0 +1,82 @@ +/* + * 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.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import org.springframework.statemachine.data.RepositoryState; + +/** + * JPA entity for states. + * + * @author Janne Valkealahti + * + */ +@Entity +public class JpaRepositoryState implements RepositoryState { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + + protected String state; + protected boolean initial; + + /** + * Instantiates a new jpa repository state. + */ + public JpaRepositoryState() { + } + + public JpaRepositoryState(String state) { + this.state = state; + } + + /** + * Instantiates a new jpa repository state. + * + * @param state the state + * @param initial the initial + */ + public JpaRepositoryState(String state, boolean initial) { + super(); + this.state = state; + this.initial = initial; + } + + @Override + public String getState() { + return state; + } + + @Override + public void setState(String state) { + this.state = state; + } + + @Override + public boolean isInitial() { + return initial; + } + + @Override + public void setInitial(boolean initial) { + 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 new file mode 100644 index 00000000..e9998147 --- /dev/null +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaRepositoryTransition.java @@ -0,0 +1,90 @@ +/* + * 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.jpa; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +import org.springframework.statemachine.data.RepositoryTransition; + +/** + * JPA entity for transitions. + * + * @author Janne Valkealahti + * + */ +@Entity +public class JpaRepositoryTransition implements RepositoryTransition { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private long id; + + private String source; + private String target; + private String event; + + /** + * Instantiates a new jpa repository transition. + */ + public JpaRepositoryTransition() { + } + + /** + * Instantiates a new jpa repository transition. + * + * @param source the source + * @param target the target + * @param event the event + */ + public JpaRepositoryTransition(String source, String target, String event) { + this.source = source; + this.target = target; + this.event = event; + } + + @Override + public String getSource() { + return source; + } + + @Override + public void setSource(String source) { + this.source = source; + } + + @Override + public String getTarget() { + return target; + } + + @Override + public void setTarget(String target) { + this.target = target; + } + + @Override + public String getEvent() { + return event; + } + + @Override + public void setEvent(String event) { + this.event = event; + } +} diff --git a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaStateRepository.java b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaStateRepository.java new file mode 100644 index 00000000..36e23b75 --- /dev/null +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaStateRepository.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.jpa; + +import org.springframework.statemachine.data.StateRepository; + +/** + * A {@link StateRepository} interface for JPA used for states. + * + * @author Janne Valkealahti + * + */ +public interface JpaStateRepository extends StateRepository { +} diff --git a/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaTransitionRepository.java b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaTransitionRepository.java new file mode 100644 index 00000000..3b9c0bf1 --- /dev/null +++ b/spring-statemachine-data/jpa/src/main/java/org/springframework/statemachine/data/jpa/JpaTransitionRepository.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.jpa; + +import org.springframework.statemachine.data.StateRepository; +import org.springframework.statemachine.data.TransitionRepository; + +/** + * A {@link StateRepository} interface for JPA used for transitions. + * + * @author Janne Valkealahti + * + */ +public interface JpaTransitionRepository extends TransitionRepository { +} diff --git a/spring-statemachine-data/jpa/src/test/java/org/springframework/statemachine/data/jpa/JpaRepositoryTests.java b/spring-statemachine-data/jpa/src/test/java/org/springframework/statemachine/data/jpa/JpaRepositoryTests.java new file mode 100644 index 00000000..a662e4b0 --- /dev/null +++ b/spring-statemachine-data/jpa/src/test/java/org/springframework/statemachine/data/jpa/JpaRepositoryTests.java @@ -0,0 +1,114 @@ +/* + * 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.jpa; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.data.RepositoryState; +import org.springframework.statemachine.data.StateRepository; +import org.springframework.statemachine.data.RepositoryTransition; +import org.springframework.statemachine.data.TransitionRepository; + +public class JpaRepositoryTests { + + @Test + public void testRepository1() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Config.class); + context.refresh(); + + JpaStateRepository statesRepository = context.getBean(JpaStateRepository.class); + JpaRepositoryState state = new JpaRepositoryState("S1"); + statesRepository.save(state); + Iterable findAll = statesRepository.findAll(); + assertThat(findAll.iterator().next().getState(), is("S1")); + + JpaTransitionRepository transitionsRepository = context.getBean(JpaTransitionRepository.class); + JpaRepositoryTransition transition = new JpaRepositoryTransition("S1", "S2", "E1"); + transitionsRepository.save(transition); + JpaRepositoryTransition transition2 = transitionsRepository.findAll().iterator().next(); + assertThat(transition2.getSource(), is("S1")); + assertThat(transition2.getTarget(), is("S2")); + assertThat(transition2.getEvent(), is("E1")); + + context.close(); + } + + @Test + public void testRepository2() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Config.class); + context.refresh(); + + @SuppressWarnings("unchecked") + StateRepository statesRepository1 = context.getBean(StateRepository.class); + JpaRepositoryState state = new JpaRepositoryState("S1"); + statesRepository1.save(state); + @SuppressWarnings("unchecked") + StateRepository statesRepository2 = context.getBean(StateRepository.class); + Iterable findAll = statesRepository2.findAll(); + assertThat(findAll.iterator().next().getState(), is("S1")); + + @SuppressWarnings("unchecked") + TransitionRepository transitionsRepository = context.getBean(TransitionRepository.class); + RepositoryTransition transition = new JpaRepositoryTransition("S1", "S2", "E1"); + transitionsRepository.save(transition); + RepositoryTransition transition2 = transitionsRepository.findAll().iterator().next(); + assertThat(transition2.getSource(), is("S1")); + assertThat(transition2.getTarget(), is("S2")); + assertThat(transition2.getEvent(), is("E1")); + + context.close(); + } + + @Test + public void testAutowire() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(Config.class, WireConfig.class); + context.refresh(); + context.close(); + } + + @EnableAutoConfiguration + static class Config { + } + + @Configuration + static class WireConfig { + + @Autowired + StateRepository statesRepository1; + + @Autowired + TransitionRepository statesRepository11; + + @SuppressWarnings("rawtypes") + @Autowired + StateRepository statesRepository2; + + @Autowired + JpaStateRepository statesRepository3; + + @Autowired + StateRepository statesRepository4; + } +} 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 new file mode 100644 index 00000000..8610f2f0 --- /dev/null +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryState.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * Generic interface representing state entity. + * + * @author Janne Valkealahti + * + */ +public interface RepositoryState { + + String getState(); + + void setState(String state); + + boolean isInitial(); + + void setInitial(boolean initial); +} 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 new file mode 100644 index 00000000..0030ba45 --- /dev/null +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryStateMachineModelFactory.java @@ -0,0 +1,73 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collection; + +import org.springframework.statemachine.config.model.ConfigurationData; +import org.springframework.statemachine.config.model.DefaultStateMachineModel; +import org.springframework.statemachine.config.model.StateData; +import org.springframework.statemachine.config.model.StateMachineModel; +import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.config.model.StatesData; +import org.springframework.statemachine.config.model.TransitionData; +import org.springframework.statemachine.config.model.TransitionsData; + +/** + * A generic {@link StateMachineModelFactory} which is backed by a Spring Data + * Repository abstraction. + * + * @author Janne Valkealahti + * + */ +public class RepositoryStateMachineModelFactory implements StateMachineModelFactory { + + private final StateRepository stateRepository; + private final TransitionRepository transitionRepository; + + /** + * Instantiates a new repository state machine model factory. + * + * @param stateRepository the state repository + * @param transitionRepository the transition repository + */ + public RepositoryStateMachineModelFactory(StateRepository stateRepository, + TransitionRepository transitionRepository) { + this.stateRepository = stateRepository; + this.transitionRepository = transitionRepository; + } + + @Override + public StateMachineModel build() { + ConfigurationData configurationData = new ConfigurationData<>(); + + Collection> stateData = new ArrayList<>(); + for (RepositoryState s : stateRepository.findAll()) { + stateData.add(new StateData(s.getState(), s.isInitial())); + } + StatesData statesData = new StatesData<>(stateData); + + Collection> transitionData = new ArrayList<>(); + for (RepositoryTransition t : transitionRepository.findAll()) { + transitionData.add(new TransitionData(t.getSource(), t.getTarget(), t.getEvent())); + } + TransitionsData transitionsData = new TransitionsData<>(transitionData); + + StateMachineModel stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData); + return stateMachineModel; + } +} diff --git a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryTransition.java b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryTransition.java new file mode 100644 index 00000000..6da1c17f --- /dev/null +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/RepositoryTransition.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * Generic interface representing transition entity. + * + * @author Janne Valkealahti + * + */ +public interface RepositoryTransition { + + String getSource(); + + void setSource(String source); + + String getTarget(); + + void setTarget(String target); + + String getEvent(); + + void setEvent(String event); +} diff --git a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/StateRepository.java b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/StateRepository.java new file mode 100644 index 00000000..0c95780e --- /dev/null +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/StateRepository.java @@ -0,0 +1,31 @@ +/* + * 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 org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.Repository; + +/** + * Generic {@link Repository} interface for states. + * + * @author Janne Valkealahti + * + * @param the state entity type + */ +@NoRepositoryBean +public interface StateRepository extends CrudRepository { +} diff --git a/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/TransitionRepository.java b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/TransitionRepository.java new file mode 100644 index 00000000..c1efdc26 --- /dev/null +++ b/spring-statemachine-data/src/main/java/org/springframework/statemachine/data/TransitionRepository.java @@ -0,0 +1,31 @@ +/* + * 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 org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.NoRepositoryBean; +import org.springframework.data.repository.Repository; + +/** + * Generic {@link Repository} interface for transitions. + * + * @author Janne Valkealahti + * + * @param the transition entity type + */ +@NoRepositoryBean +public interface TransitionRepository extends CrudRepository { +} diff --git a/spring-statemachine-samples/build.gradle b/spring-statemachine-samples/build.gradle index c53b0579..95bddd95 100644 --- a/spring-statemachine-samples/build.gradle +++ b/spring-statemachine-samples/build.gradle @@ -109,3 +109,14 @@ project('spring-statemachine-samples-ordershipping') { } } +project('spring-statemachine-samples-datajpa') { + description = 'Spring State Machine Data Jpa Sample' + dependencies { + compile project(":spring-statemachine-data-common:spring-statemachine-data-jpa") + compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion") + compile("org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion") + compile("org.springframework.boot:spring-boot-devtools:$springBootVersion") + compile("com.h2database:h2:$h2Version") + } +} + diff --git a/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/Application.java b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/Application.java new file mode 100644 index 00000000..de67a281 --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/Application.java @@ -0,0 +1,31 @@ +/* + * 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 demo.datajpa; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.orm.jpa.EntityScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; + +@SpringBootApplication +@EntityScan(basePackages = {"org.springframework.statemachine.data.jpa"}) +@EnableJpaRepositories(basePackages = {"org.springframework.statemachine.data.jpa"}) +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } +} diff --git a/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineConfig.java b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineConfig.java new file mode 100644 index 00000000..c3efbb96 --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineConfig.java @@ -0,0 +1,66 @@ +/* + * 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 demo.datajpa; + +import org.springframework.beans.factory.annotation.Autowired; +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.data.repository.init.Jackson2RepositoryPopulatorFactoryBean; +import org.springframework.statemachine.config.EnableStateMachineFactory; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineModelConfigurer; +import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.data.RepositoryState; +import org.springframework.statemachine.data.RepositoryStateMachineModelFactory; +import org.springframework.statemachine.data.RepositoryTransition; +import org.springframework.statemachine.data.StateRepository; +import org.springframework.statemachine.data.TransitionRepository; + +@Configuration +public class StateMachineConfig { + + @Bean + public Jackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() { + Jackson2RepositoryPopulatorFactoryBean factoryBean = new Jackson2RepositoryPopulatorFactoryBean(); + factoryBean.setResources(new Resource[]{new ClassPathResource("data.json")}); + return factoryBean; + } + + @Configuration + @EnableStateMachineFactory + public static class Config 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); + } + } +} diff --git a/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineController.java b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineController.java new file mode 100644 index 00000000..f1da34a2 --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineController.java @@ -0,0 +1,80 @@ +/* + * 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 demo.datajpa; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.StateMachineFactory; +import org.springframework.statemachine.data.RepositoryTransition; +import org.springframework.statemachine.data.TransitionRepository; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@Controller +public class StateMachineController { + + @Autowired + private StateMachineFactory stateMachineFactory; + + @Autowired + private TransitionRepository transitionRepository; + + @RequestMapping("/") + public String home() { + return "redirect:/state"; + } + + @RequestMapping("/state") + public String feedAndGetStates(@RequestParam(value = "events", required = false) List events, Model model) throws Exception { + + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + StateMachineLogListener listener = new StateMachineLogListener(); + stateMachine.addStateListener(listener); + stateMachine.start(); + if (events != null) { + for (String event : events) { + stateMachine.sendEvent(event); + } + } + stateMachine.stop(); + model.addAttribute("allEvents", getEvents()); + model.addAttribute("messages", createMessages(listener.getMessages())); + return "states"; + } + + private String[] getEvents() { + List events = new ArrayList<>(); + for (RepositoryTransition t : transitionRepository.findAll()) { + events.add(t.getEvent()); + } + return events.toArray(new String[0]); + } + + private String createMessages(List messages) { + StringBuilder buf = new StringBuilder(); + for (String message : messages) { + buf.append(message); + buf.append("\n"); + } + return buf.toString(); + } + +} diff --git a/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineLogListener.java b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineLogListener.java new file mode 100644 index 00000000..4ccb8d82 --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/java/demo/datajpa/StateMachineLogListener.java @@ -0,0 +1,49 @@ +/* + * 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 demo.datajpa; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.statemachine.StateContext; +import org.springframework.statemachine.StateContext.Stage; +import org.springframework.statemachine.listener.StateMachineListenerAdapter; + +public class StateMachineLogListener extends StateMachineListenerAdapter { + + private final LinkedList messages = new LinkedList(); + + public List getMessages() { + return messages; + } + + public void resetMessages() { + messages.clear(); + } + + @Override + public void stateContext(StateContext stateContext) { + if (stateContext.getStage() == Stage.STATE_ENTRY) { + messages.addFirst("Enter " + stateContext.getTarget().getId()); + } else if (stateContext.getStage() == Stage.STATE_EXIT) { + messages.addFirst("Exit " + stateContext.getSource().getId()); + } else if (stateContext.getStage() == Stage.STATEMACHINE_START) { + messages.addLast("Machine started"); + } else if (stateContext.getStage() == Stage.STATEMACHINE_STOP) { + messages.addFirst("Machine stopped"); + } + } +} diff --git a/spring-statemachine-samples/datajpa/src/main/resources/application.yml b/spring-statemachine-samples/datajpa/src/main/resources/application.yml new file mode 100644 index 00000000..d560ab3a --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/resources/application.yml @@ -0,0 +1,6 @@ +logging: + level: + root: INFO +security: + basic: + enabled: false diff --git a/spring-statemachine-samples/datajpa/src/main/resources/data.json b/spring-statemachine-samples/datajpa/src/main/resources/data.json new file mode 100644 index 00000000..2cdfb1d6 --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/resources/data.json @@ -0,0 +1,29 @@ +[ + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState", + "initial": true, + "state": "S1" + }, + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState", + "initial": false, + "state": "S2" + }, + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState", + "initial": false, + "state": "S3" + }, + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition", + "source": "S1", + "target": "S2", + "event": "E1" + }, + { + "_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition", + "source": "S2", + "target": "S3", + "event": "E2" + } +] diff --git a/spring-statemachine-samples/datajpa/src/main/resources/templates/states.html b/spring-statemachine-samples/datajpa/src/main/resources/templates/states.html new file mode 100644 index 00000000..21f38c2c --- /dev/null +++ b/spring-statemachine-samples/datajpa/src/main/resources/templates/states.html @@ -0,0 +1,32 @@ + + + + Spring Statemachine Demo + + + + +
+
+

+

    +
  • + + +
  • +
+
+ +
+
+