javaspring-statemachine

Unable to Trigger an Event from Action


I am setting up spring state-machine transactions, i want to launch Event2 on completion of Event1

These are the states i have

States -> "INITIAL", "SECOND", "THIRD"

I have configured the transactions to send SECOND_EVENT from action of FIRST_EVENT as shown below

transitions.withExternal()
    .source("INITIAL")
    .target("SECOND")
    .event("FIRST_EVENT")
    .action(new Action<String, String>() {
        @Override
        public void execute(StateContext<String, String> context) {
            System.out.println("FIRST_ACTION_EXECUTED");
            context.getStateMachine().sendEvent("SECOND_EVENT");
        }
    })
    .and()
    .withExternal()
    .source("SECOND")
    .target("THIRD")
    .event("SECOND_EVENT")
    .action(new Action<String, String>() {
        @Override
        public void execute(StateContext<String, String> context) {
            System.out.println("TEST SUCCESS");
        }
    });

Here i get evenNotAccepted exception, and i know the reason is that the statemachine.getState() is INITIAL, and it cant change the state from INITIAL to THIRD directly.

So my question is, Is there anyway that i can configure my statemachine to automatically trigger second event on completion of first event


Solution

  • It's because of the type of actions you're using. There are two main types of actions in the Spring SM - transition actions and state actions - and they resolve on different times. You can see a sequence of resolving of those actions here.

    In order to fire events to go to your next state, you should use state actions.

    You can configure state actions like so:

       builder.configureStates()
                .withStates()
                .initial(SI)
                .state(F1, context -> context.getStateMachine().sendEvent(E2))
                .state(F2, context -> context.getStateMachine().sendEvent(E3))
                .end(SF);
    
        builder.configureTransitions()
                .withExternal().source(SI).target(F1).event(E1)
                .and()
                .withExternal().source(F1).target(F2).event(E2)
                .and()
                .withExternal().source(F2).target(SF).event(E3)
                    .action(context -> System.out.println("Completing SM!"));
    

    Just firing event E1 manually, will result in the following output (events E2 and E3 are auto-fired through state actions):

    STATE ENTERED: SI
    STAGE TRANSITIONS From: null To: SI
    STATE EXITED: SI
    STATE ENTERED: F1
    STAGE TRANSITIONS From: SI To: F1
    21:45:39.153 [pool-2-thread-1] DEBUG org.springframework.statemachine.support.AbstractStateMachine - Queue event GenericMessage [payload=E2, headers={id=393a5636-a760-57d6-a98b-d31eb75f048e, timestamp=1560969939152}] SF F2 F1 SI  / F1 / uuid=4e8773c1-e755-40de-b431-e53a4b0ca64d / id=null
    STATE EXITED: F1
    STATE ENTERED: F2
    STAGE TRANSITIONS From: F1 To: F2
    21:45:39.161 [pool-2-thread-1] DEBUG org.springframework.statemachine.support.AbstractStateMachine - Queue event GenericMessage [payload=E3, headers={id=f5899e8b-e4a5-e079-3a30-66114a7accac, timestamp=1560969939161}] SF F2 F1 SI  / F2 / uuid=4e8773c1-e755-40de-b431-e53a4b0ca64d / id=null
    Completing SM!
    STATE EXITED: F2
    STATE ENTERED: SF
    STAGE TRANSITIONS From: F2 To: SF