34. Repository Support

This section contains documentation related to using 'Spring Data Repositories' used in State Machine.

34.1 Repository Config

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.

[Tip]Tip

Entity classes for RepositoryState and RepositoryTransition have machineId field which is in users disposal and can be used to differentiate between configurations for example if machines are built via StateMachineFactory.

Actual out of a box implementations are documented in below sections where images below are uml equivalent statecharts of a repository configs.

Figure 34.1. SimpleMachine

sm repository simplemachine

Figure 34.2. SimpleSubMachine

sm repository simplesubmachine

Figure 34.3. ShowcaseMachine

sm repository showcasemachine

34.1.1 JPA

Actual Repository implementations for a JPA are JpaStateRepository, JpaTransitionRepository, JpaActionRepository and JpaGuardRepository which are backed by Entity classes JpaRepositoryState, JpaRepositoryTransition, JpaRepositoryAction and JpaRepositoryGuard respectively.

[Important]Important

Version '1.2.8' unfortunately had to made a change into JPA’s Entity model regarding used table names. Previously generated table names always had a prefix 'JPA_REPOSITORY_' derived from Entity class names. As this caused breaking issues with databases imposing restrictions on database object lengths, all Entity classes have spesific definitions to force table names. For example 'JPA_REPOSITORY_STATE' is now simple 'STATE' and so on with other Entity classes.

Generic way to update states and transition manually for jpa is shown below. This is equivalent to machine shown in Figure 34.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 34.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 34.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 50, JPA Config. This example is also showing how repository can be pre-populated from existing json file having a definitions for entity classes.

34.1.2 Redis

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 34.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 34.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);
}

34.1.3 MongoDB

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 34.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 34.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);
}

34.2 Repository Persistence

Apart from storing machine configuration, shown in Section 34.1, “Repository Config”, in an external repository it is also possible to persist machine into repositories.

Interface StateMachineRepository is a central access point interacting with machine persistence and is backed by Entity class RepositoryStateMachine.

34.2.1 JPA

Actual Repository implementation for a JPA is JpaStateMachineRepository which is backed by Entity class JpaRepositoryStateMachine.

Generic way to persist machine for jpa is shown below.

@Autowired
StateMachineRepository<JpaRepositoryStateMachine> stateMachineRepository;

void persist() {

    JpaRepositoryStateMachine machine = new JpaRepositoryStateMachine();
    machine.setMachineId("machine");
    machine.setState("S1");
    // raw byte[] representation of a context
    machine.setStateMachineContext(new byte[] { 0 });

    stateMachineRepository.save(machine);
}

34.2.2 Redis

Actual Repository implementation for a Redis is RedisStateMachineRepository which is backed by Entity class RedisRepositoryStateMachine.

Generic way to persist machine for jpa is shown below.

@Autowired
StateMachineRepository<RedisRepositoryStateMachine> stateMachineRepository;

void persist() {

    RedisRepositoryStateMachine machine = new RedisRepositoryStateMachine();
    machine.setMachineId("machine");
    machine.setState("S1");
    // raw byte[] representation of a context
    machine.setStateMachineContext(new byte[] { 0 });

    stateMachineRepository.save(machine);
}

34.2.3 MongoDB

Actual Repository implementation for a MongoDB is MongoDbStateMachineRepository which is backed by Entity class MongoDbRepositoryStateMachine.

Generic way to persist machine for jpa is shown below.

@Autowired
StateMachineRepository<MongoDbRepositoryStateMachine> stateMachineRepository;

void persist() {

    MongoDbRepositoryStateMachine machine = new MongoDbRepositoryStateMachine();
    machine.setMachineId("machine");
    machine.setState("S1");
    // raw byte[] representation of a context
    machine.setStateMachineContext(new byte[] { 0 });

    stateMachineRepository.save(machine);
}