I have a simple order processing application and trying to apply spring state machine for handling states of the order. And I wondering how can I handle order's states for the same order during multiple request from rest service.
Order states:
enum OrderEvents {
FULFILL,
PAY,
CANCEL
}
Order events:
enum OrderStates {
SUBMITTED,
PAID,
FULFILLED,
CANCELLED
}
State machine configuration:
@Log
@Configuration
@EnableStateMachineFactory
class SimpleEnumStatemachineConfiguration extends StateMachineConfigurerAdapter<OrderStates, OrderEvents> {
@Override
public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions) throws Exception {
transitions
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.PAID).event(OrderEvents.PAY)
.and()
.withExternal().source(OrderStates.PAID).target(OrderStates.FULFILLED).event(OrderEvents.FULFILL)
.and()
.withExternal().source(OrderStates.SUBMITTED).target(OrderStates.CANCELLED).event(OrderEvents.CANCEL)
.and()
.withExternal().source(OrderStates.PAID).target(OrderStates.CANCELLED).event(OrderEvents.CANCEL);
}
@Override
public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states) throws Exception {
states
.withStates()
.initial(OrderStates.SUBMITTED)
.state(OrderStates.PAID)
.end(OrderStates.FULFILLED)
.end(OrderStates.CANCELLED);
}
@Override
public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config) throws Exception {
config.withConfiguration()
.autoStartup(true)
}
}
In my order service I call
StateMachine<OrderStates, OrderEvents> sm = this.factory.getStateMachine(orderIdKey);
But it seems that every time is new state machine created even for the same orderIdKey. So, how can I get access to state machine created when order was submitted on next state?
You have basically two options:
a) persist the state machine for the given orderId
, using a state machine persister as explained here.
b) create a new state machine for given orderId
(per HTTP request) and rehydrate the SM state based on the state of the order entity
for the given orderId
. SM objects are considered lightweight, so this is a viable approach as well. Below is a code sample:
StateMachine<Status, Event> build(long orderId) {
orderService.getOrder(orderId) //returns Optional
.map(order -> {
StateMachine<Status, Event> sm = stateMachineFactory.getStateMachine(Long.toString(orderId));
sm.stop();
rehydrateState(sm, sm.getExtendedState, order.getStatus());
sm.start();
return sm;
})
.orElseGet(() -> createNewStateMachine(orderId);
}
void rehydrateState(StateMachine<Status, Event> newStateMachine, ExtendedState extendedState, Status orderStatus) {
newStateMachine.getStateMachineAccessor().doWithAllRegions(sma ->
sma.resetStateMachine(new DefaultStateMachineContext<>(orderStatus, null, null, extendedState));
});
}