How to get exception thrown during state transition using spring state machine
Asked Answered
G

1

7

I am trying to understand, how an exception thrown by an action during a state transition is possible. I‘ve this simple state machine configured:

transitions
    .withExternal()
    .source(State.A1)
    .target(State.A2)
    .event(Event.E1)
    .action(executeAnActionThrowingAnException())

In my service class, I injected my state machine and send this event E1:

@Service
public class MyService() {

    @Autowired
    private StateMachine<State, Event> stateMachine;

    public void executeMyLogic() {
        stateMachine.start()
        stateMachine.sendEvent(Event.E1);
        // how to get thrown exception here
    }
}

In my service I just want to know, if and why my state machine wasn‘t able to reached State.A2. Because a thrown exception is fetched by Spring state machine, I am not able to get any response after sending the event. But the state machine hasn‘t any error, which means that

stateMachine.hasStateMachineError()

will return false. So, how can I get the information in my service, that something went wrong and more importantly what?

I appriciate your help.

Best regards

Going answered 7/3, 2019 at 15:29 Comment(0)
S
11

For transitions exceptions, there's an overload for the actions method available in the TransitionConfigurer

action(Action<S,E> action, Action<S,E> error)

This means you can specify and additional action to be triggered, if there's an exception raised during the transition. The exception is available from the StateContext passed to the action.

When your error action is triggered, you can retrieve the exception with:

context.getException();

Inside the error action you can do a couple of things to deal with the exception:

  • do logging of the exception and the context
  • transition to some error state
  • clear the context and transition to the same state and try to execute some retry logic
  • add some additional info to the context and return the context to the caller

For example:

context.getVariables().put("hasError", true); 
context.getVariables().put("error", ex);

And in your service(caller) you handle the exception as you like, for example:

public void executeMyLogic() {
    stateMachine.start()
    stateMachine.sendEvent(Event.E1);
    if (stateMachine.getExtendedState().getVariables().containsKey("hasError") {
      throw (RuntimeException)stateMachine.getExtendedState().getVariables().get("error")
    }
}
Shadwell answered 8/3, 2019 at 6:53 Comment(4)
I first thought there must be another way. But, obviously there is not. So, I use your suggested way and it's working. Thanks.Going
I am sending state events in a lot of places on my project so I don't want to put that control every place. is there any appropriate way to do it?Windowlight
Not that I am aware of. You can think about utilizing a transition to an error state and managing everything there, but depends on the usecase really.Shadwell
@SametBaskıcı you can have a common method that sends the event and checks for the extended state for error and throw an exception if needed. In that way, the check can be minimized to a single method.Sum

© 2022 - 2024 — McMap. All rights reserved.