Ejemplo de máquina de estados con Spring Statemachine

Escrito por el , actualizado el .
java planeta-codigo spring
Enlace permanente Comentarios

Spring

Java

Hace ya unos años escribí un ejemplo y un artículo con una implementación propia de una máquina de estado en Java, para algún caso muy básico puede ser suficiente pero para algo serio no es la opción a elegir. Pasado un tiempo de ese ejemplo y artículo descubrí uno de los muchos proyectos de Spring útiles para una aplicación, para las necesidades más habítuales tiene un proyecto que lo proporciona y para las menos habituales es también posible que la proporcione como en el caso del proyecto Spring Statemachine que precisamente tiene el mismo objetivo de implementar una máquina de estados cubriendo muchos casos de uso.

El uso de una máquina de estados permite modelar un proceso con cierta complejidad. Puede ser el ciclo de vida de una compra, un envío, un proceso documental, financiero, … cualquiera en el que intervengan estados, transiciones entre esos estados y realización de acciones necesarias para proporcionar la funcionalidad deseada.

Una máquina de estados se compone de un conjunto de estados finito, de transiciones en esos estados, de eventos que disparan las transiciones y cambios de estado, de acciones asociadas a los estados, a las transiciones o a la entrada o salida de un estado, guards que permiten decidir que transición se escoge entre varias en los choices, forks en las que el flujo sigue por varios caminos en paralelo, temporizadores que pasado un tiempo disparan una transición, seguridad para proteger la ejecución de eventos, transiciones y acciones con Spring Security, persistencia tanto para la configuración como para el estado en bases de datos relacionales o NoSQL como Redis y MongoDB. Una lista bastante completa de características que cubrirá las necesidades de la mayoría de aplicaciones.

Aparte de proporcionar una librería para crear máquinas de estados en Java otra ventaja es que el flujo de un proceso queda recogido y documentado en la implementación de la máquina de estados. De que estados, transiciones y eventos se compone en un punto más centralizado lo que en otro caso podría estar repartido por el código de la aplicación utilizando una solución propia codificada expresamente para el caso.

En el ejemplo he definido el siguiente grafo de estados y transiciones que contiene estado inicial, choice, fork, join, jerarquía de estados con en el estado Tasks y un estado final. Una selección completa del conjunto de tipos de estados. En este caso las flechas van únicamente en una dirección pero perfectamente el flujo podría tener transiciones que volviesen a estados anteriores creando ciclos.

Grafo de la máquina de estados

Grafo de la máquina de estados

El uso de Spring Statemachine es relativamente sencillo. En el ejemplo se define en un enumerado la lista de estados, también la lista de eventos o transiciones. Una de las formas de definir el grafo dela máquina de estados es mediante código Java y utilizando Spring definiendo un bean. La clase StateMachineBuilder facilita la construcción de la definición de la máquina de estados, por una parte está la configuración general o de infraestructura con el método configureConfiguration(). Con el método configureStates() se define los estados de la máquina y su tipo (inicial, final, normal, choice, fork, join,) así como las acciones que se deseen ejecutar al entrar, en el estado y al salir del estado con los métodos stateEntry(), stateDo() y stateExit(). Con el método withStates()() se pueden definir submáquinas o una jerarquía de estados como en el caso del estado Tasks. Finalmente hay que definir cuales son las posibles transiciones de los estados y que eventos las disparan. Normalmente las transiciones se definen con el método withExternal() pero en los casos de los estados de tipo choice, fork y join se define con los métodos withChoice(), withFork() y withJoin().

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package io.github.picodotdev.blogbitix.springstatemachine;

...

@SpringBootApplication
public class Main implements CommandLineRunner {

    public static enum States {
        START, STATE1, CHOICE, CHOICE1, CHOICE2, FORK, TASKS, TASK11, TASK12, TASK13, TASK21, TASK22, TASK23, JOIN, STATE2, END
    }

    public static enum Events {
        START_STATE1, STATE1_CHOICE, CHOICE1_FORK, CHOICE2_FORK, TASK11_TASK12, TASK12_TASK13, TASK21_TASK22, TASK22_TASK23, STATE2_END
    }

    @Autowired
    private ApplicationContext context;

    @Autowired
    private StateMachine<States, Events> machine1;

