Refactor region handling and persistence
- Make kryo in AbstractKryoStateMachineSerialisationService aware of same classloader most likely use in an app. This takes away some of those weird kryo errors you see with a web apps. - Add context references concept to StateMachineContext which can be used to store reference id and then individual running machines with regions can independently store their states. Whole machine state can then get restored more accurately. - Add new `region(String id)` to StateConfigurer which can be used to set region id. This is equivalent as setting region id with json based machine structure where you need to define region id's with orthogonal regions are in use. - Add new datajpamultipersist sample showing running regions and how those are persisted to a database. - Fixes #617 - Fixes #605 - Fixes #615
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -42,6 +42,13 @@ public interface StateMachineContext<S, E> {
|
||||
*/
|
||||
List<StateMachineContext<S, E>> getChilds();
|
||||
|
||||
/**
|
||||
* Gets the child context references if any.
|
||||
*
|
||||
* @return the child context references
|
||||
*/
|
||||
List<String> getChildReferences();
|
||||
|
||||
/**
|
||||
* Gets the state.
|
||||
*
|
||||
|
||||
@@ -80,6 +80,7 @@ import org.springframework.statemachine.state.StateMachineState;
|
||||
import org.springframework.statemachine.support.DefaultExtendedState;
|
||||
import org.springframework.statemachine.support.LifecycleObjectSupport;
|
||||
import org.springframework.statemachine.support.StateMachineInterceptor;
|
||||
import org.springframework.statemachine.support.StateMachineInterceptorAdapter;
|
||||
import org.springframework.statemachine.support.tree.Tree;
|
||||
import org.springframework.statemachine.support.tree.Tree.Node;
|
||||
import org.springframework.statemachine.support.tree.TreeTraverser;
|
||||
@@ -219,9 +220,14 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
|
||||
if (initialCount > 1) {
|
||||
for (Collection<StateData<S, E>> regionStateDatas : regionsStateDatas) {
|
||||
// try to build reqion id's
|
||||
Object rId = regionStateDatas.iterator().next().getRegion();
|
||||
String mId = machineId != null ? machineId : stateMachineModel.getConfigurationData().getMachineId();
|
||||
mId = mId + "#" + (rId != null ? rId.toString() : "");
|
||||
|
||||
machine = buildMachine(machineMap, stateMap, holderList, regionStateDatas, transitionsData, resolveBeanFactory(stateMachineModel),
|
||||
contextEvents, defaultExtendedState, stateMachineModel.getTransitionsData(), resolveTaskExecutor(stateMachineModel),
|
||||
resolveTaskScheduler(stateMachineModel), machineId, null, stateMachineModel);
|
||||
resolveTaskScheduler(stateMachineModel), mId, null, stateMachineModel);
|
||||
regionStack.push(new MachineStackItem<S, E>(machine));
|
||||
machines.add(machine);
|
||||
}
|
||||
@@ -359,10 +365,12 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
List<StateMachineInterceptor<S,E>> interceptors = stateMachineModel.getConfigurationData().getStateMachineInterceptors();
|
||||
if (interceptors != null) {
|
||||
for (final StateMachineInterceptor<S, E> interceptor : interceptors) {
|
||||
machine.getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<S,E>>() {
|
||||
// add persisting interceptor hooks to all regions
|
||||
RegionPersistingInterceptorAdapter<S, E> adapter = new RegionPersistingInterceptorAdapter<>(interceptor, machine);
|
||||
machine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<S,E>>() {
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.addStateMachineInterceptor(interceptor);
|
||||
function.addStateMachineInterceptor(adapter);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -377,6 +385,42 @@ public abstract class AbstractStateMachineFactory<S, E> extends LifecycleObjectS
|
||||
return delegateAutoStartup(machine);
|
||||
}
|
||||
|
||||
private static class RegionPersistingInterceptorAdapter<S, E> extends StateMachineInterceptorAdapter<S, E> {
|
||||
|
||||
private final StateMachineInterceptor<S, E> interceptor;
|
||||
private final StateMachine<S, E> rootStateMachine;
|
||||
|
||||
public RegionPersistingInterceptorAdapter(StateMachineInterceptor<S, E> interceptor, StateMachine<S, E> rootStateMachine) {
|
||||
this.interceptor = interceptor;
|
||||
this.rootStateMachine = rootStateMachine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
interceptor.preStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
interceptor.preStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
interceptor.postStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
interceptor.postStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Instructs this factory to handle auto-start flag manually
|
||||
* by calling lifecycle start method.
|
||||
|
||||
@@ -47,7 +47,7 @@ public class DefaultStateConfigurer<S, E>
|
||||
implements StateConfigurer<S, E> {
|
||||
|
||||
private Object parent;
|
||||
private final Object region = UUID.randomUUID().toString();
|
||||
private Object region = UUID.randomUUID().toString();
|
||||
private final Map<S, StateData<S, E>> incomplete = new HashMap<S, StateData<S, E>>();
|
||||
private S initialState;
|
||||
private Action<S, E> initialAction;
|
||||
@@ -123,6 +123,12 @@ public class DefaultStateConfigurer<S, E>
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> region(String id) {
|
||||
this.region = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateConfigurer<S, E> end(S end) {
|
||||
this.ends.add(end);
|
||||
|
||||
@@ -63,6 +63,14 @@ public interface StateConfigurer<S, E> extends
|
||||
*/
|
||||
StateConfigurer<S, E> parent(S state);
|
||||
|
||||
/**
|
||||
* Specify a region for these states configured by this configurer instance.
|
||||
*
|
||||
* @param id the region id
|
||||
* @return configurer for chaining
|
||||
*/
|
||||
StateConfigurer<S, E> region(String id);
|
||||
|
||||
/**
|
||||
* Specify a state {@code S}.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -211,11 +211,22 @@ public class DistributedStateMachine<S, E> extends LifecycleObjectSupport implem
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
preStateChange(state, message, transition, stateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateContext<S, E> preTransition(StateContext<S, E> stateContext) {
|
||||
return stateContext;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
@@ -18,8 +18,11 @@ package org.springframework.statemachine.persist;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.statemachine.ExtendedState;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
@@ -56,36 +59,61 @@ import org.springframework.util.Assert;
|
||||
public abstract class AbstractPersistingStateMachineInterceptor<S, E, T> extends StateMachineInterceptorAdapter<S, E>
|
||||
implements StateMachinePersist<S, E, T> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AbstractPersistingStateMachineInterceptor.class);
|
||||
private Function<StateMachine<S, E>, Map<Object, Object>> extendedStateVariablesFunction = new AllVariablesFunction<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("preStateChange with stateMachine " + stateMachine);
|
||||
log.debug("preStateChange with root stateMachine " + rootStateMachine);
|
||||
log.debug("preStateChange with state " + state);
|
||||
}
|
||||
// try to persist context and in case of failure, interceptor
|
||||
// call chain aborts transition
|
||||
// TODO: should probably come up with a policy vs. not force feeding this functionality
|
||||
try {
|
||||
write(buildStateMachineContext(stateMachine, state), (T)stateMachine.getId());
|
||||
write(buildStateMachineContext(stateMachine, rootStateMachine, state), (T)stateMachine.getId());
|
||||
} catch (Exception e) {
|
||||
throw new StateMachineException("Unable to persist stateMachineContext", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
preStateChange(state, message, transition, stateMachine, stateMachine);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition, StateMachine<S, E> stateMachine) {
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("postStateChange with stateMachine " + stateMachine);
|
||||
log.debug("postStateChange with root stateMachine " + rootStateMachine);
|
||||
log.debug("postStateChange with state " + state);
|
||||
}
|
||||
// initial transitions are never intercepted as those cannot fail or get aborted.
|
||||
// for now, handle persistence in post state change
|
||||
// TODO: consider intercept initial transition, but not aborting if error is thrown?
|
||||
if (state != null && transition != null && transition.getKind() == TransitionKind.INITIAL) {
|
||||
try {
|
||||
write(buildStateMachineContext(stateMachine, state), (T)stateMachine.getId());
|
||||
write(buildStateMachineContext(stateMachine, rootStateMachine, state), (T)stateMachine.getId());
|
||||
} catch (Exception e) {
|
||||
throw new StateMachineException("Unable to persist stateMachineContext", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
postStateChange(state, message, transition, stateMachine, stateMachine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write {@link StateMachineContext} into persistent store.
|
||||
*
|
||||
@@ -119,22 +147,27 @@ public abstract class AbstractPersistingStateMachineInterceptor<S, E, T> extends
|
||||
* Builds the state machine context.
|
||||
*
|
||||
* @param stateMachine the state machine
|
||||
* @param rootStateMachine the root state machine
|
||||
* @param state the state
|
||||
* @return the state machine context
|
||||
*/
|
||||
protected StateMachineContext<S, E> buildStateMachineContext(StateMachine<S, E> stateMachine, State<S, E> state) {
|
||||
protected StateMachineContext<S, E> buildStateMachineContext(StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine, State<S, E> state) {
|
||||
ExtendedState extendedState = new DefaultExtendedState();
|
||||
extendedState.getVariables().putAll(extendedStateVariablesFunction.apply(stateMachine));
|
||||
|
||||
ArrayList<StateMachineContext<S, E>> childs = new ArrayList<StateMachineContext<S, E>>();
|
||||
List<StateMachineContext<S, E>> childs = new ArrayList<StateMachineContext<S, E>>();
|
||||
List<String> childRefs = new ArrayList<>();
|
||||
S id = null;
|
||||
if (state.isSubmachineState()) {
|
||||
id = getDeepState(state);
|
||||
} else if (state.isOrthogonal()) {
|
||||
Collection<Region<S, E>> regions = ((AbstractState<S, E>)state).getRegions();
|
||||
for (Region<S, E> r : regions) {
|
||||
StateMachine<S, E> rsm = (StateMachine<S, E>) r;
|
||||
childs.add(buildStateMachineContext(rsm, state));
|
||||
if (stateMachine.getState().isOrthogonal()) {
|
||||
Collection<Region<S, E>> regions = ((AbstractState<S, E>)state).getRegions();
|
||||
for (Region<S, E> r : regions) {
|
||||
// realistically we can only add refs because reqions are independent
|
||||
// and when restoring, those child contexts need to get dehydrated
|
||||
childRefs.add(r.getId());
|
||||
}
|
||||
}
|
||||
id = state.getId();
|
||||
} else {
|
||||
@@ -160,7 +193,7 @@ public abstract class AbstractPersistingStateMachineInterceptor<S, E, T> extends
|
||||
}
|
||||
}
|
||||
}
|
||||
return new DefaultStateMachineContext<S, E>(childs, id, null, null, extendedState, historyStates, stateMachine.getId());
|
||||
return new DefaultStateMachineContext<S, E>(childRefs, childs, id, null, null, extendedState, historyStates, stateMachine.getId());
|
||||
}
|
||||
|
||||
private S getDeepState(State<S, E> state) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017 the original author or authors.
|
||||
* Copyright 2017-2019 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.
|
||||
@@ -152,7 +152,8 @@ public class DefaultStateMachineService<S, E> implements StateMachineService<S,
|
||||
return stateMachine;
|
||||
}
|
||||
stateMachine.stop();
|
||||
stateMachine.getStateMachineAccessor().doWithAllRegions(new StateMachineFunction<StateMachineAccess<S, E>>() {
|
||||
// only go via top region
|
||||
stateMachine.getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<S, E>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -67,6 +67,7 @@ import org.springframework.statemachine.transition.TransitionKind;
|
||||
import org.springframework.statemachine.trigger.DefaultTriggerContext;
|
||||
import org.springframework.statemachine.trigger.Trigger;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -697,19 +698,23 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
}
|
||||
stateSet = true;
|
||||
break;
|
||||
} else if (!stateMachineContext.getChilds().isEmpty()) {
|
||||
} else if (stateMachineContext.getChilds() != null && !stateMachineContext.getChilds().isEmpty()) {
|
||||
// we're here because root machine only have regions
|
||||
if (s.isOrthogonal()) {
|
||||
Collection<Region<S, E>> regions = ((AbstractState<S, E>)s).getRegions();
|
||||
|
||||
for (Region<S, E> region : regions) {
|
||||
for (final StateMachineContext<S, E> child : stateMachineContext.getChilds()) {
|
||||
((StateMachine<S, E>)region).getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<S,E>>() {
|
||||
// only call if reqion id matches with context id
|
||||
if (ObjectUtils.nullSafeEquals(region.getId(), child.getId())) {
|
||||
((StateMachine<S, E>)region).getStateMachineAccessor().doWithRegion(new StateMachineFunction<StateMachineAccess<S,E>>() {
|
||||
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.resetStateMachine(child);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void apply(StateMachineAccess<S, E> function) {
|
||||
function.resetStateMachine(child);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -872,7 +877,7 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
private boolean callPreStateChangeInterceptors(State<S,E> state, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
try {
|
||||
getStateMachineInterceptors().preStateChange(state, message, transition, stateMachine);
|
||||
getStateMachineInterceptors().preStateChange(state, message, transition, this, stateMachine);
|
||||
} catch (Exception e) {
|
||||
log.info("Interceptors threw exception, skipping state change", e);
|
||||
return false;
|
||||
@@ -882,8 +887,9 @@ public abstract class AbstractStateMachine<S, E> extends StateMachineObjectSuppo
|
||||
|
||||
private void callPostStateChangeInterceptors(State<S,E> state, Message<E> message, Transition<S,E> transition, StateMachine<S, E> stateMachine) {
|
||||
try {
|
||||
getStateMachineInterceptors().postStateChange(state, message, transition, stateMachine);
|
||||
getStateMachineInterceptors().postStateChange(state, message, transition, this, stateMachine);
|
||||
} catch (Exception e) {
|
||||
log.warn("Interceptors threw exception in post state change", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2016 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -35,6 +35,7 @@ public class DefaultStateMachineContext<S, E> implements StateMachineContext<S,
|
||||
|
||||
private final String id;
|
||||
private final List<StateMachineContext<S, E>> childs;
|
||||
private final List<String> childRefs;
|
||||
private final S state;
|
||||
private final Map<S, S> historyStates;
|
||||
private final E event;
|
||||
@@ -125,6 +126,31 @@ public class DefaultStateMachineContext<S, E> implements StateMachineContext<S,
|
||||
public DefaultStateMachineContext(List<StateMachineContext<S, E>> childs, S state, E event,
|
||||
Map<String, Object> eventHeaders, ExtendedState extendedState, Map<S, S> historyStates, String id) {
|
||||
this.childs = childs;
|
||||
this.childRefs = null;
|
||||
this.state = state;
|
||||
this.event = event;
|
||||
this.eventHeaders = eventHeaders;
|
||||
this.extendedState = extendedState;
|
||||
this.historyStates = historyStates != null ? historyStates : new HashMap<S, S>();
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new default state machine context.
|
||||
*
|
||||
* @param childRefs the child state machine context refs
|
||||
* @param childs the child state machine contexts
|
||||
* @param state the state
|
||||
* @param event the event
|
||||
* @param eventHeaders the event headers
|
||||
* @param extendedState the extended state
|
||||
* @param historyStates the history state mappings
|
||||
* @param id the machine id
|
||||
*/
|
||||
public DefaultStateMachineContext(List<String> childRefs, List<StateMachineContext<S, E>> childs, S state, E event,
|
||||
Map<String, Object> eventHeaders, ExtendedState extendedState, Map<S, S> historyStates, String id) {
|
||||
this.childs = childs;
|
||||
this.childRefs = childRefs;
|
||||
this.state = state;
|
||||
this.event = event;
|
||||
this.eventHeaders = eventHeaders;
|
||||
@@ -143,6 +169,11 @@ public class DefaultStateMachineContext<S, E> implements StateMachineContext<S,
|
||||
return childs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getChildReferences() {
|
||||
return childRefs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public S getState() {
|
||||
return state;
|
||||
@@ -170,7 +201,8 @@ public class DefaultStateMachineContext<S, E> implements StateMachineContext<S,
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DefaultStateMachineContext [id=" + id + ", childs=" + childs + ", state=" + state + ", historyStates=" + historyStates
|
||||
+ ", event=" + event + ", eventHeaders=" + eventHeaders + ", extendedState=" + extendedState + "]";
|
||||
return "DefaultStateMachineContext [id=" + id + ", childs=" + childs + ", childRefs=" + childRefs + ", state="
|
||||
+ state + ", historyStates=" + historyStates + ", event=" + event + ", eventHeaders=" + eventHeaders
|
||||
+ ", extendedState=" + extendedState + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -54,6 +54,19 @@ public interface StateMachineInterceptor<S, E> {
|
||||
void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine);
|
||||
|
||||
/**
|
||||
* Called prior of a state change. Throwing an exception
|
||||
* from this method will stop a state change logic.
|
||||
*
|
||||
* @param state the state
|
||||
* @param message the message
|
||||
* @param transition the transition
|
||||
* @param stateMachine the state machine
|
||||
* @param rootStateMachine the root state machine
|
||||
*/
|
||||
void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine);
|
||||
|
||||
/**
|
||||
* Called after a state change.
|
||||
*
|
||||
@@ -65,6 +78,18 @@ public interface StateMachineInterceptor<S, E> {
|
||||
void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine);
|
||||
|
||||
/**
|
||||
* Called after a state change.
|
||||
*
|
||||
* @param state the state
|
||||
* @param message the message
|
||||
* @param transition the transition
|
||||
* @param stateMachine the state machine
|
||||
* @param rootStateMachine the root state machine
|
||||
*/
|
||||
void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine);
|
||||
|
||||
/**
|
||||
* Called prior of a start of a transition. Returning
|
||||
* {@code null} from this method will break the transtion
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -41,11 +41,21 @@ public class StateMachineInterceptorAdapter<S, E> implements StateMachineInterce
|
||||
StateMachine<S, E> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateContext<S, E> preTransition(StateContext<S, E> stateContext) {
|
||||
return stateContext;
|
||||
@@ -60,5 +70,4 @@ public class StateMachineInterceptorAdapter<S, E> implements StateMachineInterce
|
||||
public Exception stateMachineError(StateMachine<S, E> stateMachine, Exception exception) {
|
||||
return exception;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -102,6 +102,13 @@ public class StateMachineInterceptorList<S, E> {
|
||||
}
|
||||
}
|
||||
|
||||
public void preStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
for (StateMachineInterceptor<S, E> interceptor : interceptors) {
|
||||
interceptor.preStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post state change.
|
||||
*
|
||||
@@ -117,6 +124,13 @@ public class StateMachineInterceptorList<S, E> {
|
||||
}
|
||||
}
|
||||
|
||||
public void postStateChange(State<S, E> state, Message<E> message, Transition<S, E> transition,
|
||||
StateMachine<S, E> stateMachine, StateMachine<S, E> rootStateMachine) {
|
||||
for (StateMachineInterceptor<S, E> interceptor : interceptors) {
|
||||
interceptor.postStateChange(state, message, transition, stateMachine, rootStateMachine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre transition.
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2018 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -1294,6 +1294,12 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<String, String> state, Message<String> message,
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine,
|
||||
StateMachine<String, String> rootStateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateContext<String, String> postTransition(StateContext<String, String> stateContext) {
|
||||
return stateContext;
|
||||
@@ -1304,6 +1310,12 @@ public class DocsConfigurationSampleTests extends AbstractStateMachineTests {
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<String, String> state, Message<String> message,
|
||||
Transition<String, String> transition, StateMachine<String, String> stateMachine,
|
||||
StateMachine<String, String> rootStateMachine) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exception stateMachineError(StateMachine<String, String> stateMachine,
|
||||
Exception exception) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
* Copyright 2016-2019 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.
|
||||
@@ -323,6 +323,39 @@ public class StateMachinePersistTests extends AbstractStateMachineTests {
|
||||
assertThat(stateMachine.isComplete(), is(true));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testRegions2() throws Exception {
|
||||
context.register(Config9.class);
|
||||
context.refresh();
|
||||
StateMachine<String, String> stateMachine = context.getBean(StateMachineSystemConstants.DEFAULT_ID_STATEMACHINE, StateMachine.class);
|
||||
stateMachine.start();
|
||||
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S11", "S21", "S31"));
|
||||
|
||||
stateMachine.sendEvent("E1");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S12", "S21", "S31"));
|
||||
stateMachine.sendEvent("E2");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S12", "S22", "S31"));
|
||||
stateMachine.sendEvent("E3");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S12", "S22", "S32"));
|
||||
|
||||
InMemoryStateMachinePersist1 stateMachinePersist = new InMemoryStateMachinePersist1();
|
||||
StateMachinePersister<String, String, String> persister = new DefaultStateMachinePersister<>(stateMachinePersist);
|
||||
|
||||
persister.persist(stateMachine, "xxx");
|
||||
stateMachine = persister.restore(stateMachine, "xxx");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S12", "S22", "S32"));
|
||||
|
||||
stateMachine.sendEvent("E4");
|
||||
stateMachine.sendEvent("E5");
|
||||
stateMachine.sendEvent("E6");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S13", "S23", "S33"));
|
||||
|
||||
stateMachine = persister.restore(stateMachine, "xxx");
|
||||
assertThat(stateMachine.getState().getIds(), containsInAnyOrder("S12", "S22", "S32"));
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config1 extends StateMachineConfigurerAdapter<String, String> {
|
||||
@@ -685,6 +718,67 @@ public class StateMachinePersistTests extends AbstractStateMachineTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine
|
||||
static class Config9 extends StateMachineConfigurerAdapter<String, String> {
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial("S11")
|
||||
.state("S11")
|
||||
.state("S12")
|
||||
.state("S13")
|
||||
.and()
|
||||
.withStates()
|
||||
.initial("S21")
|
||||
.state("S21")
|
||||
.state("S22")
|
||||
.state("S23")
|
||||
.and()
|
||||
.withStates()
|
||||
.initial("S31")
|
||||
.state("S31")
|
||||
.state("S32")
|
||||
.state("S33");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source("S11")
|
||||
.target("S12")
|
||||
.event("E1")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S21")
|
||||
.target("S22")
|
||||
.event("E2")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S31")
|
||||
.target("S32")
|
||||
.event("E3")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S12")
|
||||
.target("S13")
|
||||
.event("E4")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S22")
|
||||
.target("S23")
|
||||
.event("E5")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("S32")
|
||||
.target("S33")
|
||||
.event("E6");
|
||||
}
|
||||
}
|
||||
|
||||
static class InMemoryStateMachinePersist1 implements StateMachinePersist<String, String, String> {
|
||||
|
||||
private final HashMap<String, StateMachineContext<String, String>> contexts = new HashMap<>();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-2017 the original author or authors.
|
||||
* Copyright 2015-2019 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.
|
||||
@@ -619,6 +619,13 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preStateChange(State<States, Events> state, Message<Events> message,
|
||||
Transition<States, Events> transition, StateMachine<States, Events> stateMachine,
|
||||
StateMachine<States, Events> rootStateMachine) {
|
||||
preStateChange(state, message, transition, stateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<States, Events> state, Message<Events> message,
|
||||
Transition<States, Events> transition, StateMachine<States, Events> stateMachine) {
|
||||
@@ -627,6 +634,13 @@ public class StateChangeInterceptorTests extends AbstractStateMachineTests {
|
||||
postStateChangeLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postStateChange(State<States, Events> state, Message<Events> message,
|
||||
Transition<States, Events> transition, StateMachine<States, Events> stateMachine,
|
||||
StateMachine<States, Events> rootStateMachine) {
|
||||
postStateChange(state, message, transition, stateMachine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateContext<States, Events> preTransition(StateContext<States, Events> stateContext) {
|
||||
return stateContext;
|
||||
|
||||
Reference in New Issue
Block a user