使用PersistHandlerConfig中的StateMachineFactory

时间:2016-08-12 14:24:09

标签: spring-statemachine

我们正在使用PersistHandlerConfig(持久配方)与SSM进行POC。 它看起来不错,但还出现了一些其他问题。

由于它需要是一个多用户的webapp,每个用户只有一个实例,我们试图遵循St​​ateMachineFactory建议并更改代码,但Persistence的配方看起来还没准备好处理这种情况。

有一种方法可以在PersistHandlerConfig中获取所需的statetemachine(来自其sm id)吗?

@Configuration
public class PersistHandlerConfig {

    @Autowired
    private StateMachineFactory<Tasks, Events> statemachinefactory;

    @Bean
    public Persist persist() {
        return new Persist(persistStateMachineHandler());
    }

    // Here is the problem, since this instance should be the one is in use
    // it should be instantiated with the sm id currently activating the persistence
    @Bean
    public PersistStateMachineHandler persistStateMachineHandler() {
        return new PersistStateMachineHandler(statemachinefactory.getStateMachine());
    }

}

2 个答案:

答案 0 :(得分:1)

在这种情况下,对于webapp和多个用户来说,事情变得有点棘手。 Scope示例显示如何将计算机用作会话范围bean(每个用户一台计算机),Eventservice显示如何在每台请求中使用一台计算机,其中计算机被池化(按请求重用)。

这取决于您拥有多少用户作为ssm是相对较重的组件,因此在这些情况下重用机器是个好主意。此外,您可能会遇到会话作用域bean的问题,因为ssm不可序列化。 Spring将会话范围的bean存储在HttpSession中,即tomcat可能会尝试为群集服务器保留。

答案 1 :(得分:0)

我的多用户环境遇到了同样的问题。食谱概念并没有真正起作用。对所有请求仅使用一个状态机不是线程安全的。我用PersistStateMachineHandler的样式编写了自己的类,但没有使用给定的StateMachine但使用给定的StateMachineFactory。

public class PersistentStateMachineHandler<S, T> {
    private final StateMachineFactory<S, T> stateMachineFactory;
    private final PersistingStateChangeInterceptor<S, T> interceptor = new PersistingStateChangeInterceptor<>();
    private final CompositePersistStateChangeListener<S, T> listeners = new CompositePersistStateChangeListener<>();

    public PersistentStateMachineHandler(StateMachineFactory<S, T> stateMachineFactory) {
        this.stateMachineFactory = stateMachineFactory;
    }

    public void addPersistentStateChangeListener(PersistentStateChangeListener<S, T> persistentStateChangeListener) {
        listeners.register(persistentStateChangeListener);
    }

    public boolean handleEventWithState(Map<Object, Object> variables, S state, T event) {
        StateMachine<S, T> stateMachine = stateMachineFactory.getStateMachine();
        stateMachine.getStateMachineAccessor().doWithAllRegions(function -> function.addStateMachineInterceptor(interceptor));
        if (state == null) {
            state = stateMachine.getInitialState().getId();
        }
        ExtendedState extendedState = new DefaultExtendedState(variables);
        List<StateMachineAccess<S, T>> withAllRegions = stateMachine.getStateMachineAccessor().withAllRegions();
        for (StateMachineAccess<S, T> a : withAllRegions) {
            a.resetStateMachine(new DefaultStateMachineContext<S, T>(state, null, null, extendedState));
        }
        stateMachine.start();
        return stateMachine.sendEvent(event);
    }

    public interface PersistentStateChangeListener<S, T> {
        void persist(State<S, T> state, Message<T> message, Transition<S, T> transition, StateMachine<S, T> stateMachine);
    }

    private class PersistingStateChangeInterceptor<X, Y> extends StateMachineInterceptorAdapter<S, T> {
        @Override
        public void preStateChange(State<S, T> state, Message<T> message, Transition<S, T> transition, StateMachine<S, T> stateMachine) {
            listeners.persist(state, message, transition, stateMachine);
        }
    }

    private class CompositePersistStateChangeListener<X, Y> extends AbstractCompositeListener<PersistentStateChangeListener<S, T>> implements
        PersistentStateChangeListener<S, T> {

        @Override
        public void persist(State<S, T> state, Message<T> message, Transition<S, T> transition, StateMachine<S, T> stateMachine) {
        for (Iterator<PersistentStateChangeListener<S, T>> iterator = getListeners().reverse(); iterator.hasNext();) {
            PersistentStateChangeListener<S, T> listener = iterator.next();
            listener.persist(state, message, transition, stateMachine);
        }
    }
}

}

与春季的食谱实施相比,我的实施还有两个优势。

  1. 此实现可以与枚举一起使用,而不仅仅与字符串
  2. 一起使用
  3. 该实现可以与通过计时器触发状态更改的状态机一起使用,因为状态不在事件的消息中携带,但它使用状态机本身的extendedState。
  4. 当然,对于每个事件,都会创建一个新的状态机,但我发现无法确定现有状态机是否仍处于活动状态(例如,异步任务运行,计时器运行)。如果可以找到确定方法,可以使用状态机池。