Chapter 4.  Persistence

This chapter provides the reader with detailed insight into the Business Process Manager's "persistence" functionality.
Most of the time, the jBPM is used to execute processes that span several transactions. The main purpose of the persistence functionality is to store process executions when wait states occur. It is helpful to think of the process executions as state machines. The intention is to move the process execution state machine from one state to the next within a single transaction.
A process definition can be represented in any of three different forms, namely XML, Java object or a jBPM database record. (Run-time data and log information can also be represented in either of the latter two formats.)
The Transformations and Different Forms

Figure 4.1. The Transformations and Different Forms

Note

To learn more about XML representations of process definitions and process archives, see Chapter 14, jBPM Process Definition Language .

Note

To learn more about how to deploy a process archive to the database, read Section 14.1.1, “ Deploying a Process Archive ” .

4.1.  The Persistence Application Programming Interface

4.1.1.  Relationship with the Configuration Framework

The persistence application programming interface is integrated with the configuration framework, (see Chapter 3, Configuration .) This has been achieved by the exposure of some of the convenience persistence methods on the JbpmContext, allowing the jBPM context block to call persistence API operations.
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  // Invoke persistence operations here
} finally {
  jbpmContext.close();
}

4.1.2.  Convenience Methods on JbpmContext

The three most commonly performed persistence operations are:
  1. process. deployment
  2. new process execution commencement
  3. process execution continuation
Process deployment is normally undertaken directly from the Graphical Process Designer or from the deployprocess ant task. However, to do it directly from Java, use this code:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  ProcessDefinition processDefinition = ...;
  jbpmContext.deployProcessDefinition(processDefinition);
} finally {
  jbpmContext.close();
}
Create a new process execution by specifying the process definition of which it will be an instance. The most common way to do this is by referring to the name of the process. The jBPM will then find the latest version of that process in the database. Here is some demonstration code:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	String processName = ...;
	ProcessInstance processInstance = 
		jbpmContext.newProcessInstance(processName);
} finally {
	jbpmContext.close();
}
To continue a process execution, fetch the process instance, the token or the taskInstance from the database and invoke some methods on the POJO (Plain Old Java Object) jBPM objects. Afterwards, save the updates made to the processInstance into the database.
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	long processInstanceId = ...;
	ProcessInstance processInstance = 
		jbpmContext.loadProcessInstance(processInstanceId);
		processInstance.signal();
		jbpmContext.save(processInstance);
} finally {
	jbpmContext.close();
}
Note that it is not necessary to explicitly invoke the jbpmContext.save method if the ForUpdate methods are used in the JbpmContext class. This is because the save process will run automatically when the jbpmContext class is closed. For example, one may wish to inform the jBPM that a taskInstance has completed. This can cause an execution to continue, so the processInstance related to the taskInstance must be saved. The most convenient way to do this is by using the loadTaskInstanceForUpdate method:
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
	long taskInstanceId = ...;
	TaskInstance taskInstance = 
		jbpmContext.loadTaskInstanceForUpdate(taskInstanceId);
	taskInstance.end();
	} 
finally {
		jbpmContext.close();
}

Important

Read the following explanation to learn how the jBPM manages the persistence feature and uses Hibernate's functionality.
The JbpmConfiguration maintains a set of ServiceFactories. They are configured via the jbpm.cfg.xml file and instantiated as they are needed.
The DbPersistenceServiceFactory is only instantiated the first time that it is needed. After that, ServiceFactorys are maintained in the JbpmConfiguration.
A DbPersistenceServiceFactory manages a Hibernate ServiceFactory but this is only instantiated the first time that it is requested.
DbPersistenceServiceFactory parameters:
  1. isTransactionEnabled
  2. sessionFactoryJndiName
  3. dataSourceJndiName
  4. isCurrentSessionEnabled
The Persistence-Related Classes

Figure 4.2. The Persistence-Related Classes

When the jbpmConfiguration.createJbpmContext() class is invoked, only the JbpmContext is created. No further persistence-related initializations occur at this time. The JbpmContext manages a DbPersistenceService class, which is instantiated when it is first requested. The DbPersistenceService class manages the Hibernate session, which is also only instantiated the first time it is required. (In other words, a Hibernate session will only be opened when the first operation that requires persistence is invoked.)

4.2.  Configuring the Persistence Service

4.2.1. The DbPersistenceServiceFactory

The DbPersistenceServiceFactory class has three more configuration properties: isTransactionEnabled, sessionFactoryJndiName, and dataSourceJndiName. To specify any of these properties in the jbpm.cfg.xml file, specify the Service Factory as a bean within the factory element. This sample code demonstrates how to do so:
<jbpm-context>
  <service name="persistence">
    <factory>
      <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
        <field name="isTransactionEnabled"><false /></field>
        <field name="sessionFactoryJndiName">
          <string value="java:/myHibSessFactJndiName" />
        </field>
        <field name="dataSourceJndiName">
          <string value="java:/myDataSourceJndiName" />
        </field>
      </bean>
    </factory>
  </service>
