pranay jain
pranay jain

Reputation: 372

Spring State Machine - Change state based on conditions

I am trying to create a simple state machine as shown below enter image description here

For this I have below config

 @Override
 public void configure(final StateMachineStateConfigurer<States, Events> states) throws Exception {
     states
    .withStates()
    .states(EnumSet.allOf(States.class))
    .initial(States.NEW)
    .end(States.ERROR)
    .end(States.DELIVER);
}

and below transitions

@Override
public void configure(final StateMachineTransitionConfigurer<States, Events> transitions)
  throws Exception {
     transitions
    .withExternal()
    .source(States.NEW).target(States.PACKAGED).event(Events.pack)
    .and()
    .withExternal()
    .source(States.PACKAGED).target(States.PROCESS).event(Events.process)
    .and()
    .withExternal()
    .source(States.PACKAGED).target(States.ERROR).event(Events.error)
    .and()
    .withExternal()
    .source(States.PROCESS).target(States.DELIVER).event(Events.deliver)
    .and()
    .withExternal()
    .source(States.PROCESS).target(States.ERROR).event(Events.error);
}

I am trying to right a condition such that when packaging or processing the order, if any error is encountered the state of the order should be error state. I noticed that there are transition action that can be added while configuring the transition as below

public Action<States, Events> packageAction() {
  // Packaging logic
  if(packed){
    return context -> context.getStateMachine().sendEvent(Events.process);  
  }else{
    return context -> context.getStateMachine().sendEvent(Events.error);
  }
}

But after running the application it doesn't work. Is this the right way to publish events conditionally?

Upvotes: 0

Views: 1946

Answers (1)

Daniel Vilas-Boas
Daniel Vilas-Boas

Reputation: 896

Its hard to say without seeing the stacktrace, but one thing I noticed in your configuration is that you cannot have two end states since it breaks a Finite State Machine.

Regarding you question about the transition logic, the right way of doing it is with a choice() pseudo state and guards. Guards are interfaces that should contain logic to allow a choice transition to define which state it should enter. In your case, it would be something like this:

@Override
    public void configure(final StateMachineStateConfigurer<> states)
            throws
            Exception {

.withStates()
    .states(EnumSet.allOf(States.class))
    .initial(States.NEW)
    .choice(States.PROCESS)
    .end(States.DELIVER);
}


@Override
    public void configure(final StateMachineTransitionConfigurer<> transitions)
            throws
            Exception {
//Configs
.and()
                .withChoice()
                .source(States.PROCESS)
                //you can replace the guard with a lambda expression
                //for multiple options, use .then
                .first(States.ERROR, hasErrorGuard)
                .last(States.DELIVER, successAction, errorAction)

Upvotes: 2

Related Questions