spring-projects / spring-statemachine

Spring Statemachine is a framework for application developers to use state machine concepts with Spring.
1.54k stars 607 forks source link

FSM_Having an unexpected State Name Problem !!! Please Help !!! #1066

Open cgarip opened 2 years ago

cgarip commented 2 years ago

Discussed in https://github.com/spring-projects/spring-statemachine/discussions/1061

Originally posted by **cgarip** August 18, 2022 I am having a problem with getting an unexcepted current state name from the method "stateContext" of Listener life cycle. === Following is the example detail. let say 1. the configuration of FSM flow I created is something like this. A -> B -> C -> auditD -> auditE -> End 2. Also have defined a own Listener from where to update the state changes' log. the method from Listener class I choose to update logs is called "stateContext". because only from that method I can get more informaition such as headers after event is sent 3. I have three modules to get it ran all together. let say module X, Y, Z X module is all about FSM code Y module is the place(war) to call X module and to run only A -> B -> C ( here is like to get user's web form inputs ) Z module is the place(war) to call X module and to run auditD-> auditE-> End ( here is to starting auditing ) === problem description Input : 1. The current state after running FSM on Y module is "C" 2. after step1 above and on Z module continuing running the FSM and sending the events until the current state is auditE Expected final state ouput after sending event on Z module should be "End". BUT it is "C" A -> B -> C -> auditD -> auditE -> End ( this should be the result I expected ) A -> B -> C -> auditD -> auditE -> C ( this is actually result that happened and I unexpected ) 2. The value I unexpected is on the method stateContext(StateContext stateContext) from listener stateContext.getStateMachine().getState(), it returns the state "C". But what I expected should be "End" 3. But there is another method from listener called "stateChanged", the current state is actually already changed to "End" as shown below @Override public void stateChanged(State from, State to) { logger.debug("###genListener### stateChanged from {} to {}", from.getId(), to.getId()); } to.getId() <--- this is actually shown the state "End" which is I expected and normal 4. I won't have such problem if I only have two modules like following module_M (all FSM code) module_N ( a web war ) 5. I won't have such problem if I have three modules design but using the way of UML configuration == what my FSM configuration code is following @Override public void configure(StateMachineConfigurationConfigurer config) throws Exception { config.withConfiguration().autoStartup(false); config.withPersistence().runtimePersister(stateMachineRuntimePersister()); config.withMonitoring().monitor(stateMachineMonitor()); } @Override public void configure(StateMachineStateConfigurer states) throws Exception { states .withStates() .initial(AcctOpeningStockStates.SI) .end(AcctOpeningStockStates.End) .states(EnumSet.allOf(AcctOpeningStockStates.class)); } @Override public void configure(StateMachineTransitionConfigurer transitions) throws Exception { transitions .withExternal() .name("InitialDone") .source(AcctOpeningStockStates.SI) .target(AcctOpeningStockStates.ChoiceAcctType) .event(AcctOpeningStockEvents.InitialDone) .and() .withExternal() .name("ChoiceAcctTypeDone") .source(AcctOpeningStockStates.ChoiceAcctType) .target(AcctOpeningStockStates.KeyinMobile) .event(AcctOpeningStockEvents.ChoiceAcctTypeDone) .guard(choiceAcctTypeDoneGuard()) .action(choiceAcctTypeDoneAction()) .and() .withExternal() .name("KeyinMobileDone") .source(AcctOpeningStockStates.KeyinMobile) .target(AcctOpeningStockStates.VerifyOtp) .event(AcctOpeningStockEvents.KeyinMobileDone) .guard(keyinMobileDoneGuard()) .action(keyinMobileDoneAction()) .and() .withExternal() .name("VerifyOtpDone") .source(AcctOpeningStockStates.VerifyOtp) .target(AcctOpeningStockStates.ChoiceVerifyType) .event(AcctOpeningStockEvents.VerifyOtpDone) .action(verifyOtpDoneAction(), verifyOtpDoneErrAction()) .and() .withExternal() .name("ChoiceVerifyTypeDone") .source(AcctOpeningStockStates.ChoiceVerifyType) .target(AcctOpeningStockStates.VerifyCreditCard) .event(AcctOpeningStockEvents.ChoiceVerifyTypeDone) .action(choiceVerifyTypeDoneAction(), choiceVerifyTypeDoneErrAction()) .and() .withExternal() .name("ChoiceVerifyType2Done") .source(AcctOpeningStockStates.ChoiceVerifyType2) .target(AcctOpeningStockStates.VerifyOtherBankAccount) .event(AcctOpeningStockEvents.ChoiceVerifyType2Done) .action(choiceVerifyTypeDoneAction(), choiceVerifyTypeDoneErrAction()) .and() .withExternal() .name("VerifyCreditCardDone") .source(AcctOpeningStockStates.VerifyCreditCard) .target(AcctOpeningStockStates.UploadImg) .event(AcctOpeningStockEvents.VerifyCreditCardDone) .action(verifyOtpDoneAction(), verifyOtpDoneErrAction()) .and() .withExternal() .name("VerifyOtherBankAccountDone") .source(AcctOpeningStockStates.VerifyOtherBankAccount) .target(AcctOpeningStockStates.UploadImg) .event(AcctOpeningStockEvents.VerifyOtherBankAccountDone) .action(verifyOtpDoneAction(), verifyOtpDoneErrAction()) .and() .withExternal() .name("UploadImgDone") .source(AcctOpeningStockStates.UploadImg) .target(AcctOpeningStockStates.FillinFullInfo) .event(AcctOpeningStockEvents.UploadImgDone) .action(uploadImgDoneAction(), uploadImgDoneErrAction()) .and() .withExternal() .name("FillinFullInfoDone") .source(AcctOpeningStockStates.FillinFullInfo) .target(AcctOpeningStockStates.SignAgreement) .event(AcctOpeningStockEvents.FillinFullInfoDone) .action(fillinFullInfoDoneAction(), fillinFullInfoDoneErrAction()) .and() .withExternal() .name("SignAgreementDone") .source(AcctOpeningStockStates.SignAgreement) .target(AcctOpeningStockStates.StaffAudit) .event(AcctOpeningStockEvents.SignAgreementDone) .action(verifyOtpDoneAction(), verifyOtpDoneErrAction()) .and() .withExternal() .name("StaffAuditDone") .source(AcctOpeningStockStates.StaffAudit) .target(AcctOpeningStockStates.ManagerAudit) .event(AcctOpeningStockEvents.StaffAuditDone) .guard(staffAuditDoneGuard()) .action(staffAuditDoneAction(), staffAuditDoneErrAction()) .and() .withExternal() .name("ManagerAuditDone") .source(AcctOpeningStockStates.ManagerAudit) .target(AcctOpeningStockStates.CompliantAudit) .event(AcctOpeningStockEvents.ManagerAuditDone) .guard(managerAuditDoneGuard()) .action(managerAuditDoneAction(), managerAuditDoneErrAction()) .and() .withExternal() .name("CompliantAuditDone") .source(AcctOpeningStockStates.CompliantAudit) .target(AcctOpeningStockStates.End) .event(AcctOpeningStockEvents.CompliantAuditDone) .action(compliantAuditDoneAction(), compliantAuditDoneErrAction()); }