ããã¯ããªã«ãããããŠæžãããã®ïŒ
以åã«ãSpring StatemachineããšããããåãããŠã¿ãŸããã
Spring Statemachineを試してみる - CLOVER🍀
ä»åã¯ããã¢ã¯ã·ã§ã³ããšãããã®ãæ±ã£ãŠã¿ãããšæããŸãã
Spring Statemachineã«ãããã¢ã¯ã·ã§ã³
ã¢ã¯ã·ã§ã³ãã©ããããã®ãªã®ãã¯ãSpring Statemachineã®çšèªéã確èªããŠã¿ãŸãããã
ãã¢ã¯ã·ã§ã³ãã¯ãé·ç§»ã®æäžã«ããªã¬ãŒãããå®è¡ããããã®ã¿ããã§ãã
Action
A action is a behavior run during the triggering of the transition.
Spring Statemachine / Appendices / Appendix B: State Machine Concepts / Glossary
ã¯ã©ãã·ã¥ã³ãŒã¹ã§ã®ã¢ã¯ã·ã§ã³ã®èª¬æãèŠãŠã¿ãŸãããã
ã¢ã¯ã·ã§ã³ã¯ãã¹ããŒããã·ã³ã®ç¶æ
å€åããŠãŒã¶ãŒã®ã³ãŒãã«çµã³ã€ãããã®ãšããããšã«ãªã£ãŠããŸããã¹ããŒããã·ã³ã¯ãã¹ããŒããã·ã³ã«
ãããæ§ã
ãªå€åãã¹ãããïŒã¹ããŒãã®éå§ãçµäºãªã©ïŒãç¶æ
é·ç§»ã«ãããŠãã¢ã¯ã·ã§ã³ãå®è¡ã§ããŸãã
Actions really glue state machine state changes to a userâs own code. A state machine can run an action on various changes and on the steps in a state machine (such as entering or exiting a state) or doing a state transition.
ãŸããã¢ã¯ã·ã§ã³ã¯ã¹ããŒãã®ã³ã³ããã¹ãã«ã¢ã¯ã»ã¹ã§ãããããã¢ã¯ã·ã§ã³ãæ§æããã³ãŒãå
ã§ã¹ããŒããã·ã³ãšããåãããããšã
ã§ããŸãã
Actions usually have access to a state context, which gives running code a choice to interact with a state machine in various ways. State context exposes a whole state machine so that a user can access extended state variables, event headers (if a transition is based on an event), or an actual transition (where it is possible to see more detailed about where this state change is coming from and where it is going).
ãšããããã§ãã¢ã¯ã·ã§ã³ãšã¯ã¹ããŒããã·ã³ã®ç¶æ å€åãªã©ã«åãããŠããªã«ãåŠçãå®è¡ãããã®ã®ããã§ããã
ããæžããšãè¿ããã®ãšããŠãªã¹ããŒããã£ãæ°ãããŸãããªã¹ããŒã¯ãã¹ããŒããã·ã³ã®éå§ãçµäºãªã©ãå«ããŠãã¹ããŒããã·ã³ã«
èµ·ãã£ãã€ãã³ãã«å¯ŸããŠçŽä»ãããã®ã§ããã¢ã¯ã·ã§ã³ããããããã¡ãã£ãšç¯å²ãåºããã§ããã
Spring Statemachine / Using Spring Statemachine / Listening to State Machine Events
äžæ¹ã§ããã¡ããèŠããšã¢ã¯ã·ã§ã³ã«å¯ŸããŠããªã¹ããŒãšäŒŒããããªããšãæžãããŠããããããŸããâŠã
You can run actions in various places in a state machine and its states lifecycle
Spring Statemachine / Using Spring Statemachine / Using Actions
ãã®ç®æã§ã¯ãã¹ããŒãã®éå§ãçµäºã«Springã®BeanãšããŠå®çŸ©ããã¢ã¯ã·ã§ã³ãçŽä»ããŠããäŸã«ãªã£ãŠããŸãã
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.SI) .state(States.S1, action1(), action2()) .state(States.S2, action1(), action2()) .state(States.S3, action1(), action3()); }
Beanå®çŸ©ã
@Bean public Action<States, Events> action1() { return new Action<States, Events>() { @Override public void execute(StateContext<States, Events> 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<States, Events> { @Override public void execute(StateContext<States, Events> context) { } } public class SpelAction extends SpelExpressionAction<States, Events> { public SpelAction(Expression expression) { super(expression); } }
ãšããããã§ãSpringã®Beanãã¢ã¯ã·ã§ã³ãšããŠäœ¿ããããã§ããå
容ã¯ãSpELã§ãæžããããã§ãããèšè¿°ã§ããããšã«ã¯ãèªç±åºŠã
ããããã§ããã
ã¢ã¯ã·ã§ã³ã®æ§æã«é¢ããèšè¿°ãèŠãŠã¿ãŸãããã
Spring Statemachine / Using Spring Statemachine / Statemachine Configuration / Configuring Actions
ã¢ã¯ã·ã§ã³ã¯ãé·ç§»ïŒTransitionïŒãšã¹ããŒãã«å¯ŸããŠå®çŸ©ã§ãããšæžãããŠããŸãã
You can define actions to be executed with transitions and states.
ãã¡ãã¯ãé·ç§»ã«å¯Ÿããå®çŸ©ã®äŸã«ãªã£ãŠããŸãã
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.S1) .target(States.S2) .event(Events.E1) .action(action()); }
ãã¡ãã¯ã¹ããŒãã«å¯Ÿããå®çŸ©ã®äŸã
@Override public void configure(StateMachineStateConfigurer<States, Events> 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()); }
StateMachineTransitionConfigurer
ã䜿ã£ãŠå®çŸ©ããããStateMachineStateConfigurer
ã䜿ã£ãŠå®çŸ©ãããããšãããšããã§ããã
å®éã«ã¢ã¯ã·ã§ã³ãå®çŸ©ããã€ã³ã¿ãŒãã§ãŒã¹ã¯ãã¡ãã®ããã§ããã
- TransitionConfigurer (Spring State Machine 3.2.0 API)
- StateConfigurer (Spring State Machine 3.2.0 API)
ãããŸã§èŠãŠãããšãã¢ã¯ã·ã§ã³ãšããã®ã¯å€§ãã以äžã®2ã€ã®ãã®ã«åãããããã§ããã
- é·ç§»ã«å¯Ÿããã¢ã¯ã·ã§ã³ ⊠ãœãŒã¹ãšãªãã¹ããŒããããã¿ãŒã²ãããšãªãã¹ããŒããžã®é·ç§»æã«å®è¡ããã
- ã¹ããŒãã«å¯Ÿããã¢ã¯ã·ã§ã³ ⊠ããã¹ããŒãã®éå§ãçµäºæã«å®è¡ããã
ããã¥ã¡ã³ããããå°ã远ã£ãŠã¿ãŸãããã
é·ç§»ã«å¯Ÿããã¢ã¯ã·ã§ã³
é·ç§»ã«å¯ŸããŠã¢ã¯ã·ã§ã³ãå®çŸ©ããå Žåã¯ãç¶æ å€åãããªã¬ãŒãšããŠçºçããé·ç§»ã®çµæãšããŠãåžžã«ã¢ã¯ã·ã§ã³ãå®è¡ãããŸãã
An action is always run as a result of a transition that originates from a trigger.
é·ç§»ã«çŽä»ããã¢ã¯ã·ã§ã³ã§ãšã©ãŒãçºçããå Žåã¯ãaction
ã¡ãœããã®ç¬¬2åŒæ°ã«æž¡ããã¢ã¯ã·ã§ã³ã§ãã³ããªã³ã°ã§ããããã§ãã
TransitionConfigurer (Spring State Machine 3.2.0 API)
ãã¡ãã®äŸã§ããã
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.S1) .target(States.S2) .event(Events.E1) .action(action(), errorAction()); }
ãšã©ãŒãã³ããªã³ã°çšã®ã¢ã¯ã·ã§ã³ã«ã¯ãçºçããäŸå€ãå«ãŸããStateContext
ãæž¡ãããããã§ãã
ãªããActions#errorCallingAction
ã䜿çšããããšã§ãéåžžã®ã¢ã¯ã·ã§ã³ãšãšã©ãŒçšã®ã¢ã¯ã·ã§ã³ãåæããŠã²ãšã€ã®ã¢ã¯ã·ã§ã³ãšããŠ
èšå®ããããšãã§ããããã§ãã
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.S1) .target(States.S2) .event(Events.E1) .action(Actions.errorCallingAction(action(), errorAction())); }
ã¹ããŒãã«å¯Ÿããã¢ã¯ã·ã§ã³
ã¹ããŒãã«å¯Ÿããã¢ã¯ã·ã§ã³ã¯ããã¡ãã«èšè¿°ããããŸãã
ã¢ã¯ã·ã§ã³ã®å®çŸ©ã¯ããã¡ãã§è¡ããŸãã
StateConfigurer (Spring State Machine 3.2.0 API)
ã¹ããŒãã«å¯Ÿããã¢ã¯ã·ã§ã³ã¯ãã¹ããŒãã®éå§ãšçµäºã«é¢é£ä»ããããã¢ã¯ã·ã§ã³ããããããããç°ãªãæ¹æ³ã§å®è¡ãããŸãã
ããã¯ãã¹ããŒãã®éå§ã§å®è¡ãããç¹å®ã®ã¢ã¯ã·ã§ã³ãå®äºããåã«ã¹ããŒããçµäºãããšãã®ã¢ã¯ã·ã§ã³ããã£ã³ã»ã«ããå¯èœæ§ã
ããããã§ãã
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.
ã¢ã¯ã·ã§ã³ã¯Reactorã®ã¹ã±ãžã¥ãŒã©ãŒã䜿ã£ãŠãµãã¹ã¯ã©ã€ãããããšã§å®è¡ãããŸãããã®ãããã¹ã¬ããã®å²ã蟌ã¿ããã³ããªã³ã°ãã
å¿
èŠãããããšãæå³ããŠããŸãã
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.
ã¢ã¯ã·ã§ã³ã«å¯Ÿããå®è¡ããªã·ãŒãæå®ã§ããããã§ããç¶æ ãå®äºããæããŸãã¯ã¿ã€ã ã¢ãŠãããæã«ãã£ã³ã»ã«ãããã©ãããã§ããã
StateDoActionPolicy (Spring State Machine 3.2.0 API)
ã¢ã¯ã·ã§ã³ã®ãšã©ãŒãã³ããªã³ã°ã«å¯Ÿããèšè¿°ã¯ããã¡ãã
ãã¡ãã¯ã3çš®é¡ã®ã¿ã€ãã³ã°ãã¹ãŠã«ã¢ã¯ã·ã§ã³ããã³ãšã©ãŒã¢ã¯ã·ã§ã³ãçŽä»ããäŸã§ãã
@Override public void configure(StateMachineStateConfigurer<States, Events> 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); }
ãšã©ãŒã¢ã¯ã·ã§ã³ã«æž¡ãããStateContext
ã«ã¯ãäŸå€æ
å ±ãå«ãŸããããšã¯é·ç§»ã«å¯Ÿããã¢ã¯ã·ã§ã³ãšåãã§ããã
ãšããã§ãDo
ã®ã¿ã€ãã³ã°ãããã¥ã¡ã³ãã«åºãŠããŠããªãæ°ãããŸããâŠãããšã§ç¢ºèªããŠã¿ãŸãããâŠã
ã¹ããŒãã«å¯Ÿããã¢ã¯ã·ã§ã³ã¯çŽä»ããè¡ãããªãšãŒã·ã§ã³ãå€ããã³ã¬ã¯ã·ã§ã³ã§è€æ°ã®ã¢ã¯ã·ã§ã³ãçŽä»ããããšãã§ããŸãã
ãšãããããããã¥ã¡ã³ããçºããã®ã¯ãããªãšããã§ãããããæ¬¡ã¯ãå®éã«Spring Statemachineã§ã¢ã¯ã·ã§ã³ãåãããŠã¿ãŸãããã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã§ãã
$ java --version openjdk 17.0.4 2022-07-19 OpenJDK Runtime Environment (build 17.0.4+8-Ubuntu-120.04) OpenJDK 64-Bit Server VM (build 17.0.4+8-Ubuntu-120.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.6 (84538c9988a25aec085021c365c560670ad80f63) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.4, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.4.0-125-generic", arch: "amd64", family: "unix"
ãããžã§ã¯ããäœæãã
Spring Bootãããžã§ã¯ããäœæããŸãã
$ curl -s https://start.spring.io/starter.tgz \ -d bootVersion=2.6.7 \ -d javaVersion=17 \ -d name=statemachine-action \ -d groupId=org.littlewings \ -d artifactId=statemachine-action \ -d version=0.0.1-SNAPSHOT \ -d packageName=org.littlewings.spring.statemachine \ -d baseDir=statemachine-action | tar zxvf -
å°ãå€ãã§ãããããã¥ã¡ã³ãã«ç¿ã£ãŠSpring Bootã®ããŒãžã§ã³ã¯2.6.7ã«ããŠãããŸãã
Spring Statemachine / Getting started / Using Maven
ãããžã§ã¯ãå ãžç§»åã
$ cd statemachine-action
èªåçæããããœãŒã¹ã³ãŒãã¯åé€ããŠãããŸãã
$ rm src/main/java/org/littlewings/spring/statemachine/StatemachineActionApplication.java src/test/java/org/littlewings/spring/statemachine/StatemachineActionApplicationTests.java
MavenäŸåé¢ä¿ãªã©ã
<properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
spring-boot-starter
ãspring-statemachine-starter
ã«å€æŽããŸãã
<dependencies> <dependency> <groupId>org.springframework.statemachine</groupId> <artifactId>spring-statemachine-starter</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
ã§ã¯ããœãŒã¹ã³ãŒããäœæããŠãããŸãã
ã¹ããŒããå®çŸ©ããenum
ã
src/main/java/org/littlewings/spring/statemachine/States.java
package org.littlewings.spring.statemachine; public enum States { INITIAL_STATE, STATE1, STATE2, STATE3, STATE4, STATE5, END_STATE }
ã¡ãã£ãšå€ãã§ãããã¢ã¯ã·ã§ã³ãçŽä»ããããªãšãŒã·ã§ã³ããããã詊ãããšããçµæã§ãâŠã
ã€ãã³ããå®çŸ©ããenum
ã
src/main/java/org/littlewings/spring/statemachine/Events.java
package org.littlewings.spring.statemachine; public enum Events { EVENT1, EVENT2, EVENT3, EVENT4, EVENT5, EVENT6 }
ã¹ããŒããã·ã³ã®å®çŸ©ãã¡ãã£ãšé·ãã§ãããããã¯åŸã§èª¬æããŸãã
src/main/java/org/littlewings/spring/statemachine/StateMachineConfig.java
package org.littlewings.spring.statemachine; import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.EnableStateMachine; import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer; @Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> { @Override public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception { config .withConfiguration() .autoStartup(true) .machineId("my-statemachine"); } @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.INITIAL_STATE) //.initial(States.INITIAL_STATE, stateAction()) .state(States.STATE1) //.state(States.STATE1, stateAction()) .state(States.STATE2) //.state(States.STATE2, stateAction(), stateAction()) .state(States.STATE3) //.stateEntry(States.STATE3, stateEntryAction()) //.stateDo(States.STATE3, stateDoAction()) // synonym state(state, action) //.stateExit(States.STATE3, stateExitAction()) .state(States.STATE4) //.state(States.STATE4, stateAction(), stateAction()) //.stateEntry(States.STATE4, stateActionThrowException(), stateActionHandleError()) //.stateDo(States.STATE4, stateActionThrowException(), stateActionHandleError()) //.stateExit(States.STATE4, stateActionThrowException(), stateActionHandleError()) .state(States.STATE5) //.state(States.STATE5, List.of(stateAction(), stateAction()), List.of(stateAction(), stateAction())) // collection .end(States.END_STATE); // endã«ã¯actionãªã } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.INITIAL_STATE) .target(States.STATE1) .event(Events.EVENT1) //.action(transitionAction()) .and() .withExternal() .source(States.STATE1) .target(States.STATE2) .event(Events.EVENT2) //.action(transitionAction()) //.action(transitionActionThrowException(), transitionActionHandleError()) .and() .withExternal() .source(States.STATE2) .target(States.STATE3) .event(Events.EVENT3) //.action(transitionAction()) .and() .withExternal() .source(States.STATE3) .target(States.STATE4) .event(Events.EVENT4) //.action(transitionAction()) //.action(transitionActionThrowException(), transitionActionHandleError()) .and() .withExternal() .source(States.STATE4) .target(States.STATE5) .event(Events.EVENT5) //.action(transitionAction()) .and() .withExternal() .source(States.STATE5) .target(States.END_STATE) .event(Events.EVENT6); //.action(transitionAction()); } @Bean public Action<States, Events> stateAction() { return stateContext -> System.out.printf( "state action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateEntryAction() { return stateContext -> System.out.printf( "state entry action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateDoAction() { return stateContext -> System.out.printf( "state do action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateExitAction() { return stateContext -> System.out.printf( "state exit action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateActionThrowException() { return stateContext -> { System.out.printf( "state action throw exception, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); throw new RuntimeException("state action error!!"); }; } @Bean public Action<States, Events> stateActionHandleError() { return stateContext -> System.out.printf( "state action handling exception, stage = %s, state = %s, event = %s, exception message = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]", stateContext.getException().getMessage() ); } @Bean public Action<States, Events> transitionAction() { return stateContext -> System.out.printf( "transition action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> transitionActionThrowException() { return stateContext -> { System.out.printf( "transition action throw exception, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); throw new RuntimeException("transition action error!!"); }; } @Bean public Action<States, Events> transitionActionHandleError() { return stateContext -> System.out.printf( "transition action handling exception, stage = %s, state = %s, event = %s, exception message = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]", stateContext.getException().getMessage() ); } }
ã¹ããŒããã·ã³ã䜿ãã¯ã©ã¹ã
src/main/java/org/littlewings/spring/statemachine/Runner.java
package org.littlewings.spring.statemachine; import java.util.concurrent.TimeUnit; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.messaging.support.MessageBuilder; import org.springframework.statemachine.StateMachine; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; @Component public class Runner implements ApplicationRunner { StateMachine<States, Events> stateMachine; public Runner(StateMachine<States, Events> stateMachine) { this.stateMachine = stateMachine; } @Override public void run(ApplicationArguments args) throws Exception { stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT1).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT2).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT3).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT4).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT5).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT6).build())) .blockFirst(); } }
main
ã¡ãœãããæã£ãã¯ã©ã¹ã
src/main/java/org/littlewings/spring/statemachine/App.java
package org.littlewings.spring.statemachine; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String... args) { SpringApplication.run(App.class, args); } }
ä»åã®ã¡ã€ã³ã¯ãã¡ããã¹ããŒããã·ã³ã«å®çŸ©ããã¹ããŒããé·ç§»ã«çŽä»ããã¢ã¯ã·ã§ã³ã®ç¢ºèªãªã®ã§ããã
æåã«èŒãããã®ããããããšãã¡ããã¡ãããŠããã®ã§ããŸãã¯ã¹ããŒããšé·ç§»ã®ããŒã¹ã®å®çŸ©ãèŒããŸãããã
@Configuration @EnableStateMachine public class StateMachineConfig extends EnumStateMachineConfigurerAdapter<States, Events> { @Override public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception { config .withConfiguration() .autoStartup(true) .machineId("my-statemachine"); } @Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.INITIAL_STATE) .state(States.STATE1) .state(States.STATE2) .state(States.STATE3) .state(States.STATE4) .state(States.STATE5) .end(States.END_STATE); } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.INITIAL_STATE) .target(States.STATE1) .event(Events.EVENT1) .and() .withExternal() .source(States.STATE1) .target(States.STATE2) .event(Events.EVENT2) .and() .withExternal() .source(States.STATE2) .target(States.STATE3) .event(Events.EVENT3) .and() .withExternal() .source(States.STATE3) .target(States.STATE4) .event(Events.EVENT4) .and() .withExternal() .source(States.STATE4) .target(States.STATE5) .event(Events.EVENT5) .and() .withExternal() .source(States.STATE5) .target(States.END_STATE) .event(Events.EVENT6); } ãçç¥ã }
ãŸã ãããã«ãã¢ã¯ã·ã§ã³ã¯çŽä»ããŠããŸããã
ãã®ç¶æ ã§ãã¢ããªã±ãŒã·ã§ã³ãå®è¡ã
$ mvn spring-boot:run
ãŸããç¹ã«ãªã«ã衚瀺ãããªãã®ã§ããã
2022-09-02 00:48:08.062 INFO 25583 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.statemachine.config.configuration.StateMachineAnnotationPostProcessorConfiguration' of type [org.springframework.statemachine.config.configuration.StateMachineAnnotationPostProcessorConfiguration$$EnhancerBySpringCGLIB$$95adde42] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2022-09-02 00:48:08.450 INFO 25583 --- [ main] org.littlewings.spring.statemachine.App : Started App in 1.26 seconds (JVM running for 1.549) 2022-09-02 00:48:08.498 INFO 25583 --- [ionShutdownHook] o.s.s.support.LifecycleObjectSupport : destroy called
ãããããã¹ããŒããé·ç§»ã«ã¢ã¯ã·ã§ã³ãçŽä»ããŠãããŸãã
ã¹ããŒãã«ã¢ã¯ã·ã§ã³ãçŽä»ãã
ãŸãã¯ãã¹ããŒãã«ã¢ã¯ã·ã§ã³ãçŽä»ããŠã¿ãŸãã
以äžã®ããã«ã¢ã¯ã·ã§ã³ãçŽä»ããŸããã
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.INITIAL_STATE, stateAction()) .state(States.STATE1, stateAction()) .state(States.STATE2, stateAction(), stateAction()) .stateEntry(States.STATE3, stateEntryAction()) .stateDo(States.STATE3, stateDoAction()) // synonym state(state, action) .stateExit(States.STATE3, stateExitAction()) .state(States.STATE4, stateAction(), stateAction()) .state(States.STATE5, List.of(stateAction(), stateAction()), List.of(stateAction(), stateAction())) // collection .end(States.END_STATE); // endã«ã¯actionãªã }
ã¹ããŒãã«å¯ŸããŠã²ãšã€ã¢ã¯ã·ã§ã³ãçŽä»ããå Žåã¯ãã¹ããŒãã®éå§æãžã®çŽä»ã
.state(States.STATE1, stateAction())
2ã€ã®å Žåã¯éå§ãšçµäºã
.state(States.STATE2, stateAction(), stateAction())
éå§ãšçµäºã®ã¿ã€ãã³ã°å¥ã®çŽä»ïŒDo
ã¯Entry
ã®å¥åã®ããã§ãïŒã
.stateEntry(States.STATE3, stateEntryAction())
.stateDo(States.STATE3, stateDoAction()) // synonym state(state, action)
.stateExit(States.STATE3, stateExitAction())
éå§ãšçµäºã®ã¿ã€ãã³ã°ã§ãè€æ°ã®ã¢ã¯ã·ã§ã³ãçŽä»ãšããããªãšãŒã·ã§ã³ã§ãã
.state(States.STATE5, List.of(stateAction(), stateAction()), List.of(stateAction(), stateAction())) // collection
æåŸã®ã¹ããŒãã«ã¯ãã¢ã¯ã·ã§ã³ã¯çŽä»ããããªãã¿ããã§ãã
.end(States.END_STATE); // endã«ã¯actionãªã
çŽä»ããã¢ã¯ã·ã§ã³ã®å®çŸ©ã¯ãã¡ãã§ãã¹ããŒãžãã¹ããŒããçºçããã€ãã³ãã®æ å ±ãæšæºåºåã«æžãåºããŠããŸãã
@Bean public Action<States, Events> stateAction() { return stateContext -> System.out.printf( "state action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateEntryAction() { return stateContext -> System.out.printf( "state entry action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateDoAction() { return stateContext -> System.out.printf( "state do action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); } @Bean public Action<States, Events> stateExitAction() { return stateContext -> System.out.printf( "state exit action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); }
ã§ã¯ãåãããŠã¿ãŸãã
$ mvn spring-boot:run
System.out.println
ããŠããéšåãæãåºããšãããããåºåãåŸãããŸããã
state action, stage = TRANSITION, state = INITIAL_STATE, event = [none] state action, stage = STATE_ENTRY, state = STATE1, event = EVENT1 state action, stage = STATE_ENTRY, state = STATE2, event = EVENT2 state action, stage = STATE_EXIT, state = STATE3, event = EVENT3 state entry action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state do action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state exit action, stage = STATE_EXIT, state = STATE4, event = EVENT4 state action, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action, stage = STATE_EXIT, state = STATE5, event = EVENT5 state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6
ãœãŒã¹ã³ãŒããšçŽä»ãããšããããªæãã§ããããã
// .initial(States.INITIAL_STATE, stateAction()) state action, stage = TRANSITION, state = INITIAL_STATE, event = [none] // .state(States.STATE1, stateAction()) state action, stage = STATE_ENTRY, state = STATE1, event = EVENT1 .state(States.STATE2, stateAction(), stateAction()) state action, stage = STATE_ENTRY, state = STATE2, event = EVENT2 state action, stage = STATE_EXIT, state = STATE3, event = EVENT3 // .stateEntry(States.STATE3, stateEntryAction()) // .stateDo(States.STATE3, stateDoAction()) // synonym state(state, action) // .stateExit(States.STATE3, stateExitAction()) state entry action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state do action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state exit action, stage = STATE_EXIT, state = STATE4, event = EVENT4 // .state(States.STATE4, stateAction(), stateAction()) state action, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action, stage = STATE_EXIT, state = STATE5, event = EVENT5 // .state(States.STATE5, List.of(stateAction(), stateAction()), List.of(stateAction(), stateAction())) state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6
endã«ã¯ã¢ã¯ã·ã§ã³ã¯çŽä»ããããŸããã§ãããã
次ã«ãäžéšã®ã¢ã¯ã·ã§ã³ã®çŽä»ãã倿ŽããŠã¿ãŸãã
states .withStates() .initial(States.INITIAL_STATE, stateAction()) .state(States.STATE1, stateAction()) .state(States.STATE2, stateAction(), stateAction()) .stateEntry(States.STATE3, stateEntryAction()) .stateDo(States.STATE3, stateDoAction()) // synonym state(state, action) .stateExit(States.STATE3, stateExitAction()) .stateEntry(States.STATE4, stateActionThrowException(), stateActionHandleError()) .stateDo(States.STATE4, stateActionThrowException(), stateActionHandleError()) .stateExit(States.STATE4, stateActionThrowException(), stateActionHandleError()) .state(States.STATE5, List.of(stateAction(), stateAction()), List.of(stateAction(), stateAction())) // collection .end(States.END_STATE); // endã«ã¯actionãªã
å€ãã£ãã®ã¯ãSTATE4
ã§ããã
ãã¡ãã«ã¯ãäŸå€ãæããã¢ã¯ã·ã§ã³ãšãã¢ã¯ã·ã§ã³ããæããããäŸå€ããã³ããªã³ã°ããã¢ã¯ã·ã§ã³ãçŽä»ããŠããŸãã
ã¢ã¯ã·ã§ã³ã®å®çŸ©ã¯ããã¡ãã
@Bean public Action<States, Events> stateActionThrowException() { return stateContext -> { System.out.printf( "state action throw exception, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); throw new RuntimeException("state action error!!"); }; } @Bean public Action<States, Events> stateActionHandleError() { return stateContext -> System.out.printf( "state action handling exception, stage = %s, state = %s, event = %s, exception message = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]", stateContext.getException().getMessage() ); }
å®è¡ã
$ mvn spring-boot:run
ã¹ã¿ãã¯ãã¬ãŒã¹ãåºåãã€ã€ãããã¹ãŠã®ã¹ããŒããçµãŠçµäºããŸããã
state action, stage = TRANSITION, state = INITIAL_STATE, event = [none] 2022-09-02 01:05:35.735 INFO 26623 --- [ main] org.littlewings.spring.statemachine.App : Started App in 1.017 seconds (JVM running for 1.256) state action, stage = STATE_ENTRY, state = STATE1, event = EVENT1 state action, stage = STATE_ENTRY, state = STATE2, event = EVENT2 state action, stage = STATE_EXIT, state = STATE3, event = EVENT3 state entry action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state do action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 state exit action, stage = STATE_EXIT, state = STATE4, event = EVENT4 state action throw exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action handling exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4, exception message = state action error!! 2022-09-02 01:05:35.777 WARN 26623 --- [ main] o.s.statemachine.state.ObjectState : Entry action execution error java.lang.RuntimeException: state action error!! at org.littlewings.spring.statemachine.StateMachineConfig.lambda$stateActionThrowException$4(StateMachineConfig.java:159) ~[classes/:na] at org.springframework.statemachine.action.Actions$2.execute(Actions.java:71) ~[spring-statemachine-core-3.2.0.jar:3.2.0] at org.springframework.statemachine.action.Actions.lambda$null$0(Actions.java:98) ~[spring-statemachine-core-3.2.0.jar:3.2.0] at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:73) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:32) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:252) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:203) ~[reactor-core-3.4.17.jar:3.4.17] ãçç¥ã at reactor.core.publisher.Flux.subscribe(Flux.java:8455) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.Flux.blockFirst(Flux.java:2599) ~[reactor-core-3.4.17.jar:3.4.17] at org.littlewings.spring.statemachine.Runner.run(Runner.java:33) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.7.jar:2.6.7] at org.littlewings.spring.statemachine.App.main(App.java:9) ~[classes/:na] state action throw exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action handling exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4, exception message = state action error!! state action throw exception, stage = STATE_EXIT, state = STATE5, event = EVENT5 state action handling exception, stage = STATE_EXIT, state = STATE5, event = EVENT5, exception message = state action error!! 2022-09-02 01:05:35.784 WARN 26623 --- [ main] o.s.statemachine.state.ObjectState : Exit action execution error java.lang.RuntimeException: state action error!! at org.littlewings.spring.statemachine.StateMachineConfig.lambda$stateActionThrowException$4(StateMachineConfig.java:159) ~[classes/:na] at org.springframework.statemachine.action.Actions$2.execute(Actions.java:71) ~[spring-statemachine-core-3.2.0.jar:3.2.0] at org.springframework.statemachine.action.Actions.lambda$null$0(Actions.java:98) ~[spring-statemachine-core-3.2.0.jar:3.2.0] at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:73) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoRunnable.call(MonoRunnable.java:32) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:252) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:203) ~[reactor-core-3.4.17.jar:3.4.17] ãçç¥ã at reactor.core.publisher.Flux.subscribe(Flux.java:8469) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:200) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.MonoFlatMapMany.subscribeOrReturn(MonoFlatMapMany.java:49) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.Flux.subscribe(Flux.java:8455) ~[reactor-core-3.4.17.jar:3.4.17] at reactor.core.publisher.Flux.blockFirst(Flux.java:2599) ~[reactor-core-3.4.17.jar:3.4.17] at org.littlewings.spring.statemachine.Runner.run(Runner.java:36) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:768) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.7.jar:2.6.7] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.7.jar:2.6.7] at org.littlewings.spring.statemachine.App.main(App.java:9) ~[classes/:na] state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_ENTRY, state = STATE5, event = EVENT5 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6 state action, stage = STATE_EXIT, state = END_STATE, event = EVENT6
Spring StatemachineãšReactorãäŸå€ãæŸã£ãŠããŸãããå®è¡ã¯ãã®ãŸãŸç¶ããŠããŸããã
äŸå€ãæãããã³ããªã³ã°ããŠããã¢ã¯ã·ã§ã³ãšãœãŒã¹ã³ãŒãã®å¯Ÿæ¯ãèŒãããšãããªæãã§ãã
// .stateEntry(States.STATE4, stateActionThrowException(), stateActionHandleError()) state action throw exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action handling exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4, exception message = state action error!! // .stateDo(States.STATE4, stateActionThrowException(), stateActionHandleError()) state action throw exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4 state action handling exception, stage = STATE_ENTRY, state = STATE4, event = EVENT4, exception message = state action error!! // .stateExit(States.STATE4, stateActionThrowException(), stateActionHandleError()) state action throw exception, stage = STATE_EXIT, state = STATE5, event = EVENT5 state action handling exception, stage = STATE_EXIT, state = STATE5, event = EVENT5, exception message = state action error!!
ã ãããé°å²æ°ã¯ããããŸãããã
ã¡ãªã¿ã«ããšã©ãŒãã³ããªã³ã°çšã®ã¢ã¯ã·ã§ã³ã§äŸå€ã¯æ±ã£ãŠããã¯ããªã®ã«ãã©ãããŠSpring StatemachineãšReactorã§ã¹ã¿ãã¯ãã¬ãŒã¹ã
çŸããããšãããšããšã©ãŒçšã®ã¢ã¯ã·ã§ã³ãåŒã³åºããåŸã§äŸå€ãåã¹ããŒããŠããããã®ããã§ãâŠã
ã§ã¯ã1床ã¹ããŒãã®å®çŸ©ããã¢ã¯ã·ã§ã³ã®çŽä»ããå€ããŸãã
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.INITIAL_STATE) .state(States.STATE1) .state(States.STATE2) .state(States.STATE3) .state(States.STATE4) .state(States.STATE5) .end(States.END_STATE); }
é·ç§»ã«ã¢ã¯ã·ã§ã³ãçŽä»ãã
次ã¯ãé·ç§»ã«å¯ŸããŠã¢ã¯ã·ã§ã³ãçŽä»ããŠã¿ãŸãã
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.INITIAL_STATE) .target(States.STATE1) .event(Events.EVENT1) .action(transitionAction()) .and() .withExternal() .source(States.STATE1) .target(States.STATE2) .event(Events.EVENT2) .action(transitionAction()) .and() .withExternal() .source(States.STATE2) .target(States.STATE3) .event(Events.EVENT3) .action(transitionAction()) .and() .withExternal() .source(States.STATE3) .target(States.STATE4) .event(Events.EVENT4) .action(transitionAction()) .and() .withExternal() .source(States.STATE4) .target(States.STATE5) .event(Events.EVENT5) .action(transitionAction()) .and() .withExternal() .source(States.STATE5) .target(States.END_STATE) .event(Events.EVENT6)//; .action(transitionAction()); }
ä»åã¯ããã¹ãŠã®é·ç§»ã«å¯ŸããŠåãã¢ã¯ã·ã§ã³ãçŽä»ããŠããŸãã
ã¢ã¯ã·ã§ã³ã®å®çŸ©ã¯ããã¡ãã
@Bean public Action<States, Events> transitionAction() { return stateContext -> System.out.printf( "transition action, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); }
å®è¡ããŠã¿ãŸãã
$ mvn spring-boot:run
çµæã¯ããšãŠãã·ã³ãã«ã§ãã
transition action, stage = TRANSITION, state = STATE1, event = EVENT1 transition action, stage = TRANSITION, state = STATE2, event = EVENT2 transition action, stage = TRANSITION, state = STATE3, event = EVENT3 transition action, stage = TRANSITION, state = STATE4, event = EVENT4 transition action, stage = TRANSITION, state = STATE5, event = EVENT5 transition action, stage = TRANSITION, state = END_STATE, event = EVENT6
ã§ã¯ãããã§é·ç§»ã®2ã€ã«äŸå€ãæ±ãã¢ã¯ã·ã§ã³ã远å ããŠã¿ãŸãããã
@Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.INITIAL_STATE) .target(States.STATE1) .event(Events.EVENT1) .action(transitionAction()) .and() .withExternal() .source(States.STATE1) .target(States.STATE2) .event(Events.EVENT2) .action(transitionActionThrowException(), transitionActionHandleError()) .and() .withExternal() .source(States.STATE2) .target(States.STATE3) .event(Events.EVENT3) .action(transitionAction()) .and() .withExternal() .source(States.STATE3) .target(States.STATE4) .event(Events.EVENT4) .action(transitionActionThrowException(), transitionActionHandleError()) .and() .withExternal() .source(States.STATE4) .target(States.STATE5) .event(Events.EVENT5) .action(transitionAction()) .and() .withExternal() .source(States.STATE5) .target(States.END_STATE) .event(Events.EVENT6)//; .action(transitionAction()); }
äŸå€ãæ±ãã¢ã¯ã·ã§ã³ã¯ããã¡ããå 容èªäœã¯ã¹ããŒãã®æãšåãã§ãã
@Bean public Action<States, Events> transitionActionThrowException() { return stateContext -> { System.out.printf( "transition action throw exception, stage = %s, state = %s, event = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]" ); throw new RuntimeException("transition action error!!"); }; } @Bean public Action<States, Events> transitionActionHandleError() { return stateContext -> System.out.printf( "transition action handling exception, stage = %s, state = %s, event = %s, exception message = %s%n", stateContext.getStage(), stateContext.getTarget().getId(), stateContext.getMessage() != null ? stateContext.getMessage().getPayload() : "[none]", stateContext.getException().getMessage() ); }
å®è¡ããŠã¿ãŸãã
$ mvn spring-boot:run
ãã¡ãã¯ãã¹ããŒãã®æãšã¯ç°ãªã£ãçµæã«ãªããŸããã
transition action, stage = TRANSITION, state = STATE1, event = EVENT1 transition action throw exception, stage = TRANSITION, state = STATE2, event = EVENT2 transition action handling exception, stage = TRANSITION, state = STATE2, event = EVENT2, exception message = transition action error!!
ã¢ã¯ã·ã§ã³ã§çºçããäŸå€ããã³ããªã³ã°ããæç¹ã§ãã¹ããŒããã·ã³ãçµäºããŠããŸããŸããã
ãã¡ãã®åŠçèªäœã¯ãã¹ãŠå®è¡ãããŠããã®ã§ãããEVENT2
ãéä¿¡ãããšããã§ãã以éã¯åãä»ããªããªã£ãŠããããã§ãã
@Override public void run(ApplicationArguments args) throws Exception { stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT1).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT2).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT3).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT4).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT5).build())) .blockFirst(); stateMachine .sendEvent(Mono.just(MessageBuilder.withPayload(Events.EVENT6).build())) .blockFirst(); }
é·ç§»ã«çŽä»ããã¢ã¯ã·ã§ã³ã®å Žåã¯ãäŸå€ããã³ããªã³ã°ããŠã忢ããŠããŸããã§ããâŠã
ã¡ãªã¿ã«ãã¢ã¯ã·ã§ã³ãäŸå€ããã³ããªã³ã°ããåŸãäŸå€ãåã¹ããŒããã®ã¯ããã£ã±ã以äžã®éšåã§ãã
ã¹ããŒããšé·ç§»ã®äž¡æ¹ã«ã¢ã¯ã·ã§ã³ãçŽä»ãã
æåŸã«ãã¹ããŒããšé·ç§»ã®äž¡æ¹ã«ã¢ã¯ã·ã§ã³ãçŽä»ããæã®åäœãèŠãŠã¿ãŸããããä»åã¯ãäŸå€ã¯äœ¿ããŸããã
@Override public void configure(StateMachineStateConfigurer<States, Events> states) throws Exception { states .withStates() .initial(States.INITIAL_STATE, stateAction()) .state(States.STATE1, stateAction()) .state(States.STATE2, stateAction(), stateAction()) .stateEntry(States.STATE3, stateEntryAction()) .stateExit(States.STATE3, stateExitAction()) .state(States.STATE4, stateAction(), stateAction()) .state(States.STATE5) .end(States.END_STATE); // endã«ã¯actionãªã } @Override public void configure(StateMachineTransitionConfigurer<States, Events> transitions) throws Exception { transitions .withExternal() .source(States.INITIAL_STATE) .target(States.STATE1) .event(Events.EVENT1) .action(transitionAction()) .and() .withExternal() .source(States.STATE1) .target(States.STATE2) .event(Events.EVENT2) .action(transitionAction()) .and() .withExternal() .source(States.STATE2) .target(States.STATE3) .event(Events.EVENT3) .action(transitionAction()) .and() .withExternal() .source(States.STATE3) .target(States.STATE4) .event(Events.EVENT4) .action(transitionAction()) .and() .withExternal() .source(States.STATE4) .target(States.STATE5) .event(Events.EVENT5) .action(transitionAction()) .and() .withExternal() .source(States.STATE5) .target(States.END_STATE) .event(Events.EVENT6)//; .action(transitionAction()); }
å®è¡ã
$ mvn spring-boot:run
çµæã¯ããããªæãã«ãªããŸããã
state action, stage = TRANSITION, state = INITIAL_STATE, event = [none] transition action, stage = TRANSITION, state = STATE1, event = EVENT1 state action, stage = STATE_ENTRY, state = STATE1, event = EVENT1 transition action, stage = TRANSITION, state = STATE2, event = EVENT2 state action, stage = STATE_ENTRY, state = STATE2, event = EVENT2 transition action, stage = TRANSITION, state = STATE3, event = EVENT3 state action, stage = STATE_EXIT, state = STATE3, event = EVENT3 state entry action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 transition action, stage = TRANSITION, state = STATE4, event = EVENT4 state exit action, stage = STATE_EXIT, state = STATE4, event = EVENT4 state action, stage = STATE_ENTRY, state = STATE4, event = EVENT4 transition action, stage = TRANSITION, state = STATE5, event = EVENT5 state action, stage = STATE_EXIT, state = STATE5, event = EVENT5 transition action, stage = TRANSITION, state = END_STATE, event = EVENT6
ãœãŒã¹ã³ãŒããšçŽä»ããŠã¿ãŸãããã
// .initial(States.INITIAL_STATE, stateAction()) state action, stage = TRANSITION, state = INITIAL_STATE, event = [none] // .source(States.INITIAL_STATE) // .target(States.STATE1) // .event(Events.EVENT1) // .action(transitionAction()) transition action, stage = TRANSITION, state = STATE1, event = EVENT1 // .state(States.STATE1, stateAction()) state action, stage = STATE_ENTRY, state = STATE1, event = EVENT1 // .source(States.STATE1) // .target(States.STATE2) // .event(Events.EVENT2) // .action(transitionAction()) transition action, stage = TRANSITION, state = STATE2, event = EVENT2 // .state(States.STATE2, stateAction(), stateAction()) state action, stage = STATE_ENTRY, state = STATE2, event = EVENT2 // .source(States.STATE2) // .target(States.STATE3) // .event(Events.EVENT3) // .action(transitionAction()) transition action, stage = TRANSITION, state = STATE3, event = EVENT3 // .state(States.STATE2, stateAction(), stateAction()) state action, stage = STATE_EXIT, state = STATE3, event = EVENT3 // .stateExit(States.STATE3, stateExitAction()) state entry action, stage = STATE_ENTRY, state = STATE3, event = EVENT3 // .source(States.STATE3) // .target(States.STATE4) // .event(Events.EVENT4) // .action(transitionAction()) transition action, stage = TRANSITION, state = STATE4, event = EVENT4 // .stateExit(States.STATE3, stateExitAction()) state exit action, stage = STATE_EXIT, state = STATE4, event = EVENT4 // .state(States.STATE4, stateAction(), stateAction()) state action, stage = STATE_ENTRY, state = STATE4, event = EVENT4 // .source(States.STATE4) // .target(States.STATE5) // .event(Events.EVENT5) // .action(transitionAction()) transition action, stage = TRANSITION, state = STATE5, event = EVENT5 // .state(States.STATE4, stateAction(), stateAction()) state action, stage = STATE_EXIT, state = STATE5, event = EVENT5 // .source(States.STATE5) // .target(States.END_STATE) // .event(Events.EVENT6)//; // .action(transitionAction()); transition action, stage = TRANSITION, state = END_STATE, event = EVENT6
ããèŠããšãã¹ããŒãéå§ã»çµäºã®éã«é·ç§»ãæãŸã£ãŠããæãã«èŠããŸãããã©ããªãã§ããããïŒ
ãŸãããªããšãªãã¢ã¯ã·ã§ã³ãã¹ããŒãããã³é·ç§»ã«çŽä»ãããšãã©ãããåäœã«ãªããã¯ããã£ãæ°ãããŸãã
ãŸãšã
Spring Statemachineã®ã¢ã¯ã·ã§ã³ã詊ããŠã¿ãŸããã
æåã¯é°å²æ°ã§åããŠã¿ãã®ã§ãããã¡ãããšããã¥ã¡ã³ããèŠãŠãããšã¢ã¯ã·ã§ã³ã®çŽä»ãå
ã¯ã¹ããŒããšé·ç§»ã®2çš®é¡ããããšããããã
ããããèãæ¹ã埮åŠã«éã£ãã®ã§ãããã¥ã¡ã³ããèªãã§ããã®ã¯å€§äºã ãªãšããæ°ã«ã¯ãªããŸããã
ãšãããããé°å²æ°ã¯ããã£ãã®ã§ä»åã¯ãããªãšããã§ã