spring-statemachine icon indicating copy to clipboard operation
spring-statemachine copied to clipboard

Deferred events are not handled in case the transition to the desired state was done by timerOnce

Open maayanhope opened this issue 2 years ago • 6 comments

seems that if there are deferred events that are pending for a state and the state is reached by a timer (timeroonce) that these deferred events are not handled

statemachine.zip

Is this a bug?

maayanhope avatar Aug 15 '23 15:08 maayanhope

Why attached a zip file? Better to add links to relevant code file, or not?

rishiraj88 avatar Aug 15 '23 15:08 rishiraj88

I wanted to send some example code, how do you normally do it?

maayanhope avatar Aug 15 '23 20:08 maayanhope

package test;

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; import org.springframework.messaging.support.MessageBuilder; import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.statemachine.listener.StateMachineListenerAdapter; import org.springframework.statemachine.state.State; import org.springframework.statemachine.transition.Transition; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import reactor.core.publisher.Mono;

import java.util.concurrent.BrokenBarrierException;

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SimpleStateMachineConfiguration3.class) public class StateMachineTester3 {

@Autowired
private StateMachineFactory<String, String> stateMachineFactory;

@Test
public void testState1() throws InterruptedException, BrokenBarrierException {


	StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine("dddd" + i);

	stateMachine.addStateListener(new StateMachineListenerAdapter<>() {
		private StateContext<String, String> stateContext;

		@Override
		public void stateContext(StateContext<String, String> stateContext) {
			this.stateContext = stateContext;
		}

		@Override
		public void eventNotAccepted(Message<String> event) {
			SimpleStateMachineConfiguration2.print("----------- EventNotAccepted: Payload: " + event.getPayload() + " state:" + stateContext.getStateMachine().getState().getId());
		}

	});
	stateMachine.start();


	stateMachine.sendEvent("E1");  // Start S1

	// 1 MS --> S1->S2
	Thread.sleep(2);
	stateMachine.sendEvent(Mono.just(MessageBuilder.withPayload("E3").build())).subscribe(ret -> {
		System.out.println(ret.getResultType()); // this event is deffered
	});

	Thread.sleep(100);
	stateMachine.sendEvent(Mono.just(MessageBuilder.withPayload("E4").build())).subscribe(ret -> {
		System.out.println(ret.getResultType()); // this event will 
	});


	Thread.sleep(3000);
	SimpleStateMachineConfiguration3.print("--- cur state = " + stateMachine.getState().getId() + " ---");
	SimpleStateMachineConfiguration3.print("--- " + SimpleStateMachineConfiguration3.sum.get() + " ---");
	Thread.sleep(1000);

}

}

maayanhope avatar Aug 15 '23 20:08 maayanhope

package test;

import org.springframework.context.annotation.Configuration; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.EnableStateMachineFactory; import org.springframework.statemachine.config.StateMachineConfigurerAdapter; import org.springframework.statemachine.config.builders.StateMachineStateConfigurer; import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

import java.util.Arrays; import java.util.HashSet; import java.util.concurrent.atomic.AtomicInteger;

@Configuration @EnableStateMachineFactory public class SimpleStateMachineConfiguration3 extends StateMachineConfigurerAdapter<String, String> {

public static AtomicInteger sum = new AtomicInteger();

public static void print(String msg) {
    System.out.println("[" + System.currentTimeMillis() + "][" + Thread.currentThread().getName() + "] " + msg);
}


public Action<String, String> printS2() {
    return stateContext -> {
        print("-----------s2?. " + stateContext.getStateMachine().getState().getId());
    };
}

public Action<String, String> print(int i) {
    return stateContext -> {
        sum.incrementAndGet();
        print("----------- cur state. " + i + " " + stateContext.getStateMachine().getState().getId());
    };
}

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

    states.withStates()
            .initial("START")
            .end("END")
            .states(new HashSet<>(Arrays.asList("S1", "S2", "S3")))
            .state("S1", "E3")

    ;


}


@Override
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {


    transitions
            .withExternal()
            .source("START").target("S1").event("E1").and()
            .withExternal()
            .source("S1").target("S2").timerOnce(10)
            .action(printS2())
            .and()
            .withExternal()
            .source("S2").target("S3").event("E3").action(print(3))
            .and()
            .withExternal()
            .source("S2").target("S2").event("E4").action(print(4)) // this is just to trigger the deferred event

    ;

}

}

maayanhope avatar Aug 15 '23 20:08 maayanhope

The best thing is to create a project on GitHub and share it.

gpando avatar Aug 15 '23 20:08 gpando

https://github.com/maayanhope/statemachinedeferredtest/tree/main/src

maayanhope avatar Aug 16 '23 06:08 maayanhope