如何解决分层状态未保留在数据库中
在持久化数据库之后,我一直在努力从数据库中获取分层状态机的正确状态。父计算机具有两个状态(“ IN_ANALYSIS”,“ OPEN”),而IN_ANALYSIS状态具有子状态(“ IN_PROGRESS”,“ PENDING_SIGNOFF”,“ PENDING_SIGNOFF2”,“已完成”。
@Override
public void configure(StateMachinestateConfigurer<States,Events> states)
throws Exception {
states
.withStates()
.initial(States.IN_ANALYSIS)
.state(States.IN_ANALYSIS)
.state(States.OPEN)
.and()
.withStates()
.parent(States.IN_ANALYSIS)
.initial(States.IN_PROGRESS)
.state(States.IN_PROGRESS)
.state(States.PENDING_SIGNOFF)
.state(States.PENDING_SIGNOFF2)
.state(States.COMPLETED,closedEntryAction(),null);
}
每当我将状态机置于诸如(PENDING_SIGNOFF,PENDING_SIGNOFF2)之类的中间子状态之一时,稍后再从数据库中再次获取它,则子状态会重置为初始状态(IN_PROGRESS ) 。
我正在使用Spring State Machine框架提供的JPA持久性
@Configuration
@Profile("jpa")
public static class JpaPersisterConfig {
@Bean
public StateMachineRuntimePersister<States,Events,String> stateMachineRuntimePersister(
JpaStateMachineRepository jpaStateMachineRepository) {
return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository);
}
}
并使用“ DefaultStateMachineservice”
@Configuration
public static class ServiceConfig {
@Bean
public StateMachineservice<States,Events> stateMachineservice(
StateMachineFactory<States,Events> stateMachineFactory,StateMachineRuntimePersister<States,String> stateMachineRuntimePersister) {
return new DefaultStateMachineservice<States,Events>(stateMachineFactory,stateMachineRuntimePersister);
}
}
解决方法
Spring似乎将分层状态另存为Data Multi Persist示例中描述的状态机上下文。 尝试通过将嵌套状态标记为区域并设置区域ID来configure regions。然后您可以通过ID和区域ID请求状态机
StateMachine<States,Events> sm = defaultStateMachineService.acquireStateMachine(stateMachineId + "#" + regionId)
,
问题出在DefaultStateMachineService类中,在获取SM期间,服务会创建一个新的SM,并且JpaPersistingStateMachineInterceptor会将新SM保持为初始状态,然后DefaultStateMachineService从DB读取状态(该状态已被覆盖)并调用restoreStateMachine方法处于覆盖状态。因此,要解决此问题,您必须创建自己的StateMachineService实现。
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.Lifecycle;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachineException;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.access.StateMachineAccess;
import org.springframework.statemachine.access.StateMachineFunction;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.service.DefaultStateMachineService;
import org.springframework.statemachine.service.StateMachineService;
import org.springframework.util.Assert;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
public class PrimeStateMachineService<S,E> implements StateMachineService<S,E>,DisposableBean {
private final static Log log = LogFactory.getLog(DefaultStateMachineService.class);
private final StateMachineFactory<S,E> stateMachineFactory;
private final Map<String,StateMachine<S,E>> machines = new HashMap<String,E>>();
private StateMachinePersist<S,E,String> stateMachinePersist;
/**
* Instantiates a new default state machine service.
*
* @param stateMachineFactory the state machine factory
*/
public PrimeStateMachineService(StateMachineFactory<S,E> stateMachineFactory) {
this(stateMachineFactory,null);
}
/**
* Instantiates a new default state machine service.
*
* @param stateMachineFactory the state machine factory
* @param stateMachinePersist the state machine persist
*/
public PrimeStateMachineService(StateMachineFactory<S,E> stateMachineFactory,StateMachinePersist<S,String> stateMachinePersist) {
Assert.notNull(stateMachineFactory,"'stateMachineFactory' must be set");
this.stateMachineFactory = stateMachineFactory;
this.stateMachinePersist = stateMachinePersist;
}
@Override
public final void destroy() throws Exception {
doStop();
}
@Override
public StateMachine<S,E> acquireStateMachine(String machineId) {
return acquireStateMachine(machineId,true);
}
@Override
public StateMachine<S,E> acquireStateMachine(String machineId,boolean start) {
log.info("Acquiring machine with id " + machineId);
StateMachine<S,E> stateMachine;
synchronized (machines) {
stateMachine = machines.get(machineId);
if (isNull(stateMachine)) {
if (nonNull(stateMachinePersist)) {
try {
StateMachineContext<S,E> stateMachineContext = stateMachinePersist.read(machineId);
if (isNull(stateMachineContext)) {
stateMachine = stateMachineFactory.getStateMachine(machineId);
log.info("Getting new machine from factory with id " + machineId);
} else {
stateMachine = restoreStateMachine(stateMachineFactory.getStateMachine(machineId),stateMachineContext);
log.info("State machine restored from repository with id " + machineId);
}
} catch (Exception e) {
log.error("Error handling context",e);
throw new StateMachineException("Unable to read context from store",e);
}
}
machines.put(machineId,stateMachine);
}
}
return handleStart(stateMachine,start);
}
@Override
public void releaseStateMachine(String machineId) {
log.info("Releasing machine with id " + machineId);
synchronized (machines) {
StateMachine<S,E> stateMachine = machines.remove(machineId);
if (stateMachine != null) {
log.info("Found machine with id " + machineId);
stateMachine.stop();
}
}
}
@Override
public void releaseStateMachine(String machineId,boolean stop) {
log.info("Releasing machine with id " + machineId);
synchronized (machines) {
StateMachine<S,E> stateMachine = machines.remove(machineId);
if (stateMachine != null) {
log.info("Found machine with id " + machineId);
handleStop(stateMachine,stop);
}
}
}
/**
* Determines if the given machine identifier denotes a known managed state machine.
*
* @param machineId machine identifier
* @return true if machineId denotes a known managed state machine currently in memory
*/
public boolean hasStateMachine(String machineId) {
synchronized (machines) {
return machines.containsKey(machineId);
}
}
/**
* Sets the state machine persist.
*
* @param stateMachinePersist the state machine persist
*/
public void setStateMachinePersist(StateMachinePersist<S,String> stateMachinePersist) {
this.stateMachinePersist = stateMachinePersist;
}
protected void doStop() {
log.info("Entering stop sequence,stopping all managed machines");
synchronized (machines) {
ArrayList<String> machineIds = new ArrayList<>(machines.keySet());
for (String machineId : machineIds) {
releaseStateMachine(machineId,true);
}
}
}
protected StateMachine<S,E> restoreStateMachine(StateMachine<S,E> stateMachine,final StateMachineContext<S,E> stateMachineContext) {
if (stateMachineContext == null) {
return stateMachine;
}
stateMachine.stop();
stateMachine
.getStateMachineAccessor()
.doWithAllRegions(function -> function.resetStateMachine(stateMachineContext));
return stateMachine;
}
protected StateMachine<S,E> handleStart(StateMachine<S,boolean start) {
if (start) {
if (!((Lifecycle) stateMachine).isRunning()) {
PrimeStateMachineService.StartListener<S,E> listener = new PrimeStateMachineService.StartListener<>(stateMachine);
stateMachine.addStateListener(listener);
stateMachine.start();
try {
listener.latch.await();
} catch (InterruptedException e) {
}
}
}
return stateMachine;
}
protected StateMachine<S,E> handleStop(StateMachine<S,boolean stop) {
if (stop) {
if (((Lifecycle) stateMachine).isRunning()) {
PrimeStateMachineService.StopListener<S,E> listener = new PrimeStateMachineService.StopListener<>(stateMachine);
stateMachine.addStateListener(listener);
stateMachine.stop();
try {
listener.latch.await();
} catch (InterruptedException e) {
}
}
}
return stateMachine;
}
private static class StartListener<S,E> extends StateMachineListenerAdapter<S,E> {
final CountDownLatch latch = new CountDownLatch(1);
final StateMachine<S,E> stateMachine;
public StartListener(StateMachine<S,E> stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public void stateMachineStarted(StateMachine<S,E> stateMachine) {
this.stateMachine.removeStateListener(this);
latch.countDown();
}
}
private static class StopListener<S,E> stateMachine;
public StopListener(StateMachine<S,E> stateMachine) {
this.stateMachine = stateMachine;
}
@Override
public void stateMachineStopped(StateMachine<S,E> stateMachine) {
this.stateMachine.removeStateListener(this);
latch.countDown();
}
}
}
,
我在功能测试上下文中遇到了同样的问题。关键是通过服务(不释放它)在内存中创建和播种所需的状态机,并端到端地运行测试。这样服务就没有提交持久化,导致状态回滚到初始状态
public static StateMachine restoreMachine(final StateMachine<States,Events> stateMachine,final States currentState,final ExtendedState extendedState) {
stateMachine.getStateMachineAccessor().doWithAllRegions(t ->
t.resetStateMachine(
new DefaultStateMachineContext<>(currentState,Events.ANY_DESIRED_STATE,null,extendedState,"your-machine-id")));
return stateMachine;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。