...
</jbpm-context>

Important

Do not mix the short and long notation for configuring the factories. (See also Section 3.1, “ Customizing Factories ”.) If the factory is just a new instance of a class, use the factory attribute to refer to its factory class name but if properties in a factory require configuration, the long notation must be used and, furthermore, the factory and the bean must be combined as nested elements.
isTransactionEnabled
By default, jBPM will begin a Hibernate transaction when the session is retrieved for the first time and, if the jbpmContext is closed, the Hibernate transaction will be ended. The transaction is then committed or rolled back depending on whether or not jbpmContext.setRollbackOnly was called. (The isRollbackOnly property is maintained in the TxService.) To disable transactions and prohibit jBPM from managing them with Hibernate, set the isTransactionEnabled property value to false. (This property only controls the behaviour of the jbpmContext; the DbPersistenceService.beginTransaction() can still be called directly directly with the application programming interface, which ignores the isTransactionEnabled setting.) To learn more about transactions, please study Section 4.2.2, “ Hibernate Transactions ”.
sessionFactoryJndiName
By default, this is null, which means that the session factory will not be fetched from JNDI. If it is set and a session factory is needed in order to create a Hibernate session, it will be fetched from JNDI.
dataSourceJndiName
By default, this is null, resulting in creation of JDBC connections being delegated to Hibernate. By specifying a data-source, one makes the Business Process Manager fetch a JDBC connection from the data-source and provide it to Hibernate whilst opening a new session.

4.2.1.1.  The Hibernate Session Factory

By default, the DbPersistenceServiceFactory uses the hibernate.cfg.xml file in the root of the classpath to create the Hibernate session factory. Note that the Hibernate configuration file resource is mapped in jbpm.hibernate.cfg.xml. Customise it by reconfiguring jbpm.cfg.xml.
<jbpm-configuration>
    <!-- configuration resource files pointing to default
         configuration files in jbpm-{version}.jar -->
    <string name='resource.hibernate.cfg.xml' 
        value='hibernate.cfg.xml' />
    <!-- <string name='resource.hibernate.properties' 
        value='hibernate.properties' /> -->
</jbpm-configuration>

Important

When resource.hibernate.properties is specified, the properties in that resource file will overwrite all of those in hibernate.cfg.xml. Instead of updating the hibernate.cfg.xml to point to the database, use hibernate.properties to handle jBPM upgrades. The hibernate.cfg.xml file can then be copied without the need to reapply the changes.

4.2.1.2.  Configuring a C3PO Connection Pool

Please refer to the Hibernate documentation at http://www.hibernate.org/214.html

4.2.1.3.  Configuring an ehCache Provider

To learn how to configure jBPM with JBossCache, read http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmConfiguration
To learn how to configure a cache provider to work with Hibernate, study http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-cache.
The hibernate.cfg.xml file that ships with jBPM includes the following line:
<property name="hibernate.cache.provider_class">
    org.hibernate.cache.HashtableCacheProvider
</property>
This is provided so that users do not have to concern themselves with configuring classpaths.

Warning

Do not use Hibernate's HashtableCacheProvider in a production environment.
To use ehcache instead of the HashtableCacheProvider, simply remove the relevant line from the classpath and substitute ehcache.jar instead. Note that one might have to search for the right ehcache library version that is compatible with one's environment.

4.2.2.  Hibernate Transactions

By default, jBPM delegates transactions to Hibernate by using the "session per transaction" pattern. jBPM will begin a Hibernate transaction when a session is opened the first time when a persistent operation is invoked on the jbpmContext. The transaction will be committed right before the Hibernate session is closed. That will happen inside the jbpmContext.close().
Use jbpmContext.setRollbackOnly() to mark a transaction for rollback. In doing so, the transaction will be rolled back imediately before the session is closed inside the jbpmContext.close() method.
To prohibit the Business Process Manager from invoking any of the transaction methods via the Hibernate application programming interface, set the isTransactionEnabled property to false, as explained in more detail in Section 4.2.1, “The DbPersistenceServiceFactory”.

4.2.3.  JTA Transactions

Managed transactions are most commonly found when jBPM is used in the JBoss Application Server. The following code sample shows a common way in which transactions are bound to JTA:
<jbpm-context>
  <service name="persistence">
    <factory>
      <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
        <field name="isTransactionEnabled"><false /></field>
        <field name="isCurrentSessionEnabled"><true /></field>
        <field name="sessionFactoryJndiName">
          <string value="java:/myHibSessFactJndiName" />
        </field>
      </bean>
    </factory>
  </service>
