長時間実行されるプロセスにスプリング ステート マシンを使用するためのアドバイスが必要です。流れをデザインしたい。次の状態があるとしましょう: 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;
}
...
}
特定のプロセスのstateMachineContextの初期化を担当するStateMachinePersist Beanがあります。
@Bean public StateMachinePersist> persist() { return new 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;
}
}
StateMachineContext のシリアル化/逆シリアル化を担当する StateMachineContextConverter があります。この操作には 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();
}
}
}