Ref doc updates
This commit is contained in:
@@ -181,6 +181,7 @@ configure(rootProject) {
|
||||
from 'spring-statemachine-samples/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/'
|
||||
include '**/*.java'
|
||||
into 'docs/src/reference/asciidoc/samples'
|
||||
}
|
||||
|
||||
@@ -24,6 +24,17 @@ include::samples/Events.java[tags=snippetA]
|
||||
== State Machine Concepts
|
||||
This appendix provides generic information about state machines.
|
||||
|
||||
=== Quick Example
|
||||
Assuming we have states _STATE1_, _STATE2_ and events _EVENT1_,
|
||||
_EVENT2_, logic of state machine can be defined as shown in below
|
||||
quick example.
|
||||
|
||||
image::images/statechart0.png[]
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/IntroSample.java[tags=snippetA]
|
||||
----
|
||||
|
||||
[glossary]
|
||||
=== Glossary
|
||||
@@ -75,28 +86,77 @@ to FALSE.
|
||||
A action is a behaviour executed during the triggering of the
|
||||
transition.
|
||||
|
||||
[[crashcourse]]
|
||||
=== A State Machines Crash Course
|
||||
This appendix provides generic crash course to a state machine
|
||||
concepts.
|
||||
|
||||
==== States
|
||||
TBD.
|
||||
A state is a model which a state machine can be in. It is always
|
||||
easier to describe state as a real world example rather than trying to
|
||||
abstract concepts with a generic documentation. For example lets take
|
||||
a simple example of a keyboard most of use are using every single day.
|
||||
If you have a full keyboard which has normal keys on a left side and
|
||||
the numeric keypad on a right side you may have noticed that the
|
||||
numeric keypad may be in a two different stated depending whether
|
||||
numlock is activated or not. If it is not active then typing will
|
||||
result navigation using arrows, etc. If numpad is active then typing
|
||||
will result numbers to be used. Essentially numpad part of a keyboard
|
||||
can be in two different states.
|
||||
|
||||
To relate state concept to programming it means that instead of using
|
||||
flags, nested if/else/break clauses or other impractical logic you
|
||||
simply rely on state, state variables or other interaction with a
|
||||
state machine.
|
||||
|
||||
==== Guard Conditions
|
||||
TBD.
|
||||
Guard conditions are expressions which evaluates either to *TRUE* or
|
||||
*FALSE* based on extended state variables and event parameters. Guards
|
||||
are used with actions and transitions to dynamically choose if
|
||||
particular action or transition should be executed. Aspects of guards,
|
||||
event parameters and extended state variables are simply to make state
|
||||
machine desing much more simple.
|
||||
|
||||
==== Events
|
||||
TBD.
|
||||
Event is the most used trigger behaviour to drive a state machine.
|
||||
There are other ways to trigger behaviour to happen in state machine
|
||||
like a timer but events are the ones which really allows user to
|
||||
interact with a state machine. Events are also called as signals to
|
||||
possibly alter a state machine state.
|
||||
|
||||
==== Transitions
|
||||
TBD.
|
||||
A transition is a relationship between a source state and a target
|
||||
state. A switch from a state to another is a _state transition_ caused
|
||||
by a _trigger_.
|
||||
|
||||
==== Actions
|
||||
TBD.
|
||||
Actions are the ones which really glues state machine state changes
|
||||
with a users own code. State machine can execute action on various
|
||||
changes and steps in a state machine like entering or exiting a state,
|
||||
or doing a state transition.
|
||||
|
||||
==== Hierarchical State Machines
|
||||
TBD.
|
||||
Concept of a hierarchical state machine is used to simplify state
|
||||
design when particular states can only exist together.
|
||||
|
||||
==== Regions
|
||||
TBD.
|
||||
Regions which are also called as orthogonal regions are usually viewed
|
||||
as exclusive OR operation applied to a states. Concept of a region in
|
||||
terms of a state machine is usually a little difficult to understang
|
||||
but things gets a little simpler with a simple example.
|
||||
|
||||
Some of use have a full size keyboard with main keys on a left side and numeric
|
||||
keys on a right side. You've probably noticed that both sides really
|
||||
have their own state which you see if you press a numlock key which
|
||||
only alters behaviour of numbad itself. If you don't have a full size
|
||||
keyboard you can buy a simple external usb numbad having only numbad
|
||||
part of a keys. If left and right side can freely exist without the
|
||||
other they must have a totally different states which means they are
|
||||
operating on different state machines.
|
||||
|
||||
It would be a little inconvenient to handle two different
|
||||
statemachines as totally separate entities because in a sense they are
|
||||
still working together in a sense. This is why orthogonal regios can
|
||||
combine together a multiple simultaneous states withing a single state
|
||||
in a state machine.
|
||||
|
||||
|
||||
BIN
docs/src/reference/asciidoc/images/statechart0.png
Normal file
BIN
docs/src/reference/asciidoc/images/statechart0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
BIN
docs/src/reference/asciidoc/images/statechart3.png
Normal file
BIN
docs/src/reference/asciidoc/images/statechart3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -1,10 +1,49 @@
|
||||
[[introduction]]
|
||||
= Introduction
|
||||
|
||||
Spring State Machine(SSM) is a framework for application developers to
|
||||
use state machine concepts with Spring.
|
||||
use traditional state machine concepts with Spring applications.
|
||||
|
||||
Before you continue it's worth to go through appendices <<glossary>>
|
||||
and <<crashcourse>> to get a generic idea of what state machines are
|
||||
mostly because rest of a documentation expects reader to be fairly
|
||||
familiar with state machine concepts.
|
||||
|
||||
== Requirements
|
||||
|
||||
Spring Statemachine {revnumber} is built and tested with JDK 7, Spring
|
||||
Spring Statemachine {revnumber} is built and tested with JDK 7 and Spring
|
||||
Framework {spring-version}.
|
||||
|
||||
== Background
|
||||
State machines are powerful because behaviour is always supposed to be
|
||||
consistent and relatively easily debugged due to ways how operational
|
||||
rules are written in stone when machine is started. Idea is that your
|
||||
application is and may exist in a finite number of states and then something
|
||||
happens which takes your application from one state to the next.
|
||||
|
||||
It is much easier to design high level logic outside of your
|
||||
application and then interact witha state machine with a various
|
||||
different ways.
|
||||
|
||||
Traditionally state machines are added to a existing project when
|
||||
developer realizes that code base is starting to look like a plate
|
||||
full of spaghetti. Spaghetti code looks like never ending hierarchical
|
||||
structure of IFs, ELSEs and BREAK clauses and probably compiler should
|
||||
ask developer to go home when things are starting to look too complex.
|
||||
|
||||
== Usage Scenarios
|
||||
|
||||
Project is a good candiate to use state machines if:
|
||||
|
||||
* Application or part of its structure can be represented as states.
|
||||
* You want to split complex logic into smaller manageable tasks.
|
||||
* Application is already suffering concurrency issues with i.e.
|
||||
something happening asynchronously.
|
||||
|
||||
You are already trying to implement a state machine if:
|
||||
|
||||
* Use of boolean flags or enums to model situations.
|
||||
* Having variables which only have meaning for some part of your
|
||||
application lifecycle.
|
||||
* Looping throught if/else structure and checking if particular flag or
|
||||
enum is set and then making further exceptions what to do when certain
|
||||
combination of your flags and enums exists or doesn't exist together.
|
||||
|
||||
|
||||
@@ -10,12 +10,6 @@ Moore presented another paper which is known as a Moore Machine. If
|
||||
you're ever read anything about state machines, names Mealy and Moore
|
||||
should have popped up at some point.
|
||||
|
||||
Traditionally state machines are added to a existing project when
|
||||
developer realizes that code base is starting to look like a plate
|
||||
full of spaghetti. Spaghetti code looks like never ending hierarchical
|
||||
structure of IFs, ELSEs and BREAK clauses and probably compiler should
|
||||
ask developer to go home when things are starting to look too complex.
|
||||
|
||||
This reference documentations contains following parts.
|
||||
|
||||
<<introduction>> introduction to this reference documentation
|
||||
|
||||
@@ -76,11 +76,10 @@ Event PUSH send
|
||||
----
|
||||
|
||||
== Showcase
|
||||
|
||||
Showcase is a complex state machine showing all possible transition
|
||||
topologies up to four levels of state nesting.
|
||||
|
||||
image::images/statechart2.png[]
|
||||
image::images/statechart2.png[width=200]
|
||||
|
||||
.States
|
||||
[source,java,indent=0]
|
||||
@@ -111,3 +110,122 @@ include::samples/demo/showcase/Application.java[tags=snippetD]
|
||||
----
|
||||
include::samples/demo/showcase/Application.java[tags=snippetE]
|
||||
----
|
||||
|
||||
== CD Player
|
||||
CD Player is a sample which resembles better use case of most of use have
|
||||
used in a real world. CD Player itself is a really simple entity where
|
||||
user can open a deck, inser or change a disk, then drive player
|
||||
functionality by pressing various buttons like _eject_, _play_,
|
||||
_stop_, _pause_, _rewind_ and _backward_.
|
||||
|
||||
How many of use have really given a thought of what it will take to
|
||||
make a code for a CD Player which interacts with a hardware. Yes,
|
||||
concept of a player is overly simple but if you look behind a scenes
|
||||
things actually get a bit convoluted.
|
||||
|
||||
You've probably noticed that if your deck is open and you press play,
|
||||
deck will close and a song will start to play if CD was inserted in
|
||||
a first place. In a sense when deck is open you first need to close
|
||||
it and then try to start playing if cd is actually instered. Hopefully
|
||||
you have now realised that a simple CD Player is not anymore so simple.
|
||||
Sure you can wrap all this with a simple class with few boolean variables
|
||||
and probably few nested if/else clauses, that will do the job, but what
|
||||
about if you need to make all this behaviour much more complex, do you
|
||||
really want to keep adding more flags and if/else clauses.
|
||||
|
||||
image::images/statechart3.png[]
|
||||
|
||||
Lets go throught how this sample and its state machine is designed and
|
||||
how those two interacts with each other.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetA]
|
||||
----
|
||||
|
||||
What we did in above configuration:
|
||||
|
||||
* We used EnumStateMachineConfigurerAdapter to configure states and
|
||||
transitions.
|
||||
* States _CLOSED_ and _OPEN_ are defined as substates of _IDLE_,
|
||||
states _PLAYING_ and _PAUSED_ are defined as substates of _BUSY_.
|
||||
* With state _CLOSED_ we added entry action as bean
|
||||
_closedEntryAction_.
|
||||
* With transition we mostly mapped events to expected state
|
||||
transitions like _EJECT_ closing and opening a deck, _PLAY_, _STOP_
|
||||
and _PAUSE_ doing their natural transitions. Few words to mention
|
||||
what we did for other transitions.
|
||||
** With source state _PLAYING_ we added a timer trigger which is
|
||||
needed to automatically track elapsed time within a playing track and
|
||||
to have facility to make a decision when to switch to next track.
|
||||
** With event _PLAY_ if source state is _IDLE_ and target state is
|
||||
_BUSY_ we defined action _playAction_ and guard _playGuard_.
|
||||
** Lastly with event _LOAD_ and state _OPEN_ we defined internal
|
||||
transition with action _loadAction_ which will insert cd disc into
|
||||
extended state variables.
|
||||
|
||||
This machine only have six states which are introduced as an enum.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetB]
|
||||
----
|
||||
|
||||
Events represent, in a sense in this example, what buttons user would
|
||||
press and if user loads a cd disc into a deck.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetC]
|
||||
----
|
||||
|
||||
Beans _cdPlayer_ and _library_ are just used with a sample to drive
|
||||
the application.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetD]
|
||||
----
|
||||
|
||||
We can define extended state variable key as simple enums.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetE]
|
||||
----
|
||||
|
||||
We wanted to make this samply type safe so we're defining our own
|
||||
annotation _@StatesOnTransition_ which have a mandatory meta
|
||||
annotation _@OnTransition_.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetF]
|
||||
----
|
||||
|
||||
_ClosedEntryAction_ is a entry action for state _CLOSED_ to simply
|
||||
send and _PLAY_ event to a statemachine if cd disc is present.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetG]
|
||||
----
|
||||
|
||||
_LoadAction_ is simply updating extended state variable if event
|
||||
headers contained information about a cd disc to load.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetH]
|
||||
----
|
||||
|
||||
_PlayAction_ is simply resetting player elapsed time which is kept as
|
||||
an extended state variable.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetI]
|
||||
----
|
||||
|
||||
_PlayGuard_ is used to guard transition from _IDLE_ to _BUSY_ with
|
||||
event _PLAY_ if extended state variable _CD_ doesn't indicate that cd
|
||||
disc has been loaded.
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/demo/cdplayer/Application.java[tags=snippetJ]
|
||||
----
|
||||
|
||||
Now lets see how this cd player works and we can go a little deeper in
|
||||
its state machine logic.
|
||||
|
||||
@@ -73,36 +73,115 @@ Configuration for state machine factory is exactly same as you've seen
|
||||
in various examples in this document where state machine configuration
|
||||
is hard coded.
|
||||
|
||||
Actually creating a state machine using _@EnableStateMachine_ will
|
||||
work via factory so _@EnableStateMachineFactory_ is merely exposing
|
||||
that factory via its interface.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetF]
|
||||
----
|
||||
|
||||
=== Factory Limitations
|
||||
Now that you've used _@EnableStateMachineFactory_ to create a factory
|
||||
instead of a state machine bean, it can be injected and used as is to
|
||||
request new state machines.
|
||||
|
||||
xxx
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetL]
|
||||
----
|
||||
|
||||
=== Factory Limitations
|
||||
Current limitation of factory is that all actions and guard it is
|
||||
associating with created state machine will share a same instances.
|
||||
This means that from your actions and guard you will need to
|
||||
specifially handle a case that same bean will be called by a different
|
||||
state machines. This limitation is something which will be resolved in
|
||||
future releases.
|
||||
|
||||
[[sm-listeners]]
|
||||
== State Machine Listeners
|
||||
|
||||
== Listening State Machine Events
|
||||
There are use cases where you just want to know what is happening with
|
||||
a state machine, react to something or simply get logging for
|
||||
debugging purposes. SSM provides interfaces for adding listeners which
|
||||
then gives an option to get callback when various state changes,
|
||||
actions, etc are happening.
|
||||
|
||||
[[sm-context]]
|
||||
== Context Integration
|
||||
You basically have two options, either to listen Spring application
|
||||
context events or directly attach listener to a state machine. Both of
|
||||
these basically will provide same information where one is producing
|
||||
events as event classes and other producing callbacks via a listener
|
||||
interface. Both of these have pros and cons which will be discussed later.
|
||||
|
||||
TBD
|
||||
|
||||
=== Annotation Support
|
||||
|
||||
TBD
|
||||
|
||||
=== Context Events
|
||||
=== Application Context Events
|
||||
Application context events classes are _OnTransitionStartEvent_,
|
||||
_OnTransitionEvent_, _OnTransitionEndEvent_, _OnStateExitEvent_,
|
||||
_OnStateEntryEvent_ and _OnStateChangedEvent_. There can be used as is
|
||||
as spring typed _ApplicationListener_ class but all also share a
|
||||
common class _StateMachineEvent_ which can be used to get events for
|
||||
all.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetG]
|
||||
----
|
||||
|
||||
=== State Machine Listener
|
||||
For using _StateMachineListener_ you can either extend it and
|
||||
implement all callback methods or use _StateMachineListenerAdapter_
|
||||
class which contains stub method implementations and choose which ones
|
||||
to override.
|
||||
|
||||
=== Limitations and Problems
|
||||
TBD ctx events may create too much traffic, etc.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetH]
|
||||
----
|
||||
|
||||
[[sm-context]]
|
||||
== Context Integration
|
||||
It is a little limited to do interaction with a state machine by
|
||||
either listening its events or using actions with states and
|
||||
transitions. Time to time this approach would be too limited and
|
||||
verbose to create interaction with the application a state machine is
|
||||
working with. For this specific use case we have made a spring style
|
||||
context intergration which easily attach state machine functionality
|
||||
into your beans.
|
||||
|
||||
=== Annotation Support
|
||||
_@WithStateMachine_ annotation can be used to associate a state
|
||||
machine with a existing bean. Withing this annotation a propertys
|
||||
_source_ and _target_ can be used to qualify a transition
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetI]
|
||||
----
|
||||
|
||||
Default _@OnTransition_ annotation can't be used with a state and
|
||||
event enums user have created due to java language limitations, thus
|
||||
string representation have to be used.
|
||||
|
||||
However if you want to have a type safe annotation it is possible to
|
||||
create a new annotation and use _@OnTransition_ as meta annotations.
|
||||
This user level annotation can make a reference to actual states and
|
||||
events enums and framework will try to match these in a same way.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::samples/DocsConfigurationSampleTests.java[tags=snippetJ]
|
||||
----
|
||||
|
||||
Above we created a _@StatesOnTransition_ annotation which defines
|
||||
`source` and `target` as a type safe manner.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
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`.
|
||||
|
||||
|
||||
16
docs/src/statecharts/statechart0.txt
Normal file
16
docs/src/statecharts/statechart0.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
+----------------------------------------+
|
||||
| SM |
|
||||
+----------------------------------------+
|
||||
| |
|
||||
| +----------+ +----------+ |
|
||||
| *-->| STATE1 | | STATE2 | |
|
||||
| +----------+ +----------+ |
|
||||
| | entry/ | | entry/ | |
|
||||
| | exit/ | | exit/ | |
|
||||
| | |--EVENT1->| | |
|
||||
| | | | | |
|
||||
| | |<-EVENT2--| | |
|
||||
| | | | | |
|
||||
| +----------+ +----------+ |
|
||||
| |
|
||||
+----------------------------------------+
|
||||
@@ -1,19 +0,0 @@
|
||||
+----------------------------------------------------------------+
|
||||
| SM |
|
||||
+----------------------------------------------------------------+
|
||||
| |
|
||||
| +----------------+ +----------------+ |
|
||||
| *-->| LOCKED | | UNLOCKED | |
|
||||
| +----------------+ +----------------+ |
|
||||
| +---| entry/ | | entry/ |---+ |
|
||||
| | | exit/ | | exit/ | | |
|
||||
| | | | | | | |
|
||||
| PUSH| | |---COIN-->| | |COIN |
|
||||
| | | | | | | |
|
||||
| | | | | | | |
|
||||
| | | |<--PUSH---| | | |
|
||||
| +-->| | | |<--+ |
|
||||
| | | | | |
|
||||
| +----------------+ +----------------+ |
|
||||
| |
|
||||
+----------------------------------------------------------------+
|
||||
@@ -15,21 +15,34 @@
|
||||
*/
|
||||
package org.springframework.statemachine.docs;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.AbstractStateMachineTests;
|
||||
import org.springframework.statemachine.StateContext;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.statemachine.action.Action;
|
||||
import org.springframework.statemachine.annotation.AnnoStates;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachineFactory;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.StateMachineFactory;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
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.transition.Transition;
|
||||
|
||||
/**
|
||||
* Tests for state machine configuration.
|
||||
@@ -185,7 +198,7 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
|
||||
|
||||
// tag::snippetG[]
|
||||
static class StateMachineEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
static class StateMachineApplicationEventListener implements ApplicationListener<StateMachineEvent> {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(StateMachineEvent event) {
|
||||
@@ -193,4 +206,78 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
}
|
||||
// end::snippetG[]
|
||||
|
||||
// tag::snippetH[]
|
||||
static class StateMachineEventListener extends StateMachineListenerAdapter<States, Events> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(State<States, Events> from, State<States, Events> to) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateEntered(State<States, Events> state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateExited(State<States, Events> state) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transition(Transition<States, Events> transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionStarted(Transition<States, Events> transition) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transitionEnded(Transition<States, Events> transition) {
|
||||
}
|
||||
}
|
||||
// end::snippetH[]
|
||||
|
||||
// tag::snippetI[]
|
||||
@WithStateMachine
|
||||
static class Bean1 {
|
||||
|
||||
@OnTransition(source = "S1", target = "S2")
|
||||
public void fromS1ToS2() {
|
||||
}
|
||||
}
|
||||
// end::snippetI[]
|
||||
|
||||
// tag::snippetJ[]
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@OnTransition
|
||||
static @interface StatesOnTransition {
|
||||
|
||||
States[] source() default {};
|
||||
|
||||
States[] target() default {};
|
||||
}
|
||||
// end::snippetJ[]
|
||||
|
||||
// tag::snippetK[]
|
||||
@WithStateMachine
|
||||
static class Bean2 {
|
||||
|
||||
@StatesOnTransition(source = States.S1, target = States.S2)
|
||||
public void fromS1ToS2() {
|
||||
}
|
||||
}
|
||||
// end::snippetK[]
|
||||
|
||||
// tag::snippetL[]
|
||||
static class Bean3 {
|
||||
|
||||
@Autowired
|
||||
StateMachineFactory<States, Events> factory;
|
||||
|
||||
void method() {
|
||||
StateMachine<States,Events> stateMachine = factory.getStateMachine();
|
||||
stateMachine.start();
|
||||
}
|
||||
}
|
||||
// end::snippetL[]
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 org.springframework.statemachine.docs;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.annotation.OnTransition;
|
||||
import org.springframework.statemachine.annotation.WithStateMachine;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
public class IntroSample {
|
||||
|
||||
// tag::snippetA[]
|
||||
static enum States {
|
||||
STATE1, STATE2
|
||||
}
|
||||
|
||||
static enum Events {
|
||||
EVENT1, EVENT2
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends EnumStateMachineConfigurerAdapter<States, Events> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<States, Events> states)
|
||||
throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(States.STATE1)
|
||||
.states(EnumSet.allOf(States.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<States, Events> transitions)
|
||||
throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source(States.STATE1).target(States.STATE2)
|
||||
.event(Events.EVENT1)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source(States.STATE2).target(States.STATE1)
|
||||
.event(Events.EVENT2);
|
||||
}
|
||||
}
|
||||
|
||||
@WithStateMachine
|
||||
static class MyBean {
|
||||
|
||||
@OnTransition(target = "STATE1")
|
||||
void toState1() {
|
||||
}
|
||||
|
||||
@OnTransition(target = "STATE2")
|
||||
void toState2() {
|
||||
}
|
||||
}
|
||||
// end::snippetA[]
|
||||
|
||||
}
|
||||
@@ -88,54 +88,29 @@ public class Application {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<States, Events> closedEntryAction() {
|
||||
return new Action<States, Events>() {
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
if (context.getTransition() != null && context.getTransition().getSource().getId() == States.CLOSED
|
||||
&& context.getMessageHeader(Variables.CD) != null) {
|
||||
context.getStateMachine().sendEvent(Events.PLAY);
|
||||
}
|
||||
}
|
||||
};
|
||||
public ClosedEntryAction closedEntryAction() {
|
||||
return new ClosedEntryAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<States, Events> loadAction() {
|
||||
return new Action<States, Events>() {
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Object cd = context.getMessageHeader(Variables.CD);
|
||||
context.getExtendedState().getVariables().put(Variables.CD, cd);
|
||||
}
|
||||
};
|
||||
public LoadAction loadAction() {
|
||||
return new LoadAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Action<States, Events> playAction() {
|
||||
return new Action<States, Events>() {
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
context.getExtendedState().getVariables().put(Variables.ELAPSEDTIME, 0l);
|
||||
}
|
||||
};
|
||||
public PlayAction playAction() {
|
||||
return new PlayAction();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Guard<States, Events> playGuard() {
|
||||
return new Guard<States, Events>() {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(StateContext<States, Events> context) {
|
||||
ExtendedState extendedState = context.getExtendedState();
|
||||
return extendedState.getVariables().get(Variables.CD) != null;
|
||||
}
|
||||
};
|
||||
public PlayGuard playGuard() {
|
||||
return new PlayGuard();
|
||||
}
|
||||
|
||||
}
|
||||
//end::snippetA[]
|
||||
|
||||
|
||||
//tag::snippetB[]
|
||||
public static enum States {
|
||||
// super state of PLAYING and PAUSED
|
||||
@@ -186,6 +161,52 @@ public class Application {
|
||||
}
|
||||
//end::snippetF[]
|
||||
|
||||
//tag::snippetG[]
|
||||
public static class ClosedEntryAction implements Action<States, Events> {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
if (context.getTransition() != null
|
||||
&& context.getTransition().getSource().getId() == States.CLOSED
|
||||
&& context.getMessageHeader(Variables.CD) != null) {
|
||||
context.getStateMachine().sendEvent(Events.PLAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
//end::snippetG[]
|
||||
|
||||
//tag::snippetH[]
|
||||
public static class LoadAction implements Action<States, Events> {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
Object cd = context.getMessageHeader(Variables.CD);
|
||||
context.getExtendedState().getVariables().put(Variables.CD, cd);
|
||||
}
|
||||
}
|
||||
//end::snippetH[]
|
||||
|
||||
//tag::snippetI[]
|
||||
public static class PlayAction implements Action<States, Events> {
|
||||
|
||||
@Override
|
||||
public void execute(StateContext<States, Events> context) {
|
||||
context.getExtendedState().getVariables().put(Variables.ELAPSEDTIME, 0l);
|
||||
}
|
||||
}
|
||||
//end::snippetI[]
|
||||
|
||||
//tag::snippetJ[]
|
||||
public static class PlayGuard implements Guard<States, Events> {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(StateContext<States, Events> context) {
|
||||
ExtendedState extendedState = context.getExtendedState();
|
||||
return extendedState.getVariables().get(Variables.CD) != null;
|
||||
}
|
||||
}
|
||||
//end::snippetJ[]
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Bootstrap.main(args);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user