</jbpm-context>
Next, configure the Hibernate session factory to use a data-source and bind Hibernate itself to the Transaction Manager. If using more than one datasource, bind them to an XA datasource.
<hibernate-configuration>
    <session-factory>

        <!-- hibernate dialect -->
        <property name="hibernate.dialect">
            org.hibernate.dialect.HSQLDialect
        </property>

        <!-- DataSource properties (begin) -->
        <property name="hibernate.connection.datasource">
            java:/JbpmDS
        </property>

        <!-- JTA transaction properties (begin) -->
        <property name="hibernate.transaction.factory_class">
            org.hibernate.transaction.JTATransactionFactory
        </property>
    
        <property name="hibernate.transaction.manager_lookup_class">
            org.hibernate.transaction.JBossTransactionManagerLookup
        </property>

        <property name="jta.UserTransaction">
            java:comp/UserTransaction
        </property>

    </session-factory>
</hibernate-configuration>

Note

For more information about binding Hibernate to a Transaction Manager, please, refer to http://www.hibernate.org/hib_docs/v3/reference/en/html_single/#configuration-optional-transactionstrategy.
Next, configure Hibernate to use an XA datasource.
These configurations allow the enterprise beans to use CMT whilst the web console uses BMT. (This is why jta.UserTransaction is also specified.)

4.2.4.  Customizing Queries

All of the SQL queries that jBPM uses are found in one central configuration file. That resource file is referenced in the hibernate.cfg.xml configuration file:
<hibernate-configuration>
    <!-- hql queries and type defs -->
    <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
</hibernate-configuration>
To customize one or more of those queries, make a back-up of the original file. Next, place the customized version somewhere on the classpath, then update the reference to org/jbpm/db/hibernate.queries.hbm.xml in the hibernate.cfg.xml to point to the customized version.

4.2.5.  Database Compatibility

The jBPM runs on any database that is supported by Hibernate.

4.2.5.1.  Isolation Level of the JDBC Connection

Set the database isolation level for the JDBC connection to at least READ_COMMITTED.

Warning

If it is set to READ_UNCOMMITTED, (isolation level zero, the only isolation level supported by Hypersonic), race conditions might occur in the job executor .These might also appear when synchronization of multiple tokens is occurring.

4.2.5.2.  Changing the Database

In order to reconfigure Business Process Manger to use a different database, follow these steps:
  • put the JDBC driver library archive in the classpath.
  • update the Hibernate configuration used by jBPM.
  • create a schema in the new database.

4.2.5.3.  The Database Schema

The jbpm.db sub-project contains drivers, instructions and scripts to help the user to start using the database of his or her choice. Refer to the readme.html (found in the root of the jbpm.db project) for more information.

Note

Whilst the JBPM is capable of generating DDL scripts for any database, these schemas are not always as efficient as they could be. Consider asking your corporation's Database Administrator to review the generated DDL, so that he or she can optimise the column types and indexes.
The following Hibernate configuration option may be of use in a development environment: set hibernate.hbm2ddl.auto to create-drop and the schema will be created automatically the first time the database is used in an application. When the application closes down, the schema will be dropped.
4.2.5.3.1. Programmatic database schema operations
jBPM provides an API for creating and droping the database schema through the org.jbpm.JbpmConfiguration methods createSchema and dropSchema. Be aware that there is no constraint on invoking these methods other than the privileges of the configured database user.

Note

The aforementioned APIs constitute a facade to the broader functionality offered by class org.jbpm.db.JbpmSchema:

4.2.5.4.  Combining Hibernate Classes

Combining Hibernate and jBPM persistent classes brings about two major benefits. Session, connection and transaction management become easier because, by combining them into one Hibernate session factory, there will be only one Hibernate session and one JDBC connection. Hence, the jBPM updates will be in the same transaction as the updates for the domain model. This eliminates the need for a transaction manager.
Secondly, it enables one to drop one's Hibernate persistence object into the process variables without any additional work.
To make this occur, create one central hibernate.cfg.xml file. It is easiest to use the default jBPM hibernate.cfg.xml as a starting point and add references to one's own Hibernate mapping files to customize it.

4.2.5.5.  Customizing the jBPM Hibernate Mapping Files

Follow these steps to customize any of the jBPM Hibernate mapping files:
  1. copy the jBPM Hibernate mapping files from the sources (src/jbpm-jpdl-sources.jar).
  2. place the copy somewhere on the classpath, (ensuring that it is not the same location as they were in previously.
  3. update the references to the customized mapping files in hibernate.cfg.xml

4.2.5.6.  Second Level Cache

jBPM uses Hibernate's second level cache to keep the process definitions in memory after loading they have been loaded once. The process definition classes and collections are configured in the Hibernate mapping files so that the cache element looks like this:
<cache usage="nonstrict-read-write"/>
Since process definitions will never change, it is acceptable to keep them in the second level cache. (See also Section 14.1.3, “ Changing Deployed Process Definitions ”.)
The default caching strategy is set to nonstrict-read-write. During run-time execution, the process definitions remain static, allowing maximum caching to be achieved. In theory, setting the caching strategy read-only would be even better for run-time execution but, that setting would not permit the deployment of new process definitions.
Having read this chapter, you have learned a great deal of theoretical information and practical advice relating to the topic of persistence in jBPM, including how to utilize Hibernate to its fullest potential.
Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.