diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateMachineExecutor.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateMachineExecutor.java index 7b6f12bd..5a6616d1 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateMachineExecutor.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/DefaultStateMachineExecutor.java @@ -378,6 +378,7 @@ public class DefaultStateMachineExecutor extends LifecycleObjectSupport im } } } + return true; } return false; } diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/DefaultStateMachineExecutorTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/DefaultStateMachineExecutorTests.java index 1d258fd8..df05704a 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/DefaultStateMachineExecutorTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/support/DefaultStateMachineExecutorTests.java @@ -33,6 +33,7 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; import org.springframework.core.task.SyncTaskExecutor; import org.springframework.messaging.Message; +import org.springframework.messaging.support.GenericMessage; import org.springframework.messaging.support.MessageBuilder; import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; import org.springframework.statemachine.StateContext; @@ -254,6 +255,66 @@ public class DefaultStateMachineExecutorTests { assertThat(transit.transitions.size(), is(2)); } + @SuppressWarnings("unchecked") + @Test + public void testEventPolling() throws Exception { + // event polling should continue even if an event is no more relevant. + SyncTaskExecutor taskExecutor = new SyncTaskExecutor(); + final CountDownLatch latch = new CountDownLatch(1); + EventTrigger triggerE1 = new EventTrigger("E1"); + + State stateS1 = mock(State.class); + when(stateS1.getId()).thenReturn("S1"); + when(stateS1.getIds()).thenReturn(Arrays.asList("S1")); + State stateS2 = mock(State.class); + when(stateS2.getId()).thenReturn("S2"); + when(stateS2.getIds()).thenReturn(Arrays.asList("S2")); + + StateMachine stateMachine = mock(StateMachine.class); + when(stateMachine.getState()).thenReturn(stateS1); + + Transition transitionS1S2 = mock(Transition.class); + when(transitionS1S2.getSource()).thenReturn(stateS1); + when(transitionS1S2.getTarget()).thenReturn(stateS2); + when(transitionS1S2.getTrigger()).thenReturn(triggerE1); + when(transitionS1S2.transit(any())).thenAnswer(x -> { + when(stateMachine.getState()).thenReturn(stateS2); + return true; + }); + + Collection> transitions = new ArrayList<>(); + transitions.add(transitionS1S2); + + Map, Transition> triggerToTransitionMap = new HashMap<>(); + triggerToTransitionMap.put(triggerE1, transitionS1S2); + + List> triggerlessTransitions = new ArrayList<>(); + + Transition initialTransition = mock(Transition.class); + Message initialEvent = null; + + DefaultStateMachineExecutor executor = new DefaultStateMachineExecutor<>( + stateMachine, + stateMachine, + transitions, + triggerToTransitionMap, + triggerlessTransitions, + initialTransition, + initialEvent, + null); + + executor.setTaskExecutor(taskExecutor); + + executor.setStateMachineExecutorTransit((x, y, z) -> latch.countDown()); + executor.start(); + //E2 should not stuck the event polling as it is not relevant. + executor.queueEvent(new GenericMessage<>("E2")); + executor.queueEvent(new GenericMessage<>("E1")); + executor.execute(); + latch.await(1, TimeUnit.SECONDS); + assertThat(stateMachine.getState().getId(), is(stateS2.getId())); + } + private static class TestStateMachineExecutorTransit implements StateMachineExecutorTransit { ArrayList> transitions = new ArrayList<>();