From d1f7f31aebdf231325da6f7a57d4dd28937c863c Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Sun, 6 Nov 2016 14:39:19 +0000 Subject: [PATCH] Fix problems in JoinPseudoState - Fix possible NPE which is a regression. - Fix state entry notification for JOIN states. - Fixes #229 --- .../buildtests/LinkedRegionsTests.java | 98 ++ .../statemachine/buildtests/linked-regions.di | 2 + .../buildtests/linked-regions.notation | 982 ++++++++++++++++++ .../buildtests/linked-regions.uml | 98 ++ .../statemachine/state/JoinPseudoState.java | 3 +- .../support/AbstractStateMachine.java | 6 +- 6 files changed, 1187 insertions(+), 2 deletions(-) create mode 100644 spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/LinkedRegionsTests.java create mode 100644 spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.di create mode 100644 spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.notation create mode 100644 spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.uml diff --git a/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/LinkedRegionsTests.java b/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/LinkedRegionsTests.java new file mode 100644 index 00000000..bb4f734f --- /dev/null +++ b/spring-statemachine-build-tests/src/test/java/org/springframework/statemachine/buildtests/LinkedRegionsTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.statemachine.buildtests; + +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +import java.util.ArrayList; + +import org.junit.Test; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.statemachine.StateMachine; +import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.builders.StateMachineModelConfigurer; +import org.springframework.statemachine.config.model.StateMachineModelFactory; +import org.springframework.statemachine.listener.StateMachineListenerAdapter; +import org.springframework.statemachine.state.State; +import org.springframework.statemachine.test.StateMachineTestPlan; +import org.springframework.statemachine.test.StateMachineTestPlanBuilder; +import org.springframework.statemachine.uml.UmlStateMachineModelFactory; + +@SuppressWarnings("unchecked") +public class LinkedRegionsTests extends AbstractBuildTests { + + @Test + public void testSimpleFlow() throws Exception { + context.register(Config1.class); + context.refresh(); + StateMachine stateMachine = context.getBean(StateMachine.class); + TestListener listener = new TestListener(); + stateMachine.addStateListener(listener); + + StateMachineTestPlan plan = + StateMachineTestPlanBuilder.builder() + .stateMachine(stateMachine) + .step().expectStateChanged(19).expectStates("S3").and() + .build(); + plan.test(); + assertThat(listener.statesEntered, not(hasItem(startsWith("JOIN")))); + } + + @Configuration + @EnableStateMachine + public static class Config1 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/buildtests/linked-regions.uml"); + } + } + + static class TestListener extends StateMachineListenerAdapter { + + final ArrayList statesEntered = new ArrayList<>(); + + @Override + public void stateEntered(State state) { + statesEntered.add(state.getId()); + if (state.getId().startsWith("JOIN")) { + System.out.println(state); + } + } + + void print() { + statesEntered.stream().forEach(System.out::println); + } + } + @Override + protected AnnotationConfigApplicationContext buildContext() { + return new AnnotationConfigApplicationContext(); + } + +} diff --git a/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.di b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.di new file mode 100644 index 00000000..bf9abab3 --- /dev/null +++ b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.di @@ -0,0 +1,2 @@ + + diff --git a/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.notation b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.notation new file mode 100644 index 00000000..90eca008 --- /dev/null +++ b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.notation @@ -0,0 +1,982 @@ + + + + + + + + + +
+ + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.uml b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.uml new file mode 100644 index 00000000..b6f920f7 --- /dev/null +++ b/spring-statemachine-build-tests/src/test/resources/org/springframework/statemachine/buildtests/linked-regions.uml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/JoinPseudoState.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/JoinPseudoState.java index 8e130523..227a55cc 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/JoinPseudoState.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/state/JoinPseudoState.java @@ -105,7 +105,8 @@ public class JoinPseudoState extends AbstractPseudoState { @Override public void onEntry(StateContext context) { - if (StateMachineUtils.isPseudoState(context.getTransition().getTarget(), PseudoStateKind.END)) { + if (context.getTransition() != null && StateMachineUtils + .isPseudoState(context.getTransition().getTarget(), PseudoStateKind.END)) { if (!notified && track.size() > 0) { track.remove(t); if (track.size() == 0) { diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java index 7567ee67..158da8b8 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/support/AbstractStateMachine.java @@ -1142,7 +1142,11 @@ public abstract class AbstractStateMachine extends StateMachineObjectSuppo } } - notifyStateEntered(buildStateContext(Stage.STATE_ENTRY, message, transition, getRelayStateMachine(), null, state)); + // with linked joins, we need to enter state but should not notify. + // state entries are needed to track join logic. + if (!StateMachineUtils.isPseudoState(state, PseudoStateKind.JOIN)) { + notifyStateEntered(buildStateContext(Stage.STATE_ENTRY, message, transition, getRelayStateMachine(), null, state)); + } log.debug("Enter state=[" + state + "]"); state.entry(stateContext); }