Data Persist is an example how state machine concepts can be used with persisting machine in an external repository. This sample is using embedded H2 database with a H2 Console to ease playing with a database. Optionally it’s also possible to enable Redis or MongoDB.
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.
@Configuration @Profile("jpa") public static class JpaPersisterConfig { @Bean public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister( JpaStateMachineRepository jpaStateMachineRepository) { return new JpaPersistingStateMachineInterceptor<>(jpaStateMachineRepository); } }
Same configuration optionally enabled with mongo profile.
@Configuration @Profile("mongo") public static class MongoPersisterConfig { @Bean public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister( MongoDbStateMachineRepository jpaStateMachineRepository) { return new MongoDbPersistingStateMachineInterceptor<>(jpaStateMachineRepository); } }
Same configuration optionally enabled with redis profile.
@Configuration @Profile("redis") public static class RedisPersisterConfig { @Bean public StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister( RedisStateMachineRepository jpaStateMachineRepository) { return new RedisPersistingStateMachineInterceptor<>(jpaStateMachineRepository); } }
StateMachine
can be configured to use runtime persistence by using
withPersistence
config method.
@Autowired private StateMachineRuntimePersister<States, Events, String> stateMachineRuntimePersister; @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-datapersist-1.2.9.RELEASE.jar
Note | |
---|---|
Profile jpa is enabled on default in application.yml. If you want to try other backends, enable mongo or redis profile. |
# java -jar spring-statemachine-samples-datapersist-1.2.9.RELEASE.jar --spring.profiles.active=jpa # java -jar spring-statemachine-samples-datapersist-1.2.9.RELEASE.jar --spring.profiles.active=mongo # java -jar spring-statemachine-samples-datapersist-1.2.9.RELEASE.jar --spring.profiles.active=redis
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'.