Fix possible NPE
- getState() now handles null better when checking running state when possibly going to return final state. - Fixes #227
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
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.StateMachineConfigurationConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineModelConfigurer;
|
||||
import org.springframework.statemachine.config.model.StateMachineModelFactory;
|
||||
import org.springframework.statemachine.test.StateMachineTestPlan;
|
||||
import org.springframework.statemachine.test.StateMachineTestPlanBuilder;
|
||||
import org.springframework.statemachine.uml.UmlStateMachineModelFactory;
|
||||
|
||||
public class EndSmokeTests extends AbstractBuildTests {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testEndState() throws Exception {
|
||||
context.register(Config1.class);
|
||||
context.refresh();
|
||||
StateMachine<String, String> stateMachine = context.getBean(StateMachine.class);
|
||||
|
||||
TestThread t = new TestThread(stateMachine);
|
||||
t.start();
|
||||
|
||||
StateMachineTestPlan<String, String> plan =
|
||||
StateMachineTestPlanBuilder.<String, String>builder()
|
||||
.stateMachine(stateMachine)
|
||||
.step().expectState("S1").and()
|
||||
.step().sendEvent("E1").expectStateChanged(1).expectState("SF").and()
|
||||
.build();
|
||||
plan.test();
|
||||
|
||||
t.runWhile = false;
|
||||
t.join();
|
||||
assertThat(t.e, nullValue());
|
||||
assertThat(t.ok, is(true));
|
||||
}
|
||||
|
||||
private static class TestThread extends Thread {
|
||||
|
||||
StateMachine<String, String> stateMachine;
|
||||
boolean ok = true;
|
||||
boolean runWhile = true;
|
||||
Exception e = null;
|
||||
|
||||
public TestThread(StateMachine<String, String> stateMachine) {
|
||||
this.stateMachine = stateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (runWhile) {
|
||||
try {
|
||||
if (stateMachine.getState() == null) {
|
||||
ok = false;
|
||||
runWhile = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.e = e;
|
||||
ok = false;
|
||||
runWhile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
public static class Config1 extends StateMachineConfigurerAdapter<String, String> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineConfigurationConfigurer<String, String> config) throws Exception {
|
||||
config
|
||||
.withConfiguration()
|
||||
.autoStartup(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
|
||||
model
|
||||
.withModel()
|
||||
.factory(modelFactory());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public StateMachineModelFactory<String, String> modelFactory() {
|
||||
return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/buildtests/end-smoke.uml");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotationConfigApplicationContext buildContext() {
|
||||
return new AnnotationConfigApplicationContext();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xmi:XMI xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"/>
|
||||
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<notation:Diagram xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" xmlns:notation="http://www.eclipse.org/gmf/runtime/1.0.2/notation" xmlns:style="http://www.eclipse.org/papyrus/infra/viewpoints/policy/style" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_Z0K0QB8jEeayofWOIaLqtg" type="PapyrusUMLStateMachineDiagram" name="StateMachine Diagram" measurementUnit="Pixel">
|
||||
<children xmi:type="notation:Shape" xmi:id="_Z0K0QR8jEeayofWOIaLqtg" type="2000">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_Z0K0Qh8jEeayofWOIaLqtg" type="2001">
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z0K0Qx8jEeayofWOIaLqtg" width="700" height="23"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_Z0K0RB8jEeayofWOIaLqtg" type="2002">
|
||||
<children xmi:type="notation:Shape" xmi:id="_Z0K0RR8jEeayofWOIaLqtg" type="3000">
|
||||
<eAnnotations xmi:type="ecore:EAnnotation" xmi:id="_Z0K0Rh8jEeayofWOIaLqtg" source="RegionAnnotationKey">
|
||||
<details xmi:type="ecore:EStringToStringMapEntry" xmi:id="_Z0K0Rx8jEeayofWOIaLqtg" key="RegionZoneKey" value=""/>
|
||||
</eAnnotations>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_Z0K0SB8jEeayofWOIaLqtg" type="3002">
|
||||
<children xmi:type="notation:Shape" xmi:id="_cHNN4B8jEeayofWOIaLqtg" type="6000">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_cHNN4h8jEeayofWOIaLqtg" type="6001"/>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_cHNN4x8jEeayofWOIaLqtg" type="19003">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_cHNN5B8jEeayofWOIaLqtg" x="40"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_cHN08B8jEeayofWOIaLqtg" type="6002">
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_cHN08R8jEeayofWOIaLqtg"/>
|
||||
</children>
|
||||
<element xmi:type="uml:State" href="end-smoke.uml#_cHHuUB8jEeayofWOIaLqtg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_cHNN4R8jEeayofWOIaLqtg" x="166" y="63"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_fdXRYB8jEeayofWOIaLqtg" type="5000">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_fdXRYh8jEeayofWOIaLqtg" type="5001">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_fdXRYx8jEeayofWOIaLqtg" x="25" y="3"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_fdXRZB8jEeayofWOIaLqtg" type="5002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_fdXRZR8jEeayofWOIaLqtg" x="25" y="-10"/>
|
||||
</children>
|
||||
<element xmi:type="uml:FinalState" href="end-smoke.uml#_fdPVkB8jEeayofWOIaLqtg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_fdXRYR8jEeayofWOIaLqtg" x="320" y="69"/>
|
||||
</children>
|
||||
<children xmi:type="notation:Shape" xmi:id="_gkJTQB8jEeayofWOIaLqtg" type="8000">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_gkJTQh8jEeayofWOIaLqtg" type="8001">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_gkJ6UB8jEeayofWOIaLqtg" x="25" y="3"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_gkJ6UR8jEeayofWOIaLqtg" type="8002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_gkJ6Uh8jEeayofWOIaLqtg" x="25" y="-10"/>
|
||||
</children>
|
||||
<element xmi:type="uml:Pseudostate" href="end-smoke.uml#_gj-UIB8jEeayofWOIaLqtg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_gkJTQR8jEeayofWOIaLqtg" x="35" y="78"/>
|
||||
</children>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z0K0SR8jEeayofWOIaLqtg"/>
|
||||
</children>
|
||||
<element xmi:type="uml:Region" href="end-smoke.uml#_Z0IYAB8jEeayofWOIaLqtg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z0K0Sh8jEeayofWOIaLqtg" width="700" height="287"/>
|
||||
</children>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z0K0Sx8jEeayofWOIaLqtg" y="23" width="700" height="287"/>
|
||||
</children>
|
||||
<element xmi:type="uml:StateMachine" href="end-smoke.uml#_ZzYxIB8jEeayofWOIaLqtg"/>
|
||||
<layoutConstraint xmi:type="notation:Bounds" xmi:id="_Z0K0TB8jEeayofWOIaLqtg" x="30" y="30" width="700" height="310"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:StringValueStyle" xmi:id="_Z0K0TR8jEeayofWOIaLqtg" name="diagram_compatibility_version" stringValue="1.1.0"/>
|
||||
<styles xmi:type="notation:DiagramStyle" xmi:id="_Z0K0Th8jEeayofWOIaLqtg"/>
|
||||
<styles xmi:type="style:PapyrusViewStyle" xmi:id="_Z0K0Tx8jEeayofWOIaLqtg">
|
||||
<owner xmi:type="uml:Model" href="end-smoke.uml#_ZygnYB8jEeayofWOIaLqtg"/>
|
||||
</styles>
|
||||
<element xmi:type="uml:StateMachine" href="end-smoke.uml#_ZzYxIB8jEeayofWOIaLqtg"/>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_hswhIB8jEeayofWOIaLqtg" type="7000" source="_gkJTQB8jEeayofWOIaLqtg" target="_cHNN4B8jEeayofWOIaLqtg">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_hsxvQB8jEeayofWOIaLqtg" type="7001">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_hsxvQR8jEeayofWOIaLqtg"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_hsxvQh8jEeayofWOIaLqtg" type="7002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_hsxvQx8jEeayofWOIaLqtg"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_hsxvRB8jEeayofWOIaLqtg" type="7003">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_hsxvRR8jEeayofWOIaLqtg" y="60"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_hsxIMB8jEeayofWOIaLqtg"/>
|
||||
<element xmi:type="uml:Transition" href="end-smoke.uml#_hrWy8B8jEeayofWOIaLqtg"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_hsxIMR8jEeayofWOIaLqtg" points="[8, -4, -113, 21]$[160, 14, 39, 39]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_htb2kB8jEeayofWOIaLqtg" id="(1.0,0.45)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_htb2kR8jEeayofWOIaLqtg" id="(0.0,0.44680851063829785)"/>
|
||||
</edges>
|
||||
<edges xmi:type="notation:Connector" xmi:id="_ipCjMB8jEeayofWOIaLqtg" type="7000" source="_cHNN4B8jEeayofWOIaLqtg" target="_fdXRYB8jEeayofWOIaLqtg">
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_ipDKQB8jEeayofWOIaLqtg" type="7001">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_ipDKQR8jEeayofWOIaLqtg"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_ipDKQh8jEeayofWOIaLqtg" type="7002">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_ipDKQx8jEeayofWOIaLqtg" x="-2" y="-18"/>
|
||||
</children>
|
||||
<children xmi:type="notation:DecorationNode" xmi:id="_ipDKRB8jEeayofWOIaLqtg" type="7003">
|
||||
<layoutConstraint xmi:type="notation:Location" xmi:id="_ipDKRR8jEeayofWOIaLqtg" y="60"/>
|
||||
</children>
|
||||
<styles xmi:type="notation:FontStyle" xmi:id="_ipCjMR8jEeayofWOIaLqtg"/>
|
||||
<element xmi:type="uml:Transition" href="end-smoke.uml#_io4yMB8jEeayofWOIaLqtg"/>
|
||||
<bendpoints xmi:type="notation:RelativeBendpoints" xmi:id="_ipCjMh8jEeayofWOIaLqtg" points="[37, 3, -127, -13]$[166, 24, 2, 8]"/>
|
||||
<sourceAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ipeoEB8jEeayofWOIaLqtg" id="(1.0,0.40425531914893614)"/>
|
||||
<targetAnchor xmi:type="notation:IdentityAnchor" xmi:id="_ipeoER8jEeayofWOIaLqtg" id="(0.0,0.55)"/>
|
||||
</edges>
|
||||
</notation:Diagram>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_ZygnYB8jEeayofWOIaLqtg" name="RootElement">
|
||||
<packagedElement xmi:type="uml:StateMachine" xmi:id="_ZzYxIB8jEeayofWOIaLqtg" name="StateMachine">
|
||||
<region xmi:type="uml:Region" xmi:id="_Z0IYAB8jEeayofWOIaLqtg" name="Region1">
|
||||
<transition xmi:type="uml:Transition" xmi:id="_hrWy8B8jEeayofWOIaLqtg" source="_gj-UIB8jEeayofWOIaLqtg" target="_cHHuUB8jEeayofWOIaLqtg"/>
|
||||
<transition xmi:type="uml:Transition" xmi:id="_io4yMB8jEeayofWOIaLqtg" source="_cHHuUB8jEeayofWOIaLqtg" target="_fdPVkB8jEeayofWOIaLqtg">
|
||||
<trigger xmi:type="uml:Trigger" xmi:id="_E3dUUB8kEeayofWOIaLqtg" event="_mg4-gB8jEeayofWOIaLqtg"/>
|
||||
</transition>
|
||||
<subvertex xmi:type="uml:State" xmi:id="_cHHuUB8jEeayofWOIaLqtg" name="S1"/>
|
||||
<subvertex xmi:type="uml:FinalState" xmi:id="_fdPVkB8jEeayofWOIaLqtg" name="SF"/>
|
||||
<subvertex xmi:type="uml:Pseudostate" xmi:id="_gj-UIB8jEeayofWOIaLqtg" name=""/>
|
||||
</region>
|
||||
</packagedElement>
|
||||
<packagedElement xmi:type="uml:Signal" xmi:id="_lSppwB8jEeayofWOIaLqtg" name="E1"/>
|
||||
<packagedElement xmi:type="uml:SignalEvent" xmi:id="_mg4-gB8jEeayofWOIaLqtg" name="SignalEventE1" signal="_lSppwB8jEeayofWOIaLqtg"/>
|
||||
</uml:Model>
|
||||
@@ -165,7 +165,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
public State<S,E> getState() {
|
||||
// if we're complete assume we're stopped
|
||||
// and state was stashed into lastState
|
||||
if (isComplete()) {
|
||||
if (lastState != null && isComplete()) {
|
||||
return lastState;
|
||||
} else {
|
||||
return currentState;
|
||||
@@ -388,11 +388,12 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
@Override
|
||||
public boolean isComplete() {
|
||||
if (currentState == null) {
|
||||
State<S, E> s = currentState;
|
||||
if (s == null) {
|
||||
return !isRunning();
|
||||
} else {
|
||||
return currentState != null && currentState.getPseudoState() != null
|
||||
&& currentState.getPseudoState().getKind() == PseudoStateKind.END;
|
||||
return s != null && s.getPseudoState() != null
|
||||
&& s.getPseudoState().getKind() == PseudoStateKind.END;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user