JPA Persist is an example how state machine concepts can be used with persisting machine in a database. This sample is using embedded H2 database with a H2 Console to ease playing with a database.
This sample uses spring-statemachine-boot
which on default
auto-configures repositories and entity classes needed for JPA.
Thus only @SpringBootApplication
is needed.
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
StateMachineRuntimePersister
is a new interface working on a runtime
level of a 'StateMachine' and its implementation
JpaPersistingStateMachineInterceptor
is meant to be used with a
'JPA'.
@Bean public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister() { return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository); }
'StateMachine' can be configured to use runtime persistence by using
withPersistence
config method.
@Autowired private JpaStateMachineRepository jpaStateMachineRepository; @Override public void configure(StateMachineConfigurationConfigurer<States, Events> config) throws Exception { config .withPersistence() .runtimePersister(stateMachineRuntimePersister()); }
In this sample we also use DefaultStateMachineService
which makes it
easier to work with multiple machines
@Bean public StateMachineService<States, Events> stateMachineService(StateMachineFactory<States, Events> stateMachineFactory, StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister) { return new DefaultStateMachineService<States, Events>(stateMachineFactory, stateMachineRuntimePersister); }
A logic using a StateMachineService
in this sample is show below.
private synchronized StateMachine<States, Events> getStateMachine(String machineId) throws Exception { listener.resetMessages(); if (currentStateMachine == null) { currentStateMachine = stateMachineService.acquireStateMachine(machineId); currentStateMachine.addStateListener(listener); currentStateMachine.start(); } else if (!ObjectUtils.nullSafeEquals(currentStateMachine.getId(), machineId)) { stateMachineService.releaseStateMachine(currentStateMachine.getId()); currentStateMachine.stop(); currentStateMachine = stateMachineService.acquireStateMachine(machineId); currentStateMachine.addStateListener(listener); currentStateMachine.start(); } return currentStateMachine; }
Let’s get into actual demo. Run the boot based sample application:
# java -jar spring-statemachine-samples-datajpapersist-1.2.8.RELEASE.jar
Accessing application via http://localhost:8080 brings up a new constructed machine with every request and you can choose to send events to a machine. Possible events and machine configuration are updated from a database with every request.
Machines in this sample have a simple configuration with states 'S1' to 'S6' and events 'E1' to 'E6' transitioning machine between those states. Two machine identifiers 'datajpapersist1' and 'datajpapersist2' can be used to request particular machine.
Sample defaults to using machine 'datajpapersist1' and goes to its initial state 'S1'.
If events 'E1' and 'E2' are sent into machine 'datajpapersist1' its state is persisted as 'S3'.
If requesting machine 'datajpapersist1' by not sending any events, machine is restored back to its persisted state 'S3'.