    @Bean
    public StateMachine<States, Events> buildMachine(DefaultAction action, DefaultErrorAction errorAction, DefaultStateMachineEventListener listener) throws Exception {
        StateMachineBuilder.Builder<States, Events> builder = StateMachineBuilder.builder();

        // https://github.com/spring-projects/spring-statemachine/issues/354
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setDaemon(true);
        taskScheduler.initialize();

        builder.configureConfiguration().withConfiguration()
                .taskScheduler(taskScheduler);

        builder.configureStates()
            .withStates()
                .initial(States.START)
                .stateEntry(States.STATE1, action, errorAction)
                .stateDo(States.STATE1, action, errorAction)
                .stateExit(States.STATE1, action, errorAction)
                .choice(States.CHOICE)
                .state(States.CHOICE1)
                .state(States.CHOICE2)
                .fork(States.FORK)
                .state(States.TASKS)
                .join(States.JOIN)
                .state(States.STATE2)
                .end(States.END)
                .and()
                .withStates()
                    .parent(States.TASKS)
                    .initial(States.TASK11)
                    .state(States.TASK12)
                    .end(States.TASK13)
                .and()
                .withStates()
                    .parent(States.TASKS)
                    .initial(States.TASK21)
                    .state(States.TASK22)
                    .end(States.TASK23);

        builder.configureTransitions()
            .withExternal()
                .source(States.START).target(States.STATE1)
                .event(Events.START_STATE1)
                .action(Actions.errorCallingAction(action, errorAction))
                .and()
            .withExternal()
                .source(States.STATE1).target(States.CHOICE)
                .event(Events.STATE1_CHOICE)
                .and()
            .withChoice()
                .source(States.CHOICE)
                .first(States.CHOICE1, new RandomGuard())
                .last(States.CHOICE2)
                .and()
            .withExternal()
                .source(States.CHOICE1)
                .target(States.FORK)
                .event(Events.CHOICE1_FORK)
                .and()
            .withExternal()
                .source(States.CHOICE2)
                .target(States.FORK)
                .event(Events.CHOICE2_FORK)
                .and()
            .withFork()
                .source(States.FORK)
                .target(States.TASKS)
                .and()
            .withExternal()
                .state(States.TASKS)
                .source(States.TASK11)
                .target(States.TASK12)
                .event(Events.TASK11_TASK12)
                .and()
            .withExternal()
                .source(States.TASK12)
                .target(States.TASK13)
                .event(Events.TASK12_TASK13)
                .and()
            .withExternal()
                .source(States.TASK21)
                .target(States.TASK22)
                .event(Events.TASK21_TASK22)
                .and()
            .withExternal()
                .source(States.TASK22)
                .target(States.TASK23)
                .event(Events.TASK22_TASK23)
                .and()
            .withJoin()
                .source(States.TASK13)
                .source(States.TASK23)
                .target(States.JOIN)
                .and()
            .withExternal()
                .source(States.JOIN)
                .target(States.STATE2)
                .and()
            .withExternal()
                .source(States.STATE2)
                .target(States.END)
                .event(Events.STATE2_END);

        StateMachine<States, Events> stateMachine = builder.build();
        stateMachine.addStateListener(listener);
        return stateMachine;
    }

    ...

    public static void main(String... args) {
        SpringApplication.run(Main.class, args);
    }
}
Main-1.java

Las acciones asociadas a los estados del ejemplo simplemente emiten trazas pero tienen disponible el parámetro context para obtener datos e implementar su lógica, como es un componente de Spring podría incluso hacer uso de inyección de dependencias para utilizar otros servicios que necesitase.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package io.github.picodotdev.blogbitix.springstatemachine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;

@Component
public class DefaultAction implements Action<Main.States, Main.Events> {

    private static Logger logger = LoggerFactory.getLogger(DefaultAction.class);

    @Override
    public void execute(StateContext<Main.States, Main.Events> context) {
        logger.info("Action Source: {}, State: {}, Target: {}, Event: {}", (context.getSource() != null) ? context.getSource().getId() : null, context.getStateMachine().getState().getId(), (context.getTarget() != null) ? context.getTarget().getId() : null, context.getEvent());
    }
}
DefaultAction.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package io.github.picodotdev.blogbitix.springstatemachine;

import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.action.Action;
import org.springframework.stereotype.Component;

@Component
public class DefaultErrorAction implements Action<Main.States, Main.Events> {

    @Override
    public void execute(StateContext<Main.States, Main.Events> context) {
        context.getException().printStackTrace();
    }
}
DefaultErrorAction.java

