Rename sample datajpapersist to datapersist
- As it now supports mongo/redis using profiles, remove jpa from name. - Change build and docs. - Backport #509
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2017 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.datapersist;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
//tag::snippetA[]
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
}
|
||||
//end::snippetA[]
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2017-2018 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.datapersist;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
import org.springframework.statemachine.data.jpa.JpaPersistingStateMachineInterceptor;
|
||||
import org.springframework.statemachine.data.jpa.JpaStateMachineRepository;
|
||||
import org.springframework.statemachine.data.mongodb.MongoDbPersistingStateMachineInterceptor;
|
||||
import org.springframework.statemachine.data.mongodb.MongoDbStateMachineRepository;
|
||||
import org.springframework.statemachine.data.redis.RedisPersistingStateMachineInterceptor;
|
||||
import org.springframework.statemachine.data.redis.RedisStateMachineRepository;
|
||||
import org.springframework.statemachine.persist.StateMachineRuntimePersister;
|
||||
import org.springframework.statemachine.service.DefaultStateMachineService;
|
||||
import org.springframework.statemachine.service.StateMachineService;
|
||||
|
||||
@Configuration
|
||||
public class StateMachineConfig {
|
||||
|
||||
//tag::snippetA[]
|
||||
@Configuration
|
||||
@Profile("jpa")
|
||||
public static class JpaPersisterConfig {
|
||||
|
||||
@Bean
|
||||
public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister(
|
||||
JpaStateMachineRepository jpaStateMachineRepository) {
|
||||
return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
|
||||
}
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
//tag::snippetB[]
|
||||
@Configuration
|
||||
@Profile("mongo")
|
||||
public static class MongoPersisterConfig {
|
||||
|
||||
@Bean
|
||||
public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister(
|
||||
MongoDbStateMachineRepository jpaStateMachineRepository) {
|
||||
return new MongoDbPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
|
||||
}
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
//tag::snippetC[]
|
||||
@Configuration
|
||||
@Profile("redis")
|
||||
public static class RedisPersisterConfig {
|
||||
|
||||
@Bean
|
||||
public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister(
|
||||
RedisStateMachineRepository jpaStateMachineRepository) {
|
||||
return new RedisPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
|
||||
}
|
||||
}
|
||||
//end::snippetC[]
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachineFactory
|
||||
public static class MachineConfig extends StateMachineConfigurerAdapter<States, Events> {
|
||||
|
||||
//tag::snippetD[]
|
||||
@Autowired
|
||||
private StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
|
||||
throws Exception {
|
||||
config
|
||||
.withPersistence()
|
||||
.runtimePersister(stateMachineRuntimePersister);
|
||||
}
|
||||
//end::snippetD[]
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<States, Events> states)
|
||||
throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(States.S1)
|
||||
.states(EnumSet.allOf(States.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
|
||||
throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(States.S1).target(States.S2)
|
||||
.event(Events.E1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S2).target(States.S3)
|
||||
.event(Events.E2)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S3).target(States.S4)
|
||||
.event(Events.E3)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S4).target(States.S5)
|
||||
.event(Events.E4)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S5).target(States.S6)
|
||||
.event(Events.E5)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.S6).target(States.S1)
|
||||
.event(Events.E6);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ServiceConfig {
|
||||
|
||||
//tag::snippetE[]
|
||||
@Bean
|
||||
public StateMachineService<States, Events> stateMachineService(
|
||||
StateMachineFactory<States, Events> stateMachineFactory,
|
||||
StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister) {
|
||||
return new DefaultStateMachineService<States, Events>(stateMachineFactory, stateMachineRuntimePersister);
|
||||
}
|
||||
//end::snippetE[]
|
||||
}
|
||||
|
||||
public enum States {
|
||||
S1, S2, S3, S4, S5, S6;
|
||||
}
|
||||
|
||||
public enum Events {
|
||||
E1, E2, E3, E4, E5, E6;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright 2017-2018 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.datapersist;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.StateMachineContext;
|
||||
import org.springframework.statemachine.StateMachinePersist;
|
||||
import org.springframework.statemachine.service.StateMachineService;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import demo.datapersist.StateMachineConfig.Events;
|
||||
import demo.datapersist.StateMachineConfig.States;
|
||||
|
||||
@Controller
|
||||
public class StateMachineController {
|
||||
|
||||
public final static String MACHINE_ID_1 = "datajpapersist1";
|
||||
public final static String MACHINE_ID_2 = "datajpapersist2";
|
||||
private final static String[] MACHINES = new String[] { MACHINE_ID_1, MACHINE_ID_2 };
|
||||
|
||||
private final StateMachineLogListener listener = new StateMachineLogListener();
|
||||
private StateMachine<States, Events> currentStateMachine;
|
||||
|
||||
@Autowired
|
||||
private StateMachineService<States, Events> stateMachineService;
|
||||
|
||||
@Autowired
|
||||
private StateMachinePersist<States, Events, String> stateMachinePersist;
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "redirect:/state";
|
||||
}
|
||||
|
||||
@RequestMapping("/state")
|
||||
public String feedAndGetStates(
|
||||
@RequestParam(value = "events", required = false) List<Events> events,
|
||||
@RequestParam(value = "machine", required = false, defaultValue = MACHINE_ID_1) String machine,
|
||||
Model model) throws Exception {
|
||||
StateMachine<States, Events> stateMachine = getStateMachine(machine);
|
||||
if (events != null) {
|
||||
for (Events event : events) {
|
||||
stateMachine.sendEvent(event);
|
||||
}
|
||||
}
|
||||
StateMachineContext<States, Events> stateMachineContext = stateMachinePersist.read(machine);
|
||||
model.addAttribute("allMachines", MACHINES);
|
||||
model.addAttribute("machine", machine);
|
||||
model.addAttribute("allEvents", getEvents());
|
||||
model.addAttribute("messages", createMessages(listener.getMessages()));
|
||||
model.addAttribute("context", stateMachineContext != null ? stateMachineContext.toString() : "");
|
||||
return "states";
|
||||
}
|
||||
|
||||
//tag::snippetA[]
|
||||
private synchronized StateMachine<States, Events> getStateMachine(String machineId) throws Exception {
|
||||
listener.resetMessages();
|
||||
if (currentStateMachine == null) {
|
||||
currentStateMachine = stateMachineService.acquireStateMachine(machineId);
|
||||
currentStateMachine.addStateListener(listener);
|
||||
currentStateMachine.start();
|
||||
} else if (!ObjectUtils.nullSafeEquals(currentStateMachine.getId(), machineId)) {
|
||||
stateMachineService.releaseStateMachine(currentStateMachine.getId());
|
||||
currentStateMachine.stop();
|
||||
currentStateMachine = stateMachineService.acquireStateMachine(machineId);
|
||||
currentStateMachine.addStateListener(listener);
|
||||
currentStateMachine.start();
|
||||
}
|
||||
return currentStateMachine;
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
private Events[] getEvents() {
|
||||
return EnumSet.allOf(Events.class).toArray(new Events[0]);
|
||||
}
|
||||
|
||||
private String createMessages(List<String> messages) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (String message : messages) {
|
||||
buf.append(message);
|
||||
buf.append("\n");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2017 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.datapersist;
|
||||
|
||||
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;
|
||||
|
||||
import demo.datapersist.StateMachineConfig.Events;
|
||||
import demo.datapersist.StateMachineConfig.States;
|
||||
|
||||
public class StateMachineLogListener extends StateMachineListenerAdapter<States, Events> {
|
||||
|
||||
private final LinkedList<String> messages = new LinkedList<String>();
|
||||
|
||||
public List<String> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
|
||||
public void resetMessages() {
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateContext(StateContext<States, Events> 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
spring:
|
||||
profiles:
|
||||
active: jpa
|
||||
security:
|
||||
basic:
|
||||
enabled: false
|
||||
|
||||
---
|
||||
spring:
|
||||
profiles: jpa
|
||||
statemachine:
|
||||
data:
|
||||
mongo:
|
||||
repositories:
|
||||
enabled: false
|
||||
redis:
|
||||
repositories:
|
||||
enabled: false
|
||||
---
|
||||
spring:
|
||||
profiles: mongo
|
||||
statemachine:
|
||||
data:
|
||||
jpa:
|
||||
repositories:
|
||||
enabled: false
|
||||
redis:
|
||||
repositories:
|
||||
enabled: false
|
||||
---
|
||||
spring:
|
||||
profiles: redis
|
||||
statemachine:
|
||||
data:
|
||||
jpa:
|
||||
repositories:
|
||||
enabled: false
|
||||
mongo:
|
||||
repositories:
|
||||
enabled: false
|
||||
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Spring Statemachine Demo</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<a href="/h2-console" target="_blank">h2 console</a>
|
||||
</div>
|
||||
<form action="#" data-th-action="@{/state}" data-th-object="${model}" method="post">
|
||||
<div>
|
||||
<p th:text="'Choose machine'"/>
|
||||
<ul>
|
||||
<li th:each="ty : ${allMachines}">
|
||||
<input type="radio" name="machine" th:value="${ty}" th:checked="${ty} == ${machine}"/>
|
||||
<label th:text="${ty}">replaced</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p th:text="'Choose events'"/>
|
||||
<ul>
|
||||
<li th:each="ty : ${allEvents}">
|
||||
<input type="checkbox" name="events" th:value="${ty}" />
|
||||
<label th:text="${ty}">replaced</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="submit">Send Events</button>
|
||||
</form>
|
||||
<div>
|
||||
<h3>Events</h3>
|
||||
</div>
|
||||
<div>
|
||||
<textarea th:text="${messages}" rows="15" cols="100"/>
|
||||
</div>
|
||||
<div>
|
||||
<h3>StateMachineContext</h3>
|
||||
</div>
|
||||
<div>
|
||||
<textarea th:text="${context}" rows="15" cols="100"/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 2017 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.datapersist;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.annotation.DirtiesContext.ClassMode;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import demo.datapersist.Application;
|
||||
import demo.datapersist.StateMachineController;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringBootTest(classes = { Application.class })
|
||||
@WebAppConfiguration
|
||||
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class DataJpaPersistTests {
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
@Test
|
||||
public void testHome() throws Exception {
|
||||
mvc.
|
||||
perform(get("/state")).
|
||||
andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendEventE1() throws Exception {
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E1")).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendEventsE1E2() throws Exception {
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E1").param("events", "E2")).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(allOf(
|
||||
containsString("Exit S1"),
|
||||
containsString("Exit S2"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendEventE4() throws Exception {
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E1").param("machine", StateMachineController.MACHINE_ID_2)).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S1")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangeMachineRestores() throws Exception {
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E1")).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S1")));
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E1").param("machine", StateMachineController.MACHINE_ID_2)).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S1")));
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E2").param("machine", StateMachineController.MACHINE_ID_1)).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S2")));
|
||||
mvc.
|
||||
perform(get("/state").param("events", "E2").param("machine", StateMachineController.MACHINE_ID_2)).
|
||||
andExpect(status().isOk()).
|
||||
andExpect(content().string(containsString("Exit S2")));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
mvc = MockMvcBuilders.webAppContextSetup(context).build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user