The following example shows a typical `pom.xml` file, which was created by choosing various options at [https://start.spring.io](https://start.spring.io):
...
...
@@ -324,7 +324,7 @@ The expected Spring Boot-packaged fat-jar would be `target/demo-0.0.1-SNAPSHOT.j
| |You do not need the `libs-milestone` and `libs-snapshot` repositories for<br/>production development.|
## [](#developing-your-first-spring-statemachine-application)Developing Your First Spring Statemachine Application
## Developing Your First Spring Statemachine Application
You can start by creating a simple Spring Boot `Application` class
that implements `CommandLineRunner`. The following example shows how to do so:
...
...
@@ -430,9 +430,9 @@ State change to S2
These lines indicate that the machine you constructed
is moving from one state to another, as it should.
# [](#whatsnew)What’s New
# What’s New
## [](#in-1-1)In 1.1
## In 1.1
Spring Statemachine 1.1 focuses on security and better
interoperability with web applications. It includes the following:
...
...
@@ -464,7 +464,7 @@ interoperability with web applications. It includes the following:
* UI modeling support using Eclipse Papyrus. See [Eclipse Modeling Support](#sm-papyrus).
## [](#in-1-2)In 1.2
## In 1.2
Spring Statemachine 1.2 focuses on generic enhancements, better
UML support, and integrations with external config repositories.
...
...
@@ -485,7 +485,7 @@ It includes the following:
* Support for tracing and monitoring. See [Monitoring a State Machine](#sm-monitoring).
### [](#in-1-2-8)In 1.2.8
### In 1.2.8
Spring Statemachine 1.2.8 contains a bit more functionality than normally
not seen in a point release, but these changes did not merit a fork of
...
...
@@ -499,11 +499,11 @@ Spring Statemachine 1.3. It includes the following:
* Transition conflict policy. See[Configuring Common Settings](#statemachine-config-commonsettings)
## [](#in-2-0)In 2.0
## In 2.0
Spring Statemachine 2.0 focuses on Spring Boot 2.x support.
### [](#in-2-0-0)In 2.0.0
### In 2.0.0
Spring Statemachine 2.0.0 includes the following:
...
...
@@ -511,7 +511,7 @@ Spring Statemachine 2.0.0 includes the following:
* The `spring-statemachine-boot` module has been renamed to `spring-statemachine-autoconfigure`.
## [](#in-3-0)In 3.0
## In 3.0
Spring Statemachine 3.0.0 focuses on adding a Reactive support. Moving from `2.x` to `3.x` is
introducing some breaking changes which are detailed in [Reactor Migration Guide](#appendix-reactormigrationguide).
...
...
@@ -525,7 +525,7 @@ in a future releases.
At this point most of a documentation has been changed to showcase reactive interfaces
while we still keep some notes around to users still using old blocking methods.
# [](#statemachine)Using Spring Statemachine
# Using Spring Statemachine
This part of the reference documentation explains the core functionality
that Spring Statemachine provides to any Spring based application.
...
...
@@ -580,7 +580,7 @@ It includes the following topics:
* [Repository Support](#sm-repository) describes the state machine repository config support.
## [](#sm-config)Statemachine Configuration
## Statemachine Configuration
One of the common tasks when using a state machine is to design its
runtime configuration. This chapter focuses on how Spring
...
...
@@ -591,7 +591,7 @@ manageable.
| |Configuration examples in this section are not feature complete. That is,<br/>you always need to have definitions of both states and transitions.<br/>Otherwise, state machine configuration would be ill-formed. We have<br/>simply made code snippets less verbose by leaving other needed parts<br/>out.|
### [](#statemachine-config-states)Configuring States
### Configuring States
We get into more complex configuration examples a bit later in this guide, but
we first start with something simple. For most simple state
...
...
@@ -663,7 +663,7 @@ public class Config1Strings
| |Using enumerations brings a safer set of states and event types but<br/>limits possible combinations to compile time. Strings do not have this<br/>limitation and let you use more dynamic ways to build state<br/>machine configurations but do not allow same level of safety.|
You can define actions to be executed with transitions and states.
An action is always run as a result of a transition that
...
...
@@ -958,7 +958,7 @@ with states `S1`, `S2`, and `S3`. We need to clarify what is going on here:
| |Defining action with `initial()` function only runs a particular<br/>action when a state machine or sub state is started. This action<br/>is an initializing action that is run only once. An action defined<br/>with `state()` is then run if the state machine transitions back<br/>and forward between initial and non-initial states.|
Logic similar to the logic that handles errors in state transitions is also available
for entry to a state and exit from a state.
...
...
@@ -1163,13 +1163,13 @@ public class Config55
}
```
### [](#configuring-pseudo-states)Configuring Pseudo States
### Configuring Pseudo States
*Pseudo state* configuration is usually done by configuring states and
transitions. Pseudo states are automatically added to state machine as
states.
#### [](#initial-state)Initial State
#### Initial State
You can mark a particular state as initial state by using the `initial()`method. This initial action is good, for example, to initialize
extended state variables. The following example shows how to use the `initial()` method:
...
...
@@ -1204,7 +1204,7 @@ public class Config11
}
```
#### [](#terminate-state)Terminate State
#### Terminate State
You can mark a particular state as being an end state by using the `end()` method.
You can do so at most once for each individual sub-machine or region.
...
...
@@ -1229,7 +1229,7 @@ public class Config1Enums
}
```
#### [](#state-history)State History
#### State History
You can define state history once for each individual state machine.
You need to choose its state identifier and set either `History.SHALLOW` or`History.DEEP`. The following example uses `History.SHALLOW`:
...
...
@@ -1276,7 +1276,7 @@ state transition is not defined, then normal entry into a region is
done. This default transition is also used if a machine’s history is
a final state.
#### [](#choice-state)Choice State
#### Choice State
Choice needs to be defined in both states and transitions to work
properly. You can mark a particular state as being a choice state by using the `choice()`method. This state needs to match source state when a transition is
...
...
@@ -1394,7 +1394,7 @@ public class Config23
| |Junction have same api format meaning actions can be defined<br/>similarly.|
#### [](#statemachine-config-states-junction)Junction State
#### Junction State
You need to define a junction in both states and transitions for it to work
properly. You can mark a particular state as being a choice state by using the `junction()`method. This state needs to match the source state when a transition is
...
...
@@ -1464,7 +1464,7 @@ public class Config20
| |The difference between choice and junction is purely academic, as both are<br/>implemented with `first/then/last` structures . However, in theory, based<br/>on UML modeling, `choice` allows only one incoming transition while`junction` allows multiple incoming transitions. At a code level, the<br/>functionality is pretty much identical.|
You must define a fork in both states and transitions for it to work
properly. You can mark a particular state as being a choice state by using the `fork()`method. This state needs to match source state when a transition is
...
...
@@ -1518,7 +1518,7 @@ public class Config14
}
```
#### [](#join-state)Join State
#### Join State
You must define a join in both states and transitions for it to work
properly. You can mark aparticular state as being a choice state by using the `join()`method. This state does not need to match either source states or a
...
...
@@ -1636,7 +1636,7 @@ public class Config22
}
```
#### [](#statemachine-config-states-exitentry)Exit and Entry Point States
#### Exit and Entry Point States
You can use exit and entry points to do more controlled exit and entry
from and into a submachine.
...
...
@@ -1693,7 +1693,7 @@ As shown in the preceding, you need to mark particular states as being `exit` an
and also specify `withExit()` and `withEntry()`, where those states
exit and entry respectively.
### [](#statemachine-config-commonsettings)Configuring Common Settings
### Configuring Common Settings
You can set part of a common state machine configuration by using`ConfigurationConfigurer`. With it you can set `BeanFactory` and an autostart flag
for a state machine. It also lets you register `StateMachineListener` instances,
...
...
@@ -1816,7 +1816,7 @@ For more about config model, see [StateMachine Config Model](#devdocs-configmode
| |The `withSecurity`, `withMonitoring` and `withPersistence` configuration methods<br/>are documented in [State Machine Security](#sm-security), [Monitoring a State Machine](#sm-monitoring), and[Using `StateMachineRuntimePersister`](#sm-persist-statemachineruntimepersister), respectively.|
`StateMachineModelFactory` is a hook that lets you configure a statemachine model
without using a manual configuration. Essentially, it is a third-party
...
...
@@ -1878,7 +1878,7 @@ public static class CustomStateMachineModelFactory implements StateMachineModelF
You can find an example of using this model factory integration in[Eclipse Modeling Support](#sm-papyrus). You can find more generic info about custom model integration
in [Developer Documentation](#devdocs).
### [](#statemachine-config-thingstoremember)Things to Remember
### Things to Remember
When defining actions, guards, or any other references from a
configuration, it pays to remember how Spring Framework works
...
...
@@ -1950,7 +1950,7 @@ public class Config1
}
```
## [](#sm-machineid)State Machine ID
## State Machine ID
Various classes and interfaces use `machineId` either as a variable or as a
parameter in methods. This section takes a closer look at how`machineId` relates to normal machine operation and instantiation.
...
...
@@ -1961,7 +1961,7 @@ following logs or doing deeper debugging. Having a lot of different
machine instances quickly gets developers lost in translation if there is
no easy way to identify these instances. As a result, we added the option to set the`machineId`.
| |The manual builder (see [State Machine through a Builder](#state-machine-via-builder)) uses the same configuration<br/>interface, meaning that the behavior is equivalent.|
Behind the scenes, all machine configurations are first translated into a`StateMachineModel` so that `StateMachineFactory` need not know
from where the configuration originated, as a machine can be built from
...
...
@@ -2015,7 +2015,7 @@ without a known machine id.
| |Currently, `UmlStateMachineModelFactory` does not distinguish between<br/>different machine IDs, as UML source is always coming from the same<br/>file. This may change in future releases.|
The current limitation of factory is that all the actions and guard with which it
associates a state machine share the same instance.
...
...
@@ -2078,7 +2078,7 @@ specifically handle the case in which the same bean is called by different
state machines. This limitation is something that will be resolved in
future releases.
### [](#state-machine-via-builder)State Machine through a Builder
### State Machine through a Builder
Using adapters (as shown above) has a limitation imposed by its
requirement to work through Spring `@Configuration` classes and the
...
...
@@ -2131,7 +2131,7 @@ returned from a `withConfiguration()` to setup `autoStart` and `BeanFactory`.
You can also use one to register a `StateMachineListener`. If a `StateMachine`instance returned from a builder is registered as a bean by using `@Bean`, `BeanFactory`is attached automatically. If you use instances outside of a spring application context,
you must use these methods to set up the needed facilities.
## [](#sm-deferevents)Using Deferred Events
## Using Deferred Events
When an event is sent, it may fire an `EventTrigger`, which may then cause
a transition to happen, if a state machine is in a state where a trigger is
...
...
@@ -2254,7 +2254,7 @@ sub-state that would then override the anonymous transition between
the `DEPLOY` and `DONE` states if the state machine happens to be in a`DEPLOYPREPARE` state when the `DONE` event is dispatched. In the`DEPLOYEXECUTE` state when the `DONE` event is not deferred, this event would
be handled in a super state.
## [](#sm-scopes)Using Scopes
## Using Scopes
Support for scopes in a state machine is very limited, but you can
enable `session` scope by using a normal Spring `@Scope` annotation in one of two ways:
...
...
@@ -2365,7 +2365,7 @@ public class StateMachineController {
| |Spring Statemachine poms have no dependencies to Spring MVC<br/>classes, which you will need to work with session scope. However, if you are<br/>working with a web application, you have already pulled those dependencies<br/>directly from Spring MVC or Spring Boot.|
| |Internally old `Action` interface is wrapped with a Reactor Mono Runnable as it<br/>shares same return type. We have no control what you do in that method!|
Assume that you need to create a state machine that tracks how
many times a user is pressing a key on a keyboard and then terminates
...
...
@@ -2625,7 +2625,7 @@ public class ExtendedStateVariableEventListener
}
```
## [](#sm-statecontext)Using `StateContext`
## Using `StateContext`
[`StateContext`](https://docs.spring.io/spring-statemachine/docs/3.0.1/api/org/springframework/statemachine/StateContext.html) is one of the most important objects
when working with a state machine, as it is passed into various methods
...
...
@@ -2657,19 +2657,19 @@ You can use `StateContext` to get access to the following:
`StateContext` is passed into various components, such as`Action` and `Guard`.
### [](#sm-statecontext-stage)Stages
### Stages
[`Stage`](https://docs.spring.io/spring-statemachine/docs/3.0.1/api/org/springframework/statemachine/StateContext.Stage.html) is arepresentation of a `stage` on
which a state machine is currently interacting with a user. The currently available
stages are `EVENT_NOT_ACCEPTED`, `EXTENDED_STATE_CHANGED`,`STATE_CHANGED`, `STATE_ENTRY`, `STATE_EXIT`, `STATEMACHINE_ERROR`,`STATEMACHINE_START`, `STATEMACHINE_STOP`, `TRANSITION`,`TRANSITION_START`, and `TRANSITION_END`. These states may look familiar, as
they match how you can interact with listeners (as described in[Listening to State Machine Events](#sm-listeners)).
## [](#sm-triggers)Triggering Transitions
## Triggering Transitions
Driving a state machine is done by using transitions, which are triggered
by triggers. The currently supported triggers are `EventTrigger` and`TimerTrigger`.
### [](#using-eventtrigger)Using `EventTrigger`
### Using `EventTrigger`
`EventTrigger` is the most useful trigger, because it lets you
directly interact with a state machine by sending events to it. These
`StateMachineEventResult` contains more detailed information about a result
of a event sending. From this you can get a `Region` which handled an event,`Message` itself and what was an actual `ResultType`. From `ResultType` you
can see if message was accepted, denied or deferred. Generally speaking when
subscribtion completes, events are passed into a machine.
### [](#using-timertrigger)Using `TimerTrigger`
### Using `TimerTrigger`
`TimerTrigger` is useful when something needs to be triggered
automatically without any user interaction. `Trigger` is added to a
...
...
@@ -2833,7 +2833,7 @@ after the state is entered (after a delay defined in a timer).
| |Use `timerOnce()` if you want something to happen after a delay<br/>exactly once when state is entered.|
Application context events classes are `OnTransitionStartEvent`,`OnTransitionEvent`, `OnTransitionEndEvent`, `OnStateExitEvent`,`OnStateEntryEvent`, `OnStateChangedEvent`, `OnStateMachineStart`,`OnStateMachineStop`, and others that extend the base event class,`StateMachineEvent`. These can be used as is with a Spring`ApplicationListener`.
...
...
@@ -2901,7 +2901,7 @@ public class ManualBuilderConfig {
You can enable all the features of `@WithStateMachine` by using
the `@EnableWithStateMachine` annotation, which imports the needed
...
...
@@ -3129,7 +3129,7 @@ static class Bean17 {
| |If a machine is not created as a bean, you need to set`BeanFactory` for a machine, as shown in the prededing example. Otherwise, tge machine is<br/>unaware of handlers that call your `@WithStateMachine` methods.|
The annotations for transitions are `@OnTransition`, `@OnTransitionStart`,
and `@OnTransitionEnd`.
...
...
@@ -3257,7 +3257,7 @@ public class Bean7 {
}
```
### [](#state-annotations)State Annotations
### State Annotations
The following annotations for states are available: `@OnStateChanged`, `@OnStateEntry`, and`@OnStateExit`. The following example shows how to use `OnStateChanged` annotation (the
other two work the same way):
...
...
@@ -3325,7 +3325,7 @@ public class Bean11 {
}
```
### [](#event-annotation)Event Annotation
### Event Annotation
There is one event-related annotation. It is named `@OnEventNotAccepted`.
If you specify the `event` property, you can listen for a specific event not being
All generic configurations for security are done in`SecurityConfigurer`, which is obtained from`StateMachineConfigurationConfigurer`. By default, security is disabled,
even if Spring Security classes are
...
...
@@ -3562,7 +3562,7 @@ If you absolutely need to, you can customize `AccessDecisionManager` for both ev
transitions. If you do not define decision managers or
set them to `null`, default managers are created internally.
### [](#securing-events)Securing Events
### Securing Events
Event security is defined on a global level by a `SecurityConfigurer`.
The following example shows how to enable event security:
...
...
@@ -3590,7 +3590,7 @@ expression needs to return either `TRUE` or `FALSE`. We also defined an
attribute of `ROLE_ANONYMOUS` and a `ComparisonType` of `ANY`. For more about using attributes
and expressions, see [Using Security Attributes and Expressions](#sm-security-attributes-expressions).
### [](#securing-transitions)Securing Transitions
### Securing Transitions
You can define transition security globally, as the following example shows.
Security expressions must return either `TRUE` or `FALSE`.
...
...
@@ -3753,12 +3753,12 @@ describes the most often used built-in expressions:
| `hasPermission(Object target, Object permission)` | Returns `true` if the user has access to the provided target for the<br/>given permission — for example, `hasPermission(domainObject, 'read')`. |
|`hasPermission(Object targetId, String targetType, Object<br/>permission)`| Returns `true` if the user has access to the provided target for the<br/>given permission — for example, `hasPermission(1,<br/>'com.example.domain.Message', 'read')`. |
#### [](#event-attributes)Event Attributes
#### Event Attributes
You can match an event ID by using a prefix of `EVENT_`. For example, matching
event `A` would match an attribute of `EVENT_A`.
#### [](#event-expressions)Event Expressions
#### Event Expressions
The base class for the expression root object for events is`EventSecurityExpressionRoot`. It provides access to a `Message`object, which is passed around with eventing. `EventSecurityExpressionRoot`has only one method, which the following table describes:
...
...
@@ -3766,11 +3766,11 @@ The base class for the expression root object for events is`EventSecurityExpress
The base class for the expression root object for transitions is`TransitionSecurityExpressionRoot`. It provides access to a`Transition`object, which is passed around for transition changes.`TransitionSecurityExpressionRoot` has two methods, which the following
table describes:
...
...
@@ -3780,7 +3780,7 @@ table describes:
|`hasSource(Object source)`|Returns `true` if the transition source matches given source.|
|`hasTarget(Object target)`|Returns `true` if the transition target matches given target.|
`RepositoryStateMachinePersist` (which implements`StateMachinePersist`) offers support for persisting a state machine into Redis.
The specific implementation is a`RedisStateMachineContextRepository`, which uses `kryo` serialization to
...
...
@@ -4109,7 +4109,7 @@ a `StateMachinePersist` and uses `String` as its context object.
`RedisStateMachineContextRepository` needs a`RedisConnectionFactory` for it to work. We recommend using a`JedisConnectionFactory` for it, as the preceding example shows.
`StateMachineRuntimePersister` is a simple extension to`StateMachinePersist` that adds an interface-level method to get`StateMachineInterceptor` associated with it. This interceptor is then
required to persist a machine during state changes without needing to
...
...
@@ -4122,21 +4122,21 @@ and `RedisPersistingStateMachineInterceptor`.
| |See the [Data Persist](#statemachine-examples-datapersist) sample for detailed usage.|
The auto-configuration module (`spring-statemachine-autoconfigure`) contains all
the logic for integrating with Spring Boot, which provides functionality for
auto-configuration and actuators. All you need is to have this Spring Statemachine
library as part of a boot application.
### [](#sm-boot-monitoring)Monitoring and Tracing
### Monitoring and Tracing
`BootStateMachineMonitor` is created automatically and associated with
a state machine. `BootStateMachineMonitor` is a custom `StateMachineMonitor`implementation that integrates with Spring Boot’s `MeterRegistry` and endpoints
through a custom `StateMachineTraceRepository`. Optionally, you can disable this auto-configuration
by setting the `spring.statemachine.monitor.enabled` key to`false`. The[Monitoring](#statemachine-examples-monitoring) sample shows how to use this auto-configuration.
### [](#repository-config)Repository Config
### Repository Config
If the required classes are found from the classpath, Spring Data Repositories
and entity class scanning is automatically auto-configured
...
...
@@ -4144,7 +4144,7 @@ for [Repository Support](#sm-repository).
The currently supported configurations are `JPA`, `Redis`, and`MongoDB`. You can disable repository auto-configuration by using the`spring.statemachine.data.jpa.repositories.enabled`,`spring.statemachine.data.redis.repositories.enabled` and`spring.statemachine.data.mongo.repositories.enabled` properties, respectively.
## [](#sm-monitoring)Monitoring a State Machine
## Monitoring a State Machine
You can use `StateMachineMonitor` to get more information about the
durations of how long transitions and actions take to execute. The following listing
...
...
@@ -4208,7 +4208,7 @@ public class Config1 extends StateMachineConfigurerAdapter<String, String> {
| |See the [Monitoring](#statemachine-examples-monitoring) sample for detailed usage.|
| |All possible options for expected results are documented in the Javadoc for[`StateMachineTestPlanStepBuilder`](https://docs.spring.io/spring-statemachine/docs/3.0.1/api/org/springframework/statemachine/test/StateMachineTestPlanBuilder.StateMachineTestPlanStepBuilder.html).|
Defining a state machine configuration with UI modeling is supported
through the Eclipse Papyrus framework.
...
...
@@ -4431,7 +4431,7 @@ Behind the scenes, a raw UML file would look like the following example:
| |When opening an existing model that has been defined as UML, you have three<br/>files: `.di`, `.notation`, and `.uml`. If a model was not created in your<br/>eclipse’s session, it does not understand how to open an actual state<br/>chart. This is a known issue in the Papyrus plugin, and there is an easy<br/>workaround. In a Papyrus perspective, you can see a model explorer for<br/>your model. Double click Diagram StateMachine Diagram, which<br/>instructs Eclipse to open this specific model in its proper Papyrus<br/>modeling plugin.|
After a UML file is in place in your project, you can import it into your
configuration by using `StateMachineModelConfigurer`, where`StateMachineModelFactory` is associated with a model.`UmlStateMachineModelFactory` is a special factory that knows how to
...
...
@@ -4476,7 +4476,7 @@ functionalities up to the actual implementation. The following sections go
through how Spring Statemachine implements UML models based on
Transitions can also happen based on timed events. Spring Statemachine
support two types of timers, ones which fires continuously on a
...
...
@@ -4631,7 +4631,7 @@ shows the result:
Then the user can pick one of these timed events instead of a
signal event for a particular transition.
### [](#sm-papyrus-choice)Defining a Choice
### Defining a Choice
A choice is defined by drawing one incoming transition into a
CHOICE state and drawing multiple outgoing transitions from it to target
...
...
@@ -4651,11 +4651,11 @@ The following image shows the result of making a choice with three branches:
| |Junction works similarly same, except that it allows multiple incoming<br/>transitions. Thus, its behavior compared to Choice is purely<br/>academic. The actual logic to select the outgoing transition is exactly the same.|
### [](#defining-entry-and-exit-points)Defining Entry and Exit Points
### Defining Entry and Exit Points
You can use EntryPoint and ExitPoint to create controlled entry and exit
with states that have sub-states. In the following state chart, events `E1` and`E2` have normal state behavior by entering and exiting state`S2`, where normal state behavior happens by entering initial state`S21`.
...
...
@@ -4673,20 +4673,20 @@ respectively. The following image shows the result:
| |If state is defined as a sub-machine reference and you need to use entry and exit points,<br/>you must externally define a ConnectionPointReference, with<br/>its entry and exit reference set to point to a correct entry or exit point<br/>within a submachine reference. Only after that, is it possible to<br/>target a transition that correctly links from the outside to the inside of<br/>a sub-machine reference. With ConnectionPointReference, you may need<br/>to find these settings from Properties → Advanced → UML →<br/>Entry/Exit. The UML specification lets you define multiple entries and exits. However,<br/>with a state machine, only one is allowed.|
### [](#defining-history-states)Defining History States
### Defining History States
When working with history states, three different concepts are in play.
UML defines a Deep History and a Shallow History. The Default History
State comes into play when history state is not yet known. These are
represented in following sections.
#### [](#shallow-history)Shallow History
#### Shallow History
In the following image, Shallow History is selected and a transition is defined into it:
![papyrus gs 18](images/papyrus-gs-18.png)
#### [](#deep-history)Deep History
#### Deep History
Deep History is used for state that has other deep nested states,
thus giving a chance to save whole nested state structure.
...
...
@@ -4694,7 +4694,7 @@ The following image shows a definition that uses Deep History:
![papyrus gs 19](images/papyrus-gs-19.png)
#### [](#default-history)Default History
#### Default History
In cases where a Transition terminates on a history when
the state has not been entered before it had reached its
...
...
@@ -4708,7 +4708,7 @@ never been active, as its history has never been recorded. If state`S2` has been
![papyrus gs 20](images/papyrus-gs-20.png)
### [](#defining-forks-and-joins)Defining Forks and Joins
### Defining Forks and Joins
Both Fork and Join are represented as bars in Papyrus. As shown
in the next image, you need to draw one outgoing transition from `FORK` into state`S2` to have orthogonal regions. `JOIN` is then the reverse, where
...
...
@@ -4716,37 +4716,37 @@ joined states are collected together from incoming transitions.
![papyrus gs 21](images/papyrus-gs-21.png)
### [](#defining-actions)Defining Actions
### Defining Actions
You can assoiate swtate entry and exit actions by using a behavior.
For more about this, see [Defining a Bean Reference](#sm-papyrus-beanref).
#### [](#using-an-initial-action)Using an Initial Action
#### Using an Initial Action
An initial action (as shown in [Configuring Actions](#statemachine-config-actions)) is defined
in UML by adding an action in the transition that leads from the Initial State
marker into the actual state. This Action is then run when the state
machine is started.
### [](#defining-guards)Defining Guards
### Defining Guards
You can define a guard by first adding a Constraint and then defining
its Specification as OpaqueExpression, which works in the same way
as [Defining a Bean Reference](#sm-papyrus-beanref).
### [](#sm-papyrus-beanref)Defining a Bean Reference
### Defining a Bean Reference
When you need to make a bean reference in any UML effect,
action, or guard, you can do so with`FunctionBehavior` or `OpaqueBehavior`, where the defined language needs to
be `bean` and the language body msut have a bean reference id.
### [](#sm-papyrus-spelref)Defining a SpEL Reference
### Defining a SpEL Reference
When you need to use a SpEL expression instead of a bean reference in
any UML effect, action, or guard, you can do so by using`FunctionBehavior` or `OpaqueBehavior`, where the defined language needs to
be `spel` and the language body must be a SpEL expression.
### [](#sm-papyrus-submachineref)Using a Sub-Machine Reference
### Using a Sub-Machine Reference
Normally, when you use sub-states, you draw those into the state
chart itself. The chart may become too complex and big to
...
...
@@ -4772,7 +4772,7 @@ sub-state.
![papyrus gs 15](images/papyrus-gs-15.png)
### [](#sm-papyrus-import)Using a Machine Import
### Using a Machine Import
It’s also possible to use import functionality where uml files can reference to other models.
...
...
@@ -4805,12 +4805,12 @@ public static class Config3 extends StateMachineConfigurerAdapter<String, String
| |Links between files in uml models needs to be relative as<br/>otherwise things break when model files are copied out from a<br/>classpath to a temporary directory so that eclipse parsing classes can<br/>read those.|
The actual repository implementation for MongoDB is`MongoDbStateMachineRepository`, which is backed by the entity class`MongoDbRepositoryStateMachine`.
...
...
@@ -5252,7 +5252,7 @@ void persist() {
}
```
# [](#statemachine-recipes)Recipes
# Recipes
This chapter contains documentation for existing built-in state
machine recipes.
...
...
@@ -5269,7 +5269,7 @@ make it easy for you to reuse and extend.
| |Recipes are a great way to make external contributions to the Spring<br/>Statemachine project. If you are not ready to contribute to the<br/>framework core itself, a custom and common recipe is a great way to<br/>share functionality with other users.|
| |The filenames for the jars to which we refer in this section are populated during a<br/>build of this document, meaning that, if you build samples from<br/>master, you have files with a `BUILD-SNAPSHOT` postfix.|
The data persist sample shows how you can state machine concepts
with a persisting machine in an external repository. This sample uses
...
...
@@ -8231,7 +8231,7 @@ The following image shows the result of doing so:
If you then request state machine `datajpapersist1` but send no events,
the state machine is restored back to its persisted state, `S3`.
## [](#statemachine-examples-datajpamultipersist)Data Multi Persist
## Data Multi Persist
The data multi ersist sample is an extension of two other samples:[JPA Configuration](#statemachine-examples-datajpa) and [Data Persist](#statemachine-examples-datapersist).
We still keep machine configuration in a database and persist into a
...
...
@@ -8362,7 +8362,7 @@ different regions in a database have different contexts:
The monitoring sample shows how you can use state machine concepts to
monitor state machine transitions and actions.
...
...
@@ -8667,11 +8667,11 @@ You can also view tracing from Spring Boot by running the following `curl`comman
]
```
# [](#statemachine-faq)FAQ
# FAQ
This chapter answers the questions that Spring Statemachine users most often ask.
## [](#state-changes)State Changes
## State Changes
How can I automatically transit to the next state?
...
...
@@ -8689,7 +8689,7 @@ You can choose from three approaches:
state transition into the next state when state is entered and its
actions has been completed.
## [](#extended-state)Extended State
## Extended State
How I can initialize variables on state machine start?
...
...
@@ -8701,14 +8701,14 @@ this initial transition, you can run a simple action that, within
a `StateContext`, can do whatever it likes with extended state
variables.
# [](#appendices)Appendices
# Appendices
## [](#support-content)Appendix A: Support Content
## Appendix A: Support Content
This appendix provides generic information about the classes and
material that are used in this reference documentation.
### [](#classes-used-in-this-document)Classes Used in This Document
### Classes Used in This Document
The following listings show the classes used throughout this reference guide:
...
...
@@ -8739,11 +8739,11 @@ public enum Events {
}
```
## [](#state-machine-concepts)Appendix B: State Machine Concepts
## Appendix B: State Machine Concepts
This appendix provides generial information about state machines.
### [](#quick-example)Quick Example
### Quick Example
Assuming we have states named `STATE1` and `STATE2` and events named `EVENT1` and`EVENT2`, you can define the logic of the state machine as the following image shows:
...
...
@@ -8823,7 +8823,7 @@ public class MyApp {
}
```
### [](#glossary)Glossary
### Glossary
**State Machine**
...
...
@@ -8920,12 +8920,12 @@ to `FALSE`.
A action is a behavior run during the triggering of the
transition.
### [](#crashcourse)A State Machine Crash Course
### A State Machine Crash Course
This appendix provides a generic crash course to state machine
concepts.
#### [](#states)States
#### States
A state is a model in which a state machine can be. It is always
easier to describe state as a real world example rather than trying to use
...
...
@@ -8944,7 +8944,7 @@ flags, nested if/else/break clauses, or other impractical (and sometimes tortuou
rely on state, state variables, or another interaction with a
state machine.
#### [](#pseudo-states)Pseudo States
#### Pseudo States
Pseudostate is a special type of state that usually introduces more
higher-level logic into a state machine by either giving a state a
...
...
@@ -8952,7 +8952,7 @@ special meaning (such as initial state). A state machine can then internally
react to these states by doing various actions that are available in UML state
machine concepts.
##### [](#initial)Initial
##### Initial
The **Initial pseudostate** state is always needed for every single state
machine, whether you have a simple one-level state machine or a more
...
...
@@ -8960,7 +8960,7 @@ complex state machine composed of submachines or regions. The initial
state defines where a state machine should go when it starts.
Without it, a state machine is ill-formed.
##### [](#end)End
##### End
The **Terminate pseudostate** (which is also called “end state”) indicates
that a particular state machine has reached its final state. Effectively,
...
...
@@ -8968,7 +8968,7 @@ this mean that a state machine no longer processes any events and does
not transit to any other state. However, in the case where submachines are
regions, a state machine can restart from its terminal state.
##### [](#choice)Choice
##### Choice
You can use the **Choice pseudostate** choose a dynamic conditional branch of
a transition from this state. The dynamic condition is evaluated by guards
...
...
@@ -8977,7 +8977,7 @@ simple if/elseif/else structure is used to make sure that one
branch is selected. Otherwise, the state machine might end up in a deadlock,
and the configuration is ill-formed.
##### [](#junction)Junction
##### Junction
The **Junction pseudostate** is functionally similar to choice, as both are
implemented with if/elseif/else structures. The only real difference is
...
...
@@ -8986,7 +8986,7 @@ allows only one. Thus difference is largely academic but does have some
differences, such as when a state machine is designed is used with a real UI modeling
framework.
##### [](#history)History
##### History
You can use the **History pseudostate** to remember the last active state
configuration. After a state machine has exited, you can use a history state
...
...
@@ -9013,7 +9013,7 @@ active. Otherwise, the normal history entry into the region is executed.
If no default history transition is defined, the standard default entry of
the region is performed.
##### [](#fork)Fork
##### Fork
You can use the **Fork pseudostate** to do an explicit entry into one or more regions.
The following image shows how a fork works:
...
...
@@ -9025,7 +9025,7 @@ means that regions are activated by entering their initial states. You
can also add targets directly to any state in a region, which
allows more controlled entry into a state.
##### [](#join)Join
##### Join
The **Join pseudostate** merges together several transitions that
originate from different regions. It is generally used to wait
...
...
@@ -9039,7 +9039,7 @@ join states are the terminal states of the participating regions.
You can also define source states to be any state in a
region, which allows controlled exit from regions.
##### [](#entry-point)Entry Point
##### Entry Point
An **Entry Point pseudostate** represents an entry point for a state
machine or a composite state that provides encapsulation of the insides
...
...
@@ -9047,7 +9047,7 @@ of the state or state machine. In each region of the state machine or
composite state that owns the entry point, there is at most a single
transition from the entry point to a vertex within that region.
##### [](#exit-point)Exit Point
##### Exit Point
An **Exit Point pseudostate** is an exit point of a state machine or
composite state that provides encapsulation of the insides of the state
...
...
@@ -9056,7 +9056,7 @@ region of the composite state (or a state machine referenced by a
submachine state) imply exiting of this composite state or submachine
state (with execution of its associated exit behavior).
#### [](#guard-conditions)Guard Conditions
#### Guard Conditions
Guard conditions are expressions which evaluates to either `TRUE` or`FALSE`, based on extended state variables and event parameters. Guards
are used with actions and transitions to dynamically choose whether a
...
...
@@ -9064,7 +9064,7 @@ particular action or transition should be run. The various spects of guards,
event parameters, and extended state variables exist to make state
machine design much more simple.
#### [](#events)Events
#### Events
Event is the most-used trigger behavior to drive a state machine.
There are other ways to trigger behavior in a state machine
...
...
@@ -9072,20 +9072,20 @@ There are other ways to trigger behavior in a state machine
interact with a state machine. Events are also called “signals”.
They basically indicate something that can possibly alter a state machine state.
#### [](#transitions)Transitions
#### Transitions
A transition is a relationship between a source state and a target
state. A switch from one state to another is a state transition caused
by a trigger.
##### [](#internal-transition)Internal Transition
##### Internal Transition
Internal transition is used when an action needs to be run without
causing a state transition. In an internal transition, the source state and the target
state is always the same, and it is identical with a self-transition in the
absence of state entry and exit actions.
##### [](#external-versus-local-transitions)External versus Local Transitions
##### External versus Local Transitions
In most cases, external and local transitions are functionally
equivalent, except in cases where the transition happens between super
...
...
@@ -9098,11 +9098,11 @@ with very simplistic super and sub states:
One design decision of a `Distributed State Machine` was not to make each
individual state machine instance be aware that it is part of a
...
...
@@ -9262,12 +9262,12 @@ As mentioned in [Using Distributed States](#sm-distributed), distributed states
wrapping an instance of a `StateMachine` in a`DistributedStateMachine`. The specific `StateMachineEnsemble`implementation is `ZookeeperStateMachineEnsemble` provides
integration with Zookeeper.
### [](#the-role-of-zookeeperstatemachinepersist)The Role of `ZookeeperStateMachinePersist`
### The Role of `ZookeeperStateMachinePersist`
We wanted to have a generic interface (`StateMachinePersist`) that
Can persist `StateMachineContext` into arbitrary storage and`ZookeeperStateMachinePersist` implements this interface for`Zookeeper`.
### [](#the-role-of-zookeeperstatemachineensemble)The Role of `ZookeeperStateMachineEnsemble`
### The Role of `ZookeeperStateMachineEnsemble`
While a distributed state machine uses one set of serialized
contexts to update its own state, with zookeeper, we have a
...
...
@@ -9294,7 +9294,7 @@ The size of a circular buffer is mandated to be a power of two, to avoid
trouble when the integer goes to overflow. For this reason, we need not
Main task for a work for `3.x` has been to both internally and externally to move and change
as much as we can from imperative code into a reactive world. This means that some
...
...
@@ -9531,7 +9531,7 @@ of a main interfaces has added a new reative methods and most of a internal exec
(where applicable) has been moved over to handled by a reactor. Essentially what this means is that thread handling model is considerably different compared to `2.x`. Following chapters
go throught all these changes.
### [](#communicating-with-a-machine)Communicating with a Machine
### Communicating with a Machine
We’ve added new reactive methods to `StateMachine` while still keeping old blocking event
methods in place.
...
...
@@ -9580,7 +9580,7 @@ but are deprecated to get removed in future releases.
boolean accepted = machine.sendEvent("EVENT");
```
### [](#taskexecutor-and-taskscheduler)TaskExecutor and TaskScheduler
### TaskExecutor and TaskScheduler
StateMachine execution with `TaskExecutor` and state action scheduling with `TaskScheduler`has been fully replaced in favour or Reactor execution and scheduling.
...
...
@@ -9588,7 +9588,7 @@ Essentially execution outside of a main thread is needed in two places, firstly
be always be executed independently. Currently we’ve chosen to just use *Reactor*`Schedulers.parallel()` for these which should give relatively good results as it
tries to automatically use available number of cpu cores from a system.
### [](#reactive-examples)Reactive Examples
### Reactive Examples
While most of an examples are still same, we’ve overhauled some of them and
如果你刚刚开始使用 Spring Statemachine,这是适合你的一节!这里,我们回答基本的“`what?`”、“`how?`”和“`why?`”问题。我们从温和地介绍 Spring 静态机械开始。然后,我们构建了我们的第一个 Spring Statemachine 应用程序,并讨论了一些核心原则。
## [](#system-requirement)系统需求
## 系统需求
Spring StateMachine3.0.1 是用 JDK8(所有工件都具有 JDK7 兼容性)和 Spring Framework5.3.8 构建和测试的。在其核心系统中,它不需要 Spring 框架之外的任何其他依赖关系。
其他可选部分(例如[使用分布状态](#sm-distributed))对 ZooKeeper 具有依赖性,而[状态机示例](#statemachine-examples)则对`spring-shell`和`spring-boot`具有依赖性,这会将其他依赖性拉出框架本身之外。此外,可选的安全和数据访问功能具有对 Spring 安全和 Spring 数据模块的依赖性。
## [](#modules)模块
## 模块
下表描述了 Spring Statemachine 可用的模块。
...
...
@@ -93,7 +93,7 @@ Spring StateMachine3.0.1 是用 JDK8(所有工件都具有 JDK7 兼容性)
使用适配器(如上面所示)有其通过 Spring `@Configuration`类和应用程序上下文工作的要求所施加的限制。虽然这是一个配置状态机的非常清晰的模型,但它限制了编译时的配置,而这并不总是用户想要做的。如果需要构建更多的动态状态机,那么可以使用一个简单的构建器模式来构建类似的实例。通过使用字符串作为状态和事件,你可以使用此 Builder 模式在 Spring 应用程序上下文之外构建完全动态的状态机。下面的示例展示了如何做到这一点:
你需要了解何时需要将公共配置与从构建器实例化的机器一起使用。你可以使用从`withConfiguration()`返回的配置器来设置`autoStart`和`BeanFactory`。你也可以使用一个来注册`StateMachineListener`。如果通过使用`@Bean`将从构建器返回的`StateMachine`实例注册为 Bean,则`BeanFactory`将自动附加。如果在 Spring 应用程序上下文之外使用实例,则必须使用这些方法来设置所需的设施。
在某些用例中,你希望了解状态机正在发生什么,对某些事情做出反应,或者获取日志详细信息以用于调试目的。 Spring StateMachine 提供了用于添加侦听器的接口。然后,这些侦听器给出一个选项,在发生各种状态更改、操作等时获得回调。
你基本上有两种选择:监听 Spring 应用程序上下文事件或直接将监听器附加到状态机。这两者基本上提供了相同的信息。一个生成事件作为事件类,另一个通过侦听器接口生成回调。这两点都有优点和缺点,我们将在后面进行讨论。
### [](#application-context-events)应用程序上下文事件
### 应用程序上下文事件
应用程序上下文事件类是`OnTransitionStartEvent`,`OnTransitionEvent`,`OnTransitionEndEvent`,`OnStateExitEvent`,`OnStateEntryEvent`,`OnStateChangedEvent`,`OnStateMachineStart`,`OnStateMachineStop`,以及扩展基本事件类的其他类,`StateMachineEvent`。这些可以与 Spring `ApplicationListener`一起使用。
...
...
@@ -2503,7 +2503,7 @@ public class ManualBuilderConfig {
Spring 应用程序上下文不是那里最快的事件总线,因此我们建议对状态机发送的事件的速率给予一些考虑。为了获得更好的性能,使用`StateMachineListener`接口可能会更好。出于这个特定的原因,你可以使用带有`@EnableStateMachine`和`@EnableStateMachineFactory`的`contextEvents`标志来禁用 Spring 应用程序上下文事件,如上一节所示。下面的示例展示了如何禁用 Spring 应用程序上下文事件:
...
...
@@ -2601,7 +2601,7 @@ public class Config9
}
```
## [](#sm-context)上下文集成
## 上下文集成
通过监听状态机的事件或使用带有状态和转换的操作来与状态机进行交互是有点限制的。这种方法有时会过于有限和冗长,无法与状态机所使用的应用程序创建交互。对于这个特定的用例,我们进行了 Spring 风格的上下文集成,可以轻松地将状态机功能插入到 bean 中。
...
...
@@ -2656,7 +2656,7 @@ public @interface WithMyBean {
你可以通过使用`@EnableWithStateMachine`注释启用`@WithStateMachine`的所有特性,该注释将所需的配置导入 Spring 应用程序上下文。`@EnableStateMachine`和`@EnableStateMachineFactory`都已经使用此注释进行了注释,因此没有必要再次添加它。但是,如果一台机器是在没有配置适配器的情况下构建和配置的,则必须使用`@EnableWithStateMachine`才能使用`@WithStateMachine`的这些功能。下面的示例展示了如何做到这一点:
使用普通的 Java 序列化不能持久保存`StateMachine`,因为对象图太丰富,并且包含太多对其他 Spring 上下文类的依赖关系。`StateMachineContext`是状态机的运行时表示形式,你可以使用它将现有机器恢复到由特定的`StateMachineContext`对象表示的状态。
自动配置模块(`spring-statemachine-autoconfigure`)包含与 Spring 引导集成的所有逻辑,该引导为自动配置和执行器提供了功能。你所需要的只是将这个 Spring StateMachine 库作为引导应用程序的一部分。
### [](#sm-boot-monitoring)监视和跟踪
### 监视和跟踪
`BootStateMachineMonitor`将自动创建并与状态机关联。`BootStateMachineMonitor`是一个自定义`StateMachineMonitor`实现,它与 Spring boot 的`MeterRegistry`和通过自定义`StateMachineTraceRepository`的端点集成。你可以通过将`spring.statemachine.monitor.enabled`键设置为`StateMachineTraceRepository`来禁用此自动配置。[Monitoring](#statemachine-examples-monitoring)示例展示了如何使用这种自动配置。
### [](#repository-config)存储库配置
### 存储库配置
如果从 Classpath 中找到了所需的类,则 Spring 数据存储库和实体类扫描将自动为`StateMachineTraceRepository`自动配置。
@@ -3873,7 +3873,7 @@ public static class Config2 extends StateMachineConfigurerAdapter<String, String
}
```
### [](#creating-a-model)创建一个模型
### 创建一个模型
我们首先创建一个空的状态机模型,如下图所示:
...
...
@@ -3891,7 +3891,7 @@ public static class Config2 extends StateMachineConfigurerAdapter<String, String
在前面的图片中,你应该创建了一个名为`model`的示例。你应该已经结束了三个文件:`model.di`、`model.notation`和`model.uml`。然后,你可以在任何其他 Eclipse 实例中使用这些文件。此外,还可以将`model.uml`导入 Spring Statemachine。
`Distributed State Machine`的一个设计决策是,不要让每个单独的状态机实例意识到它是“分布式集成”的一部分。因为`StateMachine`的主要功能和特性可以通过其接口访问,所以将此实例包装在`DistributedStateMachine`中是有意义的,该实例拦截所有状态机通信,并与一个集成协作来协调分布式状态更改。
...
...
@@ -7878,11 +7878,11 @@ Spring 状态机不强制使用单线程执行模型,因为,一旦使用了
从本质上讲,在主线程之外的执行需要在两个地方执行,首先是 *state actions*,它需要是可删除的,其次是*地区*,它应该总是独立执行的。目前,我们选择只使用*反应堆*`Schedulers.parallel()`,这应该会带来相对较好的结果,因为它试图自动使用系统中可用的 CPU 内核数量。