Skip to content
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

Not able to catch and rethrow spring state machine error #553

Open
ArpanMajumdar opened this issue May 7, 2018 · 9 comments
Open

Not able to catch and rethrow spring state machine error #553

ArpanMajumdar opened this issue May 7, 2018 · 9 comments
Labels
type/question Is a plain question

Comments

@ArpanMajumdar
Copy link

ArpanMajumdar commented May 7, 2018

I am working on a project that deals with a contract workflow. I want the state machine to throw an error if a wrong event is sent. I tried using listener to catch the exception thrown by state machine as given in the documentation but I am not able to rethrow error to show an exception message.

Here is my listener

public class ErrorStateMachineListener
    extends StateMachineListenerAdapter<AgreementSubStatus, AgreementEvents> {

  @Override
  public void stateMachineError(
      StateMachine<AgreementSubStatus, AgreementEvents> stateMachine, Exception exception) {
    log.error("Access is denied. Illegal state transition");
    throw new AccessDeniedException("Access is denied");
  }

@Override
  public void eventNotAccepted(Message<AgreementEvents> event) {
    log.info("Inside eventNotAccepted");
    throw new AccessDeniedException("Access is denied");
  }
}

Here is the state machine configuration where I registered the listener.

    // Configure state machine
    builder
        .configureConfiguration()
        .withConfiguration()
        .autoStartup(true)
        .listener(listener())
        .and()
        .withSecurity()
        .enabled(true);

Here is the stack trace obtained after throwing custom exception from the listener.

com.tgt.tvi.libs.basesecurity.exception.AccessDeniedException: Access is denied
	at com.tgt.tvi.apis.agreement.statemachine.ErrorStateMachineListener.eventNotAccepted(ErrorStateMachineListener.java:59)
	at org.springframework.statemachine.listener.CompositeStateMachineListener.eventNotAccepted(CompositeStateMachineListener.java:82)
	at org.springframework.statemachine.support.StateMachineObjectSupport.notifyEventNotAccepted(StateMachineObjectSupport.java:193)
	at org.springframework.statemachine.support.AbstractStateMachine.notifyEventNotAccepted(AbstractStateMachine.java:224)
	at org.springframework.statemachine.support.AbstractStateMachine.sendEventInternal(AbstractStateMachine.java:606)
	at org.springframework.statemachine.support.AbstractStateMachine.sendEvent(AbstractStateMachine.java:218)
	at org.springframework.statemachine.support.AbstractStateMachine.sendEvent(AbstractStateMachine.java:230)

I've also tried putting a try-catch block around sendEvent method but it doesn't come inside the catch block.

try{
        stateMachine.sendEvent(action);
      }catch (Exception e){
        log.error("State machine exception");
   }

Please tell what approach should I take to catch state machine errors to rethrow them and perform some logic.

@jvalkeal
Copy link
Contributor

None of these would not work as any user level interaction cannot break machine. Can't you just check from StateMachine.sendEvent() if it returns false and then throw exception of your own. But do remember that depending on your machine structure, even if event is accepted, in theoretical level it doesn't necessarily mean that machine is actually going to do any state changes.

@jvalkeal jvalkeal added the type/question Is a plain question label May 10, 2018
@CodeYogiCo
Copy link

@jvalkeal one unusal behaviour on similar lines. I have spring security configured. And my transition has security enabled. If a wrong role tries to do a transition , it logs AccessDeniedException.However it does not throw that exception

 transitions
        // When initially agreement lifecycle is started
        .withExternal()
        .source(AgreementSubStatus.S1)
        .target(AgreementSubStatus.S2)
        .event(AgreementEvents.CREATED)
        .action(this.S1toS2(),errorAction())
        .secured(IS_ADMIN_OR_BUYER_OR_FINOPS_OR_SUPPORT)

This is the exception being thrown

org.springframework.security.access.AccessDeniedException: Access is denied
	at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:84)
	at org.springframework.statemachine.security.StateMachineSecurityInterceptor.decide(StateMachineSecurityInterceptor.java:190)
	at org.springframework.statemachine.security.StateMachineSecurityInterceptor.decide(StateMachineSecurityInterceptor.java:157)
	at org.springframework.statemachine.security.StateMachineSecurityInterceptor.preTransition(StateMachineSecurityInterceptor.java:109)
	at org.springframework.statemachine.support.StateMachineInterceptorList.preTransition(StateMachineInterceptorList.java:128)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.handleTriggerTrans(DefaultStateMachineExecutor.java:266)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.handleTriggerTrans(DefaultStateMachineExecutor.java:211)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.processTriggerQueue(DefaultStateMachineExecutor.java:449)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.access$200(DefaultStateMachineExecutor.java:65)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor$1.run(DefaultStateMachineExecutor.java:323)
	at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.scheduleEventQueueProcessing(DefaultStateMachineExecutor.java:352)
	at org.springframework.statemachine.support.DefaultStateMachineExecutor.execute(DefaultStateMachineExecutor.java:163)
	at org.springframework.statemachine.support.AbstractStateMachine.sendEventInternal(AbstractStateMachine.java:604)
	at org.springframework.statemachine.support.AbstractStateMachine.sendEvent(AbstractStateMachine.java:218)

However i went ahead and looked into the code .Its just logging it but not being thrown

try {
                                stateContext = this.interceptors.preTransition(stateContext);
                            } catch (Exception var16) {
                                log.info("Interceptors threw exception", var16);
                                stateContext = null;
                            }

Can you help me how can i handle such scenraiors

@jvalkeal
Copy link
Contributor

Well security integration is designed to work internally effectively denying transitions. In this case it breaks from a loop if you follow logic around those lines.

@ArpanMajumdar
Copy link
Author

@jvalkeal How do I catch this error if it isn't being rethrown? Should I just check if stateContext is null?

@Bonbons
Copy link

Bonbons commented Nov 14, 2018

Well security integration is designed to work internally effectively denying transitions. In this case it breaks from a loop if you follow logic around those lines.

But, we need to know why transition is denied. The reason is only accessible in the log. We got to indicate to the final user why the machine is still une the same state.

@Bonbons
Copy link

Bonbons commented Nov 17, 2018

@jvalkeal
Ok, but it would be nice to be able to check all error after the loop on transitions.
In this case, it would be interesting to put the AccessDeniedException in a stack. Then, to provide the stack atfer the loop.

@tovisofer
Copy link

tovisofer commented Mar 10, 2019

Hi @jvalkeal,
I saw similar question\bug report mentioned three times.
Is anyone handling it?

#548
#340

@Achilles718611
Copy link

Achilles718611 commented Oct 28, 2019

We can set statemachine error using machine.setStateMachineError(new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "XXX"));
How we can get this error after send event?

i.e machine.sendEvent(event);
When process event, we set stateMachineError, but I don't know how I can retrieve that error later.

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

7 participants