diff --git a/docs/src/reference/asciidoc/images/sm-scope-1.png b/docs/src/reference/asciidoc/images/sm-scope-1.png new file mode 100644 index 00000000..18e50571 Binary files /dev/null and b/docs/src/reference/asciidoc/images/sm-scope-1.png differ diff --git a/docs/src/reference/asciidoc/images/statechart12.png b/docs/src/reference/asciidoc/images/statechart12.png new file mode 100644 index 00000000..baf3c38b Binary files /dev/null and b/docs/src/reference/asciidoc/images/statechart12.png differ diff --git a/docs/src/reference/asciidoc/sm-examples.adoc b/docs/src/reference/asciidoc/sm-examples.adoc index 25fa7663..017a64ca 100644 --- a/docs/src/reference/asciidoc/sm-examples.adoc +++ b/docs/src/reference/asciidoc/sm-examples.adoc @@ -26,6 +26,8 @@ normal build cycle. Samples in this chapter are: <> Web. +<> 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=` 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=` 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] diff --git a/docs/src/reference/asciidoc/sm.adoc b/docs/src/reference/asciidoc/sm.adoc index 917aa384..d5d7316b 100644 --- a/docs/src/reference/asciidoc/sm.adoc +++ b/docs/src/reference/asciidoc/sm.adoc @@ -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 <> 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 diff --git a/settings.gradle b/settings.gradle index 95c385b6..b13782aa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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') { diff --git a/spring-statemachine-samples/build.gradle b/spring-statemachine-samples/build.gradle index 0d8e5114..b7d6585e 100644 --- a/spring-statemachine-samples/build.gradle +++ b/spring-statemachine-samples/build.gradle @@ -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") + } +} diff --git a/spring-statemachine-samples/scope/src/main/java/demo/scope/Application.java b/spring-statemachine-samples/scope/src/main/java/demo/scope/Application.java new file mode 100644 index 00000000..03921cae --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/java/demo/scope/Application.java @@ -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); + } +} diff --git a/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineConfig.java b/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineConfig.java new file mode 100644 index 00000000..679d23dd --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineConfig.java @@ -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 { + + @Override + public void configure(StateMachineConfigurationConfigurer config) + throws Exception { + config + .withConfiguration() + .autoStartup(true); + } + + @Override + public void configure(StateMachineStateConfigurer states) + throws Exception { + states + .withStates() + .initial(States.S0) + .states(EnumSet.allOf(States.class)); + } + + @Override + public void configure(StateMachineTransitionConfigurer 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; + } + +} diff --git a/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineController.java b/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineController.java new file mode 100644 index 00000000..0e1e1ae0 --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/java/demo/scope/StateMachineController.java @@ -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 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"; + } + +} \ No newline at end of file diff --git a/spring-statemachine-samples/scope/src/main/resources/logback.xml b/spring-statemachine-samples/scope/src/main/resources/logback.xml new file mode 100644 index 00000000..7d5b0732 --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/resources/logback.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/spring-statemachine-samples/scope/src/main/resources/statechartmodel.txt b/spring-statemachine-samples/scope/src/main/resources/statechartmodel.txt new file mode 100644 index 00000000..4f758b14 --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/resources/statechartmodel.txt @@ -0,0 +1,12 @@ ++---------------------------------------------------+ +| SM | ++---------------------------------------------------+ +| | +| +--------+ A +--------+ B +--------+ | +| *-->| S0 |------>| S1 |------>| S2 | | +| +--------+ +--------+ +--------+ | +| ^ | | +| | C | | +| +----------------------------------+ | +| | ++---------------------------------------------------+ diff --git a/spring-statemachine-samples/scope/src/main/resources/templates/states.html b/spring-statemachine-samples/scope/src/main/resources/templates/states.html new file mode 100644 index 00000000..a9227246 --- /dev/null +++ b/spring-statemachine-samples/scope/src/main/resources/templates/states.html @@ -0,0 +1,16 @@ + + + + Spring Statemachine Scope Demo + + + +

+

+ + + +
+
+
+