# Spring Statemachine - Reference Documentation
## Preface
The concept of a state machine is most likely older than any reader
of this reference documentation and definitely older than the Java
language itself. Description of finite automata dates back to 1943
when gentlemen Warren McCulloch and Walter Pitts wrote a paper about
it. Later George H. Mealy presented a state machine concept (known as
a “Mealy Machine”) in 1955. A year later, in 1956, Edward F.
Moore presented another paper, in which he described what is known as
a “Moore Machine”. If
you have ever read anything about state machines, the names, Mealy and Moore,
should have popped up at some point.
This reference documentation contains the following parts:
[Introduction](#introduction) contains introduction to this reference documentation.
[Using Spring Statemachine](#statemachine) describes the usage of Spring Statemachine(SSM).
[State Machine Examples](#statemachine-examples) contains more detailed state machine examples.
[FAQ](#statemachine-faq) contains frequently asked questions.
[Appendices](#appendices) contains generic information about used material and state machines.
# Introduction
Spring Statemachine (SSM) is a framework that lets application developers
use traditional state machine concepts with Spring applications. SSM
provides the following features:
* Easy-to-use flat (one-level) state machine for simple use cases.
* Hierarchical state machine structure to ease complex state
configuration.
* State machine regions to provide even more complex state
configurations.
* Usage of triggers, transitions, guards, and actions.
* Type-safe configuration adapter.
* State machine event listeners.
* Spring IoC integration to associate beans with a state machine.
Before you continue, we recommend going through the appendices [Glossary](#glossary)and [A State Machine Crash Course](#crashcourse) to get a generic idea of what state machines are.
The rest of the documentation expects you to be
familiar with state machine concepts.
## Background
State machines are powerful because their behavior is always guaranteed to be
consistent and relatively easily debugged due to how operational
rules are written in stone when a machine is started. The idea is that your
application is now in and may exist in a finite number of states. Then something
happens that takes your application from one state to the next.
A state machine is driven by triggers, which are based on either
events or timers.
It is much easier to design high-level logic outside of your
application and then interact with a state machine in various
different ways. You can interact with a state machine by
sending events, listening to what a state machine does, or requesting
the current state.
Traditionally, state machines are added to an existing project when
developers realize that the code base is starting to look like a plate
full of spaghetti. Spaghetti code looks like a never ending, hierarchical
structure of IF, ELSE, and BREAK clauses, and compilers should probably
ask developers to go home when things are starting to look too complex.
## Usage Scenarios
A project is a good candidate to use a state machine when:
* You can represent the application or part of its structure as states.
* You want to split complex logic into smaller manageable tasks.
* The application is already suffering concurrency issues with (for example)
something happening asynchronously.
You are already trying to implement a state machine when you:
* Use boolean flags or enums to model situations.
* Have variables that have meaning only for some part of your
application lifecycle.
* Loop through an if-else structure (or, worse, multiple such structures),
check whether a particular flag or
enum is set, and then make further exceptions about what to do when certain
combinations of your flags and enums exist or do not exist.
# Getting started
If you are just getting started with Spring Statemachine,
this is the section for you! Here, we answer the basic
“`what?`”, “`how?`” and “`why?`” questions. We start with a gentle
introduction to Spring Statemachine. We then build our
first Spring Statemachine application and discuss some
core principles as we go.
## System Requirement
Spring Statemachine 3.0.1 is built and tested with
JDK 8 (all artifacts have JDK 7 compatibility) and Spring
Framework 5.3.8. It does not require any other
dependencies outside of Spring Framework within its core system.
Other optional parts (such as [Using Distributed States](#sm-distributed)) have dependencies on
Zookeeper, while [State Machine Examples](#statemachine-examples) has dependencies
on `spring-shell` and `spring-boot`, which pull other dependencies
beyond the framework itself. Also, the optional security and data access features have
dependencies to on Spring Security and Spring Data modules.
## Modules
The following table describes the modules that are available for Spring Statemachine.
| Module | Description |
|------------------------------------|----------------------------------------------------------------------------------------------------------------------|
| `spring-statemachine-core` | The core system of Spring Statemachine. |
|`spring-statemachine-recipes-common`| Common recipes that do not require dependencies outside of the core
framework. |
| `spring-statemachine-kryo` | `Kryo` serializers for Spring Statemachine. |
| `spring-statemachine-data-common` | Common support module for `Spring Data`. |
| `spring-statemachine-data-jpa` | Support module for `Spring Data JPA`. |
| `spring-statemachine-data-redis` | Support module for `Spring Data Redis`. |
| `spring-statemachine-data-mongodb` | Support module for `Spring Data MongoDB`. |
| `spring-statemachine-zookeeper` | Zookeeper integration for a distributed state machine. |
| `spring-statemachine-test` | Support module for state machine testing. |
| `spring-statemachine-cluster` |Support module for Spring Cloud Cluster.
Note that Spring Cloud Cluster has been superseded by Spring Integration.|
| `spring-statemachine-uml` | Support module for UI UML modeling with Eclipse Papyrus. |
|`spring-statemachine-autoconfigure` | Support module for Spring Boot. |
| `spring-statemachine-bom` | Bill of Materials pom. |
| `spring-statemachine-starter` | Spring Boot starter. |
## Using Gradle
The following listing shows a typical `build.gradle` file created by choosing various settings at [https://start.spring.io](https://start.spring.io):
```
buildscript {
ext {
springBootVersion = '2.4.8'
}
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
ext {
springStatemachineVersion = '3.0.1'
}
dependencies {
compile('org.springframework.statemachine:spring-statemachine-starter')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.statemachine:spring-statemachine-bom:${springStatemachineVersion}"
}
}
```
| |Replace `0.0.1-SNAPSHOT` with a version you want to use.|
|---|--------------------------------------------------------|
With a normal project structure, you can build this project with the following command:
```
# ./gradlew clean build
```
The expected Spring Boot-packaged fat jar would be `build/libs/demo-0.0.1-SNAPSHOT.jar`.
| |You do not need the`libs-milestone` and `libs-snapshot` repositories for
production development.|
|---|----------------------------------------------------------------------------------------------------|
## Using Maven
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):
```
4.0.0
com.example
demo
0.0.1-SNAPSHOT
jar
gs-statemachine
Demo project for Spring Statemachine
org.springframework.boot
spring-boot-starter-parent
2.4.8
UTF-8
UTF-8
1.8
3.0.1
org.springframework.statemachine
spring-statemachine-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.statemachine
spring-statemachine-bom
${spring-statemachine.version}
pom
import
org.springframework.boot
spring-boot-maven-plugin
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
```
| |Replace `0.0.1-SNAPSHOT` with a version you want to use.|
|---|--------------------------------------------------------|
With a normal project structure, you can build this project with the following command:
```
# mvn clean package
```
The expected Spring Boot-packaged fat-jar would be `target/demo-0.0.1-SNAPSHOT.jar`.
| |You do not need the `libs-milestone` and `libs-snapshot` repositories for
production development.|
|---|-----------------------------------------------------------------------------------------------------|
## 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:
```
@SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
```
Then you need to add states and events, as the following example shows:
```
public enum States {
SI, S1, S2
}
public enum Events {
E1, E2
}
```
Then you need to add state machine configuration, as the following example shows:
```
@Configuration
@EnableStateMachine
public class StateMachineConfig
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config)
throws Exception {
config
.withConfiguration()
.autoStartup(true)
.listener(listener());
}
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.SI)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.SI).target(States.S1).event(Events.E1)
.and()
.withExternal()
.source(States.S1).target(States.S2).event(Events.E2);
}
@Bean
public StateMachineListener listener() {
return new StateMachineListenerAdapter() {
@Override
public void stateChanged(State from, State to) {
System.out.println("State change to " + to.getId());
}
};
}
}
```
Then you need to implement `CommandLineRunner` and autowire `StateMachine`.
The following example shows how to do so:
```
@Autowired
private StateMachine stateMachine;
@Override
public void run(String... args) throws Exception {
stateMachine.sendEvent(Events.E1);
stateMachine.sendEvent(Events.E2);
}
```
Depending on whether you build your application with `Gradle` or `Maven`,
you can run it by using `java -jar build/libs/gs-statemachine-0.1.0.jar` or`java -jar target/gs-statemachine-0.1.0.jar`, respectively.
The result of this command should be normal Spring Boot output.
However, you should also find the following lines:
```
State change to SI
State change to S1
State change to S2
```
These lines indicate that the machine you constructed
is moving from one state to another, as it should.
# What’s New
## In 1.1
Spring Statemachine 1.1 focuses on security and better
interoperability with web applications. It includes the following:
* Comprehensive support for Spring Security has been added. See [State Machine Security](#sm-security).
* Context integration with `@WithStateMachine' has been greatly
enhanced. See [Context Integration](#sm-context).
* `StateContext` is now a first class citizen, letting you
interact with a State Machine. See [Using `StateContext`](#sm-statecontext).
* Features around persistence have been enhanced with built-in
support for redis. See [Using Redis](#sm-persist-redis).
* A new feature helps with persist operations. See[Using `StateMachinePersister`](#sm-persist-statemachinepersister).
* Configuration model classes are now in a public API.
* New features in timer-based events.
* New `Junction` pseudostate. See [Junction State](#statemachine-config-states-junction).
* New Exit Point and Entry Point pseudostates. See [Exit and Entry Point States](#statemachine-config-states-exitentry).
* Configuration model verifier.
* New samples. See [Security](#statemachine-examples-security) and [Event Service](#statemachine-examples-eventservice).
* UI modeling support using Eclipse Papyrus. See [Eclipse Modeling Support](#sm-papyrus).
## In 1.2
Spring Statemachine 1.2 focuses on generic enhancements, better
UML support, and integrations with external config repositories.
It includes the following:
* Support for UML sub-machines. See [Using a Sub-Machine Reference](#sm-papyrus-submachineref).
* A new repository abstraction that keeps machine configuration in an
external repository. See [Repository Support](#sm-repository).
* New support for state actions. See [State Actions](#state-actions).
* New transition error action concepts. See [Transition Action Error Handling](#statemachine-config-transition-actions-errorhandling).
* New action error concepts. See [State Action Error Handling](#statemachine-config-state-actions-errorhandling).
* Initial work for Spring Boot support. See [Spring Boot Support](#sm-boot).
* Support for tracing and monitoring. See [Monitoring a State Machine](#sm-monitoring).
### 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
Spring Statemachine 1.3. It includes the following:
* JPA entity classes have changed table names. See [JPA](#sm-repository-config-jpa).
* A new sample. See [Data Persist](#statemachine-examples-datapersist).
* New entity classes for persistence. See [Repository Persistence](#sm-repository-persistence).
* Transition conflict policy. See[Configuring Common Settings](#statemachine-config-commonsettings)
## In 2.0
Spring Statemachine 2.0 focuses on Spring Boot 2.x support.
### In 2.0.0
Spring Statemachine 2.0.0 includes the following:
* The format of monitoring and tracing has been changed. See [Monitoring and Tracing](#sm-boot-monitoring).
* The `spring-statemachine-boot` module has been renamed to `spring-statemachine-autoconfigure`.
## 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).
With `3.0.x` we have deprecated all blocking methods which will get removed at some point
in a future releases.
| |Please read an appendix [Reactor Migration Guide](#appendix-reactormigrationguide) carefully as it will steer you
through a process of migrating into `3.x` for cases we’re not handling internallyl.|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
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.
# Using Spring Statemachine
This part of the reference documentation explains the core functionality
that Spring Statemachine provides to any Spring based application.
It includes the following topics:
* [Statemachine Configuration](#sm-config) describes the generic configuration support.
* [State Machine ID](#sm-machineid) describes the use of machine id.
* [State Machine Factories](#sm-factories) describes the generic state machine factory support.
* [Using Deferred Events](#sm-deferevents) describes the deferred event support.
* [Using Scopes](#sm-scopes) describes the scope support.
* [Using Actions](#sm-actions) describes the actions support.
* [Using Guards](#sm-guards) describes the guard support.
* [Using Extended State](#sm-extendedstate) describes the extended state support.
* [Using `StateContext`](#sm-statecontext) describes the state context support.
* [Triggering Transitions](#sm-triggers) describes the use of triggers.
* [Listening to State Machine Events](#sm-listeners) describes the use of state machine listeners.
* [Context Integration](#sm-context) describes the generic Spring application context support.
* [Using `StateMachineAccessor`](#sm-accessor) describes the state machine internal accessor support.
* [Using `StateMachineInterceptor`](#sm-interceptor) describes the state machine error handling support.
* [State Machine Security](#sm-security) describes the state machine security support.
* [State Machine Error Handling](#sm-error-handling) describes the state machine interceptor support.
* [State Machine Services](#sm-service) describes the state machine service support.
* [Persisting a State Machine](#sm-persist) describes the state machine persisting support.
* [Spring Boot Support](#sm-boot) describes the Spring Boot support.
* [Monitoring a State Machine](#sm-monitoring) describes the monitoring and trancing support.
* [Using Distributed States](#sm-distributed) describes the distributed state machine support.
* [Testing Support](#sm-test) describes the state machine testing support.
* [Eclipse Modeling Support](#sm-papyrus) describes the state machine UML modeling support.
* [Repository Support](#sm-repository) describes the state machine repository config support.
## Statemachine Configuration
One of the common tasks when using a state machine is to design its
runtime configuration. This chapter focuses on how Spring
Statemachine is configured and how it leverages Spring’s lightweight
IoC containers to simplify the application internals to make it more
manageable.
| |Configuration examples in this section are not feature complete. That is,
you always need to have definitions of both states and transitions.
Otherwise, state machine configuration would be ill-formed. We have
simply made code snippets less verbose by leaving other needed parts
out.|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Using `enable` Annotations
We use two familiar Spring *enabler* annotations to ease configuration:`@EnableStateMachine` and `@EnableStateMachineFactory`.
These annotations, when placed in a `@Configuration` class, enable
some basic functionality needed by a state machine.
You can use `@EnableStateMachine` when you need a configuration to create an
instance of `StateMachine`. Usually, a `@Configuration` class extends adapters
(`EnumStateMachineConfigurerAdapter` or `StateMachineConfigurerAdapter`), which
lets you override configuration callback methods. We automatically
detect whether you use these adapter classes and modify the runtime configuration
logic accordingly.
You can use `@EnableStateMachineFactory` when you need a configuration to create an
instance of a `StateMachineFactory`.
| |Usage examples of these are shown in below sections.|
|---|----------------------------------------------------|
### 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
machine, you can use `EnumStateMachineConfigurerAdapter` and define
possible states and choose the initial and optional end states.
```
@Configuration
@EnableStateMachine
public class Config1Enums
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
}
```
You can also use strings instead of enumerations as states and
events by using `StateMachineConfigurerAdapter`, as shown in the next example. Most
of the configuration examples ues enumerations, but, generally speaking,
you can interchange strings and enumerations.
```
@Configuration
@EnableStateMachine
public class Config1Strings
extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("S1")
.end("SF")
.states(new HashSet(Arrays.asList("S1","S2","S3","S4")));
}
}
```
| |Using enumerations brings a safer set of states and event types but
limits possible combinations to compile time. Strings do not have this
limitation and let you use more dynamic ways to build state
machine configurations but do not allow same level of safety.|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Configuring Hierarchical States
You can define hierarchical states can by using multiple `withStates()`calls, where you can use `parent()` to indicate that these
particular states are sub-states of some other state.
The following example shows how to do so:
```
@Configuration
@EnableStateMachine
public class Config2
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.state(States.S1)
.and()
.withStates()
.parent(States.S1)
.initial(States.S2)
.state(States.S2);
}
}
```
### Configuring Regions
There are no special configuration methods to mark a collection of
states to be part of an orthogonal state. To put it simply, orthogonal
state is created when the same hierarchical state machine has multiple sets
of states, each of which has an initial state. Because an individual state
machine can only have one initial state, multiple initial states must
mean that a specific state must have multiple independent regions.
The following example shows how to define regions:
```
@Configuration
@EnableStateMachine
public class Config10
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S2)
.and()
.withStates()
.parent(States2.S2)
.initial(States2.S2I)
.state(States2.S21)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S2)
.initial(States2.S3I)
.state(States2.S31)
.end(States2.S3F);
}
}
```
When persisting machines with regions or generally
relying on any functionalities to reset a machine, you may need
to have a dedicated ID for a region. By default, this ID
is a generated UUID. As the following example shows, `StateConfigurer` has
a method called `region(String id)` that lets you set the ID for a region:
```
@Configuration
@EnableStateMachine
public class Config10RegionId
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S2)
.and()
.withStates()
.parent(States2.S2)
.region("R1")
.initial(States2.S2I)
.state(States2.S21)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S2)
.region("R2")
.initial(States2.S3I)
.state(States2.S31)
.end(States2.S3F);
}
}
```
### Configuring Transitions
We support three different types of transitions: `external`,`internal`, and `local`. Transitions are triggered either by a signal
(which is an event sent into a state machine) or by a timer.
The following example shows how to define all three kinds of transitions:
```
@Configuration
@EnableStateMachine
public class Config3
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1).target(States.S2)
.event(Events.E1)
.and()
.withInternal()
.source(States.S2)
.event(Events.E2)
.and()
.withLocal()
.source(States.S2).target(States.S3)
.event(Events.E3);
}
}
```
### Configuring Guards
You can use guards to protect state transitions. You can use the `Guard` interface
to do an evaluation where a method has access to a `StateContext`.
The following example shows how to do so:
```
@Configuration
@EnableStateMachine
public class Config4
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1).target(States.S2)
.event(Events.E1)
.guard(guard())
.and()
.withExternal()
.source(States.S2).target(States.S3)
.event(Events.E2)
.guardExpression("true");
}
@Bean
public Guard guard() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return true;
}
};
}
}
```
In the preceding example, we used two different types of guard configurations. First, we
created a simple `Guard` as a bean and attached it to the transition between
states `S1` and `S2`.
Second, we used a SPeL expression as a guard to dicate that the
expression must return a `BOOLEAN` value. Behind the scenes, this
expression-based guard is a `SpelExpressionGuard`. We attached it to
the transition between states `S2` and `S3`. Both guards
always evaluate to `true`.
### Configuring Actions
You can define actions to be executed with transitions and states.
An action is always run as a result of a transition that
originates from a trigger. The following example shows how to define an action:
```
@Configuration
@EnableStateMachine
public class Config51
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action());
}
@Bean
public Action action() {
return new Action() {
@Override
public void execute(StateContext context) {
// do something
}
};
}
}
```
In the preceding example, a single `Action` is defined as a bean named `action` and associated
with a transition from `S1` to `S2`.
The following example shows how to use an action multiple times:
```
@Configuration
@EnableStateMachine
public class Config52
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1, action())
.state(States.S1, action(), null)
.state(States.S2, null, action())
.state(States.S2, action())
.state(States.S3, action(), action());
}
@Bean
public Action action() {
return new Action() {
@Override
public void execute(StateContext context) {
// do something
}
};
}
}
```
| |Usually, you would not define the same `Action` instance for different
stages, but we did it here to not make too much noise in a code
snippet.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------|
In the preceding example, a single `Action` is defined by the bean named `action` and associated
with states `S1`, `S2`, and `S3`. We need to clarify what is going on here:
* We defined an action for the initial state, `S1`.
* We defined an entry action for state `S1` and left the exit action empty.
* We defined an exit action for state `S2` and left the entry action empty.
* We defined a single state action for state `S2`.
* We defined both entry and exit actions for state `S3`.
* Note that state `S1` is used twice with `initial()` and `state()`functions. You need to do this only if you want to define entry or exit
actions with initial state.
| |Defining action with `initial()` function only runs a particular
action when a state machine or sub state is started. This action
is an initializing action that is run only once. An action defined
with `state()` is then run if the state machine transitions back
and forward between initial and non-initial states.|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### State Actions
State actions are run differently compared to entry and exit
actions, because execution happens after state has been entered
and can be cancelled if state exit happens before a particular action
has been completed.
State actions are executed using normal reactive flow by subscribing with
a Reactor’s default parallel scheduler. This means that, whatever you do in your
action, you need to be able to catch `InterruptedException` or, more generally,
periodically check whether `Thread` is interrupted.
The following example shows typical configuration that uses default the `IMMEDIATE_CANCEL`, which
would immediately cancel a running task when its state is complete:
```
@Configuration
@EnableStateMachine
static class Config1 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config) throws Exception {
config
.withConfiguration()
.stateDoActionPolicy(StateDoActionPolicy.IMMEDIATE_CANCEL);
}
@Override
public void configure(StateMachineStateConfigurer states) throws Exception {
states
.withStates()
.initial("S1")
.state("S2", context -> {})
.state("S3");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
transitions
.withExternal()
.source("S1")
.target("S2")
.event("E1")
.and()
.withExternal()
.source("S2")
.target("S3")
.event("E2");
}
}
```
You can set a policy to `TIMEOUT_CANCEL` together with a global timeout
for each machine. This changes state behavior to await action completion
before cancelation is requested. The following example shows how to do so:
```
@Override
public void configure(StateMachineConfigurationConfigurer config) throws Exception {
config
.withConfiguration()
.stateDoActionPolicy(StateDoActionPolicy.TIMEOUT_CANCEL)
.stateDoActionPolicyTimeout(10, TimeUnit.SECONDS);
}
```
If `Event` directly takes a machine into a state so that event headers
are available to a particular action, you can also use a dedicated
event header to set a specific timeout (defined in `millis`).
You can use the reserved header value `StateMachineMessageHeaders.HEADER_DO_ACTION_TIMEOUT`for this purpose. The following example shows how to do so:
```
@Autowired
StateMachine stateMachine;
void sendEventUsingTimeout() {
stateMachine
.sendEvent(Mono.just(MessageBuilder
.withPayload("E1")
.setHeader(StateMachineMessageHeaders.HEADER_DO_ACTION_TIMEOUT, 5000)
.build()))
.subscribe();
}
```
#### Transition Action Error Handling
You can always catch exceptions manually. However, with actions defined for
transitions, you can define an error action that is called if an
exception is raised. The exception is then available from a `StateContext`passed to that action. The following example shows how to create a state
that handles an exception:
```
@Configuration
@EnableStateMachine
public class Config53
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(action(), errorAction());
}
@Bean
public Action action() {
return new Action() {
@Override
public void execute(StateContext context) {
throw new RuntimeException("MyError");
}
};
}
@Bean
public Action errorAction() {
return new Action() {
@Override
public void execute(StateContext context) {
// RuntimeException("MyError") added to context
Exception exception = context.getException();
exception.getMessage();
}
};
}
}
```
If need be, you can manually create similar logic for every action.
The following example shows how to do so:
```
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.S1)
.target(States.S2)
.event(Events.E1)
.action(Actions.errorCallingAction(action(), errorAction()));
}
```
#### State Action Error Handling
Logic similar to the logic that handles errors in state transitions is also available
for entry to a state and exit from a state.
For these situations, `StateConfigurer` has methods called `stateEntry`, `stateDo`, and`stateExit`. These methods define an `error` action together with a normal (non-error) `action`.
The following example shows how to use all three methods:
```
@Configuration
@EnableStateMachine
public class Config55
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.stateEntry(States.S2, action(), errorAction())
.stateDo(States.S2, action(), errorAction())
.stateExit(States.S2, action(), errorAction())
.state(States.S3);
}
@Bean
public Action action() {
return new Action() {
@Override
public void execute(StateContext context) {
throw new RuntimeException("MyError");
}
};
}
@Bean
public Action errorAction() {
return new Action() {
@Override
public void execute(StateContext context) {
// RuntimeException("MyError") added to context
Exception exception = context.getException();
exception.getMessage();
}
};
}
}
```
### 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
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:
```
@Configuration
@EnableStateMachine
public class Config11
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1, initialAction())
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Bean
public Action initialAction() {
return new Action() {
@Override
public void execute(StateContext context) {
// do something initially
}
};
}
}
```
#### 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.
The following example shows how to use the `end()` method:
```
@Configuration
@EnableStateMachine
public class Config1Enums
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
}
```
#### 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`:
```
@Configuration
@EnableStateMachine
public class Config12
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States3.S1)
.state(States3.S2)
.and()
.withStates()
.parent(States3.S2)
.initial(States3.S2I)
.state(States3.S21)
.state(States3.S22)
.history(States3.SH, History.SHALLOW);
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withHistory()
.source(States3.SH)
.target(States3.S22);
}
}
```
Also, as the preceding example shows, you can optionally define a default
transition from a history state into a state vertex in a same machine.
This transition takes place as a default if, for example, the machine has
never been entered — thus, no history would be available. If a default
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 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
configured for this choice.
You can configure a transition by using `withChoice()`, where you define source
state and a `first/then/last` structure, which is equivalent to a normal`if/elseif/else`. With `first` and `then`, you can specify a guard just
as you would use a condition with `if/elseif` clauses.
A transition needs to be able to exist, so you must make sure to use `last`.
Otherwise, the configuration is ill-formed. The following example shows how to define
a choice state:
```
@Configuration
@EnableStateMachine
public class Config13
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.SI)
.choice(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withChoice()
.source(States.S1)
.first(States.S2, s2Guard())
.then(States.S3, s3Guard())
.last(States.S4);
}
@Bean
public Guard s2Guard() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return false;
}
};
}
@Bean
public Guard s3Guard() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return true;
}
};
}
}
```
Actions can be run with both incoming and outgoing transitions of
a choice pseudostate. As the following example shows, one dummy lambda
action is defined that leads into a choice state and one similar dummy
lambda action is defined for one outgoing transition (where it also
defines an error action):
```
@Configuration
@EnableStateMachine
public class Config23
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.SI)
.choice(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.SI)
.action(c -> {
// action with SI-S1
})
.target(States.S1)
.and()
.withChoice()
.source(States.S1)
.first(States.S2, c -> {
return true;
})
.last(States.S3, c -> {
// action with S1-S3
}, c -> {
// error callback for action S1-S3
});
}
}
```
| |Junction have same api format meaning actions can be defined
similarly.|
|---|---------------------------------------------------------------------------|
#### 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
configured for this choice.
You can configure the transition by using `withJunction()` where you define source
state and a `first/then/last` structure (which is equivalent to a normal`if/elseif/else`). With `first` and `then`, you can specify a guard as
you would use a condition with `if/elseif` clauses.
A transition needs to be able to exist, so you must make sure to use `last`.
Otherwise, the configuration is ill-formed.
The following example uses a junction:
```
@Configuration
@EnableStateMachine
public class Config20
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.SI)
.junction(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withJunction()
.source(States.S1)
.first(States.S2, s2Guard())
.then(States.S3, s3Guard())
.last(States.S4);
}
@Bean
public Guard s2Guard() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return false;
}
};
}
@Bean
public Guard s3Guard() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return true;
}
};
}
}
```
| |The difference between choice and junction is purely academic, as both are
implemented with `first/then/last` structures . However, in theory, based
on UML modeling, `choice` allows only one incoming transition while`junction` allows multiple incoming transitions. At a code level, the
functionality is pretty much identical.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
#### Fork State
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
configured for this fork.
The target state needs to be a super state or an immediate state in a
regions. Using a super state as a target takes all regions into
initial states. Targeting individual state gives more controlled entry
into regions. The following example uses a fork:
```
@Configuration
@EnableStateMachine
public class Config14
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.fork(States2.S2)
.state(States2.S3)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S2I)
.state(States2.S21)
.state(States2.S22)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S3I)
.state(States2.S31)
.state(States2.S32)
.end(States2.S3F);
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withFork()
.source(States2.S2)
.target(States2.S22)
.target(States2.S32);
}
}
```
#### 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
target state in a transition configuration.
You can select a target state where a transition goes when all source states
have been joined. If you use state hosting regions as the source, the end
states of a region are used as joins. Otherwise, you can pick any
states from a region. The following exmaple uses a join:
```
@Configuration
@EnableStateMachine
public class Config15
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S3)
.join(States2.S4)
.state(States2.S5)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S2I)
.state(States2.S21)
.state(States2.S22)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S3I)
.state(States2.S31)
.state(States2.S32)
.end(States2.S3F);
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withJoin()
.source(States2.S2F)
.source(States2.S3F)
.target(States2.S4)
.and()
.withExternal()
.source(States2.S4)
.target(States2.S5);
}
}
```
You can also have multiple transitions originate from a
join state. It this case, we advise you to use guards and define your guards
such that only one guard evaluates to `TRUE` at any given time. Otherwise,
transition behavior is not predictable. This is shown in the following example, where the guard
checks whether the extended state has variables:
```
@Configuration
@EnableStateMachine
public class Config22
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States2.S1)
.state(States2.S3)
.join(States2.S4)
.state(States2.S5)
.end(States2.SF)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S2I)
.state(States2.S21)
.state(States2.S22)
.end(States2.S2F)
.and()
.withStates()
.parent(States2.S3)
.initial(States2.S3I)
.state(States2.S31)
.state(States2.S32)
.end(States2.S3F);
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withJoin()
.source(States2.S2F)
.source(States2.S3F)
.target(States2.S4)
.and()
.withExternal()
.source(States2.S4)
.target(States2.S5)
.guardExpression("!extendedState.variables.isEmpty()")
.and()
.withExternal()
.source(States2.S4)
.target(States2.SF)
.guardExpression("extendedState.variables.isEmpty()");
}
}
```
#### Exit and Entry Point States
You can use exit and entry points to do more controlled exit and entry
from and into a submachine.
The following example uses the `withEntry` and `withExit` methods to define entry points:
```
@Configuration
@EnableStateMachine
static class Config21 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("S1")
.state("S2")
.state("S3")
.and()
.withStates()
.parent("S2")
.initial("S21")
.entry("S2ENTRY")
.exit("S2EXIT")
.state("S22");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source("S1").target("S2")
.event("E1")
.and()
.withExternal()
.source("S1").target("S2ENTRY")
.event("ENTRY")
.and()
.withExternal()
.source("S22").target("S2EXIT")
.event("EXIT")
.and()
.withEntry()
.source("S2ENTRY").target("S22")
.and()
.withExit()
.source("S2EXIT").target("S3");
}
}
```
As shown in the preceding, you need to mark particular states as being `exit` and`entry` states. Then you create a normal transitions into those states
and also specify `withExit()` and `withEntry()`, where those states
exit and entry respectively.
### 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,
configure transition conflict policy and region execution policy.
The following example shows how to use `ConfigurationConfigurer`:
```
@Configuration
@EnableStateMachine
public class Config17
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config)
throws Exception {
config
.withConfiguration()
.autoStartup(true)
.machineId("myMachineId")
.beanFactory(new StaticListableBeanFactory())
.listener(new StateMachineListenerAdapter())
.transitionConflictPolicy(TransitionConflictPolicy.CHILD)
.regionExecutionPolicy(RegionExecutionPolicy.PARALLEL);
}
}
```
By default, the state machine `autoStartup` flag is disabled, because all
instances that handle sub-states are controlled by the state machine itself
and cannot be automatically started. Also, it is much safer to leave
whether a machine should be started
automatically or not to the user. This flag controls only the autostart of a
top-level state machine.
Setting `machineId` within a configuration class is simply a convenience for those times when
you want or need to do it there.
Registering `StateMachineListener` instances is also partly for
convenience but is required if you want to catch a callback during a
state machine lifecycle, such as getting notified of a state machine’s
start and stop events. Note that you cannot listen a state
machine’s start events if `autoStartup` is enabled, unless you register a listener
during a configuration phase.
You can use `transitionConflictPolicy` when multiple
transition paths could be selected. One usual use case for this is when a
machine contains anonymous transitions that lead out from a sub-state
and a parent state and you want to define a policy in which one is
selected. This is a global setting within a machine instance and
defaults to `CHILD`.
You can use `withDistributed()` to configure `DistributedStateMachine`. It
lets you set a `StateMachineEnsemble`, which (if it exists) automatically
wraps any created `StateMachine` with `DistributedStateMachine` and
enables distributed mode. The following example shows how to use it:
```
@Configuration
@EnableStateMachine
public class Config18
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config)
throws Exception {
config
.withDistributed()
.ensemble(stateMachineEnsemble());
}
@Bean
public StateMachineEnsemble stateMachineEnsemble()
throws Exception {
// naturally not null but should return ensemble instance
return null;
}
}
```
For more about distributed states, see [Using Distributed States](#sm-distributed).
The `StateMachineModelVerifier` interface is used internally to
do some sanity checks for a state machine’s structure. Its purpose is to
fail fast early instead of letting common configuration errors into a
state machine. By default, a verifier is automatically enabled and the`DefaultStateMachineModelVerifier` implementation is used.
With `withVerifier()`, you can disable verifier or set a custom one if
needed. The following example shows how to do so:
```
@Configuration
@EnableStateMachine
public class Config19
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config)
throws Exception {
config
.withVerifier()
.enabled(true)
.verifier(verifier());
}
@Bean
public StateMachineModelVerifier verifier() {
return new StateMachineModelVerifier() {
@Override
public void verify(StateMachineModel model) {
// throw exception indicating malformed model
}
};
}
}
```
For more about config model, see [StateMachine Config Model](#devdocs-configmodel).
| |The `withSecurity`, `withMonitoring` and `withPersistence` configuration methods
are documented in [State Machine Security](#sm-security), [Monitoring a State Machine](#sm-monitoring), and[Using `StateMachineRuntimePersister`](#sm-persist-statemachineruntimepersister), respectively.|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Configuring Model
`StateMachineModelFactory` is a hook that lets you configure a statemachine model
without using a manual configuration. Essentially, it is a third-party
integration to integrate into a configuration model.
You can hook `StateMachineModelFactory` into a configuration model by
using a `StateMachineModelConfigurer`. The following example shows how to do so:
```
@Configuration
@EnableStateMachine
public static class Config1 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineModelConfigurer model) throws Exception {
model
.withModel()
.factory(modelFactory());
}
@Bean
public StateMachineModelFactory modelFactory() {
return new CustomStateMachineModelFactory();
}
}
```
The follwoing example uses `CustomStateMachineModelFactory` to
define two states (`S1` and `S2`) and an event (`E1`) between those
states:
```
public static class CustomStateMachineModelFactory implements StateMachineModelFactory {
@Override
public StateMachineModel build() {
ConfigurationData configurationData = new ConfigurationData<>();
Collection> stateData = new ArrayList<>();
stateData.add(new StateData("S1", true));
stateData.add(new StateData("S2"));
StatesData statesData = new StatesData<>(stateData);
Collection> transitionData = new ArrayList<>();
transitionData.add(new TransitionData("S1", "S2", "E1"));
TransitionsData transitionsData = new TransitionsData<>(transitionData);
StateMachineModel stateMachineModel = new DefaultStateMachineModel(configurationData,
statesData, transitionsData);
return stateMachineModel;
}
@Override
public StateMachineModel build(String machineId) {
return build();
}
}
```
| |Defining a custom model is usually not what people are looking for,
although it is possible. However, it is a central concept of allowing
external access to this configuration model.|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
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).
### Things to Remember
When defining actions, guards, or any other references from a
configuration, it pays to remember how Spring Framework works
with beans. In the next example, we have defined a normal configuration with
states `S1` and `S2` and four transitions between those. All transitions
are guarded by either `guard1` or `guard2`. You must ensure that`guard1` is created as a real bean because it is annotated with`@Bean`, while `guard2` is not.
This means that event `E3` would get the `guard2` condition as`TRUE`, and `E4` would get the `guard2` condition as `FALSE`, because those are
coming from plain method calls to those functions.
However, because `guard1` is defined as a `@Bean`, it is proxied by the
Spring Framework. Thus, additional calls to its method result in
only one instantiation of that instance. Event `E1` would first get the
proxied instance with condition `TRUE`, while event `E2` would get the same
instance with `TRUE` condition when the method call was defined with`FALSE`. This is not a Spring State Machine-specific behavior. Rather, it is
how Spring Framework works with beans.
The following example shows how this arrangement works:
```
@Configuration
@EnableStateMachine
public class Config1
extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("S1")
.state("S2");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source("S1").target("S2").event("E1").guard(guard1(true))
.and()
.withExternal()
.source("S1").target("S2").event("E2").guard(guard1(false))
.and()
.withExternal()
.source("S1").target("S2").event("E3").guard(guard2(true))
.and()
.withExternal()
.source("S1").target("S2").event("E4").guard(guard2(false));
}
@Bean
public Guard guard1(final boolean value) {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return value;
}
};
}
public Guard guard2(final boolean value) {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return value;
}
};
}
}
```
## 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.
During runtime, a `machineId` really does not have any big operational
role except to distinguish machines from each other — for example, when
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`.
### Using `@EnableStateMachine`
Setting `machineId` in Java configuration as `mymachine` then exposes that value
for logs. This same `machineId` is also available from the`StateMachine.getId()` method. The following example uses the `machineId` method:
```
@Override
public void configure(StateMachineConfigurationConfigurer config)
throws Exception {
config
.withConfiguration()
.machineId("mymachine");
}
```
The following example of log output shows the `mymachine` ID:
```
11:23:54,509 INFO main support.LifecycleObjectSupport [main] -
started S2 S1 / S1 / uuid=8fe53d34-8c85-49fd-a6ba-773da15fcaf1 / id=mymachine
```
| |The manual builder (see [State Machine through a Builder](#state-machine-via-builder)) uses the same configuration
interface, meaning that the behavior is equivalent.|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Using `@EnableStateMachineFactory`
You can see the same `machineId` getting configured if you use a`StateMachineFactory` and request a new machine by using that ID,
as the following example shows:
```
StateMachineFactory factory = context.getBean(StateMachineFactory.class);
StateMachine machine = factory.getStateMachine("mymachine");
```
### Using `StateMachineModelFactory`
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
Java configuration, UML, or a repository. If you want to go crazy, you can also use a custom`StateMachineModel`, which is the lowest possible
level at which to define configuration.
What do all of these have to do with a `machineId`?`StateMachineModelFactory` also has a method with the following signature:`StateMachineModel build(String machineId)` which a `StateMachineModelFactory`implementation may choose to use.
`RepositoryStateMachineModelFactory` (see [Repository Support](#sm-repository)) uses`machineId` to support different configurations in a persistent
store through Spring Data Repository interfaces. For example, both`StateRepository` and `TransitionRepository` have a method (`List
findByMachineId(String machineId)`), to build different states and
transitions by a `machineId`. With`RepositoryStateMachineModelFactory`, if `machineId` is used as empty
or NULL, it defaults to repository configuration (in a backing-persistent model)
without a known machine id.
| |Currently, `UmlStateMachineModelFactory` does not distinguish between
different machine IDs, as UML source is always coming from the same
file. This may change in future releases.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## State Machine Factories
There are use cases when a state machine needs to be created dynamically
instead of by defining static configuration at compile time. For example,
if there are custom components that use their own state machines
and these components are created dynamically, it is impossible to have
a static state machine that is built during the application start. Internally,
state machines are always built through factory interfaces. This then
gives you an option to use this feature programmatically.
Configuration for a state machine factory is exactly the same as shown
in various examples in this document where state machine configuration
is hard coded.
### Factory through an Adapter
Actually creating a state machine by using `@EnableStateMachine`works through a factory, so `@EnableStateMachineFactory` merely exposes
that factory through its interface. The following example uses`@EnableStateMachineFactory`:
```
@Configuration
@EnableStateMachineFactory
public class Config6
extends EnumStateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.S1)
.end(States.SF)
.states(EnumSet.allOf(States.class));
}
}
```
Now that you have used `@EnableStateMachineFactory` to create a factory
instead of a state machine bean, you can inject it and use it (as is) to
request new state machines. The following example shows how to do so:
```
public class Bean3 {
@Autowired
StateMachineFactory factory;
void method() {
StateMachine stateMachine = factory.getStateMachine();
stateMachine.startReactively().subscribe();
}
}
```
#### Adapter Factory Limitations
The current limitation of factory is that all the actions and guard with which it
associates a state machine share the same instance.
This means that, from your actions and guard, you need to
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 through a Builder
Using adapters (as shown above) has a limitation imposed by its
requirement to work through Spring `@Configuration` classes and the
application context. While this is a very clear model to configure a
state machine, it limits configuration at compile time,
which is not always what a user wants to do. If there is a requirement
to build more dynamic state machines, you can use a simple builder pattern
to construct similar instances. By using strings as states and
events, you can use this builder pattern to build fully dynamic state
machines outside of a Spring application context. The following example
shows how to do so:
```
StateMachine buildMachine1() throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial("S1")
.end("SF")
.states(new HashSet(Arrays.asList("S1","S2","S3","S4")));
return builder.build();
}
```
The builder uses the same configuration interfaces behind the scenes that
the `@Configuration` model uses for adapter classes. The same model goes to
configuring transitions, states, and common configuration through a builder’s
methods. This means that whatever you can use with a normal`EnumStateMachineConfigurerAdapter` or `StateMachineConfigurerAdapter`you can use dynamically through a builder.
| |Currently, the `builder.configureStates()`, `builder.configureTransitions()`,
and `builder.configureConfiguration()` interface methods cannot be
chained together, meaning that builder methods need to be called individually.|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
The following example sets a number of options with a builder:
```
StateMachine buildMachine2() throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.autoStartup(false)
.beanFactory(null)
.listener(null);
return builder.build();
}
```
You need to understand when common configuration needs
to be used with machines instantiated from a builder. You can use a configurer
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.
## 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
evaluated successfully. Normally, this may lead to a situation where
an event is not accepted and is dropped. However, you may wish
postpone this event until a state machine enters another state. In that case,
you can accept that event. In other words, an event
arrives at an inconvenient time.
Spring Statemachine provides a mechanism for deferring events for later
processing. Every state can have a list of deferred events. If an event
in the current state’s deferred event list occurs, the event is saved
(deferred) for future processing until a state is entered that does not list
the event in its deferred event list. When such a state is entered, the
state machine automatically recalls any saved events that are no longer
deferred and then either consumes or discards these events. It is possible
for a superstate to have a transition defined on an event that is deferred
by a substate. Following same hierarchical state machines concepts, the substate
takes precedence over the superstate, the event is deferred, and the
transition for the superstate is not run. With orthogonal regions,
where one orthogonal region defers an event and another accepts the event, the
accept takes precedence and the event is consumed and not deferred.
The most obvious use case for event deferring is when an event causes
a transition into a particular state and the state machine is then returned back
to its original state where a second event should cause the same
transition. The following example shows this situation:
```
@Configuration
@EnableStateMachine
static class Config5 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("READY")
.state("DEPLOYPREPARE", "DEPLOY")
.state("DEPLOYEXECUTE", "DEPLOY");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source("READY").target("DEPLOYPREPARE")
.event("DEPLOY")
.and()
.withExternal()
.source("DEPLOYPREPARE").target("DEPLOYEXECUTE")
.and()
.withExternal()
.source("DEPLOYEXECUTE").target("READY");
}
}
```
In the preceding example, the state machine has a state of `READY`, which indicates that the machine is
ready to process events that would take it into a `DEPLOY` state, where the
actual deployment would happen. After a deploy action has been run, the machine
is returned back to the `READY` state. Sending multiple events in a`READY` state does not cause any trouble if the machine is using synchronous executors,
because event sending would block between event calls. However, if the executor uses
threads, other events may get lost, because the machine is no longer in a state where
events can be processed. Thus, deferring some of these events lets the machine
preserve them. The following example shows how to configure such an arrangement:
```
@Configuration
@EnableStateMachine
static class Config6 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("READY")
.state("DEPLOY", "DEPLOY")
.state("DONE")
.and()
.withStates()
.parent("DEPLOY")
.initial("DEPLOYPREPARE")
.state("DEPLOYPREPARE", "DONE")
.state("DEPLOYEXECUTE");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source("READY").target("DEPLOY")
.event("DEPLOY")
.and()
.withExternal()
.source("DEPLOYPREPARE").target("DEPLOYEXECUTE")
.and()
.withExternal()
.source("DEPLOYEXECUTE").target("READY")
.and()
.withExternal()
.source("READY").target("DONE")
.event("DONE")
.and()
.withExternal()
.source("DEPLOY").target("DONE")
.event("DONE");
}
}
```
In the preceding example, the state machine uses nested states instead of a flat
state model, so the `DEPLOY` event can be deferred directly in a substate.
It also shows the concept of deferring the `DONE` event in a
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.
## 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:
* If the state machine is built manually by using a builder and returned into the
context as a `@Bean`.
* Through a configuration adapter.
Both of
these need `@Scope` to be present, with `scopeName` set to`session` and `proxyMode` set to `ScopedProxyMode.TARGET_CLASS`. The following examples
show both use cases:
```
@Configuration
public class Config3 {
@Bean
@Scope(scopeName="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
StateMachine stateMachine() throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.autoStartup(true);
builder.configureStates()
.withStates()
.initial("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("S1")
.target("S2")
.event("E1");
StateMachine stateMachine = builder.build();
return stateMachine;
}
}
```
```
@Configuration
@EnableStateMachine
@Scope(scopeName="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
public static class Config4 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineConfigurationConfigurer config) throws Exception {
config
.withConfiguration()
.autoStartup(true);
}
@Override
public void configure(StateMachineStateConfigurer states) throws Exception {
states
.withStates()
.initial("S1")
.state("S2");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions) throws Exception {
transitions
.withExternal()
.source("S1")
.target("S2")
.event("E1");
}
}
```
TIP:See [Scope](#statemachine-examples-scope) for how to use session scoping.
Once you have scoped a state machine into `session`, autowiring it into
a `@Controller` gives a new state machine instance per session.
Each state machine is then destroyed when `HttpSession` is invalidated.
The following example shows how to use a state machine in a controller:
```
@Controller
public class StateMachineController {
@Autowired
StateMachine stateMachine;
@RequestMapping(path="/state", method=RequestMethod.POST)
public HttpEntity setState(@RequestParam("event") String event) {
stateMachine
.sendEvent(Mono.just(MessageBuilder
.withPayload(event).build()))
.subscribe();
return new ResponseEntity(HttpStatus.ACCEPTED);
}
@RequestMapping(path="/state", method=RequestMethod.GET)
@ResponseBody
public String getState() {
return stateMachine.getState().getId();
}
}
```
| |Using state machines in a `session` scopes needs careful planning,
mostly because it is a relatively heavy component.|
|---|-------------------------------------------------------------------------------------------------------------------------|
| |Spring Statemachine poms have no dependencies to Spring MVC
classes, which you will need to work with session scope. However, if you are
working with a web application, you have already pulled those dependencies
directly from Spring MVC or Spring Boot.|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## Using Actions
Actions are one of the most useful components that you can use to
interact and collaborate with a state machine. You can run actions
in various places in a state machine and its states lifecycle — for example,
entering or exiting states or during transitions.
The following example shows how to use actions in a state machine:
```
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial(States.SI)
.state(States.S1, action1(), action2())
.state(States.S2, action1(), action2())
.state(States.S3, action1(), action3());
}
```
In the preceding example, the `action1` and `action2` beans are attached to the `entry` and`exit` states, respectively. The following example defines those actions (and `action3`):
```
@Bean
public Action action1() {
return new Action() {
@Override
public void execute(StateContext context) {
}
};
}
@Bean
public BaseAction action2() {
return new BaseAction();
}
@Bean
public SpelAction action3() {
ExpressionParser parser = new SpelExpressionParser();
return new SpelAction(
parser.parseExpression(
"stateMachine.sendEvent(T(org.springframework.statemachine.docs.Events).E1)"));
}
public class BaseAction implements Action {
@Override
public void execute(StateContext context) {
}
}
public class SpelAction extends SpelExpressionAction {
public SpelAction(Expression expression) {
super(expression);
}
}
```
You can directly implement `Action` as an anonymous function or create
your own implementation and define the appropriate implementation as a
bean.
In the preceding example, `action3` uses a SpEL expression to send the `Events.E1` event into
a state machine.
| |`StateContext` is described in [Using `StateContext`](#sm-statecontext).|
|---|------------------------------------------------------------------------|
### SpEL Expressions with Actions
You can also use a SpEL expression as a replacement for a
full `Action` implementation.
### Reactive Actions
Normal `Action` interface is a simple functional method taking `StateContext`and returning *void*. There’s nothing blocking here until you block
in a method itself and this is a bit of a problem as framework cannot
know what’s exactly happening inside of it.
```
public interface Action {
void execute(StateContext context);
}
```
To overcome this issue we’ve internally changed `Action` handling to
process a plain java’s `Function` taking `StateContext` and returning`Mono`. This way we can call action and fully in a reactive way to
execute action only when it’s subscribed and in a non-blocking way
to wait completion.
```
public interface ReactiveAction extends Function, Mono> {
}
```
| |Internally old `Action` interface is wrapped with a Reactor Mono Runnable as it
shares same return type. We have no control what you do in that method!|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
## Using Guards
As shown in [Things to Remember](#statemachine-config-thingstoremember), the `guard1` and `guard2` beans are attached to the entry and
exit states, respectively.
The following example also uses guards on events:
```
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source(States.SI).target(States.S1)
.event(Events.E1)
.guard(guard1())
.and()
.withExternal()
.source(States.S1).target(States.S2)
.event(Events.E1)
.guard(guard2())
.and()
.withExternal()
.source(States.S2).target(States.S3)
.event(Events.E2)
.guardExpression("extendedState.variables.get('myvar')");
}
```
You can directly implement `Guard` as an anonymous function or create
your own implementation and define the appropriate implementation as a
bean. In the preceding example, `guardExpression` checkS whether the extended
state variable named `myvar` evaluates to `TRUE`.
The following example implements some sample guards:
```
@Bean
public Guard guard1() {
return new Guard() {
@Override
public boolean evaluate(StateContext context) {
return true;
}
};
}
@Bean
public BaseGuard guard2() {
return new BaseGuard();
}
public class BaseGuard implements Guard {
@Override
public boolean evaluate(StateContext context) {
return false;
}
}
```
| |`StateContext` is described in section [Using `StateContext`](#sm-statecontext).|
|---|--------------------------------------------------------------------------------|
### SpEL Expressions with Guards
You can also use a SpEL expression as a replacement for a
full Guard implementation. The only requirement is that the expression needs
to return a `Boolean` value to satisfy the `Guard` implementation. This can be
demonstrated with a `guardExpression()` function that takes an
expression as an argument.
### Reactive Guards
Normal `Guard` interface is a simple functional method taking `StateContext`and returning *boolean*. There’s nothing blocking here until you block
in a method itself and this is a bit of a problem as framework cannot
know what’s exactly happening inside of it.
```
public interface Guard {
boolean evaluate(StateContext context);
}
```
To overcome this issue we’ve internally changed `Guard` handling to
process a plain java’s `Function` taking `StateContext` and returning`Mono`. This way we can call guard and fully in a reactive way
to evaluate it only when it’s subscribed and in a non-blocking way
to wait completion with a return value.
```
public interface ReactiveGuard extends Function, Mono> {
}
```
| |Internally old `Guard` interface is wrapped with a Reactor Mono Function. We have no
control what you do in that method!|
|---|----------------------------------------------------------------------------------------------------------------------------|
## Using Extended State
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
when keys are pressed 1000 times. A possible but really naive solution
would be to create a new state for each 1000 key presses.
You might suddenly have an astronomical number of
states, which, naturally, is not very practical.
This is where extended state variables come to the rescue by not needing
to add more states to drive state machine changes. Instead,
you can do a simple variable change during a transition.
`StateMachine` has a method called `getExtendedState()`. It returns an
interface called `ExtendedState`, which gives access to extended state
variables. You can access these variables directly through a state machine or through`StateContext` during a callback from actions or transitions.
The following example shows how to do so:
```
public Action myVariableAction() {
return new Action() {
@Override
public void execute(StateContext context) {
context.getExtendedState()
.getVariables().put("mykey", "myvalue");
}
};
}
```
If you need to get notified for extended state variable
changes, you have two options: either use `StateMachineListener` or
listen for `extendedStateChanged(key, value)` callbacks. The following example
uses the `extendedStateChanged` method:
```
public class ExtendedStateVariableListener
extends StateMachineListenerAdapter {
@Override
public void extendedStateChanged(Object key, Object value) {
// do something with changed variable
}
}
```
Alternatively, you can implement a Spring Application context listener for`OnExtendedStateChanged`. As mentioned in [Listening to State Machine Events](#sm-listeners),
you can also listen all `StateMachineEvent` events.
The following example uses `onApplicationEvent` to listen for state changes:
```
public class ExtendedStateVariableEventListener
implements ApplicationListener {
@Override
public void onApplicationEvent(OnExtendedStateChanged event) {
// do something with changed variable
}
}
```
## 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
and callbacks to give the current state of a state machine and
where it is possibly going. You can think of it as a
snapshot of the current state machine stage when
is when `StateContext` is retreived.
| |In Spring Statemachine 1.0.x, `StateContext` usage was relatively naive
in terms of how it was used to pass stuff around as a simple “POJO”.
Starting from Spring Statemachine 1.1.x, its role has been greatly
improved by making it a first class citizen in a state machine.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
You can use `StateContext` to get access to the following:
* The current `Message` or `Event` (or their `MessageHeaders`, if known).
* The state machine’s `Extended State`.
* The `StateMachine` itself.
* To possible state machine errors.
* To the current `Transition`, if applicable.
* The source state of the state machine.
* The target state of the state machine.
* The current `Stage`, as described in [Stages](#sm-statecontext-stage).
`StateContext` is passed into various components, such as`Action` and `Guard`.
### 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)).
## 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`
`EventTrigger` is the most useful trigger, because it lets you
directly interact with a state machine by sending events to it. These
events are also called signals. You can add a trigger to a transition
by associating a state with it during configuration.
The following example shows how to do so:
```
@Autowired
StateMachine stateMachine;
void signalMachine() {
stateMachine
.sendEvent(Mono.just(MessageBuilder
.withPayload("E1").build()))
.subscribe();
Message message = MessageBuilder
.withPayload("E2")
.setHeader("foo", "bar")
.build();
stateMachine.sendEvent(Mono.just(message)).subscribe();
}
```
Whether you send one event or multiple events, result is always a sequence
of results. This is so because in a presence multiple reqions, results will
come back from multiple machines in those regions. This is shown
with method `sendEventCollect` which gives a list of results. Method
itself is a just a syntactic sugar collecting `Flux` as list. If there is
just one region, this list contains one result.
```
Message message1 = MessageBuilder
.withPayload("E1")
.build();
Mono>> results =
stateMachine.sendEventCollect(Mono.just(message1));
results.subscribe();
```
| |Nothing happens until returned flux is subscribed. See more about it from[StateMachineEventResult](#sm-triggers-statemachineeventresult).|
|---|-----------------------------------------------------------------------------------------------------------------------------------------|
The preceding example sends an events by constructing a `Mono` wrapping
a `Message` and subscribing into returned `Flux` of results. `Message` lets
us add arbitrary extra information to an event, which is then visible
to `StateContext` when (for example) you implement actions.
| |Message headers are generally passed on until machine runs to
completion for a specific event. For example if an event is causing
transition into a state `A` which have an anonymous transition into a
state `B`, original event is available for actions or guards in state`B`.|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
It is also possible to send a `Flux` of messages instead of sending just
one with a `Mono`.
```
Message message1 = MessageBuilder
.withPayload("E1")
.build();
Message message2 = MessageBuilder
.withPayload("E2")
.build();
Flux> results =
stateMachine.sendEvents(Flux.just(message1, message2));
results.subscribe();
```
#### StateMachineEventResult
`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`
`TimerTrigger` is useful when something needs to be triggered
automatically without any user interaction. `Trigger` is added to a
transition by associating a timer with it during a configuration.
Currently, there are two types of supported timers, one that fires
continuously and one that fires once a source state is entered.
The following example shows how to use the triggers:
```
@Configuration
@EnableStateMachine
public class Config2 extends StateMachineConfigurerAdapter {
@Override
public void configure(StateMachineStateConfigurer states)
throws Exception {
states
.withStates()
.initial("S1")
.state("S2")
.state("S3");
}
@Override
public void configure(StateMachineTransitionConfigurer transitions)
throws Exception {
transitions
.withExternal()
.source("S1").target("S2").event("E1")
.and()
.withExternal()
.source("S1").target("S3").event("E2")
.and()
.withInternal()
.source("S2")
.action(timerAction())
.timer(1000)
.and()
.withInternal()
.source("S3")
.action(timerAction())
.timerOnce(1000);
}
@Bean
public TimerAction timerAction() {
return new TimerAction();
}
}
public class TimerAction implements Action {
@Override
public void execute(StateContext context) {
// do something in every 1 sec
}
}
```
The preceding example has three states: `S1`, `S2`, and `S3`. We have a normal
external transition from `S1` to `S2` and from `S1` to `S3` with
events `E1` and `E2`, respectively. The interesting parts
for working with `TimerTrigger` are when we define
internal transitions for source states `S2` and `S3`.
For both transitions, we invoke the `Action` bean (`timerAction`), where
source state `S2` uses `timer` and `S3` uses `timerOnce`.
Values given are in milliseconds (`1000` milliseconds, or one second, in both cases).
Once a state machine receives event `E1`, it does a transition
from `S1` to `S2` and the timer kicks in. When the state is `S2`,`TimerTrigger` runs and causes a transition associated with that
state — in this case, the internal transition that has the`timerAction` defined.
Once a state machine receives the `E2`, event it does a transition
from `S1` to `S3` and the timer kicks in. This timer is executed only once
after the state is entered (after a delay defined in a timer).
| |Behind the scenes, timers are simple triggers that may cause a
transition to happen. Defining a transition with a `timer()` keeps
firing triggers and causes transition only if the source state is active.
Transition with `timerOnce()` is a little different, as it
triggers only after a delay when a source state is actually entered.|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| |Use `timerOnce()` if you want something to happen after a delay
exactly once when state is entered.|
|---|-------------------------------------------------------------------------------------------------------|
## Listening to State Machine Events
There are use cases where you want to know what is happening with
a state machine, react to something, or get logging details for
debugging purposes. Spring Statemachine provides interfaces for adding listeners. These listeners
then give an option to get callbacks when various state changes,
actions, and so on happen.
You basically have two options: listen to Spring application
context events or directly attach a listener to a state machine. Both of
these basically provide the same information. One produces
events as event classes, and the other produces callbacks via a listener
interface. Both of these have pros and cons, which we discuss later.
### Application Context Events
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`.
`StateMachine` sends context events through `StateMachineEventPublisher`.
The default implementation is automatically created if a `@Configuration`class is annotated with `@EnableStateMachine`.
The following example gets a `StateMachineApplicationEventListener`from a bean defined in a `@Configuration` class:
```
public class StateMachineApplicationEventListener
implements ApplicationListener {
@Override
public void onApplicationEvent(StateMachineEvent event) {
}
}
@Configuration
public class ListenerConfig {
@Bean
public StateMachineApplicationEventListener contextListener() {
return new StateMachineApplicationEventListener();
}
}
```
Context events are also automatically enabled by using `@EnableStateMachine`,
with `StateMachine` used to build a machine and registered as a bean,
as the following example shows:
```
@Configuration
@EnableStateMachine
public class ManualBuilderConfig {
@Bean
public StateMachine stateMachine() throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureStates()
.withStates()
.initial("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("S1")
.target("S2")
.event("E1");
return builder.build();
}
}
```
### Using `StateMachineListener`
By using `StateMachineListener`, you can either extend it and
implement all callback methods or use the `StateMachineListenerAdapter`class, which contains stub method implementations and choose which ones
to override.
The following example uses the latter approach:
```
public class StateMachineEventListener
extends StateMachineListenerAdapter {
@Override
public void stateChanged(State from, State to) {
}
@Override
public void stateEntered(State state) {
}
@Override
public void stateExited(State state) {
}
@Override
public void transition(Transition transition) {
}
@Override
public void transitionStarted(Transition transition) {
}
@Override
public void transitionEnded(Transition transition) {
}
@Override
public void stateMachineStarted(StateMachine stateMachine) {
}
@Override
public void stateMachineStopped(StateMachine stateMachine) {
}
@Override
public void eventNotAccepted(Message event) {
}
@Override
public void extendedStateChanged(Object key, Object value) {
}
@Override
public void stateMachineError(StateMachine stateMachine, Exception exception) {
}
@Override
public void stateContext(StateContext stateContext) {
}
}
```
In the preceding example, we created our own listener class
(`StateMachineEventListener`) that extends`StateMachineListenerAdapter`.
The `stateContext` listener method gives access to various`StateContext` changes on a different stages. You can find more about about it in[Using `StateContext`](#sm-statecontext).
Once you have defined your own listener, you can registered it in a
state machine by using the `addStateListener` method. It is a matter of
flavor whether to hook it up within a spring configuration or do it
manually at any time during the application life-cycle.
The following example shows how to attach a listener:
```
public class Config7 {
@Autowired
StateMachine stateMachine;
@Bean
public StateMachineEventListener stateMachineEventListener() {
StateMachineEventListener listener = new StateMachineEventListener();
stateMachine.addStateListener(listener);
return listener;
}
}
```
### Limitations and Problems
Spring application context is not the fastest event bus out there, so we
advise giving some thought to the rate of events the state machine
sends. For better performance, it may be better to use the`StateMachineListener` interface. For this specific reason,
you can use the `contextEvents` flag with `@EnableStateMachine` and`@EnableStateMachineFactory` to disable Spring application context
events, as shown in the preceding section.
The following example shows how to disable Spring application context events:
```
@Configuration
@EnableStateMachine(contextEvents = false)
public class Config8
extends EnumStateMachineConfigurerAdapter {
}
@Configuration
@EnableStateMachineFactory(contextEvents = false)
public class Config9
extends EnumStateMachineConfigurerAdapter {
}
```
## Context Integration
It is a little limited to do interaction with a state machine by
either listening to its events or using actions with states and
transitions. From time to time, this approach is going be too limited and
verbose to create interaction with the application with which a state machine
works. For this specific use case, we have made a Spring-style
context integration that easily inserts state machine functionality
into your beans.
The available annotations has been harmonized to enable access to the same
state machine execution points that are available from[Listening to State Machine Events](#sm-listeners).
You can use the `@WithStateMachine` annotation to associate a state
machine with an existing bean. Then you can start adding
supported annotations to the methods of that bean.
The following example shows how to do so:
```
@WithStateMachine
public class Bean1 {
@OnTransition
public void anyTransition() {
}
}
```
You can also attach any other state machine from an
application context by using the annotation `name` field.
The following example shows how to do so:
```
@WithStateMachine(name = "myMachineBeanName")
public class Bean2 {
@OnTransition
public void anyTransition() {
}
}
```
Sometimes, it is more convenient to use `machine id`, which is something
you can set to better identify multiple instances. This ID maps to
the `getId()` method in the `StateMachine` interface.
The following example shows how to use it:
```
@WithStateMachine(id = "myMachineId")
public class Bean16 {
@OnTransition
public void anyTransition() {
}
}
```
You can also use `@WithStateMachine` as a meta-annotation, as shown
in the preceding example. In this case, you could annotate your bean with `WithMyBean`.
The following example shows how to do so:
```
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@WithStateMachine(name = "myMachineBeanName")
public @interface WithMyBean {
}
```
| |The return type of these methods does not matter and is effectively
discarded.|
|---|----------------------------------------------------------------------------------|
### Enabling Integration
You can enable all the features of `@WithStateMachine` by using
the `@EnableWithStateMachine` annotation, which imports the needed
configuration into the Spring Application Context. Both`@EnableStateMachine` and `@EnableStateMachineFactory` are already
annotated with this annotation, so there is no need to add it again.
However, if a machine is built and configured without
configuration adapters, you must use `@EnableWithStateMachine`to use these features with `@WithStateMachine`.
The following example shows how to do so:
```
public static StateMachine buildMachine(BeanFactory beanFactory) throws Exception {
Builder builder = StateMachineBuilder.builder();
builder.configureConfiguration()
.withConfiguration()
.machineId("myMachineId")
.beanFactory(beanFactory);
builder.configureStates()
.withStates()
.initial("S1")
.state("S2");
builder.configureTransitions()
.withExternal()
.source("S1")
.target("S2")
.event("E1");
return builder.build();
}
@WithStateMachine(id = "myMachineId")
static class Bean17 {
@OnStateChanged
public void onStateChanged() {
}
}
```
| |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
unaware of handlers that call your `@WithStateMachine` methods.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
### Method Parameters
Every annotation support exactly the same set of possible method
parameters, but runtime behavior differs, depending on the
annotation itself and the stage in which the annotated method is called. To
better understand how context works, see[Using `StateContext`](#sm-statecontext).
| |For differences between method parameters, see the sections that desdribe the
individual annotation, later in this document.|
|---|--------------------------------------------------------------------------------------------------------------------------------|
Effectively, all annotated methods are called by using Spring SPel
expressions, which are built dynamically during the process. To make
this work, these expressions needs to have a root object (against which they evaluate).
This root object is a `StateContext`. We have also made some
tweaks internally so that it is possible to access `StateContext` methods
directly without going through the context handle.
The simplest method parameter is a `StateContext` itself.
The following example shows how to use it:
```
@WithStateMachine
public class Bean3 {
@OnTransition
public void anyTransition(StateContext stateContext) {
}
}
```
You can access the rest of the `StateContext` content.
The number and order of the parameters does not matter.
The following example shows how to access the various parts of the `StateContext` content:
```
@WithStateMachine
public class Bean4 {
@OnTransition
public void anyTransition(
@EventHeaders Map headers,
@EventHeader("myheader1") Object myheader1,
@EventHeader(name = "myheader2", required = false) String myheader2,
ExtendedState extendedState,
StateMachine stateMachine,
Message message,
Exception e) {
}
}
```
| |Instead of getting all event headers with `@EventHeaders`, you can use`@EventHeader`, which can bound to a single header.|
|---|-------------------------------------------------------------------------------------------------------------------------|
### Transition Annotations
The annotations for transitions are `@OnTransition`, `@OnTransitionStart`,
and `@OnTransitionEnd`.
These annotations behave exactly the same. To show how they work, we show
how `@OnTransition` is used. Within this annotation, a property’s
you can use `source` and `target` to qualify a transition. If`source` and `target` are left empty, any transition is matched.
The following example shows how to use the `@OnTransition` annotation
(remember that `@OnTransitionStart` and `@OnTransitionEnd` work the same way):
```
@WithStateMachine
public class Bean5 {
@OnTransition(source = "S1", target = "S2")
public void fromS1ToS2() {
}
@OnTransition
public void anyTransition() {
}
}
```
By default, you cannot use the `@OnTransition` annotation with a state and
event enumerations that you have created, due to Java language limitations.
For this reason, you need to use string representations.
Additionally, you can access `Event Headers` and`ExtendedState` by adding the needed arguments to a method. The method
is then called automatically with these arguments.
The following example shows how to do so:
```
@WithStateMachine
public class Bean6 {
@StatesOnTransition(source = States.S1, target = States.S2)
public void fromS1ToS2(@EventHeaders Map headers, ExtendedState extendedState) {
}
}
```
However, if you want to have a type-safe annotation, you can
create a new annotation and use `@OnTransition` as a meta-annotation.
This user-level annotation can make references to actual states and
events enumerations, and the framework tries to match these in the same way.
The following example shows how to do so:
```
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnTransition
public @interface StatesOnTransition {
States[] source() default {};
States[] target() default {};
}
```
In the preceding example, we created a `@StatesOnTransition` annotation that defines`source` and `target` in a type-safe manner.
The following example uses that annotation in a bean:
```
@WithStateMachine
public class Bean7 {
@StatesOnTransition(source = States.S1, target = States.S2)
public void fromS1ToS2() {
}
}
```
### 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):
```
@WithStateMachine
public class Bean8 {
@OnStateChanged
public void anyStateChange() {
}
}
```
As you can with [Transition Annotations](#state-machine-transition-annotations), you can define
target and source states. The following example shows how to do so:
```
@WithStateMachine
public class Bean9 {
@OnStateChanged(source = "S1", target = "S2")
public void stateChangeFromS1toS2() {
}
}
```
For type safety, new annotations need to be created for enumerations by using`@OnStateChanged` as a meta-annotation. The following examples show how to do so:
```
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@OnStateChanged
public @interface StatesOnStates {
States[] source() default {};
States[] target() default {};
}
```
```
@WithStateMachine
public class Bean10 {
@StatesOnStates(source = States.S1, target = States.S2)
public void fromS1ToS2() {
}
}
```
The methods for state entry and exit behave in the same way, as the following example shows:
```
@WithStateMachine
public class Bean11 {
@OnStateEntry
public void anyStateEntry() {
}
@OnStateExit
public void anyStateExit() {
}
}
```
### 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
accepted. If you do not specify an event, you can list for any event not being
accepted. The following example shows both ways to use the `@OnEventNotAccepted`annotation:
```
@WithStateMachine
public class Bean12 {
@OnEventNotAccepted
public void anyEventNotAccepted() {
}
@OnEventNotAccepted(event = "E1")
public void e1EventNotAccepted() {
}
}
```
### State Machine Annotations
The following annotations are available for a state machine: `@OnStateMachineStart`,`@OnStateMachineStop`, and `@OnStateMachineError`.
During a state machine’s start and stop, lifecycle methods are called.
The following example shows how to use `@OnStateMachineStart` and`@OnStateMachineStop` to listen to these events:
```
@WithStateMachine
public class Bean13 {
@OnStateMachineStart
public void onStateMachineStart() {
}
@OnStateMachineStop
public void onStateMachineStop() {
}
}
```
If a state machine goes into an error with exception, `@OnStateMachineStop`annotation is called. The following example shows how to use it:
```
@WithStateMachine
public class Bean14 {
@OnStateMachineError
public void onStateMachineError() {
}
}
```
### Extended State Annotation
There is one extended state-related annotation. It is named`@OnExtendedStateChanged`. You can also listen to changes only
for specific `key` changes. The following example shows how to use the`@OnExtendedStateChanged`, both with and without a `key` property:
```
@WithStateMachine
public class Bean15 {
@OnExtendedStateChanged
public void anyStateChange() {
}
@OnExtendedStateChanged(key = "key1")
public void key1Changed() {
}
}
```
## Using `StateMachineAccessor`
`StateMachine` is the main interface for communicating with a state machine.
From time to time, you may need to get more dynamic and
programmatic access to internal structures of a state machine and its
nested machines and regions. For these use cases, `StateMachine`exposes a functional interface called `StateMachineAccessor`, which provides
an interface to get access to individual `StateMachine` and`Region` instances.
`StateMachineFunction` is a simple functional interface that lets
you apply the `StateMachineAccess` interface to a state machine. With
JDK 7, these create code that is a little verbose code. However, with JDK 8 lambdas,
the doce is relatively non-verbose.
The `doWithAllRegions` method gives access to all `Region` instances in
a state machine. The following example shows how to use it:
```
stateMachine.getStateMachineAccessor().doWithAllRegions(function -> function.setRelay(stateMachine));
stateMachine.getStateMachineAccessor()
.doWithAllRegions(access -> access.setRelay(stateMachine));
```
The `doWithRegion` method gives access to single `Region` instance in a
state machine. The following example shows how to use it:
```
stateMachine.getStateMachineAccessor().doWithRegion(function -> function.setRelay(stateMachine));
stateMachine.getStateMachineAccessor()
.doWithRegion(access -> access.setRelay(stateMachine));
```
The `withAllRegions` method gives access to all of the `Region` instances in
a state machine. The following example shows how to use it:
```
for (StateMachineAccess access : stateMachine.getStateMachineAccessor().withAllRegions()) {
access.setRelay(stateMachine);
}
stateMachine.getStateMachineAccessor().withAllRegions()
.stream().forEach(access -> access.setRelay(stateMachine));
```
The `withRegion` method gives access to single `Region` instance in a
state machine. The following example shows how to use it:
```
stateMachine.getStateMachineAccessor()
.withRegion().setRelay(stateMachine);
```
## Using `StateMachineInterceptor`
Instead of using a `StateMachineListener` interface, you can
use a `StateMachineInterceptor`. One conceptual difference is that you can use an
interceptor to intercept and stop a current state
change or change its transition logic. Instead of implementing a full interface,
you can use an adapter class called `StateMachineInterceptorAdapter` to override
the default no-op methods.
| |One recipe ([Persist](#statemachine-recipes-persist)) and one sample
([Persist](#statemachine-examples-persist)) are related to using an
interceptor.|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------|
You can register an interceptor through `StateMachineAccessor`. The concept of
an interceptor is a relatively deep internal feature and, thus, is not
exposed directly through the `StateMachine` interface.
The following example shows how to add a `StateMachineInterceptor` and override selected
methods:
```
stateMachine.getStateMachineAccessor()
.withRegion().addStateMachineInterceptor(new StateMachineInterceptor() {
@Override
public Message preEvent(Message message, StateMachine stateMachine) {
return message;
}
@Override
public StateContext preTransition(StateContext stateContext) {
return stateContext;
}
@Override
public void preStateChange(State state, Message message,
Transition transition, StateMachine stateMachine,
StateMachine rootStateMachine) {
}
@Override
public StateContext postTransition(StateContext stateContext) {
return stateContext;
}
@Override
public void postStateChange(State state, Message message,
Transition transition, StateMachine