Skip to content

Guard only invoked once, when throwing an exception #205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
fritzrichter opened this issue Apr 22, 2016 · 6 comments
Open

Guard only invoked once, when throwing an exception #205

fritzrichter opened this issue Apr 22, 2016 · 6 comments
Labels
type/question Is a plain question

Comments

@fritzrichter
Copy link

Hey!

In order to decouple the logic into small pieces, I added the SSM recently to my project. Different states (of an object) require different properties to be set (special validation).

Example:
In the state "IN_PREPARATION", some detailed properties can still be empty.
In the state "PUBLIC_AVAILABLE", all properties have to be filled in (@notempty).

In my code, I added a Guard into the transition, which checks the additional validation. As I want to inform the user, invoking the API, I would like to throw an exception. The standard way is to just return "false", which does not provide the feedback WHY the guard actually rejected to transition.

For the first invocation it is working fine, but after I rerun the exact same call, the Guard is actually never invoked anymore.

Where is the best idea, to do the additional Validation, when not in the guard?

@jvalkeal
Copy link
Contributor

Looks like if Guard throws, its exception is not handled and ssm gets into a broken internal state. I need to check if that's a case and if so I'll fix it.

Getting into your actual question, Guard is still the driving force in a generic state machine when decisions between transitions are made, you just should not throw anything as uml spec we rely on is very clear that Guard either evaluates TRUE or FALSE.

One concept what I'm using to notify caller, the one who sends an event to machine, about the error/exception is to do a generic error state where machine goes in these situations. Within a Guard you have access to StateContext which gives you access to machine and its Extended State. Then you can store that exception there and have a listener which detects when machine enters that error state and can get the exception from Extended State. One good option to make this decision is to use choice pseudostate which is using if/elseif/else structure of transitions and choice between those is made by Guards.

You need to remember that machine execution is asynchronous and there's never any guarantees that particular event actually does anything if machine is in a state where event doesn't cause any behavioural changes. Transitions can also happen based on timed events or deferred events and those may cause something to happen in a future. With state machines you really need to start thinking things a bit differently which is sometimes hard, I know, been there, done that. :)

I'm using this error concept in spring-cloud-yarn-deployer component where actual SPI is currently synchronous. What I'm doing in a YarnTaskLauncher.java#L122 is that I'll attach a listener to a machine before I send an event and then track if machine goes into READY or ERROR state and get the exception from Extended State to be set for SettableListenableFuture. Then user of that Future either gets its value or its exception.

@jvalkeal jvalkeal added the type/question Is a plain question label Apr 22, 2016
@jvalkeal
Copy link
Contributor

Oh yeah, with 1.0.x serie things are a bit limited and above example is using 1.1.x which is in milestone. We're getting into first RC next week and RELEASE latest in 3-4 weeks.

@fritzrichter
Copy link
Author

Hey Janne, thanks for your quick reply. Ok I think I got the point of using the state-machine in a pure asynchronous way, although I don't see the reason it has to be like that. Maybe I am still wrong, but for what I want to use the SSM is the following situation:

There is an API in my microservice, which enables the management of my so called "Deal" objects. A deal is a POJO stored in Mongo and can be in different states [IN_PREPARATION, ACTIVE, DEACTIVE]. So basically it's not my application being in a certain state, it's more my objects can be in different states. When the user invokes the API, he wants to get detailed error exceptions, which help him to understand the reason why a deal can not become active for example. I was using the approach mentioned here: http://blog.mimacom.com/introducing-spring-state-machine/, but still do not really understand if that is the right approach, as for every request to the statemachine, the machine is basically reseted before. Maybe you have an idea here. Thanks, Fritz

@fritzrichter
Copy link
Author

I understood your described approach… Anyway, when the Guard is returning "false", the listener will never be invoked with the method stateContext(StateContext<States, Events> stateContext), which in the end breaks the concept of the guard (returning false, if the can not be reached) or not?

@jvalkeal
Copy link
Contributor

Well you're right, listener will not get notified as there is nothing to notify. Lets say you have states S1, S2, transition between those with event E1 and transition has a guard evaluating false. Machine will accept that event but transition is denied so nothing really happens. It's fundamentally how statemachine works.

While we could try to investigate adding more listener functionality for things like this, fear is that it is adding too much complexity into a machine itself.

Have you experimented using error state concepts to catch abnormal behaviour from an usual behaviour in a state flow as that is kinda how normal flows are designed.

@jvalkeal
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type/question Is a plain question
Projects
None yet
Development

No branches or pull requests

2 participants