Add support for choice states

- Adding choice state construction and
  guard resolving.
- Relates to #262
This commit is contained in:
Janne Valkealahti
2016-10-10 08:54:28 +01:00
parent d06d45580a
commit de3a9341de
3 changed files with 233 additions and 13 deletions

View File

@@ -31,6 +31,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateContext;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.EnableStateMachineFactory;
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
@@ -43,9 +45,11 @@ import org.springframework.statemachine.data.RepositoryTransition;
import org.springframework.statemachine.data.StateRepository;
import org.springframework.statemachine.data.TransitionRepository;
import org.springframework.statemachine.data.support.StateMachineJackson2RepositoryPopulatorFactoryBean;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.test.StateMachineTestPlan;
import org.springframework.statemachine.test.StateMachineTestPlanBuilder;
import org.springframework.statemachine.transition.TransitionKind;
import org.springframework.util.ObjectUtils;
public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
@@ -281,6 +285,74 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine6First() throws Exception {
context.register(Config6.class, FactoryConfig.class);
context.refresh();
StateMachineFactory<String, String> stateMachineFactory = context.getBean(StateMachineFactory.class);
StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("SI").and()
.step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s30").build()).expectStates("S30").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine6Then1() throws Exception {
context.register(Config6.class, FactoryConfig.class);
context.refresh();
StateMachineFactory<String, String> stateMachineFactory = context.getBean(StateMachineFactory.class);
StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("SI").and()
.step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s31").build()).expectStates("S31").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine6Then2() throws Exception {
context.register(Config6.class, FactoryConfig.class);
context.refresh();
StateMachineFactory<String, String> stateMachineFactory = context.getBean(StateMachineFactory.class);
StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("SI").and()
.step().sendEvent(MessageBuilder.withPayload("E1").setHeader("choice", "s32").build()).expectStates("S32").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine6Last() throws Exception {
context.register(Config6.class, FactoryConfig.class);
context.refresh();
StateMachineFactory<String, String> stateMachineFactory = context.getBean(StateMachineFactory.class);
StateMachine<String, String> stateMachine = stateMachineFactory.getStateMachine();
StateMachineTestPlan<String, String> plan =
StateMachineTestPlanBuilder.<String, String>builder()
.stateMachine(stateMachine)
.step().expectStates("SI").and()
.step().sendEvent(MessageBuilder.withPayload("E1").build()).expectStates("S33").and()
.build();
plan.test();
}
@Test
public void testPopulate1() {
context.register(Config2.class);
@@ -365,6 +437,33 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
}
}
@EnableAutoConfiguration
static class Config6 {
@Bean
public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() {
StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean();
factoryBean.setResources(new Resource[]{new ClassPathResource("data6.json")});
return factoryBean;
}
@Bean
public Guard<String, String> s30Guard() {
return new ChoiceGuard("s30");
}
@Bean
public Guard<String, String> s31Guard() {
return new ChoiceGuard("s31");
}
@Bean
public Guard<String, String> s32Guard() {
return new ChoiceGuard("s32");
}
}
@Configuration
@EnableStateMachineFactory
public static class FactoryConfig extends StateMachineConfigurerAdapter<String, String> {
@@ -388,4 +487,18 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
}
}
private static class ChoiceGuard implements Guard<String, String> {
private final String match;
public ChoiceGuard(String match) {
this.match = match;
}
@Override
public boolean evaluate(StateContext<String, String> context) {
return ObjectUtils.nullSafeEquals(match, context.getMessageHeaders().get("choice", String.class));
}
}
}

View File

@@ -0,0 +1,78 @@
[
{
"@id": "1",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": true,
"state": "SI"
},
{
"@id": "2",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S3",
"kind": "CHOICE"
},
{
"@id": "3",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S30"
},
{
"@id": "4",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S31"
},
{
"@id": "5",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S32"
},
{
"@id": "6",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S33"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "1",
"target": "2",
"event": "E1",
"kind": "EXTERNAL"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "3",
"guard": {
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s30Guard"
}
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "4",
"guard": {
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s31Guard"
}
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "5",
"guard": {
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s32Guard"
}
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "6"
}
]