From 693d7b8951f05584dbe37f00b441bd95331a6e68 Mon Sep 17 00:00:00 2001 From: Janne Valkealahti Date: Fri, 23 Sep 2016 15:44:13 +0100 Subject: [PATCH] Fix StateMachineModelFactory usage - When StateMachineModelFactory is passed into config it is now used everytime when machine is build which allows model factory to get a fresh model from its own storage if needed. - Shuffled stuff around in machine factory base classes to call StateMachineModelFactory only once during a machine build operation. - Other tweaks and a polish. - Fixes #254 --- .../config/AbstractStateMachineFactory.java | 114 ++++++++++-------- .../config/ObjectStateMachineFactory.java | 35 ++++-- .../config/StateMachineBuilder.java | 12 +- .../config/StateMachineConfig.java | 38 +++++- .../builders/StateMachineConfigBuilder.java | 22 +--- .../StateMachineConfiguration.java | 13 +- .../StateMachineFactoryConfiguration.java | 13 +- .../model/DefaultStateMachineModel.java | 5 - .../model/StateMachineModelFactoryTests.java | 64 +++++++++- .../uml/UmlStateMachineModelFactory.java | 5 +- 10 files changed, 223 insertions(+), 98 deletions(-) diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java index 40ffa5c1..fcb9101a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/AbstractStateMachineFactory.java @@ -21,9 +21,9 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Stack; import java.util.UUID; -import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -39,12 +39,14 @@ import org.springframework.statemachine.access.StateMachineAccess; import org.springframework.statemachine.access.StateMachineFunction; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.model.ChoiceData; +import org.springframework.statemachine.config.model.DefaultStateMachineModel; import org.springframework.statemachine.config.model.EntryData; import org.springframework.statemachine.config.model.ExitData; 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; import org.springframework.statemachine.config.model.TransitionData; import org.springframework.statemachine.config.model.TransitionsData; import org.springframework.statemachine.config.model.verifier.CompositeStateMachineModelVerifier; @@ -55,27 +57,27 @@ import org.springframework.statemachine.region.Region; import org.springframework.statemachine.security.StateMachineSecurityInterceptor; import org.springframework.statemachine.state.AbstractState; import org.springframework.statemachine.state.ChoicePseudoState; +import org.springframework.statemachine.state.ChoicePseudoState.ChoiceStateData; import org.springframework.statemachine.state.DefaultPseudoState; import org.springframework.statemachine.state.EntryPseudoState; import org.springframework.statemachine.state.ExitPseudoState; import org.springframework.statemachine.state.ForkPseudoState; import org.springframework.statemachine.state.HistoryPseudoState; import org.springframework.statemachine.state.JoinPseudoState; +import org.springframework.statemachine.state.JoinPseudoState.JoinStateData; import org.springframework.statemachine.state.JunctionPseudoState; +import org.springframework.statemachine.state.JunctionPseudoState.JunctionStateData; import org.springframework.statemachine.state.PseudoState; import org.springframework.statemachine.state.PseudoStateKind; import org.springframework.statemachine.state.RegionState; import org.springframework.statemachine.state.State; import org.springframework.statemachine.state.StateHolder; import org.springframework.statemachine.state.StateMachineState; -import org.springframework.statemachine.state.ChoicePseudoState.ChoiceStateData; -import org.springframework.statemachine.state.JoinPseudoState.JoinStateData; -import org.springframework.statemachine.state.JunctionPseudoState.JunctionStateData; import org.springframework.statemachine.support.DefaultExtendedState; import org.springframework.statemachine.support.LifecycleObjectSupport; import org.springframework.statemachine.support.tree.Tree; -import org.springframework.statemachine.support.tree.TreeTraverser; import org.springframework.statemachine.support.tree.Tree.Node; +import org.springframework.statemachine.support.tree.TreeTraverser; import org.springframework.statemachine.transition.DefaultExternalTransition; import org.springframework.statemachine.transition.DefaultInternalTransition; import org.springframework.statemachine.transition.DefaultLocalTransition; @@ -85,7 +87,6 @@ import org.springframework.statemachine.transition.TransitionKind; import org.springframework.statemachine.trigger.EventTrigger; import org.springframework.statemachine.trigger.TimerTrigger; import org.springframework.statemachine.trigger.Trigger; -import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; /** @@ -101,7 +102,9 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS private final Log log = LogFactory.getLog(AbstractStateMachineFactory.class); - private final StateMachineModel stateMachineModel; + private final StateMachineModel defaultStateMachineModel; + + private final StateMachineModelFactory stateMachineModelFactory; private Boolean contextEvents; @@ -112,11 +115,12 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS /** * Instantiates a new abstract state machine factory. * - * @param stateMachineModel the state machine model + * @param defaultStateMachineModel the default state machine model + * @param stateMachineModelFactory the state machine model factory */ - public AbstractStateMachineFactory(StateMachineModel stateMachineModel) { - Assert.notNull(stateMachineModel, "StateMachineModel must be set"); - this.stateMachineModel = stateMachineModel; + public AbstractStateMachineFactory(StateMachineModel defaultStateMachineModel, StateMachineModelFactory stateMachineModelFactory) { + this.stateMachineModelFactory = stateMachineModelFactory; + this.defaultStateMachineModel = defaultStateMachineModel; } @Override @@ -148,6 +152,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS */ @SuppressWarnings("unchecked") public StateMachine getStateMachine(UUID uuid, String machineId) { + StateMachineModel stateMachineModel = resolveStateMachineModel(); if (stateMachineModel.getConfigurationData().isVerifierEnabled()) { StateMachineModelVerifier verifier = stateMachineModel.getConfigurationData().getVerifier(); if (verifier == null) { @@ -171,7 +176,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS Map> machineMap = new HashMap>(); Map> holderMap = new HashMap>(); - Iterator>> iterator = buildStateDataIterator(); + Iterator>> iterator = buildStateDataIterator(stateMachineModel); while (iterator.hasNext()) { Node> node = iterator.next(); StateData stateData = node.getData(); @@ -201,13 +206,13 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS Collection> stateDatas = popSameParents(stateStack); int initialCount = getInitialCount(stateDatas); Collection>> regionsStateDatas = splitIntoRegions(stateDatas); - Collection> transitionsData = getTransitionData(iterator.hasNext(), stateDatas); + Collection> transitionsData = getTransitionData(iterator.hasNext(), stateDatas, stateMachineModel); if (initialCount > 1) { for (Collection> regionStateDatas : regionsStateDatas) { - machine = buildMachine(machineMap, stateMap, holderMap, regionStateDatas, transitionsData, resolveBeanFactory(), - contextEvents, defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(), - resolveTaskScheduler(), machineId, null); + machine = buildMachine(machineMap, stateMap, holderMap, regionStateDatas, transitionsData, resolveBeanFactory(stateMachineModel), + contextEvents, defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(stateMachineModel), + resolveTaskScheduler(stateMachineModel), machineId, null, stateMachineModel); regionStack.push(new MachineStackItem(machine)); } @@ -227,16 +232,16 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS states.add(rstate); Transition initialTransition = new InitialTransition(rstate); StateMachine m = buildStateMachineInternal(states, new ArrayList>(), rstate, initialTransition, - null, defaultExtendedState, null, contextEvents, resolveBeanFactory(), resolveTaskExecutor(), - resolveTaskScheduler(), beanName, + null, defaultExtendedState, null, contextEvents, resolveBeanFactory(stateMachineModel), resolveTaskExecutor(stateMachineModel), + resolveTaskScheduler(stateMachineModel), beanName, machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(), - uuid); + uuid, stateMachineModel); machine = m; } } else { - machine = buildMachine(machineMap, stateMap, holderMap, stateDatas, transitionsData, resolveBeanFactory(), contextEvents, - defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(), resolveTaskScheduler(), - machineId, uuid); + machine = buildMachine(machineMap, stateMap, holderMap, stateDatas, transitionsData, resolveBeanFactory(stateMachineModel), contextEvents, + defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(stateMachineModel), resolveTaskScheduler(stateMachineModel), + machineId, uuid, stateMachineModel); if (peek.isInitial() || (!peek.isInitial() && !machineMap.containsKey(peek.getParent()))) { machineMap.put(peek.getParent(), machine); } @@ -326,7 +331,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS return delegate; } - protected BeanFactory resolveBeanFactory() { + protected BeanFactory resolveBeanFactory(StateMachineModel stateMachineModel) { if (stateMachineModel.getConfigurationData().getBeanFactory() != null) { return stateMachineModel.getConfigurationData().getBeanFactory(); } else { @@ -334,7 +339,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } } - protected TaskExecutor resolveTaskExecutor() { + protected TaskExecutor resolveTaskExecutor(StateMachineModel stateMachineModel) { if (stateMachineModel.getConfigurationData().getTaskExecutor() != null) { return stateMachineModel.getConfigurationData().getTaskExecutor(); } else { @@ -342,7 +347,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } } - protected TaskScheduler resolveTaskScheduler() { + protected TaskScheduler resolveTaskScheduler(StateMachineModel stateMachineModel) { if (stateMachineModel.getConfigurationData().getTaskScheduler() != null) { return stateMachineModel.getConfigurationData().getTaskScheduler(); } else { @@ -350,6 +355,22 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } } + protected StateMachineModel resolveStateMachineModel() { + if (stateMachineModelFactory == null) { + return defaultStateMachineModel; + } else { + StateMachineModel m = stateMachineModelFactory.build(); + if (m.getConfigurationData() == null) { + // if model doesn't have explicit configuration data, + // get it from default model + return new DefaultStateMachineModel<>(defaultStateMachineModel.getConfigurationData(), m.getStatesData(), + m.getTransitionsData()); + } else { + return m; + } + } + } + private int getInitialCount(Collection> stateDatas) { int count = 0; for (StateData stateData : stateDatas) { @@ -373,7 +394,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS return map.values(); } - private Collection> getTransitionData(boolean roots, Collection> stateDatas) { + private Collection> getTransitionData(boolean roots, Collection> stateDatas, StateMachineModel stateMachineModel) { if (roots) { return resolveTransitionData(stateMachineModel.getTransitionsData().getTransitions(), stateDatas); } else { @@ -438,7 +459,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS Map> holderMap, Collection> stateDatas, Collection> transitionsData, BeanFactory beanFactory, Boolean contextEvents, DefaultExtendedState defaultExtendedState, TransitionsData stateMachineTransitions, TaskExecutor taskExecutor, TaskScheduler taskScheduler, String machineId, - UUID uuid) { + UUID uuid, StateMachineModel stateMachineModel) { State state = null; State initialState = null; PseudoState historyState = null; @@ -506,7 +527,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS continue; } state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); if (stateData.isInitial()) { initialState = state; initialAction = stateData.getInitialAction(); @@ -534,7 +555,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new HistoryPseudoState(PseudoStateKind.HISTORY_SHALLOW, defaultStateHolder, containingStateHolder); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); historyState = pseudoState; @@ -554,7 +575,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new HistoryPseudoState(PseudoStateKind.HISTORY_DEEP, defaultStateHolder, containingStateHolder); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); historyState = pseudoState; @@ -573,7 +594,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new ChoicePseudoState(choices); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); } else if (stateData.getPseudoStateKind() == PseudoStateKind.JUNCTION) { @@ -589,7 +610,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new JunctionPseudoState(junctions); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); } else if (stateData.getPseudoStateKind() == PseudoStateKind.ENTRY) { @@ -599,7 +620,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS if (s.equals(entry.getSource())) { PseudoState pseudoState = new EntryPseudoState(stateMap.get(entry.getTarget())); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); break; @@ -616,7 +637,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new ExitPseudoState(holder); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); break; @@ -631,7 +652,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS } PseudoState pseudoState = new ForkPseudoState(forks); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); } else if (stateData.getPseudoStateKind() == PseudoStateKind.JOIN) { @@ -675,7 +696,7 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS JoinPseudoState pseudoState = new JoinPseudoState(joins, joinTargets); state = buildStateInternal(stateData.getState(), stateData.getDeferred(), stateData.getEntryActions(), - stateData.getExitActions(), stateData.getStateActions(), pseudoState); + stateData.getExitActions(), stateData.getStateActions(), pseudoState, stateMachineModel); states.add(state); stateMap.put(stateData.getState(), state); } @@ -754,28 +775,21 @@ public abstract class AbstractStateMachineFactory extends LifecycleObjectS Transition initialTransition = new InitialTransition(initialState, initialAction); StateMachine machine = buildStateMachineInternal(states, transitions, initialState, initialTransition, null, defaultExtendedState, historyState, contextEvents, beanFactory, taskExecutor, taskScheduler, - beanName, machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(), uuid); + beanName, machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId(), uuid, stateMachineModel); return machine; } protected abstract StateMachine buildStateMachineInternal(Collection> states, - Collection> transitions, - State initialState, Transition initialTransition, - Message initialEvent, ExtendedState extendedState, - PseudoState historyState, - Boolean contextEventsEnabled, - BeanFactory beanFactory, - TaskExecutor taskExecutor, - TaskScheduler taskScheduler, - String beanName, - String machineId, - UUID uuid); + Collection> transitions, State initialState, Transition initialTransition, Message initialEvent, + ExtendedState extendedState, PseudoState historyState, Boolean contextEventsEnabled, BeanFactory beanFactory, + TaskExecutor taskExecutor, TaskScheduler taskScheduler, String beanName, String machineId, UUID uuid, + StateMachineModel stateMachineModel); protected abstract State buildStateInternal(S id, Collection deferred, Collection> entryActions, Collection> exitActions, - Collection> stateActions, PseudoState pseudoState); + Collection> stateActions, PseudoState pseudoState, StateMachineModel stateMachineModel); - private Iterator>> buildStateDataIterator() { + private Iterator>> buildStateDataIterator(StateMachineModel stateMachineModel) { Tree> tree = new Tree>(); treeAdd(tree, stateMachineModel.getStatesData().getStateData()); return new TreeTraverser>>() { diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/ObjectStateMachineFactory.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/ObjectStateMachineFactory.java index 8cfd14ed..85d53d75 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/ObjectStateMachineFactory.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/ObjectStateMachineFactory.java @@ -28,6 +28,7 @@ import org.springframework.statemachine.ObjectStateMachine; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.model.StateMachineModel; +import org.springframework.statemachine.config.model.StateMachineModelFactory; import org.springframework.statemachine.region.Region; import org.springframework.statemachine.state.ObjectState; import org.springframework.statemachine.state.PseudoState; @@ -49,18 +50,28 @@ public class ObjectStateMachineFactory extends AbstractStateMachineFactory /** * Instantiates a new object state machine factory. * - * @param stateMachineModel the state machine model + * @param defaultStateMachineModel the default state machine model */ - public ObjectStateMachineFactory(StateMachineModel stateMachineModel) { - super(stateMachineModel); + public ObjectStateMachineFactory(StateMachineModel defaultStateMachineModel) { + this(defaultStateMachineModel, null); + } + + /** + * Instantiates a new object state machine factory. + * + * @param defaultStateMachineModel the default state machine model + * @param stateMachineModelFactory the state machine model factory + */ + public ObjectStateMachineFactory(StateMachineModel defaultStateMachineModel, + StateMachineModelFactory stateMachineModelFactory) { + super(defaultStateMachineModel, stateMachineModelFactory); } @Override - protected StateMachine buildStateMachineInternal(Collection> states, - Collection> transitions, State initialState, Transition initialTransition, - Message initialEvent, ExtendedState extendedState, PseudoState historyState, - Boolean contextEventsEnabled, BeanFactory beanFactory, TaskExecutor taskExecutor, - TaskScheduler taskScheduler, String beanName, String machineId, UUID uuid) { + protected StateMachine buildStateMachineInternal(Collection> states, Collection> transitions, + State initialState, Transition initialTransition, Message initialEvent, ExtendedState extendedState, + PseudoState historyState, Boolean contextEventsEnabled, BeanFactory beanFactory, TaskExecutor taskExecutor, + TaskScheduler taskScheduler, String beanName, String machineId, UUID uuid, StateMachineModel stateMachineModel) { ObjectStateMachine machine = new ObjectStateMachine(states, transitions, initialState, initialTransition, initialEvent, extendedState, uuid); machine.setId(machineId); @@ -87,17 +98,17 @@ public class ObjectStateMachineFactory extends AbstractStateMachineFactory @Override protected State buildStateInternal(S id, Collection deferred, Collection> entryActions, Collection> exitActions, - Collection> stateActions, PseudoState pseudoState) { + Collection> stateActions, PseudoState pseudoState, StateMachineModel stateMachineModel) { ObjectState objectState = new ObjectState(id, deferred, entryActions, exitActions, stateActions, pseudoState, null, null); - BeanFactory beanFactory = resolveBeanFactory(); + BeanFactory beanFactory = resolveBeanFactory(stateMachineModel); if (beanFactory != null) { objectState.setBeanFactory(beanFactory); } - TaskExecutor taskExecutor = resolveTaskExecutor(); + TaskExecutor taskExecutor = resolveTaskExecutor(stateMachineModel); if (taskExecutor != null) { objectState.setTaskExecutor(taskExecutor); } - TaskScheduler taskScheduler = resolveTaskScheduler(); + TaskScheduler taskScheduler = resolveTaskScheduler(stateMachineModel); if (taskScheduler != null) { objectState.setTaskScheduler(taskScheduler); } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineBuilder.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineBuilder.java index 613aacc8..4f95563a 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineBuilder.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineBuilder.java @@ -126,8 +126,16 @@ public class StateMachineBuilder { TransitionsData stateMachineTransitions = stateMachineConfig.getTransitions(); StatesData stateMachineStates = stateMachineConfig.getStates(); ConfigurationData stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig(); - ObjectStateMachineFactory stateMachineFactory = new ObjectStateMachineFactory( - new DefaultStateMachineModel(stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions)); + + ObjectStateMachineFactory stateMachineFactory = null; + if (stateMachineConfig.getModel() != null && stateMachineConfig.getModel().getFactory() != null) { + stateMachineFactory = new ObjectStateMachineFactory( + new DefaultStateMachineModel(stateMachineConfigurationConfig, null, null), + stateMachineConfig.getModel().getFactory()); + } else { + stateMachineFactory = new ObjectStateMachineFactory(new DefaultStateMachineModel( + stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions), null); + } stateMachineFactory.setHandleAutostartup(stateMachineConfigurationConfig.isAutoStart()); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineConfig.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineConfig.java index d9e06a2e..bad32293 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineConfig.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/StateMachineConfig.java @@ -15,10 +15,19 @@ */ package org.springframework.statemachine.config; +import org.springframework.statemachine.config.builders.ModelData; import org.springframework.statemachine.config.model.ConfigurationData; import org.springframework.statemachine.config.model.StatesData; import org.springframework.statemachine.config.model.TransitionsData; +/** + * Generic pojo keeping relates configs together. + * + * @author Janne Valkealahti + * + * @param the type of state + * @param the type of event + */ public class StateMachineConfig { public final ConfigurationData stateMachineConfigurationConfig; @@ -27,10 +36,34 @@ public class StateMachineConfig { public final StatesData states; - public StateMachineConfig(ConfigurationData stateMachineConfigurationConfig, TransitionsData transitions, StatesData states) { + public final ModelData model; + + /** + * Instantiates a new state machine config. + * + * @param stateMachineConfigurationConfig the state machine configuration config + * @param transitions the transitions + * @param states the states + */ + public StateMachineConfig(ConfigurationData stateMachineConfigurationConfig, TransitionsData transitions, + StatesData states) { + this(stateMachineConfigurationConfig, transitions, states, null); + } + + /** + * Instantiates a new state machine config. + * + * @param stateMachineConfigurationConfig the state machine configuration config + * @param transitions the transitions + * @param states the states + * @param model the model + */ + public StateMachineConfig(ConfigurationData stateMachineConfigurationConfig, TransitionsData transitions, + StatesData states, ModelData model) { this.stateMachineConfigurationConfig = stateMachineConfigurationConfig; this.transitions = transitions; this.states = states; + this.model = model; } public ConfigurationData getStateMachineConfigurationConfig() { @@ -45,4 +78,7 @@ public class StateMachineConfig { return states; } + public ModelData getModel() { + return model; + } } \ No newline at end of file diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineConfigBuilder.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineConfigBuilder.java index 14116deb..34347e6f 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineConfigBuilder.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/builders/StateMachineConfigBuilder.java @@ -19,7 +19,6 @@ import org.springframework.statemachine.config.StateMachineConfig; import org.springframework.statemachine.config.common.annotation.AbstractConfiguredAnnotationBuilder; import org.springframework.statemachine.config.common.annotation.AnnotationBuilder; import org.springframework.statemachine.config.model.ConfigurationData; -import org.springframework.statemachine.config.model.StateMachineModel; import org.springframework.statemachine.config.model.StatesData; import org.springframework.statemachine.config.model.TransitionsData; @@ -44,25 +43,12 @@ public class StateMachineConfigBuilder StateMachineConfigurationBuilder configurationBuilder = getSharedObject(StateMachineConfigurationBuilder.class); StateMachineTransitionBuilder transitionBuilder = getSharedObject(StateMachineTransitionBuilder.class); StateMachineStateBuilder stateBuilder = getSharedObject(StateMachineStateBuilder.class); - ModelData model = (ModelData) modelBuilder.build(); ConfigurationData stateMachineConfigurationConfig = null; - TransitionsData transitions = null; - StatesData states = null; - - if (model.getFactory() != null) { - StateMachineModel stateMachineModel = model.getFactory().build(); - transitions = stateMachineModel.getTransitionsData(); - states = stateMachineModel.getStatesData(); - } stateMachineConfigurationConfig = (ConfigurationData) configurationBuilder.build(); - if (transitions == null) { - transitionBuilder.setSharedObject(ConfigurationData.class, stateMachineConfigurationConfig); - transitions = (TransitionsData) transitionBuilder.build(); - } - if (states == null) { - states = (StatesData) stateBuilder.build(); - } - return new StateMachineConfig(stateMachineConfigurationConfig, transitions, states); + transitionBuilder.setSharedObject(ConfigurationData.class, stateMachineConfigurationConfig); + TransitionsData transitions = (TransitionsData) transitionBuilder.build(); + StatesData states = (StatesData) stateBuilder.build(); + return new StateMachineConfig(stateMachineConfigurationConfig, transitions, states, model); } } diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineConfiguration.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineConfiguration.java index 341c3815..c19dd6d5 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineConfiguration.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineConfiguration.java @@ -145,8 +145,17 @@ public class StateMachineConfiguration extends TransitionsData stateMachineTransitions = stateMachineConfig.getTransitions(); StatesData stateMachineStates = stateMachineConfig.getStates(); ConfigurationData stateMachineConfigurationConfig = stateMachineConfig.getStateMachineConfigurationConfig(); - ObjectStateMachineFactory stateMachineFactory = new ObjectStateMachineFactory( - new DefaultStateMachineModel(stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions)); + + ObjectStateMachineFactory stateMachineFactory = null; + if (stateMachineConfig.getModel() != null && stateMachineConfig.getModel().getFactory() != null) { + stateMachineFactory = new ObjectStateMachineFactory( + new DefaultStateMachineModel(stateMachineConfigurationConfig, null, null), + stateMachineConfig.getModel().getFactory()); + } else { + stateMachineFactory = new ObjectStateMachineFactory(new DefaultStateMachineModel( + stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions), null); + } + stateMachineFactory.setBeanFactory(getBeanFactory()); stateMachineFactory.setContextEventsEnabled(contextEvents); stateMachineFactory.setBeanName(beanName); diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineFactoryConfiguration.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineFactoryConfiguration.java index 2e938045..dd2683a3 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineFactoryConfiguration.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/configuration/StateMachineFactoryConfiguration.java @@ -134,8 +134,17 @@ public class StateMachineFactoryConfiguration extends StatesData stateMachineStates = stateMachineConfig.getStates(); ConfigurationData stateMachineConfigurationConfig = stateMachineConfig .getStateMachineConfigurationConfig(); - ObjectStateMachineFactory objectStateMachineFactory = new ObjectStateMachineFactory( - new DefaultStateMachineModel(stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions)); + + ObjectStateMachineFactory objectStateMachineFactory = null; + if (stateMachineConfig.getModel() != null && stateMachineConfig.getModel().getFactory() != null) { + objectStateMachineFactory = new ObjectStateMachineFactory( + new DefaultStateMachineModel(stateMachineConfigurationConfig, null, null), + stateMachineConfig.getModel().getFactory()); + } else { + objectStateMachineFactory = new ObjectStateMachineFactory(new DefaultStateMachineModel( + stateMachineConfigurationConfig, stateMachineStates, stateMachineTransitions), null); + } + objectStateMachineFactory.setBeanFactory(beanFactory); objectStateMachineFactory.setContextEventsEnabled(contextEvents); // explicitly tell factory to handle auto-start because diff --git a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/DefaultStateMachineModel.java b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/DefaultStateMachineModel.java index 9c71d4b2..9b30a3d6 100644 --- a/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/DefaultStateMachineModel.java +++ b/spring-statemachine-core/src/main/java/org/springframework/statemachine/config/model/DefaultStateMachineModel.java @@ -15,8 +15,6 @@ */ package org.springframework.statemachine.config.model; -import org.springframework.util.Assert; - /** * Default implementation of a {@link StateMachineModel}. * @@ -40,9 +38,6 @@ public class DefaultStateMachineModel extends StateMachineModel { */ public DefaultStateMachineModel(ConfigurationData configurationData, StatesData statesData, TransitionsData transitionsData) { - Assert.notNull(configurationData, "Configuration must be set"); - Assert.notNull(statesData, "States must be set"); - Assert.notNull(transitionsData, "Transitions must be set"); this.configuration = configurationData; this.states = statesData; this.transitions = transitionsData; diff --git a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelFactoryTests.java b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelFactoryTests.java index 440da1c9..1492e03d 100644 --- a/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelFactoryTests.java +++ b/spring-statemachine-core/src/test/java/org/springframework/statemachine/config/model/StateMachineModelFactoryTests.java @@ -33,8 +33,10 @@ import org.springframework.statemachine.StateContext; import org.springframework.statemachine.StateMachine; import org.springframework.statemachine.action.Action; import org.springframework.statemachine.config.EnableStateMachine; +import org.springframework.statemachine.config.EnableStateMachineFactory; import org.springframework.statemachine.config.ObjectStateMachineFactory; import org.springframework.statemachine.config.StateMachineConfigurerAdapter; +import org.springframework.statemachine.config.StateMachineFactory; import org.springframework.statemachine.config.builders.StateMachineModelConfigurer; public class StateMachineModelFactoryTests extends AbstractStateMachineTests { @@ -68,6 +70,33 @@ public class StateMachineModelFactoryTests extends AbstractStateMachineTests { assertThat(stateMachine.getState().getIds(), contains("S2")); } + @Test + public void testModelRecreates() { + context.register(Config3.class); + context.refresh(); + @SuppressWarnings("unchecked") + StateMachineFactory stateMachineFactory = context.getBean(StateMachineFactory.class); + StateMachine stateMachine = stateMachineFactory.getStateMachine(); + TestStateMachineModelFactory modelFactory = context.getBean(TestStateMachineModelFactory.class); + + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), contains("S1")); + stateMachine.sendEvent("E1"); + assertThat(stateMachine.getState().getIds(), contains("S2")); + stateMachine.stop(); + + modelFactory.state1 = "SS1"; + modelFactory.state2 = "SS2"; + modelFactory.event1 = "EE1"; + + stateMachine = stateMachineFactory.getStateMachine(); + stateMachine.start(); + assertThat(stateMachine.getState().getIds(), contains("SS1")); + stateMachine.sendEvent("EE1"); + assertThat(stateMachine.getState().getIds(), contains("SS2")); + stateMachine.stop(); + } + @Configuration static class Config1 { @Bean @@ -106,9 +135,38 @@ public class StateMachineModelFactoryTests extends AbstractStateMachineTests { } } + @Configuration + @EnableStateMachineFactory + public static class Config3 extends StateMachineConfigurerAdapter { + + @Override + public void configure(StateMachineModelConfigurer model) throws Exception { + model + .withModel() + .factory(modelFactory()); + } + + @Bean + public StateMachineModelFactory modelFactory() { + return new TestStateMachineModelFactory(); + } + + @Bean + public Action action1() { + return new Action() { + @Override + public void execute(StateContext context) { + } + }; + } + } + @SuppressWarnings("unchecked") private static class TestStateMachineModelFactory implements StateMachineModelFactory, BeanFactoryAware { private BeanFactory beanFactory; + String state1 = "S1"; + String state2 = "S2"; + String event1 = "E1"; @Override public StateMachineModel build() { @@ -120,12 +178,12 @@ public class StateMachineModelFactoryTests extends AbstractStateMachineTests { ConfigurationData configurationData = new ConfigurationData<>(); Collection> stateData = new ArrayList<>(); - stateData.add(new StateData("S1", true)); - stateData.add(new StateData(null, null, "S2", null, s2Actions, null)); + stateData.add(new StateData(state1, true)); + stateData.add(new StateData(null, null, state2, null, s2Actions, null)); StatesData statesData = new StatesData<>(stateData); Collection> transitionData = new ArrayList<>(); - transitionData.add(new TransitionData("S1", "S2", "E1")); + transitionData.add(new TransitionData(state1, state2, event1)); TransitionsData transitionsData = new TransitionsData<>(transitionData); StateMachineModel stateMachineModel = new DefaultStateMachineModel<>(configurationData, statesData, transitionsData); diff --git a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlStateMachineModelFactory.java b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlStateMachineModelFactory.java index 26ec0852..e0b5dfd6 100644 --- a/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlStateMachineModelFactory.java +++ b/spring-statemachine-uml/src/main/java/org/springframework/statemachine/uml/UmlStateMachineModelFactory.java @@ -24,7 +24,6 @@ import java.nio.file.Path; import org.eclipse.uml2.uml.Model; import org.springframework.core.io.Resource; 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.StateMachineModel; import org.springframework.statemachine.config.model.StateMachineModelFactory; @@ -76,8 +75,8 @@ public class UmlStateMachineModelFactory extends AbstractStateMachineModelFactor } UmlModelParser parser = new UmlModelParser(model, this); DataHolder dataHolder = parser.parseModel(); - ConfigurationData configurationData = new ConfigurationData<>(); - return new DefaultStateMachineModel(configurationData, dataHolder.getStatesData(), dataHolder.getTransitionsData()); + // we don't set configurationData here, so assume null + return new DefaultStateMachineModel(null, dataHolder.getStatesData(), dataHolder.getTransitionsData()); } private Resource resolveResource() {