JBoss.orgCommunity Documentation

jBPM User Guide

Version 6.1.0-SNAPSHOT


1. Overview
1.1. What is jBPM?
1.2. Overview
1.3. Core Engine
1.4. Eclipse Editor
1.5. Workbench web application
1.5.1. Process Designer
1.5.2. Form Modeler
1.5.3. Process and Task management
1.5.4. Business Activity Monitoring
1.6. Documentation
2. Getting Started
2.1. Downloads
2.2. Use with Maven, Gradle, Ivy, Buildr or ANT
2.3. Getting started
2.4. Community
2.5. Sources
2.5.1. License
2.5.2. Source code
2.5.3. Building from source
3. Installer
3.1. Prerequisites
3.2. Download the installer
3.3. Demo setup
3.3.1. Control options
3.4. 10-Minute Tutorial: Using the jBPM Console
3.5. 10-Minute Tutorial: Integrate Eclipse and Web tooling
3.6. Using your own database with jBPM
3.6.1. Introduction
3.6.2. Database setup
3.6.3. Configuration
3.6.4. Using a different database
3.7. jBPM data base schema scripts (DDL scripts)
3.8. jBPM installer script
3.9. What to do if I encounter problems or have questions?
3.10. Frequently asked questions
4. Core Engine: API
4.1. The jBPM API
4.1.1. Knowledge Base
4.1.2. Session
4.1.3. Correlation key and Correlation properties
4.1.4. Events
4.2. Knowledge-based API
4.3. RuntimeManager
4.4. Control parameters
5. Core Engine: Basics
5.1. Creating a process
5.1.1. Using the graphical BPMN2 Editor
5.1.2. Defining processes using XML
5.1.3. Defining Processes Using the Process API
5.2. Details of different process constructs: Overview
5.3. Details: Process properties
5.4. Details: Events
5.4.1. Start event
5.4.2. End events
5.4.3. Intermediate events
5.5. Details: Activities
5.5.1. Script task
5.5.2. Service task
5.5.3. User task
5.5.4. Reusable sub-process
5.5.5. Business rule task
5.5.6. Embedded sub-process
5.5.7. Multi-instance sub-process
5.6. Details: Gateways
5.6.1. Diverging gateway
5.6.2. Converging gateway
5.7. Using a process in your application
5.8. Other features
5.8.1. Data
5.8.2. Constraints
5.8.3. Action scripts
5.8.4. Events
5.8.5. Timers
5.8.6. Updating processes
5.8.7. Multi-threading
6. Core Engine: BPMN 2.0
6.1. Business Process Model and Notation (BPMN) 2.0 specification
6.2. Supported elements / attributes
6.3. Examples
7. Core Engine: Persistence and transactions
7.1. Runtime State
7.1.1. Binary Persistence
7.1.2. Safe Points
7.1.3. Configuring Persistence
7.1.4. Transactions
7.1.5. Persistence and concurrency
7.2. Process Definitions
7.3. History Log
7.3.1. The jBPM Audit data model
7.3.2. Storing Process Events in a Database
7.3.3. Storing Process Events in a JMS queue for further processing
8. Core Engine: Examples
8.1. jBPM Examples
8.2. Examples
8.3. Unit tests

jBPM is a flexible Business Process Management (BPM) Suite. It is light-weight, fully open-source (distributed under Apache license) and written in Java. It allows you to model, execute, and monitor business processes throughout their life cycle.

A business process allows you to model your business goals by describing the steps that need to be executed to achieve those goals, and the order of those goals are depicted using a flow chart. This process greatly improves the visibility and agility of your business logic. jBPM focuses on executable business processes, which are business processes that contain enough detail so they can actually be executed on a BPM engine. Executable business processes bridge the gap between business users and developers as they are higher-level and use domain-specific concepts that are understood by business users but can also be executed directly.

The core of jBPM is a light-weight, extensible workflow engine written in pure Java that allows you to execute business processes using the latest BPMN 2.0 specification. It can run in any Java environment, embedded in your application or as a service.

On top of the core engine, a lot of features and tools are offered to support business processes throughout their entire life cycle:

BPM creates the bridge between business analysts, developers and end users by offering process management features and tools in a way that both business users and developers like. Domain-specific nodes can be plugged into the palette, making the processes more easily understood by business users.

jBPM supports adaptive and dynamic processes that require flexibility to model complex, real-life situations that cannot easily be described using a rigid process. We bring control back to the end users by allowing them to control which parts of the process should be executed; this allows dynamic deviation from the process.

jBPM is not just an isolated process engine. Complex business logic can be modeled as a combination of business processes with business rules and complex event processing. jBPM can be combined with the Drools project to support one unified environment that integrates these paradigms where you model your business logic as a combination of processes, rules and events.

Apart from the core engine itself, there are quite a few additional (optional) components that you can use, like an Eclipse-based or web-based designer and a management console.


