Add support for history states

- Add PseudoStateKind to RepositoryState.
- Hook shallow and deep historys in
  RepositoryStateMachineModelFactory
- Relates to #262
This commit is contained in:
Janne Valkealahti
2016-10-09 17:38:35 +01:00
parent 3cc33a6fef
commit a6d7040199
5 changed files with 128 additions and 2 deletions

View File

@@ -27,6 +27,7 @@ import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import org.springframework.statemachine.data.RepositoryState;
import org.springframework.statemachine.state.PseudoStateKind;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
@@ -48,6 +49,7 @@ public class JpaRepositoryState extends RepositoryState {
private String machineId;
private String state;
private boolean initial;
private PseudoStateKind kind;
@OneToOne(fetch = FetchType.EAGER)
private JpaRepositoryState parentState;
@@ -158,6 +160,15 @@ public class JpaRepositoryState extends RepositoryState {
this.state = state;
}
@Override
public PseudoStateKind getKind() {
return kind;
}
public void setKind(PseudoStateKind kind) {
this.kind = kind;
}
@Override
public boolean isInitial() {
return initial;

View File

@@ -242,6 +242,26 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
plan.test();
}
@SuppressWarnings("unchecked")
@Test
public void testMachine4() throws Exception {
context.register(Config4.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("S1").and()
.step().sendEvent("E1").expectStates("S2", "S20").and()
.step().sendEvent("E2").expectStates("S2", "S21").and()
.step().sendEvent("E3").expectStates("S1").and()
.step().sendEvent("E4").expectStates("S2", "S21").and()
.build();
plan.test();
}
@Test
public void testPopulate1() {
context.register(Config2.class);
@@ -304,6 +324,17 @@ public class JpaRepositoryTests extends AbstractJpaRepositoryTests {
}
}
@EnableAutoConfiguration
static class Config4 {
@Bean
public StateMachineJackson2RepositoryPopulatorFactoryBean jackson2RepositoryPopulatorFactoryBean() {
StateMachineJackson2RepositoryPopulatorFactoryBean factoryBean = new StateMachineJackson2RepositoryPopulatorFactoryBean();
factoryBean.setResources(new Resource[]{new ClassPathResource("data4.json")});
return factoryBean;
}
}
@Configuration
@EnableStateMachineFactory
public static class FactoryConfig extends StateMachineConfigurerAdapter<String, String> {

View File

@@ -0,0 +1,64 @@
[
{
"@id": "1",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": true,
"state": "S1"
},
{
"@id": "2",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"state": "S2"
},
{
"@id": "3",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": true,
"parentState": "2",
"state": "S20"
},
{
"@id": "4",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"parentState": "2",
"state": "S21"
},
{
"@id": "5",
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryState",
"initial": false,
"parentState": "2",
"state": "SH",
"kind": "HISTORY_SHALLOW"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "1",
"target": "2",
"event": "E1",
"kind": "EXTERNAL"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "3",
"target": "4",
"event": "E2",
"kind": "EXTERNAL"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "2",
"target": "1",
"event": "E3",
"kind": "EXTERNAL"
},
{
"_class": "org.springframework.statemachine.data.jpa.JpaRepositoryTransition",
"source": "1",
"target": "5",
"event": "E4",
"kind": "EXTERNAL"
}
]

View File

@@ -17,6 +17,8 @@ package org.springframework.statemachine.data;
import java.util.Set;
import org.springframework.statemachine.state.PseudoStateKind;
/**
* Generic base class representing state entity.
*
@@ -73,4 +75,11 @@ public abstract class RepositoryState extends BaseRepositoryEntity {
* @return the exit actions
*/
public abstract Set<? extends RepositoryAction> getExitActions();
/**
* Gets the pseudo state kind.
*
* @return the pseudo state kind
*/
public abstract PseudoStateKind getKind();
}

View File

@@ -27,6 +27,7 @@ import org.springframework.statemachine.action.SpelExpressionAction;
import org.springframework.statemachine.config.model.AbstractStateMachineModelFactory;
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.StateData;
import org.springframework.statemachine.config.model.StateMachineModel;
import org.springframework.statemachine.config.model.StateMachineModelFactory;
@@ -35,6 +36,7 @@ import org.springframework.statemachine.config.model.TransitionData;
import org.springframework.statemachine.config.model.TransitionsData;
import org.springframework.statemachine.guard.Guard;
import org.springframework.statemachine.guard.SpelExpressionGuard;
import org.springframework.statemachine.state.PseudoStateKind;
import org.springframework.statemachine.transition.TransitionKind;
import org.springframework.util.StringUtils;
@@ -138,11 +140,14 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
stateData.setStateActions(stateActions);
stateData.setEntryActions(entryActions);
stateData.setExitActions(exitActions);
if (s.getKind() != null) {
stateData.setPseudoStateKind(s.getKind());
}
stateDatas.add(stateData);
}
StatesData<String, String> statesData = new StatesData<>(stateDatas);
Collection<TransitionData<String, String>> transitionData = new ArrayList<>();
Collection<HistoryData<String, String>> historys = new ArrayList<HistoryData<String, String>>();
for (RepositoryTransition t : transitionRepository.findByMachineId(machineId)) {
Collection<Action<String, String>> actions = new ArrayList<Action<String, String>>();
@@ -178,8 +183,14 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
}
}
transitionData.add(new TransitionData<>(t.getSource().getState(), t.getTarget().getState(), t.getEvent(), actions, guard, kind != null ? kind : TransitionKind.EXTERNAL));
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) {
historys.add(new HistoryData<String, String>(t.getSource().getState(), t.getTarget().getState()));
}
}
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData);
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData, null, null, null, null, null, null, historys);
StateMachineModel<String, String> stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData);
return stateMachineModel;