Add scope sample

- Backport #137 fixes #143
- Tune docs for usage and sample.
This commit is contained in:
Janne Valkealahti
2015-12-13 11:13:33 +00:00
parent 41371d292b
commit 3602130cc0
12 changed files with 280 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -26,6 +26,8 @@ normal build cycle. Samples in this chapter are:
<<statemachine-examples-web>> Web.
<<statemachine-examples-scope>> Scope.
[source,text]
----
@@ -37,6 +39,13 @@ Every sample is located in its own directory under
spring-shell and you will find usual boot fat jars under every sample
projects `build/libs` directory.
[NOTE]
====
Filenames for jars we refer in this section are populated during a
build of this document, meaning if you're building samples from a
master, you have files with `BUILD-SNAPSHOT` postfix.
====
[[statemachine-examples-turnstile]]
== Turnstile
@@ -68,9 +77,9 @@ include::samples/demo/turnstile/Application.java[tags=snippetA]
You can see how this sample state machine interacts with event by
running `turnstile` sample.
[source,text]
[source,text,subs="verbatim,attributes"]
----
$ java -jar spring-statemachine-samples-turnstile-1.0.0.BUILD-SNAPSHOT.jar
$ java -jar spring-statemachine-samples-turnstile-{revnumber}.jar
sm>sm print
+----------------------------------------------------------------+
@@ -915,8 +924,12 @@ include::samples/demo/zookeeper/Application.java[tags=snippetB]
----
Let's go through a simple example where two different shell instances are
started with command `java -jar
spring-statemachine-samples-zookeeper-1.0.0.BUILD-SNAPSHOT.jar`.
started with command
[source,text,subs="attributes"]
----
@n1:~# java -jar spring-statemachine-samples-zookeeper-{revnumber}.jar
----
First open first shell instance(do not start second instance yet).
When state machine is started it will end up into its initial state
@@ -993,21 +1006,19 @@ be available from a localhost for every individual sample instance.
====
Let's go through a simple example where three different sample instances are
started with command `java -jar
spring-statemachine-samples-web-1.0.0.BUILD-SNAPSHOT.jar`. If you are
running different instances on a same host you need to distinguish
used port by adding `--server.port=<myport>` to the command. Otherwise
default port for each host will be `8080`.
started. If you are running different instances on a same host you need to
distinguish used port by adding `--server.port=<myport>` to the command.
Otherwise default port for each host will be `8080`.
In this sample run we have three hosts, `n1`, `n2` and `n3` which all
have a local zookeeper instance running and a state machine sample running
on a port `8080`.
[source,text]
[source,text,subs="attributes"]
----
@n1:~# java -jar spring-statemachine-samples-web-1.0.0.BUILD-SNAPSHOT.jar
@n2:~# java -jar spring-statemachine-samples-web-1.0.0.BUILD-SNAPSHOT.jar
@n3:~# java -jar spring-statemachine-samples-web-1.0.0.BUILD-SNAPSHOT.jar
@n1:~# java -jar spring-statemachine-samples-web-{revnumber}.jar
@n2:~# java -jar spring-statemachine-samples-web-{revnumber}.jar
@n3:~# java -jar spring-statemachine-samples-web-{revnumber}.jar
----
When all instances are running you should see all showing similar
@@ -1037,3 +1048,25 @@ all browser sessions.
image::images/sm-dist-n1-4.png[width=500]
[[statemachine-examples-scope]]
== Scope
Scope is a state machine example using a session scope to provide
individual instance for every user.
image::images/statechart12.png[width=500]
This is a simple state machine having states `S0`, `S1` and `S2`.
Transitions between those are controlled via events `A`, `B` and `C`
as shown in a state chart.
[source,text,subs="attributes"]
----
@n1:~# java -jar spring-statemachine-samples-scope-{revnumber}.jar
----
When instance is running you can open a browser and play with a state
machine. If you open same page using a different browser, i.e one in
_Chrome_ and one in _Firefox_, you should get a new state machine
instance per user session.
image::images/sm-scope-1.png[width=500]

View File