This figure gives an overview of the different components of the jBPM project. jBPM can integrate with a lot of other services (and we've shown a few using grey boxes on the figure), but here we focus on the components that are part of the jBPM project itself.

  • The process engine is the core of the project and is required if you want to execute business processes (all other components are optional, as indicated by the dashed border). Your application services typically invoke the core engine (to start processes or to signal events) whenever necessary.
    • An optional core service is the history log; this will log all information about the current and previous state of all your process instances.
    • Another optional core service is the human task service that will take care of the human task life cycle if human actors participate in the process.
  • Two types of graphical editors are supported for defining your business processes:
    • The Eclipse plugin is an extension to the Eclipse IDE, targeted towards developers, and allows you to create business processes using drag and drop, advanced debugging, etc.
    • The web-based designer allows business users to manage business processes in a web-based environment. A web-based form builder also allows you to create, generate or edit forms related to those processes (to start the process or to complete one of the user tasks).
  • The Guvnor repository is an optional component that can be used to store all your business processes. It supports collaboration, versioning, etc. There is integration with both the Eclipse plugin and web-based designer, supporting round-tripping between the different tools.
  • The web-based management console allows business users to manage their runtime (manage business processes like start new processes, inspect running instances, etc.), to manage their task list and to perform Business Activity Monitoring (BAM) and see reports.

Each of the components are described in more detail below.

The core jBPM engine is the heart of the project. It's a light-weight workflow engine that executes your business processes. It can be embedded as part of your application or deployed as a service (possibly on the cloud). Its most important features are the following:

The core engine can also be integrated with a few other (independent) core services:

Optionally, you can use one or more knowledge repositories to store your business processes (and other related artefacts). The web-based designer is integrated in the Guvnor repository, which is targeted towards business users and allows you to manage your processes separately from your application. It supports the following:

Workbench application covers complete life cycle of BPM projects starting at authoring phase, going through implementation, execution and monitoring.


The documentation is structured as follows:

All releases can be downloaded from SourceForge. Select the version you want to download and then select which artifact you want:

  • bin: all the jBPM binaries (jars) and their dependencies
  • src: the sources of the core components
  • docs: the documentation
  • examples: some jBPM examples, can be imported into Eclipse
  • installer: the jbpm-installer, downloads and installs a demo setup of jBPM
  • installer-full: the jbpm-installer, downloads and installs a demo setup of jBPM, already contains a number of dependencies prepackages (so they don't need to be downloaded separately)

The jars are also available in the central maven repository (and also in the JBoss maven repository).

If you use Maven, add KIE and jBPM dependencies in your project's pom.xml like this:


  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.jbpm</groupId>
        <artifactId>jbpm-bom</artifactId>
        <type>pom</type>
        <version>...</version>
        <scope>import</scope>
      </dependency>
      ...
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-flow</artifactId>
      <scope>runtime</scope>
    </dependency>
    ...
  <dependencies>

This is similar for Gradle, Ivy and Buildr. To identify the latest version, check the maven repository.

If you're still using ANT (without Ivy), copy all the jars from the download zip's binaries directory and manually verify that your classpath doesn't contain duplicate jars.

Here are a lot of useful links part of the jBPM community:

Please feel free to join us in our IRC channel at chat.freenode.net #jbpm. This is where most of the real-time discussion about the project takes place and where you can find most of the developers most of their time as well. Don't have an IRC client installed? Simply go to http://webchat.freenode.net/, input your desired nickname, and specify #jbpm. Then click login to join the fun.

This guide will assist you in installing and running a demo setup of the various components of the jBPM project. If you have any feedback on how to improve this guide, if you encounter problems, or if you want to help out, do not hesitate to contact the jBPM community as described in the "What to do if I encounter problems or have questions?" section.

First of all, you need to download the installer. There are two versions

  • full installer - which already contains a lot of the dependencies that are necessary during the installation
  • minimal installer - which only contains the installer and will download all dependencies

In general, it is probably best to download the full installer: jBPM-{version}-installer-full.zip

You can also find the latest snapshot release here (only minimal installer) here:

https://hudson.jboss.org/jenkins/job/jBPM/lastSuccessfulBuild/artifact/jbpm-distribution/target/

The easiest way to get started is to simply run the installation script to install the demo setup. Simply go into the install folder and run:

ant install.demo

This will:

This could take a while (REALLY, not kidding, we are downloading an application server and Eclipse installation, even if you downloaded the full installer). The script however always shows which file it is downloading (you could for example check whether it is still downloading by checking the whether the size of the file in question in the jbpm-installer/lib folder is still increasing). If you want to avoid downloading specific components (because you will not be using them or you already have them installed somewhere else), check below for running only specific parts of the demo or directing the installer to an already installed component.

Once the demo setup has finished, you can start playing with the various components by starting the demo setup:

ant start.demo

This will:

Once everything is started, you can start playing with the Eclipse tooling, jBPM console, as explained in the next three sections.

If you do not wish to use Eclipse in the demo setup, you can use the alternative commands:

    ant install.demo.noeclipse
    ant start.demo.noeclipse
    

Open up the process management console:

http://localhost:8080/jbpm-console

Log in, using krisv / krisv as username / password. The following screencast gives an overview of how to manage your process instances. It shows you:

  • How to start a new process
  • How to look up the current status of a running process instance
  • How to look up your tasks
  • How to complete a task
  • How to generate reports to monitor your process execution

Figure 3.1. 


  • To manage your process definitions and instances, click on the "Process Management" menu option at the top menu bar an select one of available options depending on you interest:
    • Process Definitions - lists all available process definitions
    • Process Instances - lists all active process instances (allows to show completed, aborted as well by changing filter criteria)
  • Process definitions panel allow you to start a new process instance by clicking on the "Play" button. You will see a process form where you need to fill in the necessary information to start the process. In this case, you need to fill in your username "krisv" and a reason for the request, after which you can complete the form and close the window. After process form window is closed process instance details panel will be shown. From there you can access additional details:
    • Process model - to visualize current state of the process
    • Process variables - to see current values of process variables
    The process instance that you just started is first requiring a self-evaluation of the user and is waiting until the user has completed this task.
  • To see the tasks that have been assigned to you, choose the "Tasks" menu option on the top bar and select "Task List" (you may need to click refresh to update your task view). The personal tasks table should show a "Performance Evaluation" task for you. You can complete this task by selecting it and clicking the "View" button. This will open the task form for performance evaluations. You can fill in the necessary data and then complete the form and close the window. After completing the task, you could check the "Process Instances" once more to check the progress of your process instance. You should be able to see that the process is now waiting for your HR manager and project manager to also perform an evaluation. You could log in as "john" / "john" and "mary" / "mary" to complete these tasks.
  • After starting and/or completing a few process instances and human tasks, you can generate a report of what has happened so far. Under "Dashboards", select "Process & Task Dashboard". By default, you will see predefined set of charts that allow to directly spot what is going on in the system. Charts can be customized as well which will be described in following chapters.

The following screencast gives an overview of how to integrate Web Console and Eclipse. It shows you:

  • How to create new project inside web console
  • How to create new process inside web console
  • How to import an existing project into your workspace
  • How to apply changes in Eclipse
  • How to push back changes done in Eclipse to Web Console

Figure 3.2. 


You could also create a new project using the jBPM project wizard. This sample project contains a simple HelloWorld BPMN2 process and an associated Java file to start the process. Simply select "File - New - jBPM Project" (if you cannot see that (because you're not in the jBPM perspective) you can do "File - New ... - Project ..." and under the "jBPM" category, select "jBPM project" and click "Next"). Give the project a name and click "Finish". You should see a new project containing a "sample.bpmn" process and a "com.sample.ProcessMain" Java class and a "com.sample.ProcessTest" JUnit test class. You can open the BPMN2 process by double-clicking it. To execute the process, right-click on ProcessMain.java and select "Run As - Java Application". You should see a "Hello World" statement in the output console. To execute the test, right-click on ProcessTest.java and select "Run As - JUnit Test". You should also see a "Hello World" statement in the output console, and the JUnit test completion in the JUnit view.

The following files define the persistence settings for the jbpm-installer demo:

There are two standalone.xml files available as jbpm allows to use JMS component for integration and thus requires standalone-full.xml to be configured. Best practice is to update both to have consistent setup but most important is to have standalone-full-as-7.1.1.Final.xml properly configured.

Do the following:

If you decide to use a different database with this demo, you need to remember the following when going through the steps above:

  • Change the JDBC URLs, usernames and passwords, and Hibernate dialect lines to match your database information in the configuration files mentioned above.
  • In order to make sure your driver will be correctly installed in the JBoss AS 7 server, you can do one of two things. Both ways are explained here.
    • Install the driver jar as a module, which is what the install script does.
    • Otherwise, you can modify and install the downloaded jar as a deployment. In this case you will have to copy the jar yourself to the standalone/deployments directory.
    If you choose to install driver as JBoss module, please do the following:
    • Disable default H2 driver properties
      # default is H2
      # H2.version=1.3.168
      # db.name=h2
      # db.driver.jar.name=${db.name}.jar
      # db.driver.download.url=http://repo1.maven.org/maven2/com/h2database/h2/${H2.version}/h2-${H2.version}.jar
                      
    • Copy one of the example configs (mysql or postgresql)
      #postresql
      db.name=postresql
      db.driver.module.prefix=org/postgresql
      db.driver.jar.name=${db.name}-jdbc.jar
      db.driver.download.url=https://repository.jboss.org/nexus/content/repositories/thirdparty-uploads/postgresql/postgresql/9.1-902.jdbc4/postgresql-9.1-902.jdbc4.jar
                    
    • Change the db.name property in build.properties to the name of the downloaded jdbc driver jar you placed in db/drivers.
    • Change the <driver> information in the <datasource> section of standalone.xml so that it refers to the name of your driver module (see next step). For example:
      <driver>postgresql</driver>
    • Further on in standalone.xml is the <drivers> section of the <datasources> (note the plural: drivers, datasources). We need to do the following with this file:
      • Change the name of the driver to match the name in the last step,
      • Give an appropriate name to the module,
      • And fill in the correct name of the XA datasource class to use.
      For example:
      <drivers>
        <driver name="postgresql" module="org.postgresql">
            <xa-datasource-class>org.postgresql.xa.PGXADataSource</xa-datasource-class>
        </driver>
      </drivers>
                    
    • Change the db.driver.module.prefix property in build.properties to the same “value” you used for the module name in standalone.xml. In the example above, I used “org.postgresql” which means that I should then use org/postgresql for the db.driver.module.prefix property.
    • Lastly, you'll have to create the db/${db.name}_module.xml file. As an example you can use db/mysql_module.xml, so just make a copy of it and:
      • Change the name of the module to match the db.driver.module.prefix property above
      • Change the name of the module resource to the name of the JDBC driver jar that was downloaded.
      The top of the original file looks like this:
      <module xmlns="urn:jboss:module:1.0" name="com.mysql">
         <resources>
           <resource-root path="mysql-connector-java.jar"/>
         </resources>
      Change those lines to look like this, for example:
      <module xmlns="urn:jboss:module:1.0" name="org.postgresql">
         <resources>
           <resource-root path="postgresql-9.1-902.jdbc4.jar"/>
         </resources>

jBPM installer ant script performs most of the work automatically and usually does not require additional attention but in case it does, here is a list of available targets that might be needed to perform some of the steps manually.

Table 3.2. jBPM installer available targets

TargetDescription
clean.dbcleans up data base used by jBPM demo (applies only to H2 data base)
clean.democleans up entire installation so new installation can be performed
clean.demo.noeclipsesame as clean.demo but does not remove eclipse
clean.eclipseremoves eclipse and its workspace
clean.generated.ddlremoves DDL scripts generated if any
clean.jbossremoves application server with all its deployments
clean.jboss.repositoryremoves repository content for demo setup (guvnor maven repo, niogit, etc)
download.dashboarddownloads jBPM dashboard component (BAM)
download.db.driverdownloads db driver configured in build.properties
download.ddl.dependenciesdownloads all dependencies required to run DDL script generation tool
download.droolsjbpm.eclipsedownloads drools and jbpm eclipse plugin
download.eclipsedownloads eclipse distribution
download.jbossdownloads Jboss Application Server
download.jBPM.bindownloads jBPM binary distribution (jBPM libs and its dependencies)
download.jBPM.consoledownloads jBPM console for JBoss AS
install.dashboard.into.jbossinstalls jBPM dashboard into JBoss AS
install.db.filesinstalls db driver as JBoss module
install.demoinstalls complete demo environment
install.demo.eclipseinstalls Eclipse with all jBPM plugins, no server installation
install.demo.noeclipsesimilar to install.demo but skips eclipse installation
install.dependenciesinstalls custom libraries (such as work item handlers, etc) into the jbpm console
install.droolsjbpm-eclipse.into.eclipseinstalls droolsjbpm eclipse plugin into eclipse
install.eclipseinstall eclipse IDE
install.jbossinstalls JBoss AS
install.jBPM-console.into.jbossinstalls jBPM console application into JBoss AS

Some common issues are explained below.

Q: What if the installer complains it cannot download component X?

A: Are you connected to the internet? Do you have a firewall turned on? Do you require a proxy? It might be possible that one of the locations we're downloading the components from is temporarily offline. Try downloading the components manually (possibly from alternate locations) and put them in the jbpm-installer/lib folder.

Q: What if the installer complains it cannot extract / unzip a certain jar/war/zip?

A: If your download failed while downloading a component, it is possible that the installer is trying to use an incomplete file. Try deleting the component in question from the jbpm-installer/lib folder and reinstall, so it will be downloaded again.

Q: What if I have been changing my installation (and it no longer works) and I want to start over again with a clean installation?

A: You can use ant clean.demo to remove all the installed components, so you end up with a fresh installation again.

Q: I sometimes see exceptions when trying to stop or restart certain services, what should I do?

A: If you see errors during shutdown, are you sure the services were still running? If you see exceptions during restart, are you sure the service you started earlier was successfully shutdown? Maybe try killing the services manually if necessary.

Q: Something seems to be going wrong when running Eclipse but I have no idea what. What can I do?

A: Always check the consoles for output like error messages or stack traces. You can also check the Eclipse Error Log for exceptions. Try adding an audit logger to your session to figure out what's happening at runtime, or try debugging your application.

Q: Something seems to be going wrong when running the a web-based application like the jbpm-console. What can I do?

A: You can check the server log for possible exceptions: jbpm-installer/jboss-as-{version}/standalone/log/server.log (for JBoss AS7) or jbpm-installer/jboss-as-{version}/server/default/log/server.log (for earlier versions).

For all other questions, try contacting the jBPM community as described in the Getting Started chapter.

This chapter introduces the API you need to load processes and execute them. For more detail on how to define the processes themselves, check out the chapter on BPMN 2.0.

To interact with the process engine (for example, to start a process), you need to set up a session. This session will be used to communicate with the process engine. A session needs to have a reference to a knowledge base, which contains a reference to all the relevant process definitions. This knowledge base is used to look up the process definitions whenever necessary. To create a session, you first need to create a knowledge base, load all the necessary process definitions (this can be from various sources, like from classpath, file system or process repository) and then instantiate a session.

Once you have set up a session, you can use it to start executing processes. Whenever a process is started, a new process instance is created (for that process definition) that maintains the state of that specific instance of the process.

For example, imagine you are writing an application to process sales orders. You could then define one or more process definitions that define how the order should be processed. When starting up your application, you first need to create a knowledge base that contains those process definitions. You can then create a session based on this knowledge base so that, whenever a new sales order comes in, a new process instance is started for that sales order. That process instance contains the state of the process for that specific sales request.

A knowledge base can be shared across sessions and usually is only created once, at the start of the application (as creating a knowledge base can be rather heavy-weight as it involves parsing and compiling the process definitions). Knowledge bases can be dynamically changed (so you can add or remove processes at runtime).

Sessions can be created based on a knowledge base and are used to execute processes and interact with the engine. You can create as many independent session as you need and creating a session is considered relatively lightweight. How many sessions you create is up to you. In general, most simple cases start out with creating one session that is then called from various places in your application. You could decide to create multiple sessions if for example you want to have multiple independent processing units (for example, if you want all processes from one customer to be completely independent from processes for another customer, you could create an independent session for each customer) or if you need multiple sessions for scalability reasons. If you don't know what to do, simply start by having one knowledge base that contains all your process definitions and create one session that you then use to execute all your processes.

The jBPM project has a clear separation between the API the users should be interacting with and the actual implementation classes. The public API exposes most of the features we believe "normal" users can safely use and should remain rather stable across releases. Expert users can still access internal classes but should be aware that they should know what they are doing and that the internal API might still change in the future.

As explained above, the jBPM API should thus be used to (1) create a knowledge base that contains your process definitions, and to (2) create a session to start new process instances, signal existing ones, register listeners, etc.

Once you've loaded your knowledge base, you should create a session to interact with the engine. This session can then be used to start new processes, signal events, etc. The following code snippet shows how easy it is to create a session based on the previously created knowledge base, and to start a process (by id).



KieSession ksession = kbase.newKieSession();
ProcessInstance processInstance = ksession.startProcess("com.sample.MyProcess");

The ProcessRuntime interface defines all the session methods for interacting with processes, as shown below.



  /**
     * Start a new process instance.  The process (definition) that should
     * be used is referenced by the given process id.
     * 
     * @param processId  The id of the process that should be started
     * @return the ProcessInstance that represents the instance of the process that was started
     */
    ProcessInstance startProcess(String processId);
    /**
     * Start a new process instance.  The process (definition) that should
     * be used is referenced by the given process id.  Parameters can be passed
     * to the process instance (as name-value pairs), and these will be set
     * as variables of the process instance. 
     * 
     * @param processId  the id of the process that should be started
     * @param parameters  the process variables that should be set when starting the process instance 
     * @return the ProcessInstance that represents the instance of the process that was started
     */
    ProcessInstance startProcess(String processId,
                                 Map<String, Object> parameters);
    /**
     * Signals the engine that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All process instances that are listening to this type
     * of (external) event will be notified.  For performance reasons, this type of event
     * signaling should only be used if one process instance should be able to notify
     * other process instances. For internal event within one process instance, use the
     * signalEvent method that also include the processInstanceId of the process instance
     * in question. 
     * 
     * @param type the type of event
     * @param event the data associated with this event
     */
    void signalEvent(String type,
                     Object event);
    /**
     * Signals the process instance that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All node instances inside the given process instance that
     * are listening to this type of (internal) event will be notified.  Note that the event
     * will only be processed inside the given process instance.  All other process instances
     * waiting for this type of event will not be notified.
     * 
     * @param type the type of event
     * @param event the data associated with this event
     * @param processInstanceId the id of the process instance that should be signaled
     */
    void signalEvent(String type,
                     Object event,
                     long processInstanceId);
    /**
     * Returns a collection of currently active process instances.  Note that only process
     * instances that are currently loaded and active inside the engine will be returned.
     * When using persistence, it is likely not all running process instances will be loaded
     * as their state will be stored persistently.  It is recommended not to use this
     * method to collect information about the state of your process instances but to use
     * a history log for that purpose.
     * 
     * @return a collection of process instances currently active in the session
     */
    Collection<ProcessInstance> getProcessInstances();
    /**
     * Returns the process instance with the given id.  Note that only active process instances
     * will be returned.  If a process instance has been completed already, this method will return
     * null.
     * 
     * @param id the id of the process instance
     * @return the process instance with the given id or null if it cannot be found
     */
    ProcessInstance getProcessInstance(long processInstanceId);
    /**
     * Aborts the process instance with the given id.  If the process instance has been completed
     * (or aborted), or the process instance cannot be found, this method will throw an
     * IllegalArgumentException.
     * 
     * @param id the id of the process instance
     */
    void abortProcessInstance(long processInstanceId);
    /**
     * Returns the WorkItemManager related to this session.  This can be used to
     * register new WorkItemHandlers or to complete (or abort) WorkItems.
     * 
     * @return the WorkItemManager related to this session
     */
    WorkItemManager getWorkItemManager();

Common requriement when working with processes is ability to assign given process instance sort of business identifier that can be later on referenced without knowing the actual (generated) id of the process instance. To provide such capabilities jBPM allows to use CorrelationKey that is composed of CorrelationProperties. CorrelationKey can have either single property describing it (which is in most cases) but it can be represented as multi valued properties set.

Correlation capabilities are provided as part of interface

CorrelationAwareProcessRuntime

that exposes following methods:



      /**
      * Start a new process instance.  The process (definition) that should
      * be used is referenced by the given process id.  Parameters can be passed
      * to the process instance (as name-value pairs), and these will be set
      * as variables of the process instance.
      *
      * @param processId  the id of the process that should be started
      * @param correlationKey custom correlation key that can be used to identify process instance
      * @param parameters  the process variables that should be set when starting the process instance
      * @return the ProcessInstance that represents the instance of the process that was started
      */
      ProcessInstance startProcess(String processId, CorrelationKey correlationKey, Map<String, Object> parameters);
      /**
      * Creates a new process instance (but does not yet start it).  The process
      * (definition) that should be used is referenced by the given process id.
      * Parameters can be passed to the process instance (as name-value pairs),
      * and these will be set as variables of the process instance.  You should only
      * use this method if you need a reference to the process instance before actually
      * starting it.  Otherwise, use startProcess.
      *
      * @param processId  the id of the process that should be started
      * @param correlationKey custom correlation key that can be used to identify process instance
      * @param parameters  the process variables that should be set when creating the process instance
      * @return the ProcessInstance that represents the instance of the process that was created (but not yet started)
      */
      ProcessInstance createProcessInstance(String processId, CorrelationKey correlationKey, Map<String, Object> parameters);
      /**
      * Returns the process instance with the given correlationKey.  Note that only active process instances
      * will be returned.  If a process instance has been completed already, this method will return
      * null.
      *
      * @param correlationKey the custom correlation key assigned when process instance was created
      * @return the process instance with the given id or null if it cannot be found
      */
      ProcessInstance getProcessInstance(CorrelationKey correlationKey);
    

Correlation is usually used with long running processes and thus require persistence to be enabled to be able to permanently store correlation information.

The session provides methods for registering and removing listeners. A ProcessEventListener can be used to listen to process-related events, like starting or completing a process, entering and leaving a node, etc. Below, the different methods of the ProcessEventListener class are shown. An event object provides access to related information, like the process instance and node instance linked to the event. You can use this API to register your own event listeners.

public interface ProcessEventListener {


  void beforeProcessStarted( ProcessStartedEvent event );
  void afterProcessStarted( ProcessStartedEvent event );
  void beforeProcessCompleted( ProcessCompletedEvent event );
  void afterProcessCompleted( ProcessCompletedEvent event );
  void beforeNodeTriggered( ProcessNodeTriggeredEvent event );
  void afterNodeTriggered( ProcessNodeTriggeredEvent event );
  void beforeNodeLeft( ProcessNodeLeftEvent event );
  void afterNodeLeft( ProcessNodeLeftEvent event );
  void beforeVariableChanged(ProcessVariableChangedEvent event);
  void afterVariableChanged(ProcessVariableChangedEvent event);
}

A note about before and after events: these events typically act like a stack, which means that any events that occur as a direct result of the previous event, will occur between the before and the after of that event. For example, if a subsequent node is triggered as result of leaving a node, the node triggered events will occur inbetween the beforeNodeLeftEvent and the afterNodeLeftEvent of the node that is left (as the triggering of the second node is a direct result of leaving the first node). Doing that allows us to derive cause relationships between events more easily. Similarly, all node triggered and node left events that are the direct result of starting a process will occur between the beforeProcessStarted and afterProcessStarted events. In general, if you just want to be notified when a particular event occurs, you should be looking at the before events only (as they occur immediately before the event actually occurs). When only looking at the after events, one might get the impression that the events are fired in the wrong order, but because the after events are triggered as a stack (after events will only fire when all events that were triggered as a result of this event have already fired). After events should only be used if you want to make sure that all processing related to this has ended (for example, when you want to be notified when starting of a particular process instance has ended.

Also note that not all nodes always generate node triggered and/or node left events. Depending on the type of node, some nodes might only generate node left events, others might only generate node triggered events. Catching intermediate events for example are not generating triggered events (they are only generating left events, as they are not really triggered by another node, rather activated from outside). Similarly, throwing intermediate events are not generating left events (they are only generating triggered events, as they are not really left, as they have no outgoing connection).

jBPM out-of-the-box provides a listener that can be used to create an audit log (either to the console or the a file on the file system). This audit log contains all the different events that occurred at runtime so it's easy to figure out what happened. Note that these loggers should only be used for debugging purposes. The following logger implementations are supported by default:

The KnowledgeRuntimeLoggerFactory lets you add a logger to your session, as shown below. When creating a console logger, the knowledge session for which the logger needs to be created must be passed as an argument. The file logger also requires the name of the log file to be created, and the threaded file logger requires the interval (in milliseconds) after which the events should be saved. You should always close the logger at the end of your application.



  KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger( ksession, "test" );
  // add invocations to the process engine here,
  // e.g. ksession.startProcess(processId);
  ...
  logger.close();

The log file that is created by the file-based loggers contains an XML-based overview of all the events that occurred at runtime. It can be opened in Eclipse, using the Audit View in the Drools Eclipse plugin, where the events are visualized as a tree. Events that occur between the before and after event are shown as children of that event. The following screenshot shows a simple example, where a process is started, resulting in the activation of the Start node, an Action node and an End node, after which the process was completed.

RuntimeManager has been introduced to simplify and empower usage of knowledge API especially in context of processes. It provides configurable strategies that control actual runtime execution (how KieSessions are provided) and by default provides following:

With that the complexity of knowing when to create, dispose, register handlers, etc is taken away from the end user and moved to the runtime manager that knows when/how to perform such operations but still allows to have a fine grained control over this process by providing comprehensive configuration of the RuntimeEnvironment.



  public interface RuntimeEnvironment {
    KieBase getKieBase();
    Environment getEnvironment();
    KieSessionConfiguration getConfiguration();
    boolean usePersistence();
    RegisterableItemsFactory getRegisterableItemsFactory();
    Mapper getMapper();
    UserGroupCallback getUserGroupCallback();
    ClassLoader getClassLoader();
    void close();
  }
  

While this interface provides mostly access to data kept as part of the runtime environment, the default implementation that is then considered as base for any extensions allows configuring the actual environment



    public class SimpleRuntimeEnvironment .... {
      ....
    public void addToEnvironment(String name, Object value)
    public void addToConfiguration(String name, String value)
    public void setUserGroupCallback(UserGroupCallback userGroupCallback)
    public void setSessionConfigProperties(Properties sessionConfigProperties)
    public void setUsePersistence(boolean usePersistence)
    public void setKieBase(KieBase kbase)
    public void setMapper(Mapper mapper)
    public void setSchedulerService(GlobalSchedulerService schedulerService)
    public void setRegisterableItemsFactory(RegisterableItemsFactory registerableItemsFactory)
    public void setEmf(EntityManagerFactory emf)
    public void setClassLoader(ClassLoader classLoader)
      ....
    }
  

Besides KieSession Runtime Manager provides access to TaskService too as integrated component of a RuntimeEngine that will always be configured and ready for communication between process engine and task service.

More about RuntimeManager and RuntimeEngine can be found in RuntimeManager chapter just to give a short preview of how it is used, here is how you can build RuntimeManager and get RuntimeEngine (that encapsulates KieSession and TaskService) from it:



    // first configure environment that will be used by RuntimeManager
    RuntimeEnvironment environment = RuntimeEnvironmentBuilder.getEmpty()
    .addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
    .get();
    // next create RuntimeManager - in this case singleton strategy is chosen
    RuntimeManager manager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(environment);
    // then get RuntimeEngine out of manager - using empty context as singleton does not kep track
    // of runtime engine as there is only one
    RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get());
    // get KieSession from runtime runtimeEngine - already initialized with all handlers, listeners, etc that were configured
    // on the environment
    KieSession ksession = runtimeEngine.getKieSession();
    // add invocations to the process engine here,
    // e.g. ksession.startProcess(processId);
    // and last dispose the runtime engine
    manager.disposeRuntimeEngine(runtimeEngine);
  

There are several control parameters available to alter engine default behavior. This allows to fine tune the execution for the environment needs and actual requirements. All of these parameters are set as JVM system properties, usually with -D when starting program e.g. application server.

Table 4.1. Control parameters

NamePossible valuesDefault valueDescription 
jbpm.ut.jndi.lookupString Alternative JNDI name to be used when there is no access to the default one (java:comp/UserTransaction) 
jbpm.enable.multi.contrue|falsefalseEnables multiple incoming/outgoing sequence flows support for activities 
jbpm.business.calendar.propertiesString/jbpm.business.calendar.propertiesAllows to provide alternative classpath location of business calendar configuration file 
jbpm.overdue.timer.delayLong2000Specifies delay for overdue timers to allow proper initialization, in milliseconds 
jbpm.process.name.comparatorString Allows to provide alternative comparator class to empower start process by name feature, if not set NumberVersionComparator is used 
jbpm.loop.level.disabledtrue|falsetrueAllows to enable or disable loop iteration tracking, to allow advanced loop support when using XOR gateways 
org.kie.mail.sessionStringmail/jbpmMailSessionAllows to provide alternative JNDI name for mail session used by Task Deadlines 
jbpm.usergroup.callback.propertiesString/jbpm.usergroup.callback.propertiesAllows to provide alternative classpath location for user group callback implementation (LDAP, DB) 
jbpm.user.group.mappingString${jboss.server.config.dir}/roles.propertiesAllows to provide alternative location of roles.properties for JBossUserGroupCallbackImpl 
jbpm.user.info.propertiesString/jbpm.user.info.propertiesAllows to provide alternative classpath location of user info configuration (used by LDAPUserInfoImpl) 
org.jbpm.ht.user.separatorString,Allows to provide alternative separator of actors and groups for user tasks, default is comma (,) 
org.quartz.propertiesString Allows to provide location of the quartz config file to activate quartz based timer service 
jbpm.data.dirString${jboss.server.data.dir} is available otherwise ${java.io.tmpdir}Allows to provide location where data files produced by jbpm should be stored 
org.kie.executor.pool.sizeInteger1Allows to provide thread pool size for jbpm executor 
org.kie.executor.retry.countInteger3Allows to provide number of retries attempted in case of error by jbpm executor 
org.kie.executor.intervalInteger3Allows to provide frequency used to check for pending jobs by jbpm executor, in seconds 
org.kie.executor.disabledtrue|falsetrueEnables or disable jbpm executor 


A business process is a graph that describes the order in which a series of steps need to be executed, using a flow chart. A process consists of a collection of nodes that are linked to each other using connections. Each of the nodes represents one step in the overall process while the connections specify how to transition from one node to the other. A large selection of predefined node types have been defined. This chapter describes how to define such processes and use them in your application.

Processes can be created by using one of the following three methods:

The graphical BPMN2 editor is an editor that allows you to create a process by dragging and dropping different nodes on a canvas and editing the properties of these nodes. The graphical BPMN2 modeler is an Eclipse plugin hosted on eclipse.org that provides number of contributors where one of them is jBPM project. Once you have set up a jBPM project (see the installer for creating a working Eclipse environment where you can start), you can start adding processes. When in a project, launch the "New" wizard (use Ctrl+N) or right-click the directory you would like to put your process in and select "New", then "File". Give the file a name and the extension bpmn (e.g. MyProcess.bpmn). This will open up the process editor (you can safely ignore the warning that the file could not be read, this is just because the file is still empty).

First, ensure that you can see the Properties View down the bottom of the Eclipse window, as it will be necessary to fill in the different properties of the elements in your process. If you cannot see the properties view, open it using the menu "Window", then "Show View" and "Other...", and under the "General" folder select the Properties View.


The process editor consists of a palette, a canvas and an outline view. To add new elements to the canvas, select the element you would like to create in the palette and then add them to the canvas by clicking on the preferred location. For example, click on the "End Event" icon in the palette of the GUI. Clicking on an element in your process allows you to set the properties of that element. You can connect the nodes (as long as it is permitted by the different types of nodes) by using "Sequence Flow" from the palette.

You can keep adding nodes and connections to your process until it represents the business logic that you want to specify.

It is also possible to specify processes using the underlying BPMN 2.0 XML directly. The syntax of these XML processes is defined using the BPMN 2.0 XML Schema Definition. For example, the following XML fragment shows a simple process that contains a sequence of a Start Event, a Script Task that prints "Hello World" to the console, and an End Event.


<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.jboss.org/drools"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"Rule Task
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:g="http://www.jboss.org/drools/flow/gpd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="com.sample.hello" name="Hello Process" >

    <!-- nodes -->
    <startEvent id="_1" name="Start" />
    <scriptTask id="_2" name="Hello" >
      <script>System.out.println("Hello World");</script>
    </scriptTask>
    <endEvent id="_3" name="End" >
        <terminateEventDefinition/>
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="com.sample.hello" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="16" y="16" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="96" y="16" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="208" y="16" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_2" >
        <di:waypoint x="40" y="40" />
        <di:waypoint x="136" y="40" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_3" >
        <di:waypoint x="136" y="40" />
        <di:waypoint x="232" y="40" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>

The process XML file consists of two parts, the top part (the "process" element) contains the definition of the different nodes and their properties, the lower part (the "BPMNDiagram" element) contains all graphical information, like the location of the nodes. The process XML consist of exactly one <process> element. This element contains parameters related to the process (its type, name, id and package name), and consists of three subsections: a header section (where process-level information like variables, globals, imports and lanes can be defined), a nodes section that defines each of the nodes in the process, and a connections section that contains the connections between all the nodes in the process. In the nodes section, there is a specific element for each node, defining the various parameters and, possibly, sub-elements for that node type.

While it is recommended to define processes using the graphical editor or the underlying XML (to shield yourself from internal APIs), it is also possible to define a process using the Process API directly. The most important process model elements are defined in the packages org.jbpm.workflow.core and org.jbpm.workflow.core.node. A "fluent API" is provided that allows you to easily construct processes in a readable manner using factories. At the end, you can validate the process that you were constructing manually.

This is a simple example of a basic process with a script task only:



RuleFlowProcessFactory factory =
    RuleFlowProcessFactory.createProcess("org.jbpm.HelloWorld");
factory
    // Header
    .name("HelloWorldProcess")
    .version("1.0")
    .packageName("org.jbpm")
    // Nodes
    .startNode(1).name("Start").done()
    .actionNode(2).name("Action")
        .action("java", "System.out.println(\"Hello World\");").done()
    .endNode(3).name("End").done()
    // Connections
    .connection(1, 2)
    .connection(2, 3);
RuleFlowProcess process = factory.validate().getProcess();
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newByteArrayResource(
    XmlBPMNProcessDumper.INSTANCE.dump(process).getBytes()), ResourceType.BPMN2);
KnowledgeBase kbase = kbuilder.newKnowledgeBase();
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ksession.startProcess("org.jbpm.HelloWorld");

You can see that we start by calling the static createProcess() method from the RuleFlowProcessFactory class. This method creates a new process with the given id and returns the RuleFlowProcessFactory that can be used to create the process. A typical process consists of three parts. The header part comprises global elements like the name of the process, imports, variables, etc. The nodes section contains all the different nodes that are part of the process. The connections section finally links these nodes to each other to create a flow chart.

In this example, the header contains the name and the version of the process and the package name. After that, you can start adding nodes to the current process. If you have auto-completion you can see that you have different methods to create each of the supported node types at your disposal.

When you start adding nodes to the process, in this example by calling the startNode(), actionNode() and endNode() methods, you can see that these methods return a specific NodeFactory, that allows you to set the properties of that node. Once you have finished configuring that specific node, the done() method returns you to the current RuleFlowProcessFactory so you can add more nodes, if necessary.

When you are finished adding nodes, you must connect them by creating connections between them. This can be done by calling the method connection, which will link previously created nodes.

Finally, you can validate the generated process by calling the validate() method and retrieve the created RuleFlowProcess object.

The following chapters will describe the different constructs that you can use to model your processes (and their properties) in detail. Executable processes in BPMN consist of different types of nodes being connected to each other using sequence flows. The BPMN 2.0 specification defines three main types of nodes:

The following sections will describe the properties of the process itself and of each of these different node types in detail, as supported by the Eclipse plugin and shown in the following figure of the palette. Note that the Eclipse property editor might show more properties for some of the supported node types, but only the properties as defined in this section are supported when using the BPMN 2.0 XML format.



Represents a timer that can trigger one or multiple times after a given period of time. A Timer Event should have one incoming connection and one outgoing connection. The timer delay specifies how long the timer should wait before triggering the first time. When a Timer Event is reached in the process, it will start the associated timer. The timer is cancelled if the timer node is cancelled (e.g., by completing or aborting the enclosing process instance). Consult the section “Timers” for more information. The Timer Event contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Timer delay: The delay that the node should wait before triggering the first time. The expression should be of the form [#d][#h][#m][#s][#[ms]]. This allows you to specify the number of days, hours, minutes, seconds and milliseconds (which is the default if you don't specify anything). For example, the expression "1h" will wait one hour before triggering the timer. The expression could also use #{expr} to dynamically derive the delay based on some process variable. Expr in this case could be a process variable, or a more complex expression based on a process variable (e.g. myVariable.getValue()).

  • Timer period: The period between two subsequent triggers. If the period is 0, the timer should only be triggered once. The expression should be of the form [#d][#h][#m][#s][#[ms]]. You can specify the number of days, hours, minutes, seconds and milliseconds (which is the default if you don't specify anything). For example, the expression "1h" will wait one hour before triggering the timer again. The expression could also use #{expr} to dynamically derive the period based on some process variable. Expr in this case could be a process variable, or a more complex expression based on a process variable (e.g. myVariable.getValue()).

Timer events could also be specified as boundary events on sub-processes and tasks that are not automatic tasks like script task that have no wait state as timer will not have a change to fire before task completion.


A Signal Event can be used to respond to internal or external events during the execution of the process. A Signal Event should have one incoming connections and one outgoing connection. It specifies the type of event that is expected. Whenever that type of event is detected, the node connected to this event node will be triggered. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • EventType: The type of event that is expected.

  • VariableName: The name of the variable that will contain the data associated with this event (if any) when this event occurs.

A process instance can be signaled that a specific event occurred using

ksession.signalEvent(eventType, data, processInstanceId)

This will trigger all (active) signal event nodes in the given process instance that are waiting for that event type. Data related to the event can be passed using the data parameter. If the event node specifies a variable name, this data will be copied to that variable when the event occurs.

It is also possible to use event nodes inside sub-processes. These event nodes will however only be active when the sub-process is active.

You can also generate a signal from inside a process instance. A script (in a script task or using on entry or on exit actions) can use

kcontext.getKnowledgeRuntime().signalEvent(eventType, data, kcontext.getProcessInstance().getId());
        

A throwing signal event could also be used to model the signaling of an event.


Represents a script that should be executed in this process. A Script Task should have one incoming connection and one outgoing connection. The associated action specifies what should be executed, the dialect used for coding the action (i.e., Java or MVEL), and the actual action code. This code can access any variables and globals. There is also a predefined variable kcontext that references the ProcessContext object (which can, for example, be used to access the current ProcessInstance or NodeInstance, and to get and set variables, or get access to the ksession using kcontext.getKnowledgeRuntime()). When a Script Task is reached in the process, it will execute the action and then continue with the next node. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Action: The action script associated with this action node.

Note that you can write any valid Java code inside a script node. This basically allows you to do anything inside such a script node. There are some caveats however:

  • When trying to create a higher-level business process, that should also be understood by business users, it is probably wise to avoid low-level implementation details inside the process, including inside these script tasks. A Script Task could still be used to quickly manipulate variables etc. but other concepts like a Service Task could be used to model more complex behaviour in a higher-level manner.
  • Scripts should be immediate. They are using the engine thread to execute the script. Scripts that could take some time to execute should probably be modeled as an asynchronous Service Task.
  • You should try to avoid contacting external services through a script node. Not only does this usually violate the first two caveats, it is also interacting with external services without the knowledge of the engine, which can be problematic, especially when using persistence and transactions. In general, it is probably wiser to model communication with an external service using a service task.
  • Scripts should not throw exceptions. Runtime exceptions should be caught and for example managed inside the script or transformed into signals or errors that can then be handled inside the process.


Represents an (abstract) unit of work that should be executed in this process. All work that is executed outside the process engine should be represented (in a declarative way) using a Service Task. Different types of services are predefined, e.g., sending an email, logging a message, etc. Users can define domain-specific services or work items, using a unique name and by defining the parameters (input) and results (output) that are associated with this type of work. Check the chapter on domain-specific processes for a detailed explanation and illustrative examples of how to define and use work items in your processes. When a Service Task is reached in the process, the associated work is executed. A Service Task should have one incoming connection and one outgoing connection.

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Parameter mapping: Allows copying the value of process variables to parameters of the work item. Upon creation of the work item, the values will be copied.

  • Result mapping: Allows copying the value of result parameters of the work item to a process variable. Each type of work can define result parameters that will (potentially) be returned after the work item has been completed. A result mapping can be used to copy the value of the given result parameter to the given variable in this process. For example, the "FileFinder" work item returns a list of files that match the given search criteria within the result parameter Files. This list of files can then be bound to a process variable for use within the process. Upon completion of the work item, the values will be copied.

  • On-entry and on-exit actions: Actions that are executed upon entry or exit of this node, respectively.

  • Additional parameters: Each type of work item can define additional parameters that are relevant for that type of work. For example, the "Email" work item defines additional parameters such as From, To, Subject and Body. The user can either provide values for these parameters directly, or define a parameter mapping that will copy the value of the given variable in this process to the given parameter; if both are specified, the mapping will have precedence. Parameters of type String can use #{expression} to embed a value in the string. The value will be retrieved when creating the work item, and the substitution expression will be replaced by the result of calling toString() on the variable. The expression could simply be the name of a variable (in which case it resolves to the value of the variable), but more advanced MVEL expressions are possible as well, e.g., #{person.name.firstname}.


Processes can also involve tasks that need to be executed by human actors. A User Task represents an atomic task to be executed by a human actor. It should have one incoming connection and one outgoing connection. User Tasks can be used in combination with Swimlanes to assign multiple human tasks to similar actors. Refer to the chapter on human tasks for more details. A User Task is actually nothing more than a specific type of service node (of type "Human Task"). A User Task contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • TaskName: The name of the human task.

  • Priority: An integer indicating the priority of the human task.

  • Comment: A comment associated with the human task.

  • ActorId: The actor id that is responsible for executing the human task. A list of actor id's can be specified using a comma (',') as separator.

  • GroupId: The group id that is responsible for executing the human task. A list of group id's can be specified using a comma (',') as separator.

  • Skippable: Specifies whether the human task can be skipped, i.e., whether the actor may decide not to execute the task.

  • Content: The data associated with this task.

  • Swimlane: The swimlane this human task node is part of. Swimlanes make it easy to assign multiple human tasks to the same actor. See the human tasks chapter for more detail on how to use swimlanes.

  • On entry and on exit actions: Action scripts that are executed upon entry and exit of this node, respectively.

  • Parameter mapping: Allows copying the value of process variables to parameters of the human task. Upon creation of the human tasks, the values will be copied.

  • Result mapping: Allows copying the value of result parameters of the human task to a process variable. Upon completion of the human task, the values will be copied. A human task has a result variable "Result" that contains the data returned by the human actor. The variable "ActorId" contains the id of the actor that actually executed the task.

A user task should define the type of task that needs to be executed (using properties like TaskName, Comment, etc.) and who needs to perform it (using either actorId or groupId). Note that if there is data related to this specific process instance that the end user needs when performing the task, this data should be passed as the content of the task. The task for example does not have access to process variables. Check out the chapter on human tasks to get more detail on how to pass data between human tasks and the process instance.


Represents the invocation of another process from within this process. A sub-process node should have one incoming connection and one outgoing connection. When a Reusable Sub-Process node is reached in the process, the engine will start the process with the given id. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • ProcessId: The id of the process that should be executed.

  • Wait for completion (by default true): If this property is true, this sub-process node will only continue if the child process that was started has terminated its execution (completed or aborted); otherwise it will continue immediately after starting the subprocess (so it will not wait for its completion).

  • Independent (by default true): If this property is true, the child process is started as an independent process, which means that the child process will not be terminated if this parent process is completed (or this sub-process node is cancelled for some other reason); otherwise the active sub-process will be cancelled on termination of the parent process (or cancellation of the sub-process node). Note that you can only set independent to "false" only when "Wait for completion" is set to true.

  • On-entry and on-exit actions: Actions that are executed upon entry or exit of this node, respectively.

  • Parameter in/out mapping: A sub-process node can also define in- and out-mappings for variables. The variables given in the "in" mapping will be used as parameters (with the associated parameter name) when starting the process. The variables of the child process that are defined for the "out" mappings will be copied to the variables of this process when the child process has been completed. Note that you can use "out" mappings only when "Wait for completion" is set to true.


Allows you to create branches in your process. A Diverging Gateway should have one incoming connection and two or more outgoing connections. There are three types of gateway nodes currently supported:

  • AND or parallel means that the control flow will continue in all outgoing connections simultaneously.

  • XOR or exclusive means that exactly one of the outgoing connections will be chosen. The decision is made by evaluating the constraints that are linked to each of the outgoing connections. The constraint with the lowest priority number that evaluates to true is selected. Constraints can be specified using different dialects. Note that you should always make sure that at least one of the outgoing connections will evaluate to true at runtime (the engine will throw an exception at runtime if it cannot find at least one outgoing connection).

  • OR or inclusive means that all outgoing connections whose condition evaluates to true are selected. Conditions are similar to the exclusive gateway, except that no priorities are taken into account. Note that you should make sure that at least one of the outgoing connections will evaluate to true at runtime because the engine will throw an exception at runtime if it cannot determine an outgoing connection.

It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Type: The type of the split node, i.e., AND, XOR or OR (see above).

  • Constraints: The constraints linked to each of the outgoing connections (in case of an exclusive or inclusive gateway).

As explained in more detail in the API chapter, there are two things you need to do to be able to execute processes from within your application: (1) you need to create a Knowledge Base that contains the definition of the process, and (2) you need to start the process by creating a session to communicate with the process engine and start the process.

  1. Creating a Knowledge Base: Once you have a valid process, you can add the process to the Knowledge Base:

    
    
    KieHelper kieHelper = new KieHelper();
    KieBase kieBase = kieHelper
    .addResource(ResourceFactory.newClassPathResource("MyProcess.bpmn2"));
          

    After adding all your process (you can add more than one process), you can create a new knowledge base like this:

    
    
    KieBase kbase = kieHelper.build();
          

    Note that this will throw an exception if the knowledge base contains errors (because it could not parse your processes correctly).

  2. Starting a process: To start a particular process, you will need to call the startProcess method on your session and pass the id of the process you want to start. For example:

    
    
    KieSession ksession = kbase.newKieSession();
    ksession.startProcess("com.sample.hello");

    The parameter of the startProcess method is the id of the process that needs to be started. When defining a process, this process id needs to be specified as a property of the process (as for example shown in the Properties View in Eclipse when you click the background canvas of your process).

    When you start the process, you may specify additional parameters that are used to pass additional input data to the process, using the startProcess(String processId, Map parameters) method. The additional set of parameters is a set of name-value pairs. These parameters are copied to the newly created process instance as top-level variables of the process, so they can be accessed in the remainder of your process directly.

While the flow chart focuses on specifying the control flow of the process, it is usually also necessary to look at the process from a data perspective. Throughout the execution of a process, data can be retrieved, stored, passed on and used.

For storing runtime data, during the execution of the process, process variables can be used. A variable is defined by a name and a data type. This could be a basic data type, such as boolean, int, or String, or any kind of Object subclass (it must implement Serializable interface). Variables can be defined inside a variable scope. The top-level scope is the variable scope of the process itself. Subscopes can be defined using a Sub-Process. Variables that are defined in a subscope are only accessible for nodes within that scope.

Whenever a variable is accessed, the process will search for the appropriate variable scope that defines the variable. Nesting of variable scopes is allowed. A node will always search for a variable in its parent container. If the variable cannot be found, it will look in that one's parent container, and so on, until the process instance itself is reached. If the variable cannot be found, a read access yields null, and a write access produces an error message, with the process continuing its execution.

Variables can be used in various ways:

Finally, processes (and rules) all have access to globals, i.e. globally defined variables and data in the Knowledge Session. Globals are directly accessible in actions just like variables. Globals need to be defined as part of the process before they can be used. You can for example define globals by clicking the globals button when specifying an action script in the Eclipse action property editor. You can also set the value of a global from the outside using ksession.setGlobal(name, value) or from inside process scripts using kcontext.getKnowledgeRuntime().setGlobal(name,value);.

Constraints can be used in various locations in your processes, for example in a diverging gateway. jBPM supports two types of constraints:

Rule constraints do not have direct access to variables defined inside the process. It is however possible to refer to the current process instance inside a rule constraint, by adding the process instance to the Working Memory and matching for the process instance in your rule constraint. We have added special logic to make sure that a variable processInstance of type WorkflowProcessInstance will only match to the current process instance and not to other process instances in the Working Memory. Note that you are however responsible yourself to insert the process instance into the session and, possibly, to update it, for example, using Java code or an on-entry or on-exit or explicit action in your process. The following example of a rule constraint will search for a person with the same name as the value stored in the variable "name" of the process:

processInstance : WorkflowProcessInstance()
Person( name == ( processInstance.getVariable("name") ) )
# add more constraints here ...

Action scripts can be used in different ways:

Actions have access to globals and the variables that are defined for the process and the predefined variable kcontext. This variable is of type org.kie.api.runtime.process.ProcessContext and can be used for several tasks:

jBPM currently supports two dialects, Java and MVEL. Java actions should be valid Java code. MVEL actions can use the business scripting language MVEL to express the action. MVEL accepts any valid Java code but additionally provides support for nested accesses of parameters (e.g., person.name instead of person.getName()), and many other scripting improvements. Thus, MVEL expressions are more convenient for the business user. For example, an action that prints out the name of the person in the "requester" variable of the process would look like this:

// Java dialect
System.out.println( person.getName() );

//  MVEL dialect
System.out.println( person.name );
    

During the execution of a process, the process engine makes sure that all the relevant tasks are executed according to the process plan, by requesting the execution of work items and waiting for the results. However, it is also possible that the process should respond to events that were not directly requested by the process engine. Explicitly representing these events in a process allows the process author to specify how the process should react to such events.

Events have a type and possibly data associated with them. Users are free to define their own event types and their associated data.

A process can specify how to respond to events by using a Message Event. An Event node needs to specify the type of event the node is interested in. It can also define the name of a variable, which will receive the data that is associated with the event. This allows subsequent nodes in the process to access the event data and take appropriate action based on this data.

An event can be signaled to a running instance of a process in a number of ways:

  • Internal event: Any action inside a process (e.g., the action of an action node, or an on-entry or on-exit action of some node) can signal the occurrence of an internal event to the surrounding process instance, using code like the following:

    kcontext.getProcessInstance().signalEvent(type, eventData);
  • External event: A process instance can be notified of an event from outside using code such as:

    processInstance.signalEvent(type, eventData);
  • External event using event correlation: Instead of notifying a process instance directly, it is also possible to have the engine automatically determine which process instances might be interested in an event using event correlation, which is based on the event type. A process instance that contains an event node listening to external events of some type is notified whenever such an event occurs. To signal such an event to the process engine, write code such as:

    ksession.signalEvent(type, eventData);

Events could also be used to start a process. Whenever a Message Start Event defines an event trigger of a specific type, a new process instance will be started every time that type of event is signalled to the process engine.

Timers wait for a predefined amount of time, before triggering, once or repeatedly. They can be used to trigger certain logic after a certain period, or to repeat some action at regular intervals.

since version 6 timers can be configured with valid ISO8601 date format that supports both one shot timers and repeatable timers. Timers can be defined as data dn time representation, time duration or repeating intervals

  • Date - 2013-12-24T20:00:00.000+02:00 - fires exactly at Christmas Eve at 8PM
  • Duration - PT1S - fires once after 1 second
  • Repeatable intervals - R/PT1S - fires every second, no limit, alternatively R5/PT1S will fire 5 times every second

The timer service is responsible for making sure that timers get triggered at the appropriate times. Timers can also be cancelled, meaning that the timer will no longer be triggered.

Timers can be used in two ways inside a process:

Over time, processes may evolve, for example because the process itself needs to be improved, or due to changing requirements. Actually, you cannot really update a process, you can only deploy a new version of the process, the old process will still exist. That is because existing process instances might still need that process definition. So the new process should have a different id, though the name could be the same, and you can use the version parameter to show when a process is updated (the version parameter is just a String and is not validated by the process framework itself, so you can select your own format for specifying minor/major updates, etc.).

Whenever a process is updated, it is important to determine what should happen to the already running process instances. There are various strategies one could consider for each running instance:

By default, jBPM uses the proceed approach, meaning that multiple versions of the same process can be deployed, but existing process instances will simply continue executing based on the process definition that was used when starting the process instance. Running process instances could always be aborted as well of course, using the process management API. Process instance migration is more difficult and is explained in the following paragraphs.

A process instance contains all the runtime information needed to continue execution at some later point in time. This includes all the data linked to this process instance (as variables), but also the current state in the process diagram. For each node that is currently active, a node instance is used to represent this. This node instance can also contain additional state linked to the execution of that specific node only. There are different types of node instances, one for each type of node.

A process instance only contains the runtime state and is linked to a particular process (indirectly, using id references) that represents the process logic that needs to be followed when executing this process instance (this clear separation of definition and runtime state allows reuse of the definition across all process instances based on this process and minimizes runtime state). As a result, updating a running process instance to a newer version so it uses the new process logic instead of the old one is simply a matter of changing the referenced process id from the old to the new id.

However, this does not take into account that the state of the process instance (the variable instances and the node instances) might need to be migrated as well. In cases where the process is only extended and all existing wait states are kept, this is pretty straightforward, the runtime state of the process instance does not need to change at all. However, it is also possible that a more sophisticated mapping is necessary. For example, when an existing wait state is removed, or split into multiple wait states, an existing process instance that is waiting in that state cannot simply be updated. Or when a new process variable is introduced, that variable might need to be initiated correctly so it can be used in the remainder of the (updated) process.

The WorkflowProcessInstanceUpgrader can be used to upgrade a workflow process instance to a newer process instance. Of course, you need to provide the process instance and the new process id. By default, jBPM will automatically map old node instances to new node instances with the same id. But you can provide a mapping of the old (unique) node id to the new node id. The unique node id is the node id, preceded by the node ids of its parents (with a colon inbetween), to uniquely identify a node when composite nodes are used (as a node id is only unique within its node container. The new node id is simply the new node id in the node container (so no unique node id here, simply the new node id). The following code snippet shows a simple example.

// create the session and start the process "com.sample.process"

KieSession ksession = ...
ProcessInstance processInstance = ksession.startProcess("com.sample.process");
// add a new version of the process "com.sample.process2"
kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(..., ResourceType.BPMN2);
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
// migrate process instance to new version
Map<String, Long> mapping = new HashMap<String, Long>();
// top level node 2 is mapped to a new node with id 3
mapping.put("2", 3L); 
// node 2, which is part of composite node 5, is mapped to a new node with id 4
mapping.put("5.2", 4L); 
WorkflowProcessInstanceUpgrader.upgradeProcessInstance(
   ksession, processInstance.getId(),
   "com.sample.process2", mapping);

If this kind of mapping is still insufficient, you can still describe your own custom mappers for specific situations. Be sure to first disconnect the process instance, change the state accordingly and then reconnect the process instance, similar to how the WorkflowProcessinstanceUpgrader does it.

In the following text, we will refer to two types of "multi-threading": logical and technical. Technical multi-threading is what happens when multiple threads or processes are started on a computer, for example by a Java or C program. Logical multi-threading is what we see in a BPM process after the process reaches a parallel gateway, for example. From a functional standpoint, the original process will then split into two processes that are executed in a parallel fashion.

Of course, the jBPM engine supports logical multi-threading: for example, processes that include a parallel gateway. We've chosen to implement logical multi-threading using one thread: a jBPM process that includes logical multi-threading will only be executed in one technical thread. The main reason for doing this is that multiple (technical) threads need to be be able to communicate state information with each other if they are working on the same process. This requirement brings with it a number of complications. While it might seem that multi-threading would bring performance benefits with it, the extra logic needed to make sure the different threads work together well means that this is not guaranteed. There is also the extra overhead incurred because we need to avoid race conditions and deadlocks.

In general, the jBPM engine executes actions in serial. For example, when the engine encounters a script task in a process, it will synchronously execute that script and wait for it to complete before continuing execution. Similarly, if a process encounters a parallel gateway, it will sequentially trigger each of the outgoing branches, one after the other. This is possible since execution is almost always instantaneous, meaning that it is extremely fast and produces almost no overhead. As a result, the user will usually not even notice this. Similarly, action scripts in a process are also synchronously executed, and the engine will wait for them to finish before continuing the process. For example, doing a Thread.sleep(...) as part of a script will not make the engine continue execution elsewhere but will block the engine thread during that period.

The same principle applies to service tasks. When a service task is reached in a process, the engine will also invoke the handler of this service synchronously. The engine will wait for the completeWorkItem(...) method to return before continuing execution. It is important that your service handler executes your service asynchronously if its execution is not instantaneous.

An example of this would be a service task that invokes an external service. Since the delay in invoking this service remotely and waiting for the results might be too long, it might be a good idea to invoke this service asynchronously. This means that the handler will only invoke the service and will notify the engine later when the results are available. In the mean time, the process engine then continues execution of the process.

Human tasks are a typical example of a service that needs to be invoked asynchronously, as we don't want the engine to wait until a human actor has responded to the request. The human task handler will only create a new task (on the task list of the assigned actor) when the human task node is triggered. The engine will then be able to continue execution on the rest of the process (if necessary) and the handler will notify the engine asynchronously when the user has completed the task.

How can we implement an asynchronous service handler? To start with, this depends on the technology you're using. If you're only using Java, you could execute the actual service in a new thread:

public class MyServiceTaskHandler implements WorkItemHandler {

        
  public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
    new Thread(new Runnable() {
      public void run() {
        // Do the heavy lifting here ...
      }
    }).start();
  }
  public void abortWorkItem(WorkItem workItem, WorkItemManager manager) {
  }
      
}

It's advisable to have your handler contact a service that executes the business operation, instead of having it perform the actual work. If anything goes wrong with a business operation, it doesn't affect your process. The loose coupling that this provides also gives you greater flexibility in reusing services and developing them.

For example, you can have your human task handler simply invoke the human task service to add a task there. To implement an asynchronous handler, you usually have to simply do an asynchronous invocation of this service. This usually depends on the technology you use to do the communication, but this might be as simple as asynchronously invoking a web service, or sending a JMS message to the external service.

jbpm-executor component provides advanced capabilities for asynchronous execution to ease the development efforts to empower solution when using jBPM. It provides out of the box

See chapter jbpm executor for more details.

The simplest way to run multiple processes is to run them all using one knowledge session. However, there are cases in which it's necessary to run multiple processes in different knowledge sessions, even in different (technical) threads. Both are supported by jBPM.

When we add persistence (using a database, for example) to a situation in which we have multiple knowledge sessions (and processes), there is a guideline that users should be aware of. The following paragraphs explain why this guideline is important to follow.

For example, a user could have a situation in which there are 2 (or more) threads running, each with its own knowledge session instance. On each thread, jBPM processes are being started using the local knowledge session instance.

In this use case, a race condition exists in which both thread A and thread B will have coincidentally simultaneously finished a process. At this point, because persistence is being used, both thread A and B will be commiting changes to the databse. If row-level locks are not possible, then the following situation can occur:

This is a deadlock situation which the database and application will not be able to solve.

However, if row-level locks are posible (and enabled!!) in the database (and tables used), then this situation will not occur.

The Business Process Model and Notation (BPMN) 2.0 specification is an OMG specification that not only defines a standard on how to graphically represent a business process (like BPMN 1.x), but now also includes execution semantics for the elements defined, and an XML format on how to store (and share) process definitions.

jBPM6 allows you to execute processes defined using the BPMN 2.0 XML format. That means that you can use all the different jBPM6 tooling to model, execute, manage and monitor your business processes using the BPMN 2.0 format for specifying your executable business processes. Actually, the full BPMN 2.0 specification also includes details on how to represent things like choreographies and collaboration. The jBPM project however focuses on that part of the specification that can be used to specify executable processes.

Executable processes in BPMN consist of a different types of nodes being connected to each other using sequence flows. The BPMN 2.0 specification defines three main types of nodes:

jBPM6 does not implement all elements and attributes as defined in the BPMN 2.0 specification. We do however support a significant subset, including the most common node types that can be used inside executable processes. This includes (almost) all elements and attributes as defined in the "Common Executable" subclass of the BPMN 2.0 specification, extended with some additional elements and attributes we believe are valuable in that context as well. The full set of elements and attributes that are supported can be found below, but it includes elements like:

For example, consider the following "Hello World" BPMN 2.0 process, which does nothing more that writing out a "Hello World" statement when the process is started.

An executable version of this process expressed using BPMN 2.0 XML would look something like this:


<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.example.org/MinimalExample"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
             xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="com.sample.HelloWorld" name="Hello World" >

    <!-- nodes -->
    <startEvent id="_1" name="StartProcess" />
    <scriptTask id="_2" name="Hello" >
      <script>System.out.println("Hello World");</script>
    </scriptTask>
    <endEvent id="_3" name="EndProcess" >
        <terminateEventDefinition/>
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="Minimal" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="15" y="91" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="95" y="88" width="83" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="258" y="86" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_2" >
        <di:waypoint x="39" y="115" />
        <di:waypoint x="75" y="46" />
        <di:waypoint x="136" y="112" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_3" >
        <di:waypoint x="136" y="112" />
        <di:waypoint x="240" y="240" />
        <di:waypoint x="282" y="110" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>

To create your own process using BPMN 2.0 format, you can

The following code fragment shows you how to load a BPMN2 process into your knowledge base ...



private static KnowledgeBase createKnowledgeBase() throws Exception {
    KieHelper kieHelper = new KieHelper();
    KieBase kieBase = kieHelper
    .addResource(ResourceFactory.newClassPathResource("sample.bpmn2"))
    .build();
    return kieBase;
}

... and how to execute this process ...

KieBase kbase = createKnowledgeBase();

KieSession ksession = kbase.newKieSession();
ksession.startProcess("com.sample.HelloWorld");

For more detail, check out the chapter on the API and the basics.

Table 6.1. Keywords

ElementSupported attributesSupported elementsExtension attributesExtension elements
definitions 

  • rootElement
  • BPMNDiagram

  
process

  • processType
  • isExecutable
  • name
  • id

  • property
  • laneSet
  • flowElement

  • packageName
  • adHoc
  • version

  • import
  • global

sequenceFlow

  • sourceRef
  • targetRef
  • isImmediate
  • name
  • id

  • conditionExpression

  • priority

 
interface

  • name
  • id
  • implementationRef

  • operation

  
operation

  • name
  • id
  • implementationRef

  • inMessageRef

  
laneSet 

  • lane

  
lane

  • name
  • id

  • flowNodeRef

  
import* 

  • name

  
global* 

  • identifier
  • type

  
     
Events    
startEvent

  • name
  • id
  • isInterrupting

  • dataOutput
  • dataOutputAssociation
  • outputSet
  • eventDefinition

  
endEvent

  • name
  • id

  • dataInput
  • dataInputAssociation
  • inputSet
  • eventDefinition

  
intermediateCatchEvent

  • name
  • id

  • dataOutput
  • dataOutputAssociation
  • outputSet
  • eventDefinition

  
intermediateThrowEvent

  • name
  • id

  • dataInput
  • dataInputAssociation
  • inputSet
  • eventDefinition

  
boundaryEvent

  • cancelActivity
  • attachedToRef
  • name
  • id

  • eventDefinition

  
terminateEventDefinition    
compensateEventDefinition

  • activityRef

  • documentation
  • extensionElements

  
conditionalEventDefinition 

  • condition

  
errorEventDefinition

  • errorRef

   
error

  • errorCode
  • id

   
escalationEventDefinition

  • escalationRef

   
escalation

  • escalationCode
  • id

   
messageEventDefinition

  • messageRef

   
message

  • itemRef
  • id

   
signalEventDefinition

  • signalRef

   
timerEventDefinition 

  • timeCycle
  • timeDuration
  • timerDate

  
     
Activities    
task

  • name
  • id

  • ioSpecification
  • dataInputAssociation
  • dataOutputAssociation

  • taskName

 
scriptTask

  • scriptFormat
  • name
  • id

  • script

  
script 

  • text[mixed content]

  
userTask

  • name
  • id

  • ioSpecification
  • dataInputAssociation
  • dataOutputAssociation
  • resourceRole
  • loopCharacteristics

 

  • onEntry-script
  • onExit-script

potentialOwner 

  • resourceAssignmentExpression

  
resourceAssignmentExpression 

  • expression

  
businessRuleTask

  • name
  • id

  • ioSpecification
  • dataInputAssociation
  • dataOutputAssociation

  • ruleFlowGroup

  • onEntry-script
  • onExit-script

manualTask

  • name
  • id

  

  • onEntry-script
  • onExit-script

sendTask

  • messageRef
  • name
  • id

  • ioSpecification
  • dataInputAssociation
  • loopCharacteristics

 

  • onEntry-script
  • onExit-script

receiveTask

  • messageRef
  • name
  • id

  • ioSpecification
  • dataOutputAssociation
  • loopCharacteristics

 

  • onEntry-script
  • onExit-script

serviceTask

  • operationRef
  • name
  • id
  • implementation

  • ioSpecification
  • dataInputAssociation
  • dataOutputAssociation
  • loopCharacteristics

 

  • onEntry-script
  • onExit-script

subProcess

  • name
  • id
  • triggeredByEvent

  • flowElement
  • property
  • loopCharacteristics

  
adHocSubProcess

  • cancelRemainingInstances
  • name
  • id

  • completionCondition
  • flowElement
  • property

  
callActivity

  • calledElement
  • name
  • id

  • ioSpecification
  • dataInputAssociation
  • dataOutputAssociation

  • waitForCompletion
  • independent

  • onEntry-script
  • onExit-script

multiInstanceLoopCharacteristics 

  • loopDataInputRef
  • inputDataItem
  • loopDataOutputRef
  • outputDataItem

  
onEntry-script*

  • scriptFormat

 

  • script

 
onExit-script*

  • scriptFormat

 

  • script

 
     
Gateways    
parallelGateway

  • gatewayDirection
  • name
  • id

   
eventBasedGateway

  • gatewayDirection
  • name
  • id

   
exclusiveGateway

  • default
  • gatewayDirection
  • name
  • id

   
inclusiveGateway

  • default
  • gatewayDirection
  • name
  • id

   
     
Data    
property

  • itemSubjectRef
  • id
  • name

   
dataObject

  • itemSubjectRef
  • id

   
itemDefinition

  • structureRef
  • id

   
ioSpecification 

  • dataInput
  • dataOutput
  • inputSet
  • outputSet

  
dataInput

  • name
  • id

   
dataInputAssociation 

  • sourceRef
  • targetRef
  • assignment

  
dataOutput

  • name
  • id

   
dataOutputAssociation 

  • sourceRef
  • targetRef
  • assignment

  
inputSet 

  • dataInputRefs

  
outputSet 

  • dataOutputRefs

  
assignment 

  • from
  • to

  
formalExpression

  • language

  • text[mixed content]

  
     
BPMNDI    
BPMNDiagram 

  • BPMNPlane

  
BPMNPlane

  • bpmnElement

  • BPMNEdge
  • BPMNShape

  
BPMNShape

  • bpmnElement

  • Bounds

  
BPMNEdge

  • bpmnElement

  • waypoint

  
Bounds

  • x
  • y
  • width
  • height

   
waypoint

  • x
  • y

   

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. Also, the process definitions themselves, and the history information (logs of current and previous process states already) can also be persisted.

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

If you configure the engine to use persistence, it will automatically store the runtime state into the database. You do not have to trigger persistence yourself, the engine will take care of this when persistence is enabled. Whenever you invoke the engine, it will make sure that any changes are stored at the end of that invocation, at so-called safe points. Whenever something goes wrong and you restore the engine from the database, you also should not reload the process instances and trigger them manually to resume execution, as process instances will automatically resume execution if they are triggered, like for example by a timer expiring, the completion of a task that was requested by that process instance, or a signal being sent to the process instance. The engine will automatically reload process instances on demand.

The runtime persistence data should in general be considered internal, meaning that you probably should not try to access these database tables directly and especially not try to modify these directly (as changing the runtime state of process instances without the engine knowing might have unexpected side-effects). In most cases where information about the current execution state of process instances is required, the use of a history log is mostly recommended (see below). In some cases, it might still be useful to for example query the internal database tables directly, but you should only do this if you know what you are doing.

jBPM uses a binary persistence mechanism, otherwise known as marshalling, which converts the state of the process instance into a binary dataset. When you use persistence with jBPM, this mechanism is used to save or retrieve the process instance state from the database. The same mechanism is also applied to the session state and any work item states.

When the process instance state is persisted, two things happen:

Apart from the process instance state, the session itself can also store some state, such as the state of timer jobs, or the session data that any business rules would be evaluated over. This session state is stored separately as a binary blob, along with the id of the session and some metadata. You can always restore session state by reloading the session with the given id. The session id can be retrieved using ksession.getId().

Note that the process instance binary datasets are usually 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 any existing variable values.

As a result of jBPM using marshalling, the data model is both simple and small:

jBPM data model

Figure 7.1. jBPM data model


The sessioninfo entity contains the state of the (knowledge) session in which the jBPM process instance is running.


The processinstanceinfo entity contains the state of the jBPM process instance.


The eventtypes entity contains information about events that a process instance will undergo or has undergone.


The workiteminfo entity contains the state of a work item.


The CorrelationKeyInfo entity contains information about correlation keys assigned to given process instance - loose relationship as this table is considered optional used only when correlation capabilities are required.


The CorrelationPropertyInfo entity contains information about correlation properties for given correlation key that is assigned to given process instance.


The ContextMappingInfo entity contains information about contextual information mapped to ksession. This is an internal part of RuntimeManager and can be considered optional when RuntimeManager is not used.


By default, the engine does not save runtime data persistently. This means you can use the engine completely without persistence (so not even requiring an in memory database) if necessary, for example for performance reasons, or when you would like to manage persistence yourself. It is, however, possible to configure the engine to do use persistence by configuring it to do so. This usually requires adding the necessary dependencies, configuring a datasource and creating the engine with persistence configured.

You need to make sure the necessary dependencies are available in the classpath of your application if you want to user persistence. By default, persistence is based on the Java Persistence API (JPA) and can thus work with several persistence mechanisms. We are using Hibernate by default.

If you're using the Eclipse IDE and the jBPM Eclipse plugin, you should make sure the necessary jars are added to your jBPM runtime directory. You don't really need to do anything (as the necessary dependencies should already be there) if you are using the jBPM runtime that is configured by default when using the jBPM installer, or if you downloaded and unzipped the jBPM runtime artifact (from the downloads) and pointed the jBPM plugin to that directory.

If you would like to manually add the necessary dependencies to your project, first of all, you need the jar file jbpm-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 and using an H2 in-memory database and Bitronix for JTA-based transaction management, the following list of additional dependencies is needed:

You can use the JPAKnowledgeService to create your knowledge session. This is slightly more complex, but gives you full access to the underlying configurations. You can create a new knowledge session using JPAKnowledgeService 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. For example:



// create the entity manager factory and register it in the environment
EntityManagerFactory emf =
    Persistence.createEntityManagerFactory( "org.jbpm.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 use 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 );

You need to add a persistence configuration to your classpath to configure JPA to use Hibernate and the H2 database (or your own preference), called persistence.xml in the META-INF directory, as shown below. For more details on how to change this for your own configuration, we refer to the JPA and Hibernate documentation for more information.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
      version="2.0"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
      http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
      xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>

  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/jbpm-ds</jta-data-source>
    <mapping-file>META-INF/JBPMorm.xml</mapping-file>
    <class>org.drools.persistence.info.SessionInfo</class>
    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
    <class>org.drools.persistence.info.WorkItemInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>
    <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</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.jta.platform"
                value="org.hibernate.service.jta.platform.internal.BitronixJtaPlatform"/>
    </properties>
  </persistence-unit>
</persistence>
    

This configuration file refers to a data source called "jdbc/jbpm-ds". If you run your application in an application server (like for example JBoss AS), these containers typically allow you to easily set up data sources using some configuration (like for example dropping a datasource configuration file in the deploy directory). Please refer to your application server documentation to know how to do this.

For example, if you're deploying to JBoss Application Server v5.x, you can create a datasource by dropping a configuration file in the deploy directory, for example:


<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>jdbc/jbpm-ds</jndi-name>
    <connection-url>jdbc:h2:tcp://localhost/~/test</connection-url>
    <driver-class>org.h2.jdbcx.JdbcDataSource</driver-class>
    <user-name>sa</user-name>
    <password></password>
  </local-tx-datasource>
</datasources>

If you are however executing in a simple Java environment, you can use the JBPMHelper class to do this for you (see below for tests only) or the following code fragment could be used to set up a data source (where we are using the H2 in-memory database in combination with Bitronix in this case).



PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName("jdbc/jbpm-ds");
ds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
ds.setMaxPoolSize(3);
ds.setAllowLocalTransactions(true);
ds.getDriverProperties().put("user", "sa");
ds.getDriverProperties().put("password", "sasa");
ds.getDriverProperties().put("URL", "jdbc:h2:mem:jbpm-db");
ds.getDriverProperties().put("driverClassName", "org.h2.Driver");
ds.init();

You need to configure the jBPM engine to use persistence, usually simply by using the appropriate constructor when creating your session. There are various ways to create a session (as we have tried to make this as easy as possible for you and have several utility classes for you, depending for example if you are trying to write a process junit test).

The easiest way to do this is to use the jbpm-test module that allows you to easily create and test your processes. The JBPMHelper class has a method to create a session, and uses a configuration file to configure this session, like whether you want to use persistence, the datasource to use, etc. The helper class will then do all the setup and configuration for you.

To configure persistence, create a jBPM.properties file and configure the following properties (note that the example below are the default properties, using an H2 in-memory database with persistence enabled, if you are fine with all of these properties, you don't need to add new properties file, as it will then use these properties by default):

# for creating a datasource
persistence.datasource.name=jdbc/jbpm-ds
persistence.datasource.user=sa
persistence.datasource.password=
persistence.datasource.url=jdbc:h2:tcp://localhost/~/jbpm-db
persistence.datasource.driverClassName=org.h2.Driver

# for configuring persistence of the session
persistence.enabled=true
persistence.persistenceunit.name=org.jbpm.persistence.jpa
persistence.persistenceunit.dialect=org.hibernate.dialect.H2Dialect

# for configuring the human task service
taskservice.enabled=true
taskservice.datasource.name=org.jbpm.task
taskservice.usergroupcallback=org.jbpm.services.task.identity.JBossUserGroupCallbackImpl
taskservice.usergroupmapping=classpath:/usergroups.properties
      

If you want to use persistence, you must make sure that the datasource (that you specified in the jBPM.properties file) is initialized correctly. This means that the database itself must be up and running, and the datasource should be registered using the correct name. If you would like to use an H2 in-memory database (which is usually very easy to do some testing), you can use the JBPMHelper class to start up this database, using:



JBPMHelper.startH2Server();
      

To register the datasource (this is something you always need to do, even if you're not using H2 as your database, check below for more options on how to configure your datasource), use:



JBPMHelper.setupDataSource();
      

Next, you can use the JBPMHelper class to create your session (after creating your knowledge base, which is identical to the case when you are not using persistence):



StatefulKnowledgeSession ksession = JBPMHelper.newStatefulKnowledgeSession(kbase);
      

Once you have done that, you can just call methods on this ksession (like startProcess) and the engine will persist all runtime state in the created datasource.

You can also use the JBPMHelper class to recreate your session (by restoring its state from the database, by passing in the session id (that you can retrieve using ksession.getId())):



StatefulKnowledgeSession ksession = JBPMHelper.loadStatefulKnowledgeSession(kbase, sessionId);
      

The jBPM engine supports JTA transactions. It also supports local transactions only when using Spring. It does not support pure local transactions at the moment. For more information about using Spring to set up persistence, please see the Spring chapter in the Drools integration guide.

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.jbpm.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" );
// commit the transaction
ut.commit();
    

Note that, if you use Bitronix as the transaction manager, you should also add a simple jndi.properties file in you root classpath to register the Bitronix transaction manager in JNDI. If you are using the jbpm-test module, this is already included by default. If not, create a file named jndi.properties with the following content:

java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory
    

If you would like to use a different JTA transaction manager, you can change the persistence.xml file to use your own transaction manager. For example, when running inside JBoss Application Server v5.x or v7.x, you can use the JBoss transaction manager. You need to change the transaction manager property in persistence.xml to:

<property name="hibernate.transaction.jta.platform" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
    

Special consideration need to be taken when embedding jBPM inside an application that executes in Container Managed Transaction (CMT) mode, for instance EJB beans. This especially applies to application servers that does not allow accessing UserTransaction instance from JNDI when being part of container managed transaction, e.g. WebSphere Application Server. Since default implementation of transaction manager in jBPM is based on UserTransaction to get transaction status which is used to decide if transaction should be started or not, in environments that prevent accessing UserTrancation it won't do its job. To secure proper execution in CMT environments a dedicated transaction manager implementation is provided:

org.jbpm.persistence.jta.ContainerManagedTransactionManager

This transaction manager expects that transaction is active and thus will always return ACTIVE when invoking getStatus method. Operations like begin, commit, rollback are no-op methods as transaction manager runs under managed transaction and can't affect it.

To configure this transaction manager following must be done:

With following configuration jBPM should run properly in CMT environment.

In many cases it will be useful (if not necessary) to store information about the execution of process instances, so that this information can be used afterwards. For example, sometimes we want to verify which actions have been executed for a particular process instance, or in general, we want to be able to monitor and analyze the efficiency of a particular process.

However, storing history information in the runtime database can result in the database rapidly increasing in size, not to mention the fact that monitoring and analysis queries might influence the performance of your runtime engine. This is why process execution history information can be stored separately.

This history log of execution information is created based on events that the process engine generates during execution. This is possible because the jBPM runtime engine provides a generic mechanism to listen to events. The necessary information can easily be extracted from these events and then persisted to a database. Filters can also be used to limit the scope of the logged information.

The jbpm-audit module contains an event listener that stores process-related information in a database using JPA or Hibernate directly. The data model itself contains three entities, one for process instance information, one for node instance information, and one for (process) variable instance information.


The ProcessInstanceLog table contains the basic log information about a process instance.


The NodeInstanceLog table contains more information about which nodes were actually executed inside each process instance. Whenever a node instance is entered from one of its incoming connections or is exited through one of its outgoing connections, that information is stored in this table.


The VariableInstanceLog table contains information about changes in variable instances. The default is to only generate log entries when (after) a variable changes. It's also possible to log entries before the variable (value) changes.


To log process history information in a database like this, you need to register the logger on your session (or working memory) like this:



EntityManagerFactory emf = ...;
StatefulKnowledgeSession ksession = ...;
AbstractAuditLogger auditLogger = AuditLoggerFactory.newJPAInstance(emf);
ksession.addProcessEventListener(auditLogger);
// invoke methods one your session here
      

Note that this logger is like any other audit logger, which means that you can add one or more filters by calling the method addFilter to ensure that only relevant information is stored in the database. Only information accepted by all your filters will appear in the database.

To specify the database where the information should be stored, modify the file persistence.xml file to include the audit log classes as well (ProcessInstanceLog, NodeInstanceLog and VariableInstanceLog), as shown below.


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>


<persistence
  version="2.0"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
  http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
  xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>

  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/jbpm-ds</jta-data-source>
    <mapping-file>META-INF/JBPMorm.xml</mapping-file>
    <class>org.drools.persistence.info.SessionInfo</class>
    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
    <class>org.drools.persistence.info.WorkItemInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>
    <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class>

    <class>org.jbpm.process.audit.ProcessInstanceLog</class>
    <class>org.jbpm.process.audit.NodeInstanceLog</class>
    <class>org.jbpm.process.audit.VariableInstanceLog</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.jta.platform"
      value="org.hibernate.service.jta.platform.internal.BitronixJtaPlatform"/>
    </properties>
  </persistence-unit>
</persistence>
    

All this information can easily be queried and used in a lot of different use cases, ranging from creating a history log for one specific process instance to analyzing the performance of all instances of a specific process.

This audit log should only be considered a default implementation. We don't know what information you need to store for analysis afterwards, and for performance reasons it is recommended to only store the relevant data. Depending on your use cases, you might define your own data model for storing the information you need, and use the process event listeners to extract that information.

The examples module contains a number of examples, from basic to advanced:

Check out the chapter on testing and debugging if you want to learn more how to debug these example processes.