It is also possible to keep machine configuration in an external storage where it will be loaded on demand instead of creating a static configuration either using JavaConfig or UML based config. This integration works via Spring Data Repository abstraction.
We have created special StateMachineModelFactory
implementation
called RepositoryStateMachineModelFactory
which is able to use base
repository interfaces StateRepository
, TransitionRepository
,
ActionRepository
and GuardRepository
accompanied with base entity
interfaces RepositoryState
, RepositoryTransition
,
RepositoryAction
and RepositoryGuard
respectively.
Due to way how Entities and Repositories work in a Spring Data,
from a user perspective read access can be fully abstracted as it is
done in RepositoryStateMachineModelFactory
as there is no need to
know what is a real mapped Entity class Repository is working
with. Writing into a Repository is always dependant of using a real
Repository specific Entity class. From machine configuration point
of view we don’t need to know these, meaning we don’t need to know
actual implementation whether that is JPA, Redis or anything else
what Spring Data supports. Using a real Repository related
Entity class comes into play when you manually try to write new
states or transitions into a backed repository.
Actual out of a box implementations are documented in below sections where images below are uml equivalent statecharts of a repository configs.
Actual Repository implementations for a JPA are
JpaStateRepository
, JpaTransitionRepository
, JpaActionRepository
and JpaGuardRepository
which are backed by
Entity classes JpaRepositoryState
, JpaRepositoryTransition
,
JpaRepositoryAction
and JpaRepositoryGuard
respectively.
Generic way to update states and transition manually for jpa is shown below. This is equivalent to machine shown in Figure 31.1, “SimpleMachine”.
@Autowired StateRepository<JpaRepositoryState> stateRepository; @Autowired TransitionRepository<JpaRepositoryTransition> transitionRepository; void addConfig() { JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true); JpaRepositoryState stateS2 = new JpaRepositoryState("S2"); JpaRepositoryState stateS3 = new JpaRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "E1"); JpaRepositoryTransition transitionS2ToS3 = new JpaRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); }
This is equivalent to machine shown in Figure 31.2, “SimpleSubMachine”.
@Autowired StateRepository<JpaRepositoryState> stateRepository; @Autowired TransitionRepository<JpaRepositoryTransition> transitionRepository; void addConfig() { JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true); JpaRepositoryState stateS2 = new JpaRepositoryState("S2"); JpaRepositoryState stateS3 = new JpaRepositoryState("S3"); JpaRepositoryState stateS21 = new JpaRepositoryState("S21", true); stateS21.setParentState(stateS2); JpaRepositoryState stateS22 = new JpaRepositoryState("S22"); stateS22.setParentState(stateS2); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); stateRepository.save(stateS21); stateRepository.save(stateS22); JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "E1"); JpaRepositoryTransition transitionS2ToS3 = new JpaRepositoryTransition(stateS21, stateS22, "E2"); JpaRepositoryTransition transitionS21ToS22 = new JpaRepositoryTransition(stateS2, stateS3, "E3"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); transitionRepository.save(transitionS21ToS22); }
This is equivalent to machine shown in Figure 31.3, “ShowcaseMachine”.
First you access all repositories.
@Autowired StateRepository<JpaRepositoryState> stateRepository; @Autowired TransitionRepository<JpaRepositoryTransition> transitionRepository; @Autowired ActionRepository<JpaRepositoryAction> actionRepository; @Autowired GuardRepository<JpaRepositoryGuard> guardRepository;
Create actions and guards.
JpaRepositoryGuard foo0Guard = new JpaRepositoryGuard(); foo0Guard.setName("foo0Guard"); JpaRepositoryGuard foo1Guard = new JpaRepositoryGuard(); foo1Guard.setName("foo1Guard"); JpaRepositoryAction fooAction = new JpaRepositoryAction(); fooAction.setName("fooAction"); guardRepository.save(foo0Guard); guardRepository.save(foo1Guard); actionRepository.save(fooAction);
Create states.
JpaRepositoryState stateS0 = new JpaRepositoryState("S0", true); stateS0.setInitialAction(fooAction); JpaRepositoryState stateS1 = new JpaRepositoryState("S1", true); stateS1.setParentState(stateS0); JpaRepositoryState stateS11 = new JpaRepositoryState("S11", true); stateS11.setParentState(stateS1); JpaRepositoryState stateS12 = new JpaRepositoryState("S12"); stateS12.setParentState(stateS1); JpaRepositoryState stateS2 = new JpaRepositoryState("S2"); stateS2.setParentState(stateS0); JpaRepositoryState stateS21 = new JpaRepositoryState("S21", true); stateS21.setParentState(stateS2); JpaRepositoryState stateS211 = new JpaRepositoryState("S211", true); stateS211.setParentState(stateS21); JpaRepositoryState stateS212 = new JpaRepositoryState("S212"); stateS212.setParentState(stateS21); stateRepository.save(stateS0); stateRepository.save(stateS1); stateRepository.save(stateS11); stateRepository.save(stateS12); stateRepository.save(stateS2); stateRepository.save(stateS21); stateRepository.save(stateS211); stateRepository.save(stateS212);
Finally create transitions.
JpaRepositoryTransition transitionS1ToS1 = new JpaRepositoryTransition(stateS1, stateS1, "A"); transitionS1ToS1.setGuard(foo1Guard); JpaRepositoryTransition transitionS1ToS11 = new JpaRepositoryTransition(stateS1, stateS11, "B"); JpaRepositoryTransition transitionS21ToS211 = new JpaRepositoryTransition(stateS21, stateS211, "B"); JpaRepositoryTransition transitionS1ToS2 = new JpaRepositoryTransition(stateS1, stateS2, "C"); JpaRepositoryTransition transitionS1ToS0 = new JpaRepositoryTransition(stateS1, stateS0, "D"); JpaRepositoryTransition transitionS211ToS21 = new JpaRepositoryTransition(stateS211, stateS21, "D"); JpaRepositoryTransition transitionS0ToS211 = new JpaRepositoryTransition(stateS0, stateS211, "E"); JpaRepositoryTransition transitionS1ToS211 = new JpaRepositoryTransition(stateS1, stateS211, "F"); JpaRepositoryTransition transitionS2ToS21 = new JpaRepositoryTransition(stateS2, stateS21, "F"); JpaRepositoryTransition transitionS11ToS211 = new JpaRepositoryTransition(stateS11, stateS211, "G"); JpaRepositoryTransition transitionS0 = new JpaRepositoryTransition(stateS0, stateS0, "H"); transitionS0.setKind(TransitionKind.INTERNAL); transitionS0.setGuard(foo0Guard); transitionS0.setActions(new HashSet<>(Arrays.asList(fooAction))); JpaRepositoryTransition transitionS1 = new JpaRepositoryTransition(stateS1, stateS1, "H"); transitionS1.setKind(TransitionKind.INTERNAL); JpaRepositoryTransition transitionS2 = new JpaRepositoryTransition(stateS2, stateS2, "H"); transitionS2.setKind(TransitionKind.INTERNAL); transitionS2.setGuard(foo1Guard); transitionS2.setActions(new HashSet<>(Arrays.asList(fooAction))); JpaRepositoryTransition transitionS11ToS12 = new JpaRepositoryTransition(stateS11, stateS12, "I"); JpaRepositoryTransition transitionS12ToS212 = new JpaRepositoryTransition(stateS12, stateS212, "I"); JpaRepositoryTransition transitionS211ToS12 = new JpaRepositoryTransition(stateS211, stateS12, "I"); JpaRepositoryTransition transitionS11 = new JpaRepositoryTransition(stateS11, stateS11, "J"); JpaRepositoryTransition transitionS2ToS1 = new JpaRepositoryTransition(stateS2, stateS1, "K"); transitionRepository.save(transitionS1ToS1); transitionRepository.save(transitionS1ToS11); transitionRepository.save(transitionS21ToS211); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS1ToS0); transitionRepository.save(transitionS211ToS21); transitionRepository.save(transitionS0ToS211); transitionRepository.save(transitionS1ToS211); transitionRepository.save(transitionS2ToS21); transitionRepository.save(transitionS11ToS211); transitionRepository.save(transitionS0); transitionRepository.save(transitionS1); transitionRepository.save(transitionS2); transitionRepository.save(transitionS11ToS12); transitionRepository.save(transitionS12ToS212); transitionRepository.save(transitionS211ToS12); transitionRepository.save(transitionS11); transitionRepository.save(transitionS2ToS1);
Complete example can be found from sample Chapter 47, JPA Config. This example is also showing how repository can be pre-populated from existing json file having a definitions for entity classes.
Actual Repository implementations for a Redis are
RedisStateRepository
, RedisTransitionRepository
, RedisActionRepository
and RedisGuardRepository
which are backed by
Entity classes RedisRepositoryState
, RedisRepositoryTransition
,
RedisRepositoryAction
and RedisRepositoryGuard
respectively.
Generic way to update states and transition manually for redis is shown below. This is equivalent to machine shown in Figure 31.1, “SimpleMachine”.
@Autowired StateRepository<RedisRepositoryState> stateRepository; @Autowired TransitionRepository<RedisRepositoryTransition> transitionRepository; void addConfig() { RedisRepositoryState stateS1 = new RedisRepositoryState("S1", true); RedisRepositoryState stateS2 = new RedisRepositoryState("S2"); RedisRepositoryState stateS3 = new RedisRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); RedisRepositoryTransition transitionS1ToS2 = new RedisRepositoryTransition(stateS1, stateS2, "E1"); RedisRepositoryTransition transitionS2ToS3 = new RedisRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); }
This is equivalent to machine shown in Figure 31.2, “SimpleSubMachine”.
@Autowired StateRepository<RedisRepositoryState> stateRepository; @Autowired TransitionRepository<RedisRepositoryTransition> transitionRepository; void addConfig() { RedisRepositoryState stateS1 = new RedisRepositoryState("S1", true); RedisRepositoryState stateS2 = new RedisRepositoryState("S2"); RedisRepositoryState stateS3 = new RedisRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); RedisRepositoryTransition transitionS1ToS2 = new RedisRepositoryTransition(stateS1, stateS2, "E1"); RedisRepositoryTransition transitionS2ToS3 = new RedisRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); }
Actual Repository implementations for a MongoDB are
MongoDbStateRepository
, MongoDbTransitionRepository
, MongoDbActionRepository
and MongoDbGuardRepository
which are backed by
Entity classes MongoDbRepositoryState
, MongoDbRepositoryTransition
,
MongoDbRepositoryAction
and MongoDbRepositoryGuard
respectively.
Generic way to update states and transition manually for redis is shown below. This is equivalent to machine shown in Figure 31.1, “SimpleMachine”.
@Autowired StateRepository<MongoDbRepositoryState> stateRepository; @Autowired TransitionRepository<MongoDbRepositoryTransition> transitionRepository; void addConfig() { MongoDbRepositoryState stateS1 = new MongoDbRepositoryState("S1", true); MongoDbRepositoryState stateS2 = new MongoDbRepositoryState("S2"); MongoDbRepositoryState stateS3 = new MongoDbRepositoryState("S3"); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); MongoDbRepositoryTransition transitionS1ToS2 = new MongoDbRepositoryTransition(stateS1, stateS2, "E1"); MongoDbRepositoryTransition transitionS2ToS3 = new MongoDbRepositoryTransition(stateS2, stateS3, "E2"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); }
This is equivalent to machine shown in Figure 31.2, “SimpleSubMachine”.
@Autowired StateRepository<MongoDbRepositoryState> stateRepository; @Autowired TransitionRepository<MongoDbRepositoryTransition> transitionRepository; void addConfig() { MongoDbRepositoryState stateS1 = new MongoDbRepositoryState("S1", true); MongoDbRepositoryState stateS2 = new MongoDbRepositoryState("S2"); MongoDbRepositoryState stateS3 = new MongoDbRepositoryState("S3"); MongoDbRepositoryState stateS21 = new MongoDbRepositoryState("S21", true); stateS21.setParentState(stateS2); MongoDbRepositoryState stateS22 = new MongoDbRepositoryState("S22"); stateS22.setParentState(stateS2); stateRepository.save(stateS1); stateRepository.save(stateS2); stateRepository.save(stateS3); stateRepository.save(stateS21); stateRepository.save(stateS22); MongoDbRepositoryTransition transitionS1ToS2 = new MongoDbRepositoryTransition(stateS1, stateS2, "E1"); MongoDbRepositoryTransition transitionS2ToS3 = new MongoDbRepositoryTransition(stateS21, stateS22, "E2"); MongoDbRepositoryTransition transitionS21ToS22 = new MongoDbRepositoryTransition(stateS2, stateS3, "E3"); transitionRepository.save(transitionS1ToS2); transitionRepository.save(transitionS2ToS3); transitionRepository.save(transitionS21ToS22); }