@@ -483,6 +483,11 @@ these simply needs an a `@Scope` to be present where _scopeName_ is set to
_session_ and _proxyMode_ to `ScopedProxyMode.TARGET_CLASS`. Examples for
both use cases are shown below.
[TIP]
====
See sample <<statemachine-examples-scope>> how to use session scoping.
====
[source,java,indent=0]
----
include::samples/DocsConfigurationSampleTests2.java[tags=snippetB]
@@ -508,6 +513,14 @@ Using state machines in a `session` scopes needs a careful planning
mostly because it is a relatively heavy component.
====
[NOTE]
====
Spring Statemachine poms don't have any dependencies to Spring MVC
classes which you will need to work with session scope. But if you're
working with a web application, you've already pulled those deps
directly from Spring MVC or Spring Boot.
====
[[sm-actions]]
== Using Actions
Actions are one of the most useful components from user perspective to

View File

@@ -15,6 +15,7 @@ include 'spring-statemachine-samples:washer'
include 'spring-statemachine-samples:zookeeper'
include 'spring-statemachine-samples:persist'
include 'spring-statemachine-samples:web'
include 'spring-statemachine-samples:scope'
rootProject.children.find {
if (it.name == 'spring-statemachine-recipes') {

View File

@@ -45,3 +45,12 @@ project('spring-statemachine-samples-web') {
compile("org.springframework.session:spring-session:$springSessionVersion")
}
}
project('spring-statemachine-samples-scope') {
description = 'Spring State Machine Web Scope Sample'
dependencies {
compile("org.springframework.boot:spring-boot-starter-thymeleaf:$springBootVersion")
// compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
// compile("org.springframework:spring-webmvc:$springVersion")
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2015 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.scope;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2015 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.scope;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.Scanner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.core.io.ClassPathResource;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
@Configuration
public class StateMachineConfig {
@Configuration
@EnableStateMachine
@Scope(scopeName="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
static class Config
extends EnumStateMachineConfigurerAdapter<States, Events> {
@Override
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
throws Exception {
config
.withConfiguration()
.autoStartup(true);
}
@Override
public void configure(StateMachineStateConfigurer<States, Events> states)
throws Exception {
states
.withStates()
.initial(States.S0)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
throws Exception {
transitions
.withExternal()
.source(States.S0).target(States.S1).event(Events.A)
.and()
.withExternal()
.source(States.S1).target(States.S2).event(Events.B)
.and()
.withExternal()
.source(States.S2).target(States.S0).event(Events.C);
}
}
@Bean
public String stateChartModel() throws IOException {
ClassPathResource model = new ClassPathResource("statechartmodel.txt");
InputStream inputStream = model.getInputStream();
Scanner scanner = new Scanner(inputStream);
String content = scanner.useDelimiter("\\Z").next();
scanner.close();
return content;
}
public enum States {
S0, S1, S2;
}
public enum Events {
A, B, C;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2015 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.scope;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.statemachine.StateMachine;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import demo.scope.StateMachineConfig.Events;
import demo.scope.StateMachineConfig.States;
@Controller
public class StateMachineController {
@Autowired
private StateMachine<States, Events> stateMachine;
@Autowired
@Qualifier("stateChartModel")
private String stateChartModel;
@RequestMapping("/")
public String greeting() {
return "redirect:/states";
}
@RequestMapping("/states")
public String getStates(@RequestParam(value = "event", required = false) Events event, Model model) {
if (event != null) {
stateMachine.sendEvent(event);
}
model.addAttribute("states", stateMachine.getState().getIds());
model.addAttribute("stateChartModel", stateChartModel);
return "states";
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.statemachine" level="TRACE"/>
</configuration>

View File

@@ -0,0 +1,12 @@
+---------------------------------------------------+
| SM |
+---------------------------------------------------+
| |
| +--------+ A +--------+ B +--------+ |
| *-->| S0 |------>| S1 |------>| S2 | |
| +--------+ +--------+ +--------+ |
| ^ | |
| | C | |
| +----------------------------------+ |
| |
+---------------------------------------------------+

View File

@@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spring Statemachine Scope Demo</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'States: ' + ${states}" />
<form action="#" data-th-action="@{/states}" data-th-object="${model}" method="post">
<button type="submit" name="event" value="A">Send A</button>
<button type="submit" name="event" value="B">Send B</button>
<button type="submit" name="event" value="C">Send C</button>
</form>
<pre th:text="${stateChartModel}"/>
</body>
</html>