Update ref docs
- First set of changes for doc updates for M3 - Relates #88
This commit is contained in:
@@ -246,12 +246,15 @@ configure(rootProject) {
|
||||
|
||||
task copyDocsSamples(type: Copy) {
|
||||
from 'spring-statemachine-core/src/test/java/org/springframework/statemachine/docs'
|
||||
from 'spring-statemachine-recipes/src/test/java/org/springframework/statemachine/recipes/docs'
|
||||
from 'spring-statemachine-samples/src/main/java/'
|
||||
from 'spring-statemachine-samples/washer/src/main/java/'
|
||||
from 'spring-statemachine-samples/tasks/src/main/java/'
|
||||
from 'spring-statemachine-samples/turnstile/src/main/java/'
|
||||
from 'spring-statemachine-samples/showcase/src/main/java/'
|
||||
from 'spring-statemachine-samples/cdplayer/src/main/java/'
|
||||
from 'spring-statemachine-samples/persist/src/main/java/'
|
||||
from 'spring-statemachine-samples/zookeeper/src/main/java/'
|
||||
include '**/*.java'
|
||||
into 'docs/src/reference/asciidoc/samples'
|
||||
}
|
||||
|
||||
@@ -304,3 +304,11 @@ still working together in a sense. This is why orthogonal regions can
|
||||
combine together a multiple simultaneous states within a single state
|
||||
in a state machine.
|
||||
|
||||
[appendix]
|
||||
[[appendices-zookeeper]]
|
||||
== Distributed State Machine with Zookeeper
|
||||
This appendix provides more detailed technical documentation about
|
||||
using a Zookeeper with a Spring State Machine.
|
||||
|
||||
tbd.
|
||||
|
||||
|
||||
BIN
docs/src/reference/asciidoc/images/statechart10.png
Normal file
BIN
docs/src/reference/asciidoc/images/statechart10.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -18,6 +18,7 @@ include::introduction.adoc[]
|
||||
[[springandsm]]
|
||||
include::sm.adoc[]
|
||||
|
||||
include::recipes.adoc[]
|
||||
include::sm-examples.adoc[]
|
||||
include::faq.adoc[]
|
||||
include::appendix.adoc[]
|
||||
|
||||
130
docs/src/reference/asciidoc/recipes.adoc
Normal file
130
docs/src/reference/asciidoc/recipes.adoc
Normal file
@@ -0,0 +1,130 @@
|
||||
[[statemachine-recipes]]
|
||||
= Recipes
|
||||
This chapter contains documentation for existing built-in state
|
||||
machine recipes.
|
||||
|
||||
What exactly is a recipe? As Spring Statemachine is always going to be
|
||||
a foundational framework meaning that its core will not have that much
|
||||
higher level functionality or dependencies outside of a Spring
|
||||
Framework. Correct usage of a state machine may be a little difficult
|
||||
time to time and there's always some common use cases how state
|
||||
machine can be used. Recipe modules are meant to provide a higher
|
||||
level solutions to these common use cases and also provide examples
|
||||
beyond samples how framework can be used.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Recipes are a great way to make external contributions this Spring
|
||||
Statemachine project. If you're not ready to contribute to the
|
||||
framework core itself, a custom and common recipe is a great way to
|
||||
share functionality among other users.
|
||||
====
|
||||
|
||||
[[statemachine-recipes-persist]]
|
||||
== Persist
|
||||
Persist recipe is a simple utility which allows to use a single state
|
||||
machine instance to persist and update a state of an arbitrary item in
|
||||
a repository.
|
||||
|
||||
Recipe's main class is `PersistStateMachineHandler` which assumes user
|
||||
to do three different things:
|
||||
|
||||
- An instance of a `StateMachine<String, String>` needs to be used
|
||||
with a `PersistStateMachineHandler`. States and Events are required
|
||||
to be type of Strings.
|
||||
- `PersistStateChangeListener` need to be registered with handler
|
||||
order to react to persist request.
|
||||
- Method `handleEventWithState` is used to orchestrate state changes.
|
||||
|
||||
There is a sample demonstrating usage of this recipe at
|
||||
<<statemachine-examples-persist>>.
|
||||
|
||||
[[statemachine-recipes-tasks]]
|
||||
== Tasks
|
||||
Tasks recipe is a concept to execute DAG of `Runnable` instances using
|
||||
a state machine. This recipe has been developed from ideas introduced
|
||||
in sample <<statemachine-examples-tasks>>.
|
||||
|
||||
Generic concept of a state machine is shown below. In this state chart
|
||||
everything under `TASKS` just shows a generic concept of how a single
|
||||
task is executed. Because this recipe allows to register deep
|
||||
hierarcical DAG of tasks, meaning a real state chart would be deep
|
||||
nested collection of sub-states and regions, there's no need to be
|
||||
more presise.
|
||||
|
||||
For example if you have only two registered tasks, below state chart
|
||||
would be correct with `TASK_id` replaced with `TASK_1` and `TASK_2` if
|
||||
registered tasks ids are `1` and `2`.
|
||||
|
||||
image::images/statechart9.png[width=500]
|
||||
|
||||
Executing a `Runnable` may result an error and especially if a complex
|
||||
DAG of tasks is involved it is desirable that there is a way to handle
|
||||
tasks execution errors and then having a way to continue execution
|
||||
without executing already successfully executed tasks. Addition to
|
||||
this it would be nice if some execution errors can be handled
|
||||
automatically and as a last fallback, if error can't be handled
|
||||
automatically, state maching is put into a state where user can handle
|
||||
errors manually.
|
||||
|
||||
`TasksHandler` contains a builder method to configure handler instance
|
||||
and follows a simple builder patter. This builder can be used to
|
||||
register `Runnable` tasks, `TasksListener` instances, define
|
||||
`StateMachinePersist` hook, and setup custom `TaskExecutor` instance.
|
||||
|
||||
Now lets take a simple `Runnable` just doing a simple sleep as shown
|
||||
below. This is a base of all examples in this chapter.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetAA]
|
||||
----
|
||||
|
||||
To execute multiple `sleepRunnable` tasks just register tasks and
|
||||
execute `runTasks()` method from `TasksHandler`.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetB]
|
||||
----
|
||||
|
||||
Order to listen what is happening with a task execution an instance of
|
||||
a `TasksListener` can be registered with a `TasksHandler`. Recipe
|
||||
provides an adapter `TasksListenerAdapter` if you dont' want to
|
||||
implement a full interface. Listener provides a various hooks to
|
||||
listen tasks execution events.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetAB]
|
||||
----
|
||||
|
||||
Listeners can be either registered via a builder or directly with a
|
||||
`TasksHandler` as shown above.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetC]
|
||||
----
|
||||
|
||||
Above sample show how to create a deep nested DAG of tasks. Every task
|
||||
needs to have an unique identifier and optionally as task can be
|
||||
defined to be a sub-task. Effectively this will create a DAG of tasks.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetD]
|
||||
----
|
||||
|
||||
When error happens and a state machine running these tasks goes into a
|
||||
`ERROR` state, user can call handler methods `fixCurrentProblems` to
|
||||
reset current state of tasks kept in a state machine extended state
|
||||
variables. Handler method `continueFromError` can then be used to
|
||||
instruct state machine to transition from `ERROR` state back to
|
||||
`READY` state where tasks can be executed again.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsTasksSampleTests.java[tags=snippetE]
|
||||
----
|
||||
|
||||
@@ -20,6 +20,7 @@ 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.
|
||||
|
||||
[[statemachine-examples-turnstile]]
|
||||
== Turnstile
|
||||
|
||||
Turnstile is a simple device which gives you an access if payment is
|
||||
@@ -442,6 +443,7 @@ What happened in above run:
|
||||
* We print lcd status and request next track.
|
||||
* We stop playing.
|
||||
|
||||
[[statemachine-examples-tasks]]
|
||||
== Tasks
|
||||
|
||||
Tasks is a sample demonstrating a parallel task handling within a
|
||||
@@ -701,3 +703,194 @@ What happened in above run:
|
||||
* State is restored via HISTORY state which takes state machine back
|
||||
to its previous known state.
|
||||
|
||||
[[statemachine-examples-persist]]
|
||||
== Persist
|
||||
Persist is a sample using recipe <<statemachine-recipes-persist>> to
|
||||
demonstate how a database entry update logic can be controlled by a
|
||||
state machine.
|
||||
|
||||
The state machine logic and configuration is shown above:
|
||||
|
||||
image::images/statechart10.png[width=500]
|
||||
|
||||
.StateMachine Config
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Application.java[tags=snippetA]
|
||||
----
|
||||
|
||||
`PersistStateMachineHandler` can be created using a below config:
|
||||
|
||||
.Handler Config
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Application.java[tags=snippetB]
|
||||
----
|
||||
|
||||
Order class used with this sample is shown below:
|
||||
|
||||
.Order Class
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Application.java[tags=snippetC]
|
||||
----
|
||||
|
||||
Now let's see how this example works.
|
||||
|
||||
[source,text]
|
||||
----
|
||||
sm>persist db
|
||||
Order [id=1, state=PLACED]
|
||||
Order [id=2, state=PROCESSING]
|
||||
Order [id=3, state=SENT]
|
||||
Order [id=4, state=DELIVERED]
|
||||
|
||||
sm>persist process 1
|
||||
Exit state PLACED
|
||||
Entry state PROCESSING
|
||||
|
||||
sm>persist db
|
||||
Order [id=2, state=PROCESSING]
|
||||
Order [id=3, state=SENT]
|
||||
Order [id=4, state=DELIVERED]
|
||||
Order [id=1, state=PROCESSING]
|
||||
|
||||
sm>persist deliver 3
|
||||
Exit state SENT
|
||||
Entry state DELIVERED
|
||||
|
||||
sm>persist db
|
||||
Order [id=2, state=PROCESSING]
|
||||
Order [id=4, state=DELIVERED]
|
||||
Order [id=1, state=PROCESSING]
|
||||
Order [id=3, state=DELIVERED]
|
||||
----
|
||||
|
||||
What happened in above run:
|
||||
|
||||
* We listed rows from an existing embedded database which is already
|
||||
populated with sample data.
|
||||
* We request to update order `1` into `PROCESSING` state.
|
||||
* We list db entries again and see that state has been changed from
|
||||
`PLACED` into a `PROCESSING`.
|
||||
* We do update for order `3` to update state from `SENT` into
|
||||
`DELIVERED`.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
If you're wondering where is the database because there are literally no
|
||||
signs of it in a sample code. Sample is based on Spring Boot and
|
||||
because necessary classes are in a classpath, embedded `HSQL` instance
|
||||
is created automatically.
|
||||
|
||||
Spring Boot will even create an instance of `JdbcTemplate` which you
|
||||
can just autowire like how it's done in `Persist.java`.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Persist.java[tags=snippetA]
|
||||
----
|
||||
====
|
||||
|
||||
Finally we need to handle state changes:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Persist.java[tags=snippetB]
|
||||
----
|
||||
|
||||
And use a `PersistStateChangeListener` to update database:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/persist/Persist.java[tags=snippetC]
|
||||
----
|
||||
|
||||
[[statemachine-examples-zookeeper]]
|
||||
== Zookeeper
|
||||
Zookeeper is a distributed version from sample
|
||||
<<statemachine-examples-turnstile>>.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This sample needs and external `Zookeeper` instance accessible from
|
||||
`localhost` with default port and settings.
|
||||
====
|
||||
|
||||
Configuration of this sample is almost same as `turnstile` sample. We
|
||||
only add configuration for distributed state machine where we
|
||||
configure `StateMachineEnsemble`.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/zookeeper/Application.java[tags=snippetA]
|
||||
----
|
||||
|
||||
Actual `StateMachineEnsemble` needs to be created as bean together
|
||||
with `CuratorFramework` client.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/zookeeper/Application.java[tags=snippetB]
|
||||
----
|
||||
|
||||
Lets 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`.
|
||||
|
||||
First open first shell instance(do not start second instance yet).
|
||||
When state machine is started it will end up into its initial state
|
||||
`LOCKED`. Then send event `COIN` to transit into `UNLOCKED` state.
|
||||
|
||||
.Shell1
|
||||
[source,text]
|
||||
----
|
||||
sm>sm start
|
||||
Entry state LOCKED
|
||||
State machine started
|
||||
|
||||
sm>sm event COIN
|
||||
Exit state LOCKED
|
||||
Entry state UNLOCKED
|
||||
Event COIN send
|
||||
|
||||
sm>sm state
|
||||
UNLOCKED
|
||||
----
|
||||
|
||||
Open second shell instance and start a state machine. You should see
|
||||
that distributed state `UNLOCKED` is entered instead of default
|
||||
initial state `LOCKED`.
|
||||
|
||||
.Shell2
|
||||
[source,text]
|
||||
----
|
||||
sm>sm start
|
||||
State machine started
|
||||
|
||||
sm>sm state
|
||||
UNLOCKED
|
||||
----
|
||||
|
||||
Then from either of a shells(we use second instance here) send event
|
||||
`PUSH` to transit from `UNLOCKED` into `LOCKED` state.
|
||||
|
||||
.Shell2
|
||||
[source,text]
|
||||
----
|
||||
sm>sm event PUSH
|
||||
Exit state UNLOCKED
|
||||
Entry state LOCKED
|
||||
Event PUSH send
|
||||
----
|
||||
|
||||
In other shell you should see state getting changed automatically
|
||||
based on distributed state kept in Zookeeper.
|
||||
|
||||
.Shell1
|
||||
[source,text]
|
||||
----
|
||||
sm>Exit state UNLOCKED
|
||||
Entry state LOCKED
|
||||
----
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ simply made code snippets less verbose by leaving other needed parts
|
||||
away.
|
||||
====
|
||||
|
||||
[[statemachine-config]]
|
||||
=== Configuring States
|
||||
We'll get into more complex configuration examples a bit later but
|
||||
lets first start with a something simple. For most simple state
|
||||
@@ -208,6 +209,7 @@ states from a regions.
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetU]
|
||||
----
|
||||
|
||||
[[statemachine-config-commonsettings]]
|
||||
=== Configuring Common Settings
|
||||
Some of a common state machine configuration can be set via a
|
||||
`ConfigurationConfigurer`. This allows to set `BeanFactory`,
|
||||
@@ -216,7 +218,7 @@ and register `StateMachineListener` instances.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetY]
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetYA]
|
||||
----
|
||||
|
||||
State machine `autoStartup` flag is disabled by default because all
|
||||
@@ -236,6 +238,18 @@ start/stop events. Naturally it is not possible to listen a state
|
||||
machine start events if `autoStartup` is enabled unless listener can
|
||||
be registered during a configuration phase.
|
||||
|
||||
`DistributedStateMachine` is configured via `withDistributed()` which
|
||||
allows to set a `StateMachineEnsemble` which if exists automatically
|
||||
wraps created `StateMachine` with `DistributedStateMachine` and
|
||||
enables distributed mode.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetYB]
|
||||
----
|
||||
|
||||
More about distributed states, refer to section <<sm-distributed>>.
|
||||
|
||||
[[sm-factories]]
|
||||
== State Machine Factories
|
||||
There are use cases when state machine needs to be created dynamically
|
||||
@@ -523,3 +537,143 @@ include::samples/DocsConfigurationSampleTests.java[tags=snippetK]
|
||||
In your own bean you can then use this _@StatesOnTransition_ as is and
|
||||
use type safe `source` and `target`.
|
||||
|
||||
[[sm-accessor]]
|
||||
== State Machine Accessor
|
||||
`StateMachine` is a main interface to communicate with a state machine
|
||||
itself. Time to time there is a need to get more dynamical and
|
||||
programmatic access to internal structures of a state machine and its
|
||||
nested machines and regions. For these use cases a `StateMachine` is
|
||||
exposing a functional interface `StateMachineAccessor` which provides
|
||||
an interface to get access to individual `StateMachine` and
|
||||
`Region` instances.
|
||||
|
||||
`StateMachineFunction` is a simple functional interface which allows
|
||||
to apply `StateMachineAccess` interface into a state machine. With
|
||||
jdk7 these will create a little verbose code but with jdk8 lambdas
|
||||
things look relatively non-verbose.
|
||||
|
||||
Method `doWithAllRegions` gives access to all `Region` instances in
|
||||
a state machine.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetZA]
|
||||
----
|
||||
|
||||
Method `doWithRegion` gives access to single `Region` instance in a
|
||||
state machine.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetZB]
|
||||
----
|
||||
|
||||
Method `withAllRegions` gives access to all `Region` instances in
|
||||
a state machine.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetZC]
|
||||
----
|
||||
|
||||
Method `withRegion` gives access to single `Region` instance in a
|
||||
state machine.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetZD]
|
||||
----
|
||||
|
||||
[[sm-interceptor]]
|
||||
== State Machine Interceptor
|
||||
Instead of using a `StateMachineListener` interface one option is to
|
||||
use a `StateMachineInterceptor`. One conceptual difference is that an
|
||||
interceptor can be used to intercept and stop a current state
|
||||
change or transition logic. Instead of implementing full interface,
|
||||
adapter class `StateMachineInterceptorAdapter` can be used to override
|
||||
default no-op methods.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
There is one recipe <<statemachine-recipes-persist>> and one sample
|
||||
<<statemachine-examples-persist>> which are related to use of an
|
||||
interceptor.
|
||||
====
|
||||
|
||||
Interceptor can be registered via `StateMachineAccessor`. Concept of
|
||||
an interceptor is relatively deep internal feature and thus is not
|
||||
exposed directly via `StateMachine` interface.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetZH]
|
||||
----
|
||||
|
||||
|
||||
[[sm-persist]]
|
||||
== Persisting State Machine
|
||||
Traditionally an instance of a state machine is used as is within a
|
||||
running program. More dynamic behaviour is possible to achieve via
|
||||
dynamic builders and factories which allows state machine
|
||||
instantiation on-demand. Building an instance of a state machine is
|
||||
relatively heavy operation so if there is a need to i.e. handle
|
||||
arbitrary state change in a database using a state machine we need to
|
||||
find a better and faster way to do it.
|
||||
|
||||
Persist feature allows user to save a state of a state machine itself
|
||||
into an external repository and later reset a state machine based of
|
||||
serialized state. For example if you have a database table keeping
|
||||
orders it would be way too expensive to update order state via a state
|
||||
machine if a new instance would need to be build for every change.
|
||||
Persist feature allows you to reset a state machine state without
|
||||
instantiating a new state machine instance.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
There is one recipe <<statemachine-recipes-persist>> and one sample
|
||||
<<statemachine-examples-persist>> which provides more info about
|
||||
persisting states.
|
||||
====
|
||||
|
||||
While it is possible to build a custom persistence feature using a
|
||||
`StateMachineListener` it has one conceptual problem. When listener
|
||||
notifies a change of state, state change has already happened. If a
|
||||
custom persistent method within a listener fails to update serialized
|
||||
state in an external repository, state in a state machine and state in
|
||||
an external repository are then in inconsistent state.
|
||||
|
||||
State machine interceptor can be used instead of where attempt to save
|
||||
serialized state into an external storage is done during the a state
|
||||
change within a state machine. If this interceptor callback fails,
|
||||
state change attempt will be halted and instead of ending into an
|
||||
inconsistent state, user can then handle this error manually. Using
|
||||
the interceptors are discussed in <<sm-interceptor>>.
|
||||
|
||||
[[sm-distributed]]
|
||||
== Using Distributed States
|
||||
Distributed state is probably one of a most compicated concepts of a
|
||||
Spring State Machine. What exactly is a distributed state? A state
|
||||
within a single state machine is naturally really simple to understand
|
||||
but when there is a need to introduce a shared distributed state
|
||||
thoughout a state machines, things will get a little complicated.
|
||||
|
||||
For configuration support see section
|
||||
<<statemachine-config-commonsettings>> and actual usage example see
|
||||
sample <<statemachine-examples-zookeeper>>.
|
||||
|
||||
`Distributed State Machine` is implemented via a
|
||||
`DistributedStateMachine` class which simply wraps an actual instance
|
||||
of a `StateMachine`. `DistributedStateMachine` intercepts
|
||||
communication with a `StateMachine` instance and works with
|
||||
distributed state abstractions handled via interface
|
||||
`StateMachineEnsemble`. Depending on an actual implementation
|
||||
`StateMachinePersist` interface may also be used to serialize a
|
||||
`StateMachineContext` which contains enought information to reset a
|
||||
`StateMachine`.
|
||||
|
||||
While `Distributed State Machine` is implemented via an abstraction,
|
||||
only one implementation currently exists based on `Zookeeper`.
|
||||
|
||||
Current technical documentation of a `Zookeeker` based distributed
|
||||
state machine can be found from an appendice <<appendices-zookeeper>>.
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.access.StateMachineAccess;
|
||||
import org.springframework.statemachine.access.StateMachineFunction;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.action.SpelExpressionAction;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
@@ -46,17 +48,19 @@ import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineBuilder;
|
||||
import org.springframework.statemachine.config.StateMachineBuilder.Builder;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.StateMachineBuilder.Builder;
|
||||
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.config.configurers.StateConfigurer.History;
|
||||
import org.springframework.statemachine.ensemble.StateMachineEnsemble;
|
||||
import org.springframework.statemachine.event.StateMachineEvent;
|
||||
import org.springframework.statemachine.guard.Guard;
|
||||
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
|
||||
import org.springframework.statemachine.state.State;
|
||||
import org.springframework.statemachine.support.StateMachineInterceptor;
|
||||
import org.springframework.statemachine.transition.Transition;
|
||||
|
||||
/**
|
||||
@@ -735,7 +739,7 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
// tag::snippetY[]
|
||||
// tag::snippetYA[]
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config17
|
||||
@@ -754,6 +758,118 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippetY[]
|
||||
// end::snippetYA[]
|
||||
|
||||
// tag::snippetYB[]
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config18
|
||||
extends EnumStateMachineConfigurerAdapter<States, Events> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<States, Events> config)
|
||||
throws Exception {
|
||||
config
|
||||
.withDistributed()
|
||||
.ensemble(stateMachineEnsemble());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StateMachineEnsemble<States, Events> stateMachineEnsemble()
|
||||
throws Exception {
|
||||
// naturally not null but should return ensemble instance
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
// end::snippetYB[]
|
||||
|
||||
public static class AccessorSamples {
|
||||
|
||||
StateMachine<String, String> stateMachine = null;
|
||||
|
||||
void s1() {
|
||||
// tag::snippetZA[]
|
||||
stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<String,String>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<String, String> function) {
|
||||
function.setRelay(stateMachine);
|
||||
}
|
||||
});
|
||||
|
||||
stateMachine.getStateMachineAccessor()
|
||||
.doWithAllRegions(access -> access.setRelay(stateMachine));
|
||||
// end::snippetZA[]
|
||||
}
|
||||
|
||||
void s2() {
|
||||
// tag::snippetZB[]
|
||||
stateMachine.getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<String,String>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<String, String> function) {
|
||||
function.setRelay(stateMachine);
|
||||
}
|
||||
});
|
||||
|
||||
stateMachine.getStateMachineAccessor()
|
||||
.doWithRegion(access -> access.setRelay(stateMachine));
|
||||
// end::snippetZB[]
|
||||
}
|
||||
|
||||
void s3() {
|
||||
// tag::snippetZC[]
|
||||
for (StateMachineAccess<String, String> access : stateMachine.getStateMachineAccessor().withAllRegions()) {
|
||||
access.setRelay(stateMachine);
|
||||
}
|
||||
|
||||
stateMachine.getStateMachineAccessor().withAllRegions()
|
||||
.stream().forEach(access -> access.setRelay(stateMachine));
|
||||
// end::snippetZC[]
|
||||
}
|
||||
|
||||
void s4() {
|
||||
// tag::snippetZD[]
|
||||
stateMachine.getStateMachineAccessor()
|
||||
.withRegion().setRelay(stateMachine);
|
||||
// end::snippetZD[]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class InterceptorSamples {
|
||||
|
||||
StateMachine<String, String> stateMachine = null;
|
||||
|
||||
void s1() {
|
||||
// tag::snippetZH[]
|
||||
stateMachine.getStateMachineAccessor()
|
||||
.withRegion().addStateMachineInterceptor(new StateMachineInterceptor<String, String>() {
|
||||
|
||||
@Override
|
||||
public StateContext<String, String> preTransition(StateContext<String, String> stateContext) {
|
||||
return stateContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<String, String> state, Message<String> message,
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateContext<String, String> postTransition(StateContext<String, String> stateContext) {
|
||||
return stateContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<String, String> state, Message<String> message,
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
|
||||
}
|
||||
});
|
||||
// end::snippetZH[]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ public class Application {
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
//tag::snippetB[]
|
||||
@Configuration
|
||||
static class PersistHandlerConfig {
|
||||
|
||||
@@ -84,7 +85,9 @@ public class Application {
|
||||
}
|
||||
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
//tag::snippetC[]
|
||||
public static class Order {
|
||||
int id;
|
||||
String state;
|
||||
@@ -100,6 +103,7 @@ public class Application {
|
||||
}
|
||||
|
||||
}
|
||||
//end::snippetC[]
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Bootstrap.main(args);
|
||||
|
||||
@@ -36,8 +36,10 @@ public class Persist {
|
||||
|
||||
private final PersistStateMachineHandler handler;
|
||||
|
||||
//tag::snippetA[]
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
//end::snippetA[]
|
||||
|
||||
private final PersistStateChangeListener listener = new LocalPersistStateChangeListener();
|
||||
|
||||
@@ -62,6 +64,7 @@ public class Persist {
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
//tag::snippetB[]
|
||||
public void change(int order, String event) {
|
||||
Order o = jdbcTemplate.queryForObject("select id, state from orders where id = ?", new Object[]{order}, new RowMapper<Order>() {
|
||||
public Order mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
@@ -70,7 +73,9 @@ public class Persist {
|
||||
});
|
||||
handler.handleEventWithState(MessageBuilder.withPayload(event).setHeader("order", order).build(), o.state);
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
//tag::snippetC[]
|
||||
private class LocalPersistStateChangeListener implements PersistStateChangeListener {
|
||||
|
||||
@Override
|
||||
@@ -82,5 +87,6 @@ public class Persist {
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::snippetC[]
|
||||
|
||||
}
|
||||
|
||||
@@ -32,18 +32,19 @@ import org.springframework.statemachine.zookeeper.ZookeeperStateMachineEnsemble;
|
||||
@Configuration
|
||||
public class Application {
|
||||
|
||||
//tag::snippetA[]
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class StateMachineConfig
|
||||
extends StateMachineConfigurerAdapter<String, String> {
|
||||
|
||||
//tag::snippetA[]
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception {
|
||||
config
|
||||
.withDistributed()
|
||||
.ensemble(stateMachineEnsemble());
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states)
|
||||
@@ -69,6 +70,7 @@ public class Application {
|
||||
.event("PUSH");
|
||||
}
|
||||
|
||||
//tag::snippetB[]
|
||||
@Bean
|
||||
public StateMachineEnsemble<String, String> stateMachineEnsemble() throws Exception {
|
||||
return new ZookeeperStateMachineEnsemble<String, String>(curatorClient(), "/foo");
|
||||
@@ -79,27 +81,20 @@ public class Application {
|
||||
CuratorFramework client = CuratorFrameworkFactory.builder().defaultData(new byte[0])
|
||||
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
||||
.connectString("localhost:2181").build();
|
||||
// for testing we start it here, thought initiator
|
||||
// is trying to start it if not already done
|
||||
client.start();
|
||||
return client;
|
||||
}
|
||||
|
||||
//end::snippetB[]
|
||||
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
//tag::snippetB[]
|
||||
public static enum States {
|
||||
LOCKED, UNLOCKED
|
||||
}
|
||||
//end::snippetB[]
|
||||
|
||||
//tag::snippetC[]
|
||||
public static enum Events {
|
||||
COIN, PUSH
|
||||
}
|
||||
//end::snippetC[]
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Bootstrap.main(args);
|
||||
|
||||
Reference in New Issue
Block a user