JBoss.orgCommunity Documentation

Chapter 6.  Persistence

6.1. The Persistence Application Programming Interface
6.1.1. Relationship with the Configuration Framework
6.1.2. Convenience Methods on JbpmContext
6.2. Configuring the Persistence Service
6.2.1. The DbPersistenceServiceFactory
6.2.2. Hibernate Transactions
6.2.3. JTA Transactions
6.2.4. Customizing Queries
6.2.5. Database Compatibility

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 6.1. The Transformations and Different Forms


Note

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

Note

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

The persistence application programming interface is integrated with the configuration framework, (see Chapter 5, 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();
}

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.


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.)

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 5.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 6.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.

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.

Managed transactions are most commonly found when jBPM is used in conjuction with 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>

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.)

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

The example configuration file, src/config.files, specifies the use of the Hypersonic in-memory database, which is ideal for development and testing purposes. (Hypersonic retains all data in memory and does not store anything on disk.)

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 17.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.