En el caso de un choice se utilizan los métodos first(), then() y last() que una cláusula guard en los dos primeros determinan a que estado se transiciona. Una cláusula guard es una instancia de la clase Guard que contiene un método que devuelve un boolean según su lógica. Si es verdadero se selecciona el estado asociado, para la lógical se puede utilizar la información de contexto StateContext entre otra información como la que se haya asociado en los diferentes estados con el método getExtendedState() y la proporcionada en la ejecución del evento con getMessageHeaders().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package io.github.picodotdev.blogbitix.springstatemachine;

import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.guard.Guard;

import java.util.Random;

public class RandomGuard implements Guard<Main.States, Main.Events> {

    @Override
    public boolean evaluate(StateContext<Main.States, Main.Events> context) {
        return new Random().nextBoolean();
    }
}
RandomGuard.java

En este ejemplo se utiliza Spring Boot, aparte de las configuraciones anteriores se realizan dos cosas adicionales. Una de ellas es utilizar un ThreadPoolTaskScheduler en modo demonio de modo que la máquina virtual y el programa pueda finalizar correctamente. La otra es que con una implementación de la clase StateMachineListenerAdapter se pueden recibir que acciones realiza la máquina de estados, en este caso para emitir trazas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package io.github.picodotdev.blogbitix.springstatemachine;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;
import org.springframework.statemachine.transition.Transition;
import org.springframework.stereotype.Component;

@Component
public class DefaultStateMachineEventListener extends StateMachineListenerAdapter<Main.States, Main.Events> {
    
    private static Logger logger = LoggerFactory.getLogger(DefaultStateMachineEventListener.class);
    
    @Override
    public void stateChanged(State<Main.States, Main.Events> from, State<Main.States, Main.Events> to) {
        logger.info("Listener stateChanged from {} to {}", (from != null) ? from.getId() : null, (to != null) ? to.getId() : null);
    }

    @Override
    public void stateEntered(State<Main.States, Main.Events> state) {
        logger.info("Listener stateEntered in {}", state.getId());
    }

    @Override
    public void stateExited(State<Main.States, Main.Events> state) {
        logger.info("Listener stateExited in {}", state.getId());
    }

    @Override
    public void transition(Transition<Main.States, Main.Events> transition) {
        logger.info("Listener transition from {} to {}", (transition.getSource() != null) ? transition.getSource().getId() :null, (transition.getTarget() != null) ? transition.getTarget().getId() : null);
    }

    @Override
    public void transitionStarted(Transition<Main.States, Main.Events> transition) {
        logger.info("Listener transitionStarted from {} to {}", (transition.getSource() != null) ? transition.getSource().getId() : null, (transition.getTarget() != null) ? transition.getTarget().getId() : null);
    }

    @Override
    public void transitionEnded(Transition<Main.States, Main.Events> transition) {
        logger.info("Listener transitionEnded from {} to {}", (transition.getSource() != null) ? transition.getSource().getId() : null, (transition.getTarget() != null) ? transition.getTarget().getId() : null);
    }

    @Override
    public void stateMachineStarted(StateMachine<Main.States, Main.Events> stateMachine) {
        logger.info("Listener stateMachineStarted");
    }

    @Override
    public void stateMachineStopped(StateMachine<Main.States, Main.Events> stateMachine) {
        logger.info("Listener stateMachineStopped");
    }

    @Override
    public void eventNotAccepted(Message<Main.Events> event) {
        logger.info("Listener eventNotAccepted {}", event);
    }

    @Override
    public void extendedStateChanged(Object key, Object value) {
        logger.info("Listener extendedStateChanged {} to {}", key, value);
    }

    @Override
    public void stateMachineError(StateMachine<Main.States, Main.Events> stateMachine, Exception exception) {
        logger.info("Listener stateMachineError {}", exception.getMessage());
    }

    @Override
    public void stateContext(StateContext<Main.States, Main.Events> stateContext) {
        logger.info("Listener stateContext (Message Payload: {}, Message Headers: {}, Variables: {}", (stateContext.getMessage() != null) ? stateContext.getMessage().getPayload() : null, stateContext.getMessageHeaders(), stateContext.getExtendedState().getVariables());
    }    
}
DefaultStateMachineEventListener.java

