Add support for junction states

- Adding junction state construction and
  guard resolving.
- Relates to #262
This commit is contained in:
Janne Valkealahti
2016-10-11 10:50:08 +01:00
parent 1beb0e6860
commit bd6696920f
3 changed files with 210 additions and 2 deletions

View File

@@ -357,6 +357,74 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine8First() throws Exception {
context.register(Config8.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("junction", "s30").build()).expectStates("S30").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine8Then1() throws Exception {
context.register(Config8.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("junction", "s31").build()).expectStates("S31").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine8Then2() throws Exception {
context.register(Config8.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("junction", "s32").build()).expectStates("S32").and()
.build();
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine8Last() throws Exception {
context.register(Config8.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);
@@ -500,6 +568,33 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
}
}
@EnableAutoConfiguration
static class Config8 {
@Bean
public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() {
StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean();
factoryBean.setResources(new Resource[]{new ClassPathResource("data8.json")});
return factoryBean;
}
@Bean
public Guard<String, String> s30Guard() {
return new JunctionGuard("s30");
}
@Bean
public Guard<String, String> s31Guard() {
return new JunctionGuard("s31");
}
@Bean
public Guard<String, String> s32Guard() {
return new JunctionGuard("s32");
}
}
@Configuration
@EnableStateMachineFactory
public static class FactoryConfig extends StateMachineConfigurerAdapter<String, String> {
@@ -537,4 +632,17 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
}
}
private static class JunctionGuard implements Guard<String, String> {
private final String match;
public JunctionGuard(String match) {
this.match = match;
}
@Override
public boolean evaluate(StateContext<String, String> context) {
return ObjectUtils.nullSafeEquals(match, context.getMessageHeaders().get("junction", String.class));
}
}
}

View File

@@ -0,0 +1,84 @@
[
{
"@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": "JUNCTION"
},
{
"@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"
},
{
"@id": "7",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s30Guard"
},
{
"@id": "8",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s31Guard"
},
{
"@id": "9",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryGuard",
"name": "s32Guard"
},
{
"_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": "7"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "4",
"guard": "8"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "5",
"guard": "9"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "6"
}
]

View File

@@ -33,6 +33,7 @@ import org.springframework.statemachine.config.model.ChoiceData;
import org.springframework.statemachine.config.model.ConfigurationData;
import org.springframework.statemachine.config.model.DefaultStateMachineModel;
import org.springframework.statemachine.config.model.HistoryData;
import org.springframework.statemachine.config.model.JunctionData;
import org.springframework.statemachine.config.model.StateData;
import org.springframework.statemachine.config.model.StateMachineModel;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
@@ -155,6 +156,7 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
Collection<TransitionData<String, String>> transitionData = new ArrayList<>();
Collection<HistoryData<String, String>> historys = new ArrayList<HistoryData<String, String>>();
Map<String, LinkedList<ChoiceData<String, String>>> choices = new HashMap<String, LinkedList<ChoiceData<String,String>>>();
Map<String, LinkedList<JunctionData<String, String>>> junctions = new HashMap<String, LinkedList<JunctionData<String,String>>>();
for (RepositoryTransition t : transitionRepository.findByMachineId(machineId)) {
Collection<Action<String, String>> actions = new ArrayList<Action<String, String>>();
@@ -194,7 +196,19 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
} else {
list.addFirst(new ChoiceData<String, String>(t.getSource().getState(), t.getTarget().getState(), guard));
}
} else if (t.getSource().getKind() == PseudoStateKind.JUNCTION) {
LinkedList<JunctionData<String, String>> list = junctions.get(t.getSource().getState());
if (list == null) {
list = new LinkedList<JunctionData<String, String>>();
junctions.put(t.getSource().getState(), list);
}
guard = resolveGuard(t);
// we want null guards to be at the end
if (guard == null) {
list.addLast(new JunctionData<String, String>(t.getSource().getState(), t.getTarget().getState(), guard));
} else {
list.addFirst(new JunctionData<String, String>(t.getSource().getState(), t.getTarget().getState(), guard));
}
} else if (t.getSource().getKind() == PseudoStateKind.HISTORY_SHALLOW) {
historys.add(new HistoryData<String, String>(t.getSource().getState(), t.getTarget().getState()));
} else if (t.getSource().getKind() == PseudoStateKind.HISTORY_DEEP) {
@@ -204,8 +218,10 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
HashMap<String, List<ChoiceData<String, String>>> choicesCopy = new HashMap<String, List<ChoiceData<String, String>>>();
choicesCopy.putAll(choices);
HashMap<String, List<JunctionData<String, String>>> junctionsCopy = new HashMap<String, List<JunctionData<String, String>>>();
junctionsCopy.putAll(junctions);
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData, choicesCopy, null, null, null, null, null, historys);
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData, choicesCopy, junctionsCopy, null, null, null, null, historys);
StateMachineModel<String, String> stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData);
return stateMachineModel;