Spring State Machine - управляйте длительными процессами

Мне нужен совет по использованию пружинного автомата для длительных процессов. Я хочу создать некоторый поток. Допустим, у меня есть следующие состояния: Start->step1->step2->step3->finish. У меня есть контроллер, который может отправлять события на конечный автомат для управления переходами между состояниями. У меня есть StateMachinePersister. У меня есть конвертер из StateMachineContext в byte[] и обратно. Звучит хорошо для моей деловой цели. Так что все должно работать нормально.

Но у меня есть проблема? Я не могу понять, как управлять случаями, когда я решу изменить поток. Я имею в виду, что если у меня есть производственная среда, в которой некоторые процессы сохраняются в состоянии "step2". Но я вынужден изменить поток. скажем, я хочу добавить шаг или удалить шаг в потоке. Думаю, у меня будут проблемы при десериализации конечного автомата.

Так что вопрос: может быть, пружинный автомат мне не подходит, или есть какие-то рецепты, как мне справиться с такими случаями?


У меня есть объект, которым я хочу управлять состояниями, переходами и т. Д.

@Entity
@Access(AccessType.FIELD)
@Table(name = "processes", indexes = @Index(columnList = "currentState"))
public class Process extends AbstractPersistable<Long> implements ContextEntity<ProcessState, ProcessEvent, Long> { // NOSONAR

    private static final long serialVersionUID = 8848887579564649636L;

    @JsonIgnore
    StateMachineContext<ProcessState, ProcessEvent> stateMachineContext; // NOSONAR

    @Enumerated(EnumType.STRING)
    ProcessState currentState;


    @Override
    public void setStateMachineContext(StateMachineContext<ProcessState, ProcessEvent> stateMachineContext) {
        if (stateMachineContext == null) {
            throw new IllegalStateException("stateMachineContext can't be null");
        }
        this.currentState = stateMachineContext.getState();
        this.stateMachineContext = stateMachineContext;
    }


    @Override
    public StateMachineContext<ProcessState, ProcessEvent> getStateMachineContext() {
        return stateMachineContext;
    }

...
}

У меня есть компонент StateMachinePersist, который отвечает за инициализацию stateMachineContext для конкретного процесса.

@Bean public StateMachinePersist> persist () {вернуть новый StateMachinePersist> () {

    @Override
    public StateMachineContext<ProcessState, ProcessEvent> read(
            ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception {
        return process.getStateMachineContext();
    }

    @Override
    public void write(StateMachineContext<ProcessState, ProcessEvent> context,
            ContextEntity<ProcessState, ProcessEvent, Serializable> process) throws Exception {
        process.setStateMachineContext(context);
    }
};

}

У меня есть StateMachineAdapter, который отвечает за сохранение и восстановление конечного автомата

public class DefaultStateMachineAdapter<S, E, T> {

    final StateMachineFactory<S, E> stateMachineFactory;

    final StateMachinePersister<S, E, T> persister;

    public DefaultStateMachineAdapter(StateMachineFactory<S, E> stateMachineFactory, StateMachinePersister<S, E, T> persister) {
        this.stateMachineFactory = stateMachineFactory;
        this.persister = persister;
    }

    public StateMachine<S, E> restore(T contextObject) throws Exception {
        StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine();
        return persister.restore(stateMachine, contextObject);
    }

    public void persist(StateMachine<S, E> stateMachine, T order) throws Exception {
        persister.persist(stateMachine, order);
    }

    public StateMachine<S, E> create() {
        StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine();
        stateMachine.start();
        return stateMachine;
    }

}

У меня есть StateMachineContextConverter, который отвечает за сериализацию / десериализацию StateMachineContext. Я использовал Kryo для этой операции.

public class StateMachineContextConverter implements AttributeConverter<StateMachineContext, byte[]> {

    @Override
    public byte[] convertToDatabaseColumn(StateMachineContext attribute) {
        return serialize(attribute);
    }

    @Override
    public StateMachineContext convertToEntityAttribute(byte[] dbData) {
        return deserialize(dbData);
    }


}

У меня есть контроллер, который отвечает за переключение состояний

public class ProcessEventController {


    final DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter;

    public ProcessEventController(DefaultStateMachineAdapter<ProcessState, ProcessEvent, ContextEntity<ProcessState, ProcessEvent, ? extends Serializable>> processStateMachineAdapter) {
        this.processStateMachineAdapter = processStateMachineAdapter;
    }

    @RequestMapping(path = "/processes/{id}/{event}", method = RequestMethod.POST)
    @Transactional
    public HttpEntity<Void> receiveEvent(@PathVariable("id") Process process, @PathVariable("event") ProcessEvent event) throws Exception {
        StateMachine<ProcessState, ProcessEvent> stateMachine = processStateMachineAdapter.restore(process);
        if (stateMachine.sendEvent(event)) {
            processStateMachineAdapter.persist(stateMachine, process);
            return ResponseEntity.accepted().build();
        } else {
            return ResponseEntity.unprocessableEntity().build();
        }
    }
}

1 ответ

Очевидно, что вы не можете изменить существующую работающую машину, но, поскольку вы уже работаете с постоянством, я предполагаю, что вы по крайней мере останавливаете машины.

Посмотрите statemachine-examples-datajpa, использующую apis существующей машины для хранения конфигурации в БД. У нас есть StateMachineModelFactory что в значительной степени позволяет хранить ваши вещи где угодно, если какая-либо встроенная реализация вам не подходит. Этот образец будет создавать новые экземпляры компьютера каждый раз, когда отправляются новые события. то есть вы можете перейти к БД с помощью встроенного редактора и добавить новые переходы без перезапуска основного процесса Java.

Это дает вам некоторую гибкость, но если вам нужно изменить ваш работающий код, то все станет невозможным.

Другие вопросы по тегам