JBoss.orgCommunity Documentation
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.)
To learn more about XML representations of process definitions and process archives, see Chapter 17, jBPM Process Definition Language .
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:
process. deployment
new process execution commencement
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();
}
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>
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.
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>
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.
Please refer to the Hibernate documentation at http://www.hibernate.org/214.html
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.
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.
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 6.2.1, “The DbPersistenceServiceFactory”.
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>
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.)
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.
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.)
Set the database isolation level for the JDBC connection to at
least READ_COMMITTED.
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.
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.
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.
Whilst the jBPM is capable of generating DDL scripts for any database, these schemas are not always optimized. Consider asking your corporation's Database Adminstrator to review the generated DDL, so that he or she can optimize 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.
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.
The aforementioned APIs constitute a facade to the broader functionality
offered by class
org.jbpm.db.JbpmSchema:
Combining Hibernate and jBPM persistent classes brings about two major benefits. Firstly, 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.
Follow these steps to customize any of the jBPM Hibernate mapping files:
copy the jBPM Hibernate mapping
files from the
sources (src/jbpm-jpdl-sources.jar).
place the copy somewhere on the classpath, (ensuring that it is not the same location as they were in previously.
update the references to the customized mapping files in
hibernate.cfg.xml
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.