Table of Contents
Drools Flow allows the persistent storage of certain information, i.e., the process runtime state, the process definitions and the history information.
Whenever a process is started, a process instance is created, which represents the execution of the process in that specific context. For example, when executing a process that specifies how to process a sales order, one process instance is created for each sales request. The process instance represents the current execution state in that specific context, and contains all the information related to that process instance. Note that it only contains the minimal runtime state that is needed to continue the execution of that process instance at some later time, but it does not include information about the history of that process instance if that information is no longer needed in the process instance.
The runtime state of an executing process can be made persistent, for example, in a database. This allows to restore the state of execution of all running processes in case of unexpected failure, or to temporarily remove running instances from memory and restore them at some later time. Drools Flow allows you to plug in different persistence strategies. By default, if you do not configure the process engine otherwise, process instances are not made persistent.
Drools Flow provides a binary persistence mechanism that allows you to save the state of a process instance as a binary dataset. This way, the state of all running process instances can always be stored in a persistent location. Note that these binary datasets usually are relatively small, as they only contain the minimal execution state of the process instance. For a simple process instance, this usually contains one or a few node instances, i.e., any node that is currently executing, and, possibly, some variable values.
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, after its start or continuation from a wait state, the engine proceeds until no more actions can be performed. 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.
By default, the engine does not save runtime data persistently. It is, however, pretty straightforward to configure the engine to do this, by adding a configuration file and the necessary dependencies. Persistence itself is based on the Java Persistence API (JPA) and can thus work with several persistence mechanisms. We are using Hibernate by default, but feel free to employ alternatives. A H2 database is used underneath to store the data, but you mighto choose your own alternative for this, too.
First of all, you need to add the necessary dependencies to your
classpath. If you're using the Eclipse IDE, you can do that by adding
the jar files to your Drools runtime directory (cf. chapter
“Drools Eclipse IDE Features”),
or by manually adding these dependencies to your project. First of all,
you need the jar file drools-persistence-jpa.jar
,
as that contains code for saving the runtime state whenever necessary.
Next, you also need various other dependencies, depending on the
persistence solution and database you are using. For the default
combination with Hibernate as the JPA persistence provider, the H2
database and Bitronix for JTA-based transaction management, the
following list of dependencies is needed:
Next, you need to configure the Drools engine to save the state of the
engine whenever necessary. The easiest way to do this is to use
JPAKnowledgeService
to create your knowledge session, based on a
Knowledge Base, a Knowledge Session Configuration (if necessary) and an
environment. The environment needs to contain a reference to your
Entity Manager Factory.
// create the entity manager factory and register it in the environment EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.drools.persistence.jpa" ); Environment env = KnowledgeBaseFactory.newEnvironment(); env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf ); // create a new knowledge session that uses JPA to store the runtime state StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); int sessionId = ksession.getId(); // invoke methods on your method here ksession.startProcess( "MyProcess" ); ksession.dispose();
You can also yse the JPAKnowledgeService
to recreate
a session based on a specific session id:
// recreate the session from database using the sessionId ksession = JPAKnowledgeService.loadStatefulKnowledgeSession( sessionId, kbase, null, env );
Note that we only save the minimal state that is needed to continue execution of the process instance at some later point. This means, for example, that it does not contain information about already executed nodes if that information is no longer relevant, or that process instances that have been completed or aborted are removed from the database. If you want to search for history-related information, you should use the history log, as explained later.
By default, drools-persistence-jpa.jar
contains a configuration
file that configures JPA to use Hibernate and the H2 database, called
persistence.xml
in the META-INF directory, as shown below.
You will need to override these defaults if you want to change them, by adding
your own persistence.xml
in your classpath, preceding the
default one in drools-persistence-jpa.jar
. Refer to
the JPA and Hibernate documentation for more information on how to do this.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <persistence version="1.0" xsi:schemaLocation= "http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd" xmlns:orm="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence"> <persistence-unit name="org.drools.persistence.jpa"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>jdbc/processInstanceDS</jta-data-source> <class>org.drools.persistence.session.SessionInfo</class> <class>org.drools.persistence.processinstance.ProcessInstanceInfo</class> <class>org.drools.persistence.processinstance.ProcessInstanceEventInfo</class> <class>org.drools.persistence.processinstance.WorkItemInfo</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.max_fetch_depth" value="3"/> <property name="hibernate.hbm2ddl.auto" value="update"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionManagerLookup"/> </properties> </persistence-unit> </persistence>
This configuration file refers to a data source called "jdbc/processInstanceDS". The following Java fragment could be used to set up this data source, where we are using the file-based H2 database.
PoolingDataSource ds = new PoolingDataSource(); ds.setUniqueName("jdbc/processInstanceDS"); ds.setClassName("org.h2.jdbcx.JdbcDataSource"); ds.setMaxPoolSize(3); ds.setAllowLocalTransactions(true); ds.getDriverProperties().put("user", "sa"); ds.getDriverProperties().put("password", "sasa"); ds.getDriverProperties().put("URL", "jdbc:h2:file:/NotBackedUp/data/process-instance-db"); ds.init();
Whenever you do not provide transaction boundaries inside your application, the engine will automatically execute each method invocation on the engine in a separate transaction. If this behavior is acceptable, you don't need to do anything else. You can, however, also specify the transaction boundaries yourself. This allows you, for example, to combine multiple commands into one transaction.
You need to register a transaction manager at the environment before using user-defined transactions. The following sample code uses the Bitronix transaction manager. Next, we use the Java Transaction API (JTA) to specify transaction boundaries, as shown below:
// create the entity manager factory and register it in the environment EntityManagerFactory emf = Persistence.createEntityManagerFactory( "org.drools.persistence.jpa" ); Environment env = KnowledgeBaseFactory.newEnvironment(); env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf ); env.set( EnvironmentName.TRANSACTION_MANAGER, TransactionManagerServices.getTransactionManager() ); // create a new knowledge session that uses JPA to store the runtime state StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); // start the transaction UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" ); ut.begin(); // perform multiple commands inside one transaction ksession.insert( new Person( "John Doe" ) ); ksession.startProcess( "MyProcess" ); ksession.fireAllRules(); // commit the transaction ut.commit();