Una vez definida la máquina de estados su uso comienza con el método start(), a continuación ya se pueden enviar eventos par provocar cambios de estado y ejecución de acciones. Se puede proporcionar información de contexto para la máquina accesible desde cualquier estado en el mapa obtenido con ExtendedState.getVariables() y al provocar eventos con la clase GenericMessage. En cualquier momento el método getState() devuelve en que estado se encuentra la máquina. Según la rama que haya elegido la clase RandomGuard en la bifurcación y el estado en el que esté la máquina se envía la transición CHOICE1_FORK o CHOICE2_FORK. El fork provoca que la máquina de estados ejecute en paralelo dos caminos, enviados los eventos correctos se llega a los estados finales de las dos ramas del subestado y se pasa automáticamente al estado JOIN que a su vez pasa automáticamente a STATE2, finalmente se pasa al estado END y termina la máquina de estados.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.github.picodotdev.blogbitix.springstatemachine;

...

@SpringBootApplication
public class Main implements CommandLineRunner {

    ...

    @Override
    public void run(String... args) throws Exception {
        Random random = new Random();

        machine1.start();
        machine1.getExtendedState().getVariables().put("variable", 42);
        machine1.sendEvent(new GenericMessage<>(Events.START_STATE1, Collections.singletonMap("key", 31)));
        machine1.sendEvent(Events.STATE1_CHOICE);
        machine1.sendEvent((machine1.getState().getId() == States.CHOICE1) ? Events.CHOICE1_FORK : Events.CHOICE2_FORK);
        machine1.sendEvent(Events.TASK11_TASK12);
        machine1.sendEvent(Events.TASK21_TASK22);
        machine1.sendEvent(Events.TASK12_TASK13);
        machine1.sendEvent(Events.TASK22_TASK23);
        machine1.sendEvent(Events.STATE2_END);
        machine1.stop();

        ...
    }

    public static void main(String... args) {
        SpringApplication.run(Main.class, args);
    }
}
Main-2.java

Otro caso de uso posible es en vez de iniciar la máquina de estados desde el inicio es iniciarla en un determinado estado para continuar con el flujo de una entidad que anteriormente se quedó en ese estado. En el caso del ejemplo la máquina de estados se continua desde el estado TASK11 y TASK22 utilizando el método resetStateMachine(), se vuelve a iniciar la máquina y se envían los eventos adecuados a partir de esos estados.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package io.github.picodotdev.blogbitix.springstatemachine;

...

@SpringBootApplication
public class Main implements CommandLineRunner {

    ...

    @Override
    public void run(String... args) throws Exception {
        ...

        machine1.start();
        machine1.sendEvent(Events.TASK11_TASK12);
        machine1.sendEvent(Events.TASK22_TASK23);
        machine1.sendEvent(Events.TASK12_TASK13);
        machine1.sendEvent(Events.STATE2_END);
        machine1.stop();
    }

    public static void main(String... args) {
        SpringApplication.run(Main.class, args);
    }
}
Main-3.java

Por defecto el contenedor de dependencias de Spring utiliza el @Scope singleton para los beans de modo que solo existe una única instancia, como las máquinas tiene estado no pueden compartirse, hay que crear una nueva en caso de querer utilizar dos simultáneamente como sería el caso de una aplicación web o un proceso que escucha mensajes de una cola, utilizando el scope prototype se crea una instancia cada vez que se necesite. La creación de más máquina de estado se indica en la documentación que es algo costoso para no tener que crearlas, tener varias instancias y limitado a cierto número se puede utilizar un pool de máquinas de estado.

En un cambio de estado se produce la siguiente secuencia de eventos y acciones.

  • Se notifica del inicio de la transición.
  • Se ejecuta la acción asociada a la transición.
  • Se notifica de la transición, de la salida del estado anterior y de la entrada en el nuevo.
  • Se ejecuta la acción de entrada al estado y del estado.
  • Se notifica del cambio de estado.
  • Se notifica de la finalización de la transición.
1
2
3
4
5
6
7
8
9
... : Listener transitionStarted from START to STATE1
... : Action Source: START, State: START, Target: STATE1, Event: START_STATE1
... : Listener transition from START to STATE1
... : Listener stateExited in START
... : Listener stateEntered in STATE1
... : Action Source: START, State: STATE1, Target: STATE1, Event: START_STATE1
... : Action Source: START, State: STATE1, Target: STATE1, Event: START_STATE1
... : Listener stateChanged from START to STATE1
... : Listener transitionEnded from START to STATE1
System.out-3

Estas son las dependencias necesarias a añadir n la herramienta de construcción y la salida en la terminal de las trazas ejecutando la máquina de estados desde el estado inicial e inicializada desde un estado en concreto.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
plugins {
    id 'java'
    id 'application'
}

mainClassName = 'io.github.picodotdev.blogbitix.springstatemachine.Main'

ext {
    versions = [
        springBoot: '2.1.3.RELEASE',
    ]
}

repositories {
    jcenter()
}

dependencies {
    implementation platform("org.springframework.boot:spring-boot-dependencies:$versions.springBoot")

    compile("org.springframework.boot:spring-boot-starter")
    compile("org.springframework.boot:spring-boot-autoconfigure")

    compile("org.springframework.statemachine:spring-statemachine-core:2.1.1.RELEASE")
}

build.gradle
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.1.3.RELEASE)
...
... : Starting Main on archlinux with PID 32679 (/home/picodotdev/Software/personal/blog-ejemplos/SpringStatemachine/build/classes/java/main started by picodotdev in /home/picodotdev/Software/personal/blog-ejemplos/SpringStatemachine)
... : No active profile set, falling back to default profiles: default
... : Initializing ExecutorService
... : Started Main in 0.719 seconds (JVM running for 0.948)
... : Listener transitionStarted from null to START
... : Listener stateContext (Message Payload: null, Message Headers: {id=8b954f2b-aeef-48dd-a4ca-e799c188d476, timestamp=1554007267639}, Variables: {}
... : Listener transition from null to START
... : Listener stateContext (Message Payload: null, Message Headers: {id=e1bf1cb1-c597-4975-6874-580b8d7c6a0a, timestamp=1554007267640}, Variables: {}
... : Listener stateEntered in START
... : Listener stateContext (Message Payload: null, Message Headers: {id=e938b91d-bada-c56b-d845-5f3958bba4f8, timestamp=1554007267641}, Variables: {}
... : Listener stateChanged from null to START
... : Listener stateContext (Message Payload: null, Message Headers: {id=b58324de-f43d-417b-cc38-86efdea7274f, timestamp=1554007267641}, Variables: {}
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=747d233f-0d46-0bd0-78be-6cf026739dd8, timestamp=1554007267642}, Variables: {}
... : Listener transitionEnded from null to START
... : Listener stateContext (Message Payload: null, Message Headers: {id=1fe8b3a2-e176-4791-dcab-d8759b2e4dc6, timestamp=1554007267642}, Variables: {}
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@51e37590
... : started STATE1 TASKS TASK13 TASK11 TASK12 TASK21 TASK23 TASK22 CHOICE1 CHOICE2 START STATE2 END JOIN FORK CHOICE  / START / uuid=a2882db4-b636-4fac-93d0-87c50434401e / id=null
... : Listener extendedStateChanged variable to 42
... : Listener stateContext (Message Payload: null, Message Headers: {id=afa8655f-09a8-a551-c11e-36a36cd2c92a, timestamp=1554007267643}, Variables: {variable=42}
... : Listener transitionStarted from START to STATE1
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Action Source: START, State: START, Target: STATE1, Event: START_STATE1
... : Listener transition from START to STATE1
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Listener stateExited in START
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Listener stateEntered in STATE1
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Action Source: START, State: STATE1, Target: STATE1, Event: START_STATE1
... : Action Source: START, State: STATE1, Target: STATE1, Event: START_STATE1
... : Listener stateChanged from START to STATE1
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Listener transitionEnded from START to STATE1
... : Listener stateContext (Message Payload: START_STATE1, Message Headers: {key=31, id=bf50ec37-b6f7-32d5-fd59-292d7f1ea265, timestamp=1554007267643}, Variables: {variable=42}
... : Listener transitionStarted from STATE1 to CHOICE
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Listener transition from STATE1 to CHOICE
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Action Source: STATE1, State: STATE1, Target: CHOICE, Event: STATE1_CHOICE
... : Listener stateExited in STATE1
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Listener stateEntered in CHOICE1
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Listener stateChanged from STATE1 to CHOICE1
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Listener transitionEnded from STATE1 to CHOICE
... : Listener stateContext (Message Payload: STATE1_CHOICE, Message Headers: {id=8c969225-fec1-6603-63dd-7ae00f8ead10, timestamp=1554007267650}, Variables: {variable=42}
... : Listener transitionStarted from CHOICE1 to FORK
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener transition from CHOICE1 to FORK
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener stateExited in CHOICE1
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener stateEntered in TASKS
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener transitionStarted from null to TASK11
... : Listener stateContext (Message Payload: null, Message Headers: {id=37715a25-6933-14b8-2e25-756a447a3630, timestamp=1554007267652}, Variables: {variable=42}
... : Listener transition from null to TASK11
... : Listener stateContext (Message Payload: null, Message Headers: {id=62093291-042b-cd75-1405-4a96be2b5682, timestamp=1554007267652}, Variables: {variable=42}
... : Listener stateEntered in TASK11
... : Listener stateContext (Message Payload: null, Message Headers: {id=a7c34605-3515-c56f-ff7a-8977d2331fd9, timestamp=1554007267652}, Variables: {variable=42}
... : Listener stateChanged from null to TASK11
... : Listener stateContext (Message Payload: null, Message Headers: {id=7b000dca-0fb2-f95a-a6db-aaf6600e1f5f, timestamp=1554007267653}, Variables: {variable=42}
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=908bcc73-0466-dec2-14b6-2822ad56c7ba, timestamp=1554007267653}, Variables: {variable=42}
... : Listener transitionEnded from null to TASK11
... : Listener stateContext (Message Payload: null, Message Headers: {id=e262f0fe-6047-9638-b592-c6e4bbc4e2ef, timestamp=1554007267653}, Variables: {variable=42}
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@5a865416
... : started TASK13 TASK11 TASK12  / TASK11 / uuid=b703837b-1a49-4dc4-83aa-5420f6cf0517 / id=null#8dd81b65-183c-43d0-8d27-c5ff71d2968a
... : Listener transitionStarted from null to TASK21
... : Listener stateContext (Message Payload: null, Message Headers: {id=fe645a0b-2b28-8734-e09a-271a7df04d17, timestamp=1554007267653}, Variables: {variable=42}
... : Listener transition from null to TASK21
... : Listener stateContext (Message Payload: null, Message Headers: {id=ab063429-8d97-27ce-c25b-3d8488bbc03f, timestamp=1554007267653}, Variables: {variable=42}
... : Listener stateEntered in TASK21
... : Listener stateContext (Message Payload: null, Message Headers: {id=309a6395-2545-89c6-833f-ba49a7893e2f, timestamp=1554007267654}, Variables: {variable=42}
... : Listener stateChanged from null to TASK21
... : Listener stateContext (Message Payload: null, Message Headers: {id=014531de-2bb4-efc8-1b8c-1bfcaf6f445c, timestamp=1554007267654}, Variables: {variable=42}
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=1375e1a3-b78f-7efb-84ed-3536e7198715, timestamp=1554007267654}, Variables: {variable=42}
... : Listener transitionEnded from null to TASK21
... : Listener stateContext (Message Payload: null, Message Headers: {id=4fe6a6a2-f279-33fb-58fa-14731e111b70, timestamp=1554007267654}, Variables: {variable=42}
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@6dc1484
... : started TASK21 TASK23 TASK22  / TASK21 / uuid=74691d25-219e-4dca-83ff-2e80ffc74f93 / id=null#5260c53d-1cbb-47a6-984a-8ef1ceb3ab31
... : Listener stateChanged from CHOICE1 to TASKS
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener transitionEnded from CHOICE1 to FORK
... : Listener stateContext (Message Payload: CHOICE1_FORK, Message Headers: {id=1f2f3a68-19e1-29d4-8677-2c63c5e360a3, timestamp=1554007267651}, Variables: {variable=42}
... : Listener transitionStarted from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener transition from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener stateExited in TASK11
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener stateEntered in TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener stateChanged from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener transitionEnded from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK11_TASK12, headers={id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}]
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=a5f14519-e027-16b9-e718-7884633feb01, timestamp=1554007267655}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK21_TASK22, headers={id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}]
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK21_TASK22, headers={id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}]
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener transitionStarted from TASK21 to TASK22
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener transition from TASK21 to TASK22
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener stateExited in TASK21
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener stateEntered in TASK22
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener stateChanged from TASK21 to TASK22
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener transitionEnded from TASK21 to TASK22
... : Listener stateContext (Message Payload: TASK21_TASK22, Message Headers: {id=2f048198-ef29-0b11-53d9-c22202b3a023, timestamp=1554007267656}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK12_TASK13, headers={id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}]
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK12_TASK13, headers={id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}]
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener transitionStarted from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener transition from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener stateExited in TASK12
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener stateEntered in TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener stateChanged from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@5a865416
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=887ca4d7-226b-9f99-3955-dce7151dcbae, timestamp=1554007267659}, Variables: {variable=42}
... : stopped TASK13 TASK11 TASK12  /  / uuid=b703837b-1a49-4dc4-83aa-5420f6cf0517 / id=null#8dd81b65-183c-43d0-8d27-c5ff71d2968a
... : Listener transitionEnded from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=1fd4d183-88d0-f30e-34a8-530c23e66b23, timestamp=1554007267657}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK22_TASK23, headers={id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}]
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener eventNotAccepted GenericMessage [payload=TASK22_TASK23, headers={id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}]
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionStarted from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transition from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener stateExited in TASK22
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener stateEntered in TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionStarted from TASK13 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transition from TASK13 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener stateExited in TASK13
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionEnded from TASK13 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionStarted from TASK23 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transition from TASK23 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener stateExited in TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionEnded from TASK23 to JOIN
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@6dc1484
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=b65b9ec8-ef97-dba7-aa77-44b8b5588ff9, timestamp=1554007267662}, Variables: {variable=42}
... : stopped TASK21 TASK23 TASK22  /  / uuid=74691d25-219e-4dca-83ff-2e80ffc74f93 / id=null#5260c53d-1cbb-47a6-984a-8ef1ceb3ab31
... : Listener stateExited in TASKS
... : Listener stateContext (Message Payload: null, Message Headers: {id=2664aa9e-1ec2-593f-3016-81231e9551a2, timestamp=1554007267662}, Variables: {variable=42}
... : Listener stateEntered in STATE2
... : Listener stateContext (Message Payload: null, Message Headers: {id=8510c1b9-6b2f-46ae-9412-9542b0098fec, timestamp=1554007267662}, Variables: {variable=42}
... : Listener stateChanged from TASKS to STATE2
... : Listener stateContext (Message Payload: null, Message Headers: {id=8d650465-62b7-f71a-070e-9b1dadf29861, timestamp=1554007267662}, Variables: {variable=42}
... : Listener stateChanged from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionEnded from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=592d65e8-6ccc-b942-0d10-d5b9b645c558, timestamp=1554007267659}, Variables: {variable=42}
... : Listener transitionStarted from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
... : Listener transition from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
... : Listener stateExited in STATE2
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
... : Listener stateEntered in END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
... : Listener stateChanged from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@51e37590
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=cf95710c-ecb5-3df6-61e3-11799f0925d7, timestamp=1554007267664}, Variables: {variable=42}
... : stopped STATE1 TASKS TASK13 TASK11 TASK12 TASK21 TASK23 TASK22 CHOICE1 CHOICE2 START STATE2 END JOIN FORK CHOICE  /  / uuid=a2882db4-b636-4fac-93d0-87c50434401e / id=null
... : Listener transitionEnded from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=f84d435c-bc74-e23d-f0b5-3191241118ef, timestamp=1554007267663}, Variables: {variable=42}
...
System.out-1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
... : started ObjectState [getIds()=[TASK11], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=1584833211, toString()=AbstractState [id=TASK11, pseudoState=org.springframework.statemachine.state.DefaultPseudoState@757194dc, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
... : started ObjectState [getIds()=[TASK22], getClass()=class org.springframework.statemachine.state.ObjectState, hashCode()=800456240, toString()=AbstractState [id=TASK22, pseudoState=null, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[], submachine=null]]
... : started RegionState [getIds()=[TASKS, TASK11, TASK22], getClass()=class org.springframework.statemachine.state.RegionState, hashCode()=1311544814, toString()=AbstractState [id=TASKS, pseudoState=org.springframework.statemachine.state.DefaultPseudoState@d0ec63, deferred=[], entryActions=[], exitActions=[], stateActions=[], regions=[TASK13 TASK11 TASK12  / TASK11 / uuid=b703837b-1a49-4dc4-83aa-5420f6cf0517 / id=null, TASK21 TASK23 TASK22  / TASK22 / uuid=74691d25-219e-4dca-83ff-2e80ffc74f93 / id=null], submachine=null]]
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@51e37590
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=a6be7fdd-8685-5b21-1f34-9dcf718ec374, timestamp=1554007267665}, Variables: {}
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@5a865416
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=3eef6870-ee58-fa1d-8798-71987d336914, timestamp=1554007267665}, Variables: {}
... : started TASK13 TASK11 TASK12  / TASK11 / uuid=b703837b-1a49-4dc4-83aa-5420f6cf0517 / id=null
... : started org.springframework.statemachine.support.DefaultStateMachineExecutor@6dc1484
... : Listener stateMachineStarted
... : Listener stateContext (Message Payload: null, Message Headers: {id=aef5d00b-59ea-8eed-81ff-c840b1ce2b94, timestamp=1554007267666}, Variables: {}
... : started TASK21 TASK23 TASK22  / TASK22 / uuid=74691d25-219e-4dca-83ff-2e80ffc74f93 / id=null
... : started STATE1 TASKS TASK13 TASK11 TASK12 TASK21 TASK23 TASK22 CHOICE1 CHOICE2 START STATE2 END JOIN FORK CHOICE  / TASKS,TASK11,TASK22 / uuid=a2882db4-b636-4fac-93d0-87c50434401e / id=null
... : Listener transitionStarted from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener transition from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener stateExited in TASK11
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener stateEntered in TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener stateChanged from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener transitionEnded from TASK11 to TASK12
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener eventNotAccepted GenericMessage [payload=TASK11_TASK12, headers={id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}]
... : Listener stateContext (Message Payload: TASK11_TASK12, Message Headers: {id=dd47c963-48b4-a47b-093a-bd52cc4371f8, timestamp=1554007267666}, Variables: {}
... : Listener eventNotAccepted GenericMessage [payload=TASK22_TASK23, headers={id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}]
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener eventNotAccepted GenericMessage [payload=TASK22_TASK23, headers={id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}]
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener transitionStarted from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener transition from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener stateExited in TASK22
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener stateEntered in TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener stateChanged from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@6dc1484
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=ff2f4d56-fde7-2ab0-c412-e5307b52cfc1, timestamp=1554007267668}, Variables: {}
... : stopped TASK21 TASK23 TASK22  /  / uuid=74691d25-219e-4dca-83ff-2e80ffc74f93 / id=null
... : Listener transitionEnded from TASK22 to TASK23
... : Listener stateContext (Message Payload: TASK22_TASK23, Message Headers: {id=285c9e5d-930d-c150-0ef0-2de6cec651ab, timestamp=1554007267667}, Variables: {}
... : Listener eventNotAccepted GenericMessage [payload=TASK12_TASK13, headers={id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}]
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener eventNotAccepted GenericMessage [payload=TASK12_TASK13, headers={id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}]
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener transitionStarted from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener transition from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener stateExited in TASK12
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener stateEntered in TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@5a865416
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=ee20242f-e669-8719-8cef-869d2c7bbb13, timestamp=1554007267669}, Variables: {}
... : stopped TASK13 TASK11 TASK12  /  / uuid=b703837b-1a49-4dc4-83aa-5420f6cf0517 / id=null
... : Listener stateExited in TASKS
... : Listener stateContext (Message Payload: null, Message Headers: {id=9835d889-b3e4-b056-2e8d-1dfc3850907e, timestamp=1554007267670}, Variables: {}
... : Listener stateEntered in STATE2
... : Listener stateContext (Message Payload: null, Message Headers: {id=e9f3c6bb-b384-455e-354c-7cc6d8b2f6a1, timestamp=1554007267670}, Variables: {}
... : Listener stateChanged from TASKS to STATE2
... : Listener stateContext (Message Payload: null, Message Headers: {id=6c0826b9-8ec5-84a8-f6d4-53b1c3311334, timestamp=1554007267670}, Variables: {}
... : Listener stateChanged from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener transitionEnded from TASK12 to TASK13
... : Listener stateContext (Message Payload: TASK12_TASK13, Message Headers: {id=5ff52736-4f77-7bf1-e0ad-9e9cef73e1aa, timestamp=1554007267668}, Variables: {}
... : Listener transitionStarted from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : Listener transition from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : Listener stateExited in STATE2
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : Listener stateEntered in END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : Listener stateChanged from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : stopped org.springframework.statemachine.support.DefaultStateMachineExecutor@51e37590
... : Listener stateMachineStopped
... : Listener stateContext (Message Payload: null, Message Headers: {id=3825827b-07cb-6df6-f6bb-ebb391030e1e, timestamp=1554007267671}, Variables: {}
... : stopped STATE1 TASKS TASK13 TASK11 TASK12 TASK21 TASK23 TASK22 CHOICE1 CHOICE2 START STATE2 END JOIN FORK CHOICE  /  / uuid=a2882db4-b636-4fac-93d0-87c50434401e / id=null
... : Listener transitionEnded from STATE2 to END
... : Listener stateContext (Message Payload: STATE2_END, Message Headers: {id=6be8a081-daec-2aca-268b-3f0af8940247, timestamp=1554007267670}, Variables: {}
... : destroy called
System.out-2
Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradle run


Comparte el artículo: