Magnolia 5.7 reached extended end of life on May 31, 2022. Support for this branch is limited, see End-of-life policy. Please note that to cover the extra maintenance effort, this EEoL period is a paid extension in the life of the branch. Customers who opt for the extended maintenance will need a new license key to run future versions of Magnolia 5.7. If you have any questions or to subscribe to the extended maintenance, please get in touch with your local contact at Magnolia.
jBPM implements a persistence layer based on JPA and Hibernate.
jBPM allows the persistent storage of certain information. This chapter describes these different types of persistence, and how to configure them. An example of the information stored is the process runtime state. Storing the process runtime state is necessary in order to be able to continue execution of a process instance at any point, if something goes wrong. -- jBPM: Persistence and Transactions
In short the persistence allows shutting down the system and restoring the state of all running processes upon restart.
In order to not have to set up a separate storage mechanism in Magnolia for the jBPM engine, Magnolia provides its own JCR persistence layer for jBPM.
Magnolia stores all runtime data from the the jBPM engine in the
workflow workspace. By default workflow is only enabled on the author instance.
jBPM stores most of the data used for its execution as binary data using marshalling mechanisms. This makes the underlaying scheme rather simple.
As we are using the singleton strategy for our runtime engine we are only dealing with one session. This session is stored under the
sessions node with
0 as static identifier and is never removed. All data is marshalled into the binary property
The session ID is not using the key generator Magnolia uses for storing processes and workItems. One reason is that by using the singleton strategy this is not necessary at the moment at least. Another reason ist that the interfaces and implementation classes used inside the jBPM persistence package is restricted to use integers as IDs, compared to processInstances and workItems using a long.
The marshalling is done inside the SessionInfo's update method, which is an object used and created by the CommandService.
Persisting the SessionInfo is handled by the
JcrSessionStore implementation in
SystemContextSessionStore where all operations are performed in Magnolia's
Processes are stored under the
processInstances node inside the
workflow workspace. As this data is used during the actual execution of processes, the data is removed when the process terminates. Magnolia's current implementation does not support logging completed processes for further auditing. For more information how this could be implemented, see the documentation of jBPM Audit data model.
For creating the IDs for process instances we use the
ProcessInstanceIdGenerator which creates a Long from the current system time. The processes are further stored hierarchical based on year, month of year and day of month.
Similar to the sessions, the processes use a marshalling mechanism and most of the runtime data is stored as a binary under the bytes property. The marshalling is performed inside ProcessInstanceInfo's update method. The info object is created by
Persisting the ProcessInstanceInfo is handled by the
JcrProcessStore implementation in
SystemContextProcessStore where all operations are performed in Magnolia's
SystemContext. Because we do not need the
correlation key used for mapping sessions to processInstances due to the
singleton strategy, those methods are currently not implemented.
Workitems are using the same hierarchical structure and keys as the processInstances. The key generator used is implemented in
The marshalling of work items happens in the update method of the
WorkItemInfo object and the binary data is stored under the
Persisting the WorkItemInfo is handled by the
JcrWorkItemStore implementation in
SystemContextWorkitemStore where all operations are performed in Magnolia's
Contrary to the simple storage scheme finding the right spots to persist the current state of a process is a bit more tricky. These spots are called safe points:
The state of a process instance is stored at so-called "safe points" during the execution of the process engine. Whenever a process instance is executing (for example when it started or continuing from a previous wait state, the engine executes the process instance until no more actions can be performed (meaning that the process instance either has completed (or was aborted), or that it has reached a wait state in all of its parallel paths). At that point, the engine has reached the next safe state, and the state of the process instance (and all other process instances that might have been affected) is stored persistently. – jBPM: Safe Points
These safe points are reached by different classes. Some of of the logic is taken care of by the
WorkItemManager where the state is persisted when the execution starts or is completed. As this is not sufficient for keeping the persisted state updated at all times we hook into the internal execution of a process with the
To persist the processes at safe points, Magnolia uses a CommandService which allows intercepting internally used Commands.
When loading or creating a
KieSession by the
JcrSessionFactory it delegates the creation to
JcrKieStoreServices which in turn creates a
CommandBasedStatefulKnowledgeSession, an implementation of the
KieSession creating Commands for each step of the process.
As an example this is how the
CommandBasedStatefulKnowledgeSession starts a process by creating a
StartProcessCommand containing the processId and the parameters. You also see how the actual execution of the command is delegated to the
When not using the
CommandBasedStatefulKnowledgeSession this would create a
ProcessInstance and directly start it.
The interceptors used for persisting the state are registered by the
JcrSessionFactory to the
The concept behind these interceptors is rather simple. They act as CommandExecutors for commands and allow adding custom logic before and after executing the command. As an example let's take a look at the
JcrPersistProcessInterceptor which takes care of persisting ProcessInstances.
Note how the interceptor creates and executes a
PersistProcessCommand in case the Command met the criteria of
isValidCommand(command). Persisting the ProcessInstance is then taken care of by the PersistProcessCommand.
A similar interceptor is used for persisting the session state.
JCR persistence diagram
This diagram shows the most important classes used for persisting sessions, workitems and processes to JCR.