JBoss jBPM 3.1

Workflow and BPM made practical


Table of Contents

1. Introduction
1.1. Overview
1.2. The jPDL starter kit
1.3. The JBoss jBPM graphical process designer
1.4. The JBoss jBPM core component
1.5. The JBoss jBPM console web application
1.6. The JBoss jBPM identity component
1.7. The JBoss jBPM scheduler
1.8. The JBoss jBPM database compatibility package
1.9. The JBoss jBPM BPEL extension
2. Getting started
2.1. Downloadables Overview
2.1.1. jBPM 3
2.1.2. jBPM Process Designer
2.1.3. jBPM BPEL extension
2.2. The JBoss jBPM project directory
2.3. CVS access
2.3.1. Anonymous CVS access
2.3.2. Developer CVS access
3. Tutorial
3.1. Hello World example
3.2. Database example
3.3. Context example: process variables
3.4. Task assignment example
3.5. Custom action example
4. Graph Oriented Programming
4.1. Introduction
4.1.1. Domain specific languages
4.1.2. Features of graph based languages
4.1.2.1. Support for wait states
4.1.2.2. Graphical representation
4.2. Graph Oriented Programming
4.2.1. The graph structure
4.2.2. An execution
4.2.3. A process language
4.2.4. Actions
4.2.5. Code example
4.3. Advanced Graph Oriented Programming Extensions
4.3.1. Process variables
4.3.2. Concurrent executions
4.3.3. Process composition
4.3.4. Synchronous execution
4.3.5. Asynchronous continuations
4.3.6. Persistence and Transactions
4.3.7. Services and the environment
4.4. Architecture
4.5. Application domains
4.5.1. Business Process Management (BPM)
4.5.1.1. Goals of BPM systems
4.5.1.2. Process development process
4.5.2. Service orchestration
4.5.2.1. Orchestration compared to Choreography
4.5.3. Pageflow
4.5.4. Visual programming
4.6. Embedding graph based languages
4.7. Market
4.7.1. The ultimate process language
4.7.2. Fragmentation
4.7.3. Other implementation techniques
5. Deployment
5.1. Java runtime environment
5.2. jBPM libraries
5.3. Third party libraries
6. Configuration
6.1. Configuration properties
6.2. Configuration files
6.2.1. Hibernate cfg xml file
6.2.2. Hibernate queries configuration file
6.2.3. Node types configuration file
6.2.4. Action types configuration file
6.2.5. Business calendar configuration file
6.2.6. Variable mapping configuration file
6.2.7. Converter configuration file
6.2.8. Default modules configuration file
6.2.9. Process archive parsers configuration file
6.3. Object factory
7. Persistence
7.1. The persistence API
7.1.1. Relation to the configuration framework
7.1.2. Convenience methods on JbpmContext
7.1.3. Advanced API usage
7.2. Configuring the persistence service
7.2.1. The hibernate session factory
7.2.2. Configuring a c3po connection pool
7.2.3. The DbPersistenceServiceFactory
7.3. Hibernate transactions
7.4. JTA transactions
7.5. User provided stuff
7.6. Customizing queries
7.7. Database compatibility
7.7.1. Changing the jBPM DB
7.7.2. The jBPM DB schema
7.8. Combining your hibernate classes
7.9. Customizing the jBPM hibernate mapping files
7.10. Second level cache
8. The jBPM Database
8.1. Switching the Database Backend
8.1.1. Installing the PostgreSQL Database Manager
8.1.2. Creating the JBoss jBPM Database
8.1.3. Update the JBoss jBPM Server Configuration
8.2. Database upgrades
8.3. Starting hsqldb manager on JBoss
9. Process Modelling
9.1. Overview
9.2. Process graph
9.3. Nodes
9.3.1. Node responsibilities
9.3.2. Nodetype task-node
9.3.3. Nodetype state
9.3.4. Nodetype decision
9.3.5. Nodetype fork
9.3.6. Nodetype join
9.3.7. Nodetype node
9.4. Transitions
9.5. Actions
9.5.1. Action configuration
9.5.2. Action references
9.5.3. Events
9.5.4. Event propagation
9.5.5. Script
9.5.6. Custom events
9.6. Superstates
9.6.1. Superstate transitions
9.6.2. Superstate events
9.6.3. Hierarchical names
9.7. Exception handling
9.8. Process composition
9.9. Custom node behaviour
9.10. Graph execution
9.11. Transaction demarcation
10. Context
10.1. Accessing variables
10.2. Variable lifetime
10.3. Variable persistence
10.4. Variables scopes
10.4.1. Variables overloading
10.4.2. Variables overriding
10.4.3. Task instance variable scope
10.5. Transient variables
10.6. Customizing variable persistence
11. Task management
11.1. Tasks
11.2. Task instances
11.2.1. Task instance lifecycle
11.2.2. Task instances and graph execution
11.3. Assignment
11.3.1. Assignment interfaces
11.3.2. The assignment data model
11.3.3. Push model
11.3.4. Pull model
11.4. Task instance variables
11.5. Task controllers
11.6. Swimlanes
11.7. Swimlane in start task
11.8. Task events
11.9. Task timers
11.10. Customizing task instances
11.11. The identity component
11.11.1. The identity model
11.11.2. Assignment expressions
11.11.2.1. First terms
11.11.2.2. Next terms
11.11.3. Removing the identity component
12. Scheduler
12.1. Timers
12.2. Scheduler deployment
13. Asynchronous continuations
13.1. The concept
13.2. An example
13.3. The command executor
13.4. jBPM's built-in asynchronous messaging
13.5. JMS for async architectures
13.6. JMS for asynchronous messaging
13.7. Future directions
14. Business calendar
14.1. Duration
14.2. Calendar configuration
15. Logging
15.1. Creation of logs
15.2. Log configurations
15.3. Log retrieval
15.4. Database warehousing
16. jBPM Process Definition Language (JPDL)
16.1. The process archive
16.1.1. Deploying a process archive
16.1.2. Process versioning
16.1.3. Changing deployed process definitions
16.1.4. Migrating process instances
16.1.5. Process conversion
16.2. Delegation
16.2.1. The jBPM class loader
16.2.2. The process class loader
16.2.3. Configuration of delegations
16.2.3.1. config-type field
16.2.3.2. config-type bean
16.2.3.3. config-type constructor
16.2.3.4. config-type configuration-property
16.3. Expressions
16.4. jPDL xml schema
16.4.1. Validation
16.4.2. process-definition
16.4.3. node
16.4.4. common node elements
16.4.5. start-state
16.4.6. end-state
16.4.7. state
16.4.8. task-node
16.4.9. process-state
16.4.10. super-state
16.4.11. fork
16.4.12. join
16.4.13. decision
16.4.14. event
16.4.15. transition
16.4.16. action
16.4.17. script
16.4.18. expression
16.4.19. variable
16.4.20. handler
16.4.21. timer
16.4.22. create-timer
16.4.23. cancel-timer
16.4.24. task
16.4.25. swimlane
16.4.26. assignment
16.4.27. controller
16.4.28. sub-process
16.4.29. condition
16.4.30. exception-handler
17. Security
17.1. Todos
17.2. Authentication
17.3. Authorization
18. TDD for workflow
18.1. Introducing TDD for workflow
18.2. XML sources
18.2.1. Parsing a process archive
18.2.2. Parsing an xml file
18.2.3. Parsing an xml String
18.3. Testing sub processes
19. Pluggable architecture

Chapter 1. Introduction

JBoss jBPM is a flexible, extensible framework for process languages. jPDL is one process language that is build on top of that common framework. It is an intuitive process language to express business processes graphically in terms of tasks, wait states for asynchronous communication, timers, automated actions,... To bind these operations together, jPDL has the most powerful and extensible control flow mechanism.

jPDL has minimal dependencies and can be used as easy as using a java library. But it can also be used in environments where extreme throughput is crucial by deploying it on a J2EE clustered application server.

jPDL can be configured with any database and it can be deployed on any application server.

1.1. Overview

The core workflow and BPM functionality is packaged as a simple java library. This library includes a service to store, update and retrieve process information from the jPDL database.

Overview of the jPDL components

Figure 1.1. Overview of the jPDL components

1.2. The jPDL starter kit

The starter kit is a download that contains all the jBPM components bundled in one easy download. The download includes:

  • runtime, the core jbpm component including the libs and this userguide.
  • server, a preconfigured jboss application server.
  • designer, the eclipse plugin for authoring jBPM processes graphically.
  • db, the jBPM database compatibility package (see below).
  • bpel, a reference to the JBoss jBPM BPEL extension.

The preconfigured JBoss application server has the following components installed :

  • The core jBPM component, packaged as a service archive
  • An integrated database with the jBPM tables: the default hypersonic database that contains the jBPM tables and already contains a process.
  • The jBPM console web application that can be used by process participants as well as jBPM administrators.
  • The jBPM scheduler for the execution of timers. The scheduler is configured in the starters kit as a servlet. The servlet will spawn a thread to monitor and execute the timers.
  • The jBPM command executor for the asynchronous execution of commands. The command executor is also configured as a servlet. The servlet will spawn a thread to monitor and execute the commands.
  • One example process is already deployed into the jBPM database.

1.3. The JBoss jBPM graphical process designer

JBoss jBPM also includes a graphical designer tool. The designer is a graphical tool for authoring business processes.

The JBoss jBPM graphical process designer is an eclipse plugin. A standalone installation of the designer tool is on the roadmap.

The most important feature of the graphical designer tool is that it includes support for both the tasks of the business analyst as well as the technical developer. This enables a smooth transition from business process modelling to the practical implementation.

The plugin is available as a local update site (plain zip file) for installation via the standard eclipse software updates mechanism. And there is also a feature package that you can unzip in your eclipse home directory.

1.4. The JBoss jBPM core component

The JBoss jBPM core component is the plain java (J2SE) software for managing process definitions and the runtime environment for execution of process instances.

JBoss jBPM is a java library. As a consequence, it can be used in any java environment like e.g. a webapplication, a swing application, an EJB, a webservice,... The jBPM library can also be packaged and exposed as a stateless session EJB. This allows clustered deployment and scalability for extreme high throughput. The stateless session EJB will be written against the J2EE 1.3 specifications so that it is deployable on any application server.

The JBoss jBPM core component is packaged as a simple java library archive. Depending on the functionalities that you use, the library jbpm-3.0.jar has some dependencies on other third party libraries such as e.g. hibernate, dom4j and others. Those dependencies are clearly documented in Chapter 5, Deployment

For its persistence, jBPM uses hibernate internally. Apart from traditional O/R mapping, hibernate also resolves the SQL dialect differences between the different databases, making jBPM portable across all current databases.

The JBoss jBPM API can be accessed from any custom java software in your project, like e.g. your web application, your EJB's, your web service components, your message driven beans or any other java component.

1.5. The JBoss jBPM console web application

The jBPM console web application serves two purposes. First, it serves as a central user interface for interacting with runtime tasks generated by the process executions. And secondly, it is an administration and monitoring console that allows to inspect and manipulate runtime instances.

1.6. The JBoss jBPM identity component

JBoss jBPM can integrate with any company directory that contains users and other organisational information. But for projects where no organisational information component is readily available, JBoss jBPM includes this component. The model used in the identity component is richer then the traditional servlet-, ejb- and portlet models.

For more information, see Section 11.11, “The identity component”

1.7. The JBoss jBPM scheduler

The JBoss jBPM scheduler is a component for monitoring and executing timers that are scheduled during process executions.

The timer component software is packaged in the core jbpm library, but it needs to be deployed in one of the following environments: either you have to configure the scheduler servlet to spawn the monitoring thread or you have to start up a separate JVM with the scheduler main.

1.8. The JBoss jBPM database compatibility package

The JBoss jBPM database compatibility package is a download package that contains all the information, drivers and scripts to get jBPM running on your database of choice.

1.9. The JBoss jBPM BPEL extension

The JBoss jBPM BPEL extension is a separatly packaged extension that extends jBPM with support for BPEL. The essence of BPEL is an xml scripting language for writing web services in terms of other web services.

Chapter 2. Getting started

This chapter takes you through the first steps of getting JBoss jBPM and provides the initial pointers to get up and running in no time.

2.1. Downloadables Overview

Listed below are the different jBPM packages that are available today. Each of these packages contains one or more downloadable files. Along with each of these files goes a description of its contents and a pointer to any relevant installation instructions if they are available.

All downloads described below can be found on the sourceforge jbpm downloads page.

2.1.1. jBPM 3

Download JBoss jBPM 3 at sourceforge.net. This is the main distribution package containing the core engine and a number of additional modules that you may need to work with jBPM.

  • The Starters Kit (jbpm-starters-kit-<version>.zip): If you want to get started with jBPM quickly, this is the file you want to download. It contains all the other modules of this package plus the graphical designer in one single download. Extract the zipped archive into a folder of your choice and read the file named 'readme.html' for more info and further installation instructions. With this starters kit you can immediately get started with the Chapter 3, Tutorial.

  • Core Engine and Identity Component (jbpm-<version>.zip): The download contains the jBPM core engine as well as the identity component for actor and group management. To start working with it, extract the archive into a folder of your choice. You will find pointers to the User's Guide and other important information resources in the 'readme.html' file in the 'jbpm-<version>' folder.

  • Database Extensions (jbpm-db-<version>.zip): The database extension pack contains the jBPM core engine as well as the identity component for actor and group management. To start working with it, extract the archive into a folder of your choice. You will find pointers to the User's Guide and other important information resources in the 'readme.html' file in the 'jbpm-<version>' folder.

2.1.2. jBPM Process Designer

Download JBoss jBPM Process Designer at sourceforge.net. The designer is an eclipse plugin and enables you to author your process definitions and to easily deploy them. The plug-in is available for download either as a zipped Eclipse feature or as a zipped Eclipse update site. There is no difference in content, the only difference is in the way you have to do the installation.

  • Eclipse Update Site (jbpm-gpd-site-<version>.zip): If you want to be absolutely sure that the designer installation goes smoothly, we recommend to use the update site mechanism together with a new Eclipse installation. Of cource the Eclipse version should match the downloaded update site archive. To get started with the designer plugin, follow the instructions in the 'readme.html' file included in the archives root folder to succesfully install the GPD.

  • Eclipse Feature (jbpm-gpd-feature-<version>.zip): If you are tired of each time having to do a fresh Eclipse installation and you are willing to cope with some possible issues, you can try the feature download. In this case installation is as easy as extracting the archive into your Eclipse installation (make sure the included 'plugins' and 'features' folders end up in the same location of your Eclipse installation) overwriting the files and folders with the same name that are possibly already present. This installation is very easy, but you could run into incompatibility issues when you overwrite plugins already present in your installation because of other features that you did install. Though they have the same name it could happen that the versions of these colliding plugins are not equal, hence the possible incompatibilities. The installation instructions are repeated in the 'readme.html' file.

2.1.3. jBPM BPEL extension

Download JBoss jBPM BPEL extension at sourceforge.net. It contains only one file : jbpm-bpel-<version>.zip. To get started with the BPEL extensions, look in the User's Guide in the 'doc' subfolder of the toplevel folder.

2.2. The JBoss jBPM project directory

  • professional support: JBoss is the company that backs this project with professional support, training and consultancy services.
  • user guide: is the document you're reading and serves as the main entry point into the project.
  • forums: get in contact with the community, ask questions and discuss jBPM
  • wiki: extra information, mostly provided by the community
  • issue tracker: for submitting bugs and feature requests
  • downloads: sourceforge download page for jBPM
  • mailing lists: mailing lists are used for announcements
  • javadocs: part of the download in the doc/javadoc directory.

2.3. CVS access

2.3.1. Anonymous CVS access

Alternatively, you can get JBoss jBPM from cvs with the following information:

  • Connection type: pserver
  • User: anonymous
  • Host: anoncvs.forge.jboss.com
  • Port: 2401 (which is the default)
  • Repository path: /cvsroot/jbpm
  • Label: :pserver:anonymous@anoncvs.forge.jboss.com:/cvsroot/jbpm

2.3.2. Developer CVS access

To get cvs developer access, you must sign contributors agreement and you need an ssh key. More information on both can be found on the JBoss cvs repository wiki page

  • Connection type: ext over ssh (extssh in eclipse)
  • User: sf.net username or jboss username
  • Host: cvs.forge.jboss.com
  • Port: 2401 (which is the default)
  • Repository path: /cvsroot/jbpm
  • Label: :pserver:anonymous@cvs.forge.jboss.com:/cvsroot/jbpm

Chapter 3. Tutorial

This tutorial will show you basic process constructs in jpdl and the usage of the API for managing the runtime executions.

The format of this tutorial is explaining a set of examples. The examples focus on a particular topic and contain extensive comments. The examples can also be fond in the jBPM download package in the directory src/java.examples.

The best way to learn is to create a project and experiment by creating variations on the examples given.

To get started for eclipse users: download jbpm-3.0-[version].zip and unzip it to your sytem. Then do "File" --> "Import..." --> "Existing Project into Workspace". Click "Next" Then, browse for the jBPM root directory and click "Finish". Now you have a jbpm.3 project in your workspace. You can now find the examples of the tutorial in src/java.examples/.... When you open these examples, you can run them with "Run" --> "Run As..." --> "JUnit Test"

jBPM includes a graphical designer tool for authoring the XML that is shown in the examples. You can find download instructions for the graphical designer in Section 2.1, “Downloadables Overview”. You don't need the graphical designer tool to complete this tutorial.

State machines can be

3.1. Hello World example

A process definition is a directed graph, made up of nodes and transitions. The hello world process has 3 nodes. To see how the pieces fit together, we're going to start with a simple process without the use of the designer tool. The following picture shows the graphical representation of the hello world process:

The hello world process graph

Figure 3.1. The hello world process graph

public void testHelloWorldProcess() {
  // This method shows a process definition and one execution
  // of the process definition.  The process definition has 
  // 3 nodes: an unnamed start-state, a state 's' and an 
  // end-state named 'end'.
  // The next line parses a piece of xml text into a
  // ProcessDefinition.  A ProcessDefinition is the formal 
  // description of a process represented as a java object.
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<process-definition>" +
    "  <start-state>" +
    "    <transition to='s' />" +
    "  </start-state>" +
    "  <state name='s'>" +
    "    <transition to='end' />" +
    "  </state>" +
    "  <end-state name='end' />" +
    "</process-definition>"
  );
  
  // The next line creates one execution of the process definition.
  // After construction, the process execution has one main path
  // of execution (=the root token) that is positioned in the
  // start-state.
  ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
  
  // After construction, the process execution has one main path
  // of execution (=the root token).
  Token token = processInstance.getRootToken();
  
  // Also after construction, the main path of execution is positioned
  // in the start-state of the process definition.
  assertSame(processDefinition.getStartState(), token.getNode());
  
  // Let's start the process execution, leaving the start-state 
  // over its default transition.
  token.signal();
  // The signal method will block until the process execution 
  // enters a wait state.

  // The process execution will have entered the first wait state
  // in state 's'. So the main path of execution is now 
  // positioned in state 's'
  assertSame(processDefinition.getNode("s"), token.getNode());

  // Let's send another signal.  This will resume execution by 
  // leaving the state 's' over its default transition.
  token.signal();
  // Now the signal method returned because the process instance 
  // has arrived in the end-state.
  
  assertSame(processDefinition.getNode("end"), token.getNode());
}

3.2. Database example

One of the basic features of jBPM is the ability to persist executions of processes in the database when they are in a wait state. The next example will show you how to store a process instance in the jBPM database. The example also suggests a context in which this might occur. Separate methods are created for different pieces of user code. E.g. an piece of user code in a webapplication starts a process and persists the execution in the database. Later, a message driven bean loads the process instance from the database and resumes its execution.

More about the jBPM persistence can be found in Chapter 7, Persistence.

public class HelloWorldDbTest extends TestCase {

  static JbpmConfiguration jbpmConfiguration = null; 

  static {
    // An example configuration file such as this can be found in 
    // 'src/config.files'.  Typically the configuration information is in the 
    // resource file 'jbpm.cfg.xml', but here we pass in the configuration 
    // information as an XML string.
    
    // First we create a JbpmConfiguration statically.  One JbpmConfiguration
    // can be used for all threads in the system, that is why we can safely 
    // make it static.

    jbpmConfiguration = JbpmConfiguration.parseXmlString(
      "<jbpm-configuration>" +
      
      // A jbpm-context mechanism separates the jbpm core 
      // engine from the services that jbpm uses from 
      // the environment.  
      
      "  <jbpm-context>" +
      "    <service name='persistence' " +
      "             factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />" + 
      "  </jbpm-context>" +
      
      // Also all the resource files that are used by jbpm are 
      // referenced from the jbpm.cfg.xml
      
      "  <string name='resource.hibernate.cfg.xml' " +
      "          value='hibernate.cfg.xml' />" +
      "  <string name='resource.business.calendar' " +
      "          value='org/jbpm/calendar/jbpm.business.calendar.properties' />" +
      "  <string name='resource.default.modules' " +
      "          value='org/jbpm/graph/def/jbpm.default.modules.properties' />" +
      "  <string name='resource.converter' " +
      "          value='org/jbpm/db/hibernate/jbpm.converter.properties' />" +
      "  <string name='resource.action.types' " +
      "          value='org/jbpm/graph/action/action.types.xml' />" +
      "  <string name='resource.node.types' " +
      "          value='org/jbpm/graph/node/node.types.xml' />" +
      "  <string name='resource.varmapping' " +
      "          value='org/jbpm/context/exe/jbpm.varmapping.xml' />" +
      "</jbpm-configuration>"
    );
  }
  
  public void setUp() {
    jbpmConfiguration.createSchema();
  }
  
  public void tearDown() {
    jbpmConfiguration.dropSchema();
  }

  public void testSimplePersistence() {
    // Between the 3 method calls below, all data is passed via the 
    // database.  Here, in this unit test, these 3 methods are executed
    // right after each other because we want to test a complete process
    // scenario.  But in reality, these methods represent different 
    // requests to a server.
    
    // Since we start with a clean, empty in-memory database, we have to 
    // deploy the process first.  In reality, this is done once by the 
    // process developer.
    deployProcessDefinition();

    // Suppose we want to start a process instance (=process execution)
    // when a user submits a form in a web application...
    processInstanceIsCreatedWhenUserSubmitsWebappForm();

    // Then, later, upon the arrival of an asynchronous message the 
    // execution must continue.
    theProcessInstanceContinuesWhenAnAsyncMessageIsReceived();
  }

  public void deployProcessDefinition() {
    // This test shows a process definition and one execution 
    // of the process definition.  The process definition has 
    // 3 nodes: an unnamed start-state, a state 's' and an 
    // end-state named 'end'.
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition name='hello world'>" +
      "  <start-state name='start'>" +
      "    <transition to='s' />" +
      "  </start-state>" +
      "  <state name='s'>" +
      "    <transition to='end' />" +
      "  </state>" +
      "  <end-state name='end' />" +
      "</process-definition>"
    );

    // Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {
      // Deploy the process definition in the database 
      jbpmContext.deployProcessDefinition(processDefinition);

    } finally {
      // Tear down the pojo persistence context.
      // This includes flush the SQL for inserting the process definition  
      // to the database.
      jbpmContext.close();
    }
  }

  public void processInstanceIsCreatedWhenUserSubmitsWebappForm() {
    // The code in this method could be inside a struts-action 
    // or a JSF managed bean. 

    // Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");
    
      // With the processDefinition that we retrieved from the database, we 
      // can create an execution of the process definition just like in the 
      // hello world example (which was without persistence).
      ProcessInstance processInstance = 
          new ProcessInstance(processDefinition);
      
      Token token = processInstance.getRootToken(); 
      assertEquals("start", token.getNode().getName());
      // Let's start the process execution
      token.signal();
      // Now the process is in the state 's'.
      assertEquals("s", token.getNode().getName());
      
      // Now the processInstance is saved in the database.  So the 
      // current state of the execution of the process is stored in the 
      // database.  
      jbpmContext.save(processInstance);
      // The method below will get the process instance back out 
      // of the database and resume execution by providing another 
      // external signal.

    } finally {
      // Tear down the pojo persistence context.
      jbpmContext.close();
    }
  }

  public void theProcessInstanceContinuesWhenAnAsyncMessageIsReceived() {
    // The code in this method could be the content of a message driven bean.

    // Lookup the pojo persistence context-builder that is configured above
    JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    try {

      GraphSession graphSession = jbpmContext.getGraphSession();
      // First, we need to get the process instance back out of the database.
      // There are several options to know what process instance we are dealing 
      // with here.  The easiest in this simple test case is just to look for 
      // the full list of process instances.  That should give us only one 
      // result.  So let's look up the process definition.
      
      ProcessDefinition processDefinition = 
          graphSession.findLatestProcessDefinition("hello world");

      // Now, we search for all process instances of this process definition.
      List processInstances = 
          graphSession.findProcessInstances(processDefinition.getId());
      
      // Because we know that in the context of this unit test, there is 
      // only one execution.  In real life, the processInstanceId can be 
      // extracted from the content of the message that arrived or from 
      // the user making a choice.
      ProcessInstance processInstance = 
          (ProcessInstance) processInstances.get(0);
      
      // Now we can continue the execution.  Note that the processInstance
      // delegates signals to the main path of execution (=the root token).
      processInstance.signal();

      // After this signal, we know the process execution should have 
      // arrived in the end-state.
      assertTrue(processInstance.hasEnded());
      
      // Now we can update the state of the execution in the database
      jbpmContext.save(processInstance);

    } finally {
      // Tear down the pojo persistence context.
      jbpmContext.close();
    }
  }
}

3.3. Context example: process variables

The process variables contain the context information during process executions. The process variables are similar to a java.util.Map that maps variable names to values, which are java objects. The process variables are persisted as a part of the process instance. To keep things simple, in this example we only show the API to work with variables, without persistence.

More information about variables can be found in Chapter 10, Context

// This example also starts from the hello world process.
// This time even without modification.
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "<process-definition>" +
  "  <start-state>" +
  "    <transition to='s' />" +
  "  </start-state>" +
  "  <state name='s'>" +
  "    <transition to='end' />" +
  "  </state>" +
  "  <end-state name='end' />" +
  "</process-definition>"
);

ProcessInstance processInstance =
  new ProcessInstance(processDefinition);

// Fetch the context instance from the process instance 
// for working with the process variables.
ContextInstance contextInstance = 
  processInstance.getContextInstance();

// Before the process has left the start-state, 
// we are going to set some process variables in the 
// context of the process instance.
contextInstance.setVariable("amount", new Integer(500));
contextInstance.setVariable("reason", "i met my deadline");

// From now on, these variables are associated with the 
// process instance.  The process variables are now accessible 
// by user code via the API shown here, but also in the actions 
// and node implementations.  The process variables are also  
// stored into the database as a part of the process instance.

processInstance.signal();

// The variables are accessible via the contextInstance. 

assertEquals(new Integer(500), 
             contextInstance.getVariable("amount"));
assertEquals("i met my deadline", 
             contextInstance.getVariable("reason"));

3.4. Task assignment example

In the next example we'll show how you can assign a task to a user. Because of the separation between the jBPM workflow engine and the organisational model, an expression language for calculating actors would always be too limited. Therefore, you have to specify an implementation of AssignmentHandler for including the calculation of actors for tasks.

public void testTaskAssignment() {
  // The process shown below is based on the hello world process.
  // The state node is replaced by a task-node.  The task-node 
  // is a node in JPDL that represents a wait state and generates 
  // task(s) to be completed before the process can continue to 
  // execute.  
  ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
    "<process-definition name='the baby process'>" +
    "  <start-state>" +
    "    <transition name='baby cries' to='t' />" +
    "  </start-state>" +
    "  <task-node name='t'>" +
    "    <task name='change nappy'>" +
    "      <assignment class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' />" +
    "    </task>" +
    "    <transition to='end' />" +
    "  </task-node>" +
    "  <end-state name='end' />" +
    "</process-definition>"
  );
  
  // Create an execution of the process definition.
  ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
  Token token = processInstance.getRootToken();
  
  // Let's start the process execution, leaving the start-state 
  // over its default transition.
  token.signal();
  // The signal method will block until the process execution 
  // enters a wait state.   In this case, that is the task-node.
  assertSame(processDefinition.getNode("t"), token.getNode());

  // When execution arrived in the task-node, a task 'change nappy'
  // was created and the NappyAssignmentHandler was called to determine
  // to whom the task should be assigned.  The NappyAssignmentHandler 
  // returned 'papa'.

  // In a real environment, the tasks would be fetched from the
  // database with the methods in the org.jbpm.db.TaskMgmtSession.
  // Since we don't want to include the persistence complexity in 
  // this example, we just take the first task-instance of this 
  // process instance (we know there is only one in this test
  // scenario).
  TaskInstance taskInstance = (TaskInstance)  
      processInstance
        .getTaskMgmtInstance()
        .getTaskInstances()
        .iterator().next();

  // Now, we check if the taskInstance was actually assigned to 'papa'.
  assertEquals("papa", taskInstance.getActorId() );
  
  // Now we suppose that 'papa' has done his duties and mark the task 
  // as done. 
  taskInstance.end();
  // Since this was the last (only) task to do, the completion of this
  // task triggered the continuation of the process instance execution.
  
  assertSame(processDefinition.getNode("end"), token.getNode());
}

3.5. Custom action example

Actions are a mechanism to bind your custom java code into a jBPM process. Actions can be associated with its own nodes (if they are relevant in the graphical representation of the process). Or actions can be placed on events like e.g. taking a transition, leaving a node or entering a node. In that case, the actions are not part of the graphical representation, but they are executed when execution fires the events in a runtime process execution.

We'll start with a look at the action implementation that we are going to use in our example : MyActionHandler. This action handler implementation does not do really spectacular things... it just sets the boolean variable isExecuted to true. The variable isExecuted is static so it can be accessed from within the action handler as well as from the action to verify it's value.

More information about actions can be found in Section 9.5, “Actions”

// MyActionHandler represents a class that could execute 
// some user code during the execution of a jBPM process.
public class MyActionHandler implements ActionHandler {

  // Before each test (in the setUp), the isExecuted member 
  // will be set to false.
  public static boolean isExecuted = false;  

  // The action will set the isExecuted to true so the 
  // unit test will be able to show when the action
  // is being executed.
  public void execute(ExecutionContext executionContext) {
    isExecuted = true;
  }
}

As mentioned before, before each test, we'll set the static field MyActionHandler.isExecuted to false;

  // Each test will start with setting the static isExecuted 
  // member of MyActionHandler to false.
  public void setUp() {
    MyActionHandler.isExecuted = false;
  }

We'll start with an action on a transition.

public void testTransitionAction() {
    // The next process is a variant of the hello world process.
    // We have added an action on the transition from state 's' 
    // to the end-state.  The purpose of this test is to show 
    // how easy it is to integrate java code in a jBPM process.
    ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
      "<process-definition>" +
      "  <start-state>" +
      "    <transition to='s' />" +
      "  </start-state>" +
      "  <state name='s'>" +
      "    <transition to='end'>" +
      "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
      "    </transition>" +
      "  </state>" +
      "  <end-state name='end' />" +
      "</process-definition>"
    );
    
    // Let's start a new execution for the process definition.
    ProcessInstance processInstance = 
      new ProcessInstance(processDefinition);
    
    // The next signal will cause the execution to leave the start 
    // state and enter the state 's'
    processInstance.signal();

    // Here we show that MyActionHandler was not yet executed. 
    assertFalse(MyActionHandler.isExecuted);
    // ... and that the main path of execution is positioned in 
    // the state 's'
    assertSame(processDefinition.getNode("s"), 
               processInstance.getRootToken().getNode());
    
    // The next signal will trigger the execution of the root 
    // token.  The token will take the transition with the
    // action and the action will be executed during the  
    // call to the signal method.
    processInstance.signal();
    
    // Here we can see that MyActionHandler was executed during 
    // the call to the signal method.
    assertTrue(MyActionHandler.isExecuted);
  }

The next example shows the same action, but now the actions are placed on the enter-node and leave-node events respectively. Note that a node has more then one event type in contrast to a transition, which has only one event. Therefore actions placed on a node should be put in an event element.

ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
  "<process-definition>" +
  "  <start-state>" +
  "    <transition to='s' />" +
  "  </start-state>" +
  "  <state name='s'>" +
  "    <event type='node-enter'>" +
  "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
  "    </event>" +
  "    <event type='node-leave'>" +
  "      <action class='org.jbpm.tutorial.action.MyActionHandler' />" +
  "    </event>" +
  "    <transition to='end'/>" +
  "  </state>" +
  "  <end-state name='end' />" +
  "</process-definition>"
);

ProcessInstance processInstance = 
  new ProcessInstance(processDefinition);

assertFalse(MyActionHandler.isExecuted);
// The next signal will cause the execution to leave the start 
// state and enter the state 's'.  So the state 's' is entered 
// and hence the action is executed. 
processInstance.signal();
assertTrue(MyActionHandler.isExecuted);

// Let's reset the MyActionHandler.isExecuted  
MyActionHandler.isExecuted = false;

// The next signal will trigger execution to leave the  
// state 's'.  So the action will be executed again. 
processInstance.signal();
// Voila.  
assertTrue(MyActionHandler.isExecuted);

Chapter 4. Graph Oriented Programming

4.1. Introduction

This chapter can be considered the manifest for JBoss jBPM. It gives a complete overview of the vision and ideas behind current strategy and future directions of the JBoss jBPM project. This vision significantly differs from the traditional approach.

First of all, we believe in multiple process languages. There are different environments and different purposes that require a their own specific process language.

Secondly, Graph Oriented Programming is a new implementation technique that serves as a basis for all graph based process languages.

The main benefit of our approach is that it defines one base technology for all types of process languages.

Current software development relies more and more on domain specific languages. A typical Java developer will use quite a few domain specific languages. The XML-files in a project that are input for various frameworks can be considered domain specific languages.

Positioning of graph based languages

Figure 4.1. Positioning of graph based languages

Domain specific languages for workflow, BPM, orchestration and pageflow are based on the execution of a directed graph. Others like hibernate mapping files, ioc-configuration are not. Graph Oriented Programming is the foundation for all domain specific languages that are based on executing a graph.

Graph Oriented Programming is a very simple technique that describes how graphs can be defined and executed on a plain OO programming language.

In Section 4.5, “Application domains”, we'll cover the most often used process languages that can be implemented using Graph Oriented Programming like workflow, BPM, orchestration and pageflow.

4.1.1. Domain specific languages

Each process language can be considered a Domain Specific Language (DSL). The DSL perspective gives developers good insight in how process languages are related to plain OO programming.

This section might give the impression that we're focussed solely on programming environments. None is less true. Graph Oriented Programming includes the whole BPM product continuum from API libraries to fully fledged BPM suite products. BPM suite products are complete software development environments that are centered around business processes. In that type of products, coding in programming languages is avoided as much as possible.

An important aspect of domain specific languages is that each language has a certain grammar. That grammar can be expressed as a domain model. In case of java this is Class, Method, Field, Constructor,... In jPDL this is Node, Transition, Action,... In rules, this is condition, concequence,...

The main idea of DSL is that developers think in those grammars when authoring artifacts for a specific language. The IDE is build around the grammar of a language. Then, there can be different editors to author the artifacts. E.g. a jPDL process has a graphical editor and a XML source view editor. Also there can be different ways to store the same artifact: for jPDL, this could be a process XML file or the serialized object graph of nodes and transition objects. Another (theoretic) example is java: you could use the java class file format on the system. When a user starts the editor, the sources are generated. When a user saves the compiled class is saved....

Where ten years ago, the biggest part of a developer was spend on writing code. Now a shift has taken place towards learning and using domain specific languages. This trend will still continue and the result is that developers will have a big choice between frameworks and writing software in the host platform. JBoss SEAM is a very big step in that direction.

Some of those languages are based on execution of a graph. E.g. jPDL for workflow in Java, BPEL for service orchestration, SEAM pageflow,... Graph Oriented Programming is a common foundation for all these type of domain specific languages.

In the future, for each language, a developer will be able to choose an editor that suites him/her best. E.g. a hard core programmer probably will prefer to edit java in the src file format cause that works really fast. But a less experienced java developer might choose a point and click editor to compose a functionality that will result in a java class. The java source editing will be much more flexible.

Another way of looking at these domain specific languages (including the programming languages) is from the perspective of structuring software. Object Oriented Programming (OOP) adds structure by grouping methods with their data. Aspect Oriented Programming (AOP) adds a way to extract cross cutting concerns. Dependency Injection (DI) and Inversion of Control (IoC) frameworks adds easy wiring of object graphs. Also graph based execution languages (as covered here) can be helpful to tackle complexity by structuring part of your software project around the execution of a graph.

An intial explanation on Domain Specific Languages (DSL) can be found on Martin Fowler's bliki. But the vision behind it is better elaborated in Martin's article about 'Language Workbenches'.

4.1.2. Features of graph based languages

There are numerous graph based process languages. There are big differences in the environment and focus. For instance, BPEL is intended as an XML based service orchestation component on top of an Enterprise Service Bus (ESB) architecture. And a pageflow process language might define how the pages of a webapplication can be navigated. These are two completely different environments.

Despite all these differences, there are two features that you'll find in almost every process language: support for wait states and a graphical represenation. This is no coincidence because it's exactly those two features that are not sufficiently supported in plain Object Oriented (OO) programming languages like Java.

Graph Oriented Programming is a technique to implement these two features in an OO programming language. The dependency of Graph Oriented Programming on OO programming implies that all concrete process languages, implemented on top of Graph Oriented Programming, will have to be developed in OOP. But this does not mean that the process languages themselves expose any of this OOP nature. E.g. BPEL doesn't have any relation to OO programming and it can be implemented on top of Graph Oriented Programming.

4.1.2.1. Support for wait states

An imperative programming language like Java are used to express a sequence of instructions to be executed by one system. There is no wait instruction. An imperative language is perfect for describing e.g. one request response cycle in a server. The system is continuously executing the sequence of instructions till the request is handled and the response is complete.

But one such request is typically part of a bigger scenario. E.g. a client submits a purchase order, this purchase order is to be validated by a purchase order manager. After approval, the information must be entered in the ERP system. Many requests to the server are part of the same bigger scenario.

So process languages are languages to describe the bigger scenario. A very important distinction we must make here is scenarios that are executable on one system (orchestration) and scenarios that describe the protocol between multiple systems (choreography). The Graph Oriented Programming implementation technique is only targets process languages that are executable on one machine (orchestration).

So an orchestration process describes the overall scenario in terms of one system. For example: A process is started when a client submits an order. The next step in the process is the order manager's approval. So the system must add an entry in the task list of the order manager and the wait till the order manager provides the required input. When the input is received, the process continues execution. Now a message is sent to the ERP system and again this system will wait until the response comes back.

So to describe the overall scenario for one system, we need a mechanism to cope with wait states.

In most of the application domains, the execution must be persisted during the wait states. That is why blocking threads is not sufficient. Clever Java programmers might think about the Object.wait() and Object.notify(); methods. Those could be used to simulate wait states but the problem is that threads are not persistable.

Continuations is a technique to make the thread (and the context variables) persistable. This could be a sufficient to solve the wait state problem. But as we will discuss in the next section, also a graphical representation is important for many of the application domains. And continuations is a technique that is based on imperative programming, so it's unsuitable for the graphical representation.

So an important aspect of the support for wait states is that executions need to be persistable. Different application domains might have different requirements for persisting such an execution. For most workflow, BPM and orchestration applications, the execution needs to be persisted in a relational database. Typically, a state transition in the process execution will correspond with one transaction in the database.

4.1.2.2. Graphical representation

Some aspects of software development can benefit very well from a graph based approach. Business Process Management is one of the most obvious application domains of graph based languages. In that example, the communication between a business analyst and the developer is improved using the graph based diagram of the business process as the common language. See also Section 4.5.1, “Business Process Management (BPM)”.

Another aspect that can benefit from a graphical representation is pageflow. In this case, the pages, navigation and action commands are shown and linked together in the graphical representation.

In Graph Oriented Programming we target graph diagrams that represent some form of execution. That is a clear differentiation with for instance UML class diagrams, which represent a static model of the OO data structure.

Also the graphical representation can be seen as a missing feature in OO programming. There is no sensible way in which the execution of an OO program can be represented graphically. So there is no direct relation between an OO program and the graphical view.

In Graph Oriented Programming, the description of the graph is central and it is a real software artifact like e.g. an XML file that describes the process graph. Since the graphical view is an intrinsic part of the software, it is always in sync. There is no need for a manual translation from the graphical requirements into a software design. The software is structured around the graph.

4.2. Graph Oriented Programming

What we present here is an implementation technique for graph based execution languages. The technique presented here is based on runtime interpretation of a graph. Other techniques for graph execution are based on message queues or code generation.

This section will explain the strategy on how graph execution can be implemented on top of an OO programming language. For those who are familiar with design patterns, it's a combination of the command pattern and the chain of responsibility pattern.

We'll start off with the simplest possible model and then extend it bit by bit.

4.2.1. The graph structure

First of all, the structure of the graph is represented with the classes Node and Transition. A transition has a direction so the nodes have leaving- and arriving transitions.

Node and Transition classes

Figure 4.2. Node and Transition classes

A node is a command and has an execute method. Subclasses of Node are supposed to override the execute method to implement some specific behaviour for that node type.

4.2.2. An execution

The execution model that we defined on this graph structure might look similar to finite state machines or UML state diagrams. In fact Graph Oriented Programming can be used to implement those kinds of behaviours, but it also can do much more.

An execution (also known as a token) is represented with a class called Execution. An execution has a reference to the current node.

The Execution class

Figure 4.3. The Execution class

Transitions are able to pass the execution from a source node to a destination node with the method take.

The Transition take method

Figure 4.4. The Transition take method

When an execution arrives in a node, that node is executed. The Node's execute method is also responsible for propagating the execution. Propagating the execution means that a node can pass the execution that arrived in the node over one of its leaving transitions to the next node.

The Node execute method

Figure 4.5. The Node execute method

When a node's execute method does not propagate the execution, it behaves as a wait state. Also when a new execution is created, it is initialized in some start node and then waits for an event.

An event is given to an execution and it can trigger the execution to start moving. If the event given to an execution relates to a leaving transition of the current node, the execution takes that transition. The execution then will continue to propagate until it enters another node that behaves as a wait state.

The Execution event method

Figure 4.6. The Execution event method

4.2.3. A process language

So now we can already see that the two main features are supported : wait states and a graphical representation. During wait states, an Execution just points to a node in the graph. Both the process graph and the Execution can be persisted: E.g. to a relational database with an O/R mapper like hibernate or by serializing the object graph to a file. Also you can see that the nodes and transitions form a graph and hence there is a direct coupling with a graphical representation.

A process language is nothing more then a set of Node-implementations. Each Node-implementation corresponds with a process construct. The exact behaviour of the process construct is implemented by overriding the execute method.

Here we show an example process language with 4 process constructs: a start state, a decision, a task and an end state. This example is unrelated to the jPDL process language.

An example process language

Figure 4.7. An example process language

Concrete node objects can now be used to create process graphs in our example process language.

An example process

Figure 4.8. An example process

When creating a new execution for this process, we start by positioning the execution in the start node. So as long as the execution does not receive an event, the execution will remain positioned in the start state.

A new execution

Figure 4.9. A new execution

Now let's look at what happens when an event is fired. In this initial situation, we fire the default event that will correspond with the default transition.

That is done by invoking the event method on the execution object. The event method will propagate find the default leaving transition and pass the execution over the transition by invoking the take method on the transition and passing itself in as a parameter.

The transition will pass on the execution to the decision node and invoke the execute method. Let's assume the decision's execute implementation performs a calculation and decides to propagate the execution by sending the 'yes'-event to the execution. That will cause the execution to continue over the 'yes' transition and the execution will arrive in the task 'doubleCheck'.

Let's assume that the execute implementation of the doubleCheck's task node is adds an entry into the checker's task list and then waits for the checker's input by not propagating the execution further.

Now, the execution will remain positioned in the doubleCheck task node. All nested invocations will start to return until the original event method returns.

An execution in the 'doubleCheck' wait state

Figure 4.10. An execution in the 'doubleCheck' wait state

4.2.4. Actions

In some application domains there must be a way to include the execution of programming logic without introducing a node for it. In Business Process Management for example this is a very important aspect. The business analyst is in charge of the graphical representation and the developer is responsible for making it executable. It is not acceptable if the developer must change the graphical diagram to include a technical detail in which the business analyst is not interested.

An Action is also a commands with an execute method. Actions can be associated with events.

There are 2 basic events fired by the Node class while an execution is executing: node-leave and node-enter. Along with the events that cause transitions to be taken this gives already a good freedom of injecting programming logic into the execution of a graph.

Actions that are normally hidden from the graphical view

Figure 4.11. Actions that are normally hidden from the graphical view

Each event can be associated with a list of actions. All the actions will be executed with the event fires.

4.2.5. Code example

In order for people to get acquinted with the principles of Graph Oriented Programming, we have developed these 4 classes in less then 130 lines of code. You can just read the code to get an idea or you can actually start playing with them and implement your own node types.

Here's the example code:

You can also download the whole (297KB) source project and start playing with it yourself. It includes an eclipse project so just importing it in your eclipse as a project should get you going. Also there are a set of tests that show basic process execution and the advanced graph execution concepts covered in the next section.

4.3. Advanced Graph Oriented Programming Extensions

The previous section introduced the plain Graph Oriented Programming model in its simplest form. This section will discuss various aspects of graph based languages and how Graph Oriented Programming can be used or extended to meet these requirements.

4.3.1. Process variables

Process variables maintain the contextual data of a process execution. In an insurance claim process, the 'claimed amount', 'approved ammount' and 'isPaid' could be good examples of process variables. In many ways, they are similar to the member fields of a class.

Graph Oriented Programming can be easily extended with support for process variables by associating a set of key-value pairs that are associated with an execution. Concurrent execution paths and process composition will complicate things a bit. Scoping rules will define the visibility of process variables in case of concurrent paths of execution or subprocesses.

'Workflow Data Patterns' is an extensive research report on the types of scoping that can be applied to process variables in the context of subprocessing and concurrent executions.

4.3.2. Concurrent executions

Suppose that you're developing a 'sale' process with a graph based process language for workflow. After the client submitted the order, there is a sequence of activities for billing the client and there's also a sequence of activities for shipping the items to the client. As you can imagine, the billing activies and shipping activities can be done in parallel.

In that case, one execution will not be sufficient to keep track of the whole process state. Let's go though the steps to extend the Graph Oriented Programming model and add support for concurrent executions.

First, let's rename the execution to an execution path. Then we can introduce a new concept called a process execution. A process execution represents one complete execution of a process and it contains many execution paths.

The execution paths can be ordered hierarchically. Meaning that one root execution path is created when a new process execution is instantiated. When the root execution path is forked into multiple concurrent execution paths, the root is the parent and the newly created execution paths are all children of the root. This way, implementation of a join can become straightforward: the implementation of the join just has to verify if all sibling-execution-paths are already positioned in the join node. If that is the case, the parent execution path can resume execution leaving the join node.

While the hierarchical execution paths and the join implementation based on sibling execution paths covers a large part of the use cases, other concurrency behaviour might be desirable in specific circumstances. For example when multiple merges relate to one split. In such a situation, other combinations of runtime data and merge implementations are required.

Actions that are normally hidden from the graphical view

Figure 4.12. Actions that are normally hidden from the graphical view

Multiple concurrent paths of execution are often mixed up with multithreaded programming. Especially in the context of workflow and BPM, these are quite different. A process specifies a state machine. Consider for a moment a state machine as being always in a stable state and state transitions are instantanious. Then you can interpret concurrent paths of execution by looking at the events that cause the state transitions. Concurrent execution then means that the events that can be handled are unrelated between the concurrent paths of execution. Now let's assume that state transitions in the process execution relates to a database transition (as explained in Section 4.3.6, “Persistence and Transactions”), then you see that multithreaded programming is actually not even required to support concurrent paths of execution.

4.3.3. Process composition

Process composition is the ability to include a sub process as part of a super process. This advanced feature makes it possible to add abstraction to process modelling. For the business analyst, this feature is important to handle break down large models in smaller blocks.

The main idea is that the super process has a node in the graph that represents a complete execution of the sub process. When an execution enters the sub-process-node in the super process, several things are to be considered:

  • First of all, a new execution is created for the sub process.
  • Optionally some of information stored in the process variables of the super process can be injected from the super process execution into the sub process execution. The most easy form is that the sub process node is configured with a set of variables that are just copied from the super process variables to the sub process variables.
  • The start-node of the sub process should have only one leaving transition. Process languages that support multiple leaving transitions must have a mechanism to choose one of those transitions based on the process variables of the super process.
  • The sub process execution is launched by sending an event that corresponds to the default leaving transition of its start state.

After the sub process entered a wait state, the super process execution will be pointing to the sub-process-node and the sub process execution will be pointing to some wait state.

When the sub process execution finishes, the super process execution can continue. The following aspects need to be considered at that time:

  • Process variable information may need to be copied back from the sub process execution into the super process execution.
  • The super process execution should continue. Typically, process languages allow only one leaving transition on a sub process node. In that case the super process execution is propagated over that default single leaving transition.
  • In case a sub process node is allowed more then one leaving transition, a mechanism has to be introduced to select a leaving transition. This selection can be based on either the sub process execution's variables or the end state of the sub process (a typical state machine can have multiple end states).

TODO: bpel's has an implicit notion of subprocessing, rather then an explicit...

4.3.4. Synchronous execution

4.3.5. Asynchronous continuations

4.3.6. Persistence and Transactions

4.3.7. Services and the environment

4.4. Architecture

4.5. Application domains

4.5.1. Business Process Management (BPM)

The goal of BPM is to make an organisation run more efficient. The first step is analysing and describing how work gets done in an organisation. Let's define a business process is a description of the way that people and systems work together to get a perticular job done. Once business processes are described, the search for optimisations can begin.

Sometimes business processes have evolved organically and merely looking at the overall business process shows some obvious inefficiencies. Searching for modifications that make a business process more efficient is called Business Process Reengineering (BPR). Once a large part of a business process is automated, statistics and audit trails can help to find and identify these inefficiencies.

Another way to improve efficiency can be to automate whole or parts of the business process using information technology.

Automating and modifying business processes are the most common ways of making an organisation run more efficient.

Managers continiously break down jobs into steps to be executed by their team members. For example a software development manager that organises a team-building event. In that case, the description of the business process might be done only in the head of the manager. Other situations like handling an insurance claim for a large insurance company require a more formal approach to BPM.

The total gain that can be obtained from managing business processes is the efficiency improvements times the number of executions of the process. The cost of managing business processes formally is the extra effort that is spent on analysing, describing, improving and automating the business processes. So that cost has to be taken into consideration when determining which processes will be selected for formal management and/or automation.

4.5.1.1. Goals of BPM systems

4.5.1.2. Process development process

4.5.2. Service orchestration

orchestration is somtimes supposed to be in the context of service orchestration.

Service

Figure 4.13. Service

4.5.2.1. Orchestration compared to Choreography

4.5.3. Pageflow

4.5.4. Visual programming

...targetted at less experienced developers.

In my opinion, the current java community is a relatively small community of very hard core power users. Of course, the point and click will have a lot of restrictions cannot handle all of Java's constructs. But it will open up a hole new can of fresh (less-experienced) software developers. So different editors for a single language can target different types/levels of developers.

4.6. Embedding graph based languages

When the BPM engine can be completely integrated into a software development project and when even the BPM engine's database tables in integrated into the project's database, then we speak of an Embeddable BPM engine. That is the goal we target with Graph Oriented Programming: a common foundation for implementing graph based languages.

4.7. Market

4.7.1. The ultimate process language

Traditionally, the vendors have been searching for the ultimate process language. The approach is to specify a process language as a set of constructs. Each construct has a graphical representation and a runtime behaviour. In other words, each construct is a node type in the process graph. And a process language is just a set of node constructs.

The idea was that the vendors were searching for the best set of process constructs to form a universally applicable process language. This vision is still found a lot today and we call it searching for the ultimate process language.

We believe that the focus should not be on trying to find the ultimate process language, but rather in finding a common foundation that can be used as a basis for process languages in different scenarios and different environment. Graph Oriented Programming as we present it next is to be seen as such a foundation.

4.7.2. Fragmentation

The current landscape of workflow, BPM and orchestration solutions is completely fragmented. In this section we'll describe two dimensions in this fragmentation. The first dimension is called the BPM product continuum and it's shown in the next picture. The term was originally coined by Derek Miers and Paul Harmon in 'The 2005 BPM Suites Report'.

On the left, you can see the programming languages. This side of the continuum is targeted towards the IT developers. Programming languages are the most flexible and it integrates completely with the other software developed for a perticular project. But it takes quite a bit of programming to implement a business process.

On the right, there are the BPM suites. These BPM suites are complete software development environments targetted to be used by business analysts. Software is developed around business processes. No programming has to be done to create executable software in these BPM suites.

The BPM product continuum.

Figure 4.14. The BPM product continuum.

Traditional products mark 1 spot in the BPM product continuum. To be complete, these products tend to aim for the far right of the continuum. This is problematic because it results in monolithic system that is very hard to integrate into a project combines plain OOP software with business processes.

Graph Oriented Programming can be built as a simple library that integrates nice with plain programming environment. On the other hand, this library can be packaged and predeployed on a server to become a BPM server. Then other products added and packaged together with the BPM server to become a complete BPM suite.

The net result is that solutions based on Graph Oriented Programming can target the whole continuum. Depending on the requirements in a perticular project, the BPM suite can be peeled and customized to the right level of integration with the software development environment.

The other dimension of fragmentation is the application domain. As show above, a BPM application domain is completely different from service orchestration or pageflow. Also in this dimension, traditional products target one single application domain, where Graph Oriented Programming covers the whole range.

If we set this out in a graph, this gives a clear insight in the current market fragmentation. In the graph based languages market, prices are high and volumes are low. Consolidation is getting started and this technology aims to be a common foundation for what is now a fragmented and confusing market landscape.

Two dimensions of fragmentation.

Figure 4.15. Two dimensions of fragmentation.

4.7.3. Other implementation techniques

Based on message queues.

Based on code generation.

Chapter 5. Deployment

jBPM is an embeddable BPM engine, which means that you can take jBPM and embed it into your own java project, rather then installing a separate product and integrate with it. One of the key aspects that make this possible is minimizing the dependencies. This chapter discusses the jbpm libraries and their dependencies.

5.1. Java runtime environment

jBPM 3 requires J2SE 1.4.2+

5.2. jBPM libraries

jbpm-[version].jar is the library with the core jbpm functionality.

jbpm-identity-[version].jar is the (optional) library containing an identity component as described in Section 11.11, “The identity component”.

5.3. Third party libraries

In a minimal deployment, you can create and run processes with jBPM by putting only the commons-logging and dom4j library in your classpath. Beware that persisting processes to a database is not supported. The dom4j library can be removed if you don't use the process xml parsing, but instead build your object graph programatically.

Table 5.1. 

LibraryUsageDescriptionDirectory
commons-logging.jarlogging in jbpm and hibernateThe jBPM code logs to commons logging. The commons logging library can be configured to dispatch the logs to e.g. java 1.4 logging, log4j, ... See the apache commons user guide for more information on how to configure commons logging. if you're used to log4j, the easiest way is to put the log4j lib and a log4j.properties in the classpath. commons logging will automatically detect this and use that configuration. lib/jboss (from jboss 4.0.3)
dom4j-1.6.1.jarprocess definitions and hibernate persistencexml parsinglib/dom4j

A typical deployment for jBPM will include persistent storage of process definitions and process executions. In that case, jBPM does not have any dependencies outside hibernate and its dependent libraries.

Of course, hibernate's required libraries depend on the environment and what features you use. For details refer to the hibernate documentation. The next table gives an indication for a plain standalone POJO development environment.

jBPM is distributed with hibernate 3.1 final. But it can also work with 3.0.x. In that case, you might have to update a few hibernate queries in the hibernate.queries.hbm.xml configuration file. For more info about customizing queries, see Section 7.6, “Customizing queries”

Table 5.2. 

LibraryUsageDescriptionDirectory
hibernate3.jarhibernate persistencethe best O/R mapperlib/hibernate (hibernate 3.1 final)
antlr-2.7.5H3.jarused in query parsing by hibernate persistenceparser librarylib/jboss (from jboss 4.0.3)
cglib-2.1_2jboss.jarhibernate persistencereflection library used for hibernate proxieslib/jboss (from jboss 4.0.3)
commons-collections.jarhibernate persistence lib/jboss (from jboss 4.0.3)
jaxen-1.1-beta-4.jarprocess definitions and hibernate persistenceXPath library (used by dom4j)lib/hibernate
jdbc2_0-stdext.jarhibernate persistence lib/hibernate
asm.jarhibernate persistenceasm byte code librarylib/hibernate
asm-attrs.jarhibernate persistenceasm byte code librarylib/hibernate

The beanshell library is optional. If you don't include it, you won't be able to use the beanshell integration in the jbpm process language and you you'll get a log message saying that jbpm couldn't load the Script class and hence, the script element won't be available.

Table 5.3. 

LibraryUsageDescriptionDirectory
bsh-1.3.0.jarbeanshell script interpreterOnly used in the script's and decision's. When you don't use these process elements, the beanshell lib can be removed, but then you have to comment out the Script.hbm.xml mapping line in the hibernate.cfg.xmllib/jboss

Chapter 6. Configuration

The simplest way to configure jBPM is by putting the jbpm.cfg.xml configuration file in the root of the classpath. If that file is not found as a resource, the default minimal configuration will be used that is included in the jbpm library. Note that the minimal configuration does not have any configurations for persistence.

The jBPM configuration is represented by the java class org.jbpm.JbpmConfiguration. Most easy way to get a hold of the JbpmConfiguration is to make use of the singleton instance method JbpmContext.getInstance().

If you want to load a configuraiton from another source, you can use the JbpmConfiguration.parseXxxx methods.

static JbpmConfinguration jbpmConfiguration = JbpmConfinguration.getInstance();

The JbpmConfiguration is threadsafe and hence can be kept in a static member. All threads can use the JbpmConfiguration as a factory for JbpmContext objects. A JbpmContext typically represents one transaction. The JbpmContext makes services available inside of a context block. A context block looks like this:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  // This is what we call a context block.
  // Here you can perform workflow operations

} finally {
  jbpmContext.close();
}

The JbpmContext makes a set of services and the configuration available to jBPM. These services are configured in the jbpm.cfg.xml configuration file and make it possible for jBPM to run in any Java environment and use whatever services are available in that environment.

Here's a typical configuration for the JbpmContext as you can find it in src/config.files/jbpm.cfg.xml:

<jbpm-configuration>

  <jbpm-context>
    <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />
    <service name='message' factory='org.jbpm.msg.db.DbMessageServiceFactory' />
    <service name='scheduler' factory='org.jbpm.scheduler.db.DbSchedulerServiceFactory' />
    <service name='logging' factory='org.jbpm.logging.db.DbLoggingServiceFactory' />
    <service name='authentication' factory='org.jbpm.security.authentication.DefaultAuthenticationServiceFactory' />
  </jbpm-context>

  <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
  <string name='resource.hibernate.cfg.xml' value='hibernate.cfg.xml' />
  <!-- <string name='resource.hibernate.properties' value='hibernate.properties' /> -->
  <string name='resource.business.calendar' value='org/jbpm/calendar/jbpm.business.calendar.properties' />
  <string name='resource.default.modules' value='org/jbpm/graph/def/jbpm.default.modules.properties' />
  <string name='resource.converter' value='org/jbpm/db/hibernate/jbpm.converter.properties' />
  <string name='resource.action.types' value='org/jbpm/graph/action/action.types.xml' />
  <string name='resource.node.types' value='org/jbpm/graph/node/node.types.xml' />
  <string name='resource.parsers' value='org/jbpm/jpdl/par/jbpm.parsers.xml' />
  <string name='resource.varmapping' value='org/jbpm/context/exe/jbpm.varmapping.xml' />

  <int name='jbpm.byte.block.size' value="1024" singleton="true" />
  <bean name='jbpm.task.instance.factory' class='org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl' singleton='true' />
  <bean name='jbpm.variable.resolver' class='org.jbpm.jpdl.el.impl.JbpmVariableResolver' singleton='true' />
  <long name='jbpm.msg.wait.timout' value='5000' singleton='true' />

</jbpm-configuration>

In this configuration file you can see 3 parts:

  • The first parse configures the jbpm context with a set of service implementations. The possible configuration options are covered in the chapters that cover the specific service implementations.

  • The second part are all mappings of references to configuration resources. These resource references can be updated if you want to customize one of these configuration files. Typically, you make a copy the default configuration which is in the jbpm-3.x.jar and put it somewhere on the classpath. Then you update the reference in this file and jbpm will use your customized version of that configuration file.

  • The third part are some miscallanious configurations used in jbpm. These configuration options are described in the chapters that cover the specific topic.

The default configured set of services is targetted at a simple webapp environment and minimal dependencies. The persistence service will obtain a jdbc connection and all the other services will use the same connection to perform their services. So all of you workflow operations are centralized into 1 transaction on A JDBC connection without the need for a transaction manager.

JbpmContext contains convenience methods for most of the common process operations:

  public void deployProcessDefinition(ProcessDefinition processDefinition) {...}
  public List getTaskList() {...}
  public List getTaskList(String actorId) {...}
  public List getGroupTaskList(List actorIds) {...}
  public TaskInstance loadTaskInstance(long taskInstanceId) {...}
  public TaskInstance loadTaskInstanceForUpdate(long taskInstanceId) {...}
  public Token loadToken(long tokenId) {...}
  public Token loadTokenForUpdate(long tokenId) {...}
  public ProcessInstance loadProcessInstance(long processInstanceId) {...}
  public ProcessInstance loadProcessInstanceForUpdate(long processInstanceId) {...}
  public ProcessInstance newProcessInstance(String processDefinitionName) {...}
  public void save(ProcessInstance processInstance) {...}
  public void save(Token token) {...}
  public void save(TaskInstance taskInstance) {...}
  public void setRollbackOnly() {...}

Note that the XxxForUpdate methods will register the loaded object for auto-save so that you don't have to call one of the save methods explicitely.

It's possible to specify multiple jbpm-contexts, but then you have to make sure that each jbpm-context is given a unique name attribute. Named contexts can be retrieved with JbpmConfiguration.createContext(String name);

A service element specifies the name of a service and the service factory for that service. The service will only be created in case it's asked for with JbpmContext.getServices().getService(String name).

The factories can also be specified as an element instead of an attribute. That might be necessary to inject some configuration information in the factory objects. The component responsible for parsing the XML, creating and wiring the objects is called the object factory.

6.1. Configuration properties

jbpm.byte.block.size: File attachments and binary variables are stored in the database. Not as blobs, but as a list of fixed sized binary objects. This is done to improve portability amongst different databases and improve overall embeddability of jBPM. This parameter controls the size of the fixed length chunks.

jbpm.task.instance.factory: To customize the way that task instances are created, specify a fully qualified class name in this property. This might be necessary when you want to customize the TaskInstance bean and add new properties to it. See also Section 11.10, “Customizing task instances” The specified class should implement org.jbpm.taskmgmt.TaskInstanceFactory.

jbpm.variable.resolver: To customize the way that jBPM will look for the first term in JSF-like expressions.

jbpm.msg.wait.timout: To customize the time inbetween polls for messages.

6.2. Configuration files

Here's a short description of all the configuration files that are customizable in jBPM.

6.2.1. Hibernate cfg xml file

This file contains hibernate configurations and references to the hibernate mapping resource files.

Location: hibernate.cfg.xml unless specified otherwise in the jbpm.hibernate.cfg.xml property in the jbpm.properties file. In the jbpm project the default hibernate cfg xml file is located in directory src/config.files/hibernate.cfg.xml

6.2.2. Hibernate queries configuration file

This file contains hibernate queries that are used in the jBPM sessions org.jbpm.db.*Session.

Location: org/jbpm/db/hibernate.queries.hbm.xml

6.2.3. Node types configuration file

This file contains the mapping of XML node elements to Node implementation classes.

Location: org/jbpm/graph/node/node.types.xml

6.2.4. Action types configuration file

This file contains the mapping of XML action elements to Action implementation classes.

Location: org/jbpm/graph/action/action.types.xml

6.2.5. Business calendar configuration file

Contains the definition of business hours and free time.

Location: org/jbpm/calendar/jbpm.business.calendar.properties

6.2.6. Variable mapping configuration file

Specifies how the values of the process variables (java objects) are converted to variable instances for storage in the jbpm database.

Location: org/jbpm/context/exe/jbpm.varmapping.xml

6.2.7. Converter configuration file

Specifies the id-to-classname mappings. The id's are stored in the database. The org.jbpm.db.hibernate.ConverterEnumType is used to map the ids to the singleton objects.

Location: org/jbpm/db/hibernate/jbpm.converter.properties

6.2.8. Default modules configuration file

specifies which modules are added to a new ProcessDefinition by default.

Location: org/jbpm/graph/def/jbpm.default.modules.properties

6.2.9. Process archive parsers configuration file

specifies the phases of process archive parsing

Location: org/jbpm/jpdl/par/jbpm.parsers.xml

6.3. Object factory

The object factory can create objects according to a beans-like xml configuration file. The configuration file specifies how objects should be created, configured and wired together to form a complete object graph. The object factory can inject the configurations and other beans into a bean.

In its simplest form, the object factory is able to create basic types and java beans from such a configuration:

<beans>
  <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance"/>
  <string name="greeting">hello world</string>
  <int name="answer">42</int>
  <boolean name="javaisold">true</boolean>
  <float name="percentage">10.2</float>
  <double name="salary">100000000.32</double>
  <char name="java">j</char>
  <null name="dusttodust" />
</beans>

---------------------------------------------------------

ObjectFactory of = ObjectFactory.parseXmlFromAbove();
assertEquals(TaskInstance.class, of.getNewObject("task").getClass());
assertEquals("hello world", of.getNewObject("greeting"));
assertEquals(new Integer(42), of.getNewObject("answer"));
assertEquals(Boolean.TRUE, of.getNewObject("javaisold"));
assertEquals(new Float(10.2), of.getNewObject("percentage"));
assertEquals(new Double(100000000.32), of.getNewObject("salary"));
assertEquals(new Character('j'), of.getNewObject("java"));
assertNull(of.getNewObject("dusttodust"));

Also you can configure lists:

<beans>
  <list name="numbers">
    <string>one</string>
    <string>two</string>
    <string>three</string>
  </list>
</beans>

and maps

<beans>
  <map name="numbers">
    <entry><key><int>1</int></key><value><string>one</string></value></entry>
    <entry><key><int>2</int></key><value><string>two</string></value></entry>
    <entry><key><int>3</int></key><value><string>three</string></value></entry>
  </map>
</beans>

Beans can be configured with direct field injection and via property setters.

<beans>
  <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <field name="name"><string>do dishes</string></field>
    <property name="actorId"><string>theotherguy</string></property>
  </bean>
</beans>

Beans can be referenced. The referenced object doesn't have to be a bean, it can be a string, integer or any other object.

<beans>
  <bean name="a" class="org.jbpm.A" />
  <ref name="b" bean="a" />
</beans>

Beans can be constructed with any constructor

<beans>
  <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor>
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
  </bean>
</beans>

... or with a factory method on a bean ...

<beans>
  <bean name="taskFactory" 
         class="org.jbpm.UnexistingTaskInstanceFactory" 
         singleton="true"/>

  <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor factory="taskFactory" method="createTask" >
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
  </bean>
</beans>

... or with a static factory method on a class ...

<beans>
  <bean name="task" class="org.jbpm.taskmgmt.exe.TaskInstance" >
    <constructor factory-class="org.jbpm.UnexistingTaskInstanceFactory" method="createTask" >
      <parameter class="java.lang.String">
        <string>do dishes</string>
      </parameter>
      <parameter class="java.lang.String">
        <string>theotherguy</string>
      </parameter>
    </constructor>
  </bean>
</beans>

Each named object can be marked as singleton with the attribute singleton="true". That means that a given object factory will always return the same object for each request. Note that singletons are not shared between different object factories.

The singleton feature causes the differentiation between the methods getObject and getNewObject. Typical users of the object factory will use the getNewObject. This means that first the object factory's object cache is cleared before the new object graph is constructed. During construction of the object graph, the non-singleton objects are stored in the object factory's object cache to allow for shared references to one object. The singleton object cache is different from the plain object cache. The singleton cache is never cleared, while the plain object cache is cleared at the start of every getNewObject method.

Chapter 7. Persistence

In most scenarios, jBPM is used to maintain execution of processes that span a long time. In this context, "a long time" means spanning several transactions. The main purpose of persistence is to store process executions during wait states. So think of the process executions as state machines. In one transaction, we want to move the process execution state machine from one state to the next.

A process definition can be represented in 3 different forms : as xml, as java objects and as records in the jBPM database. Executional (=runtime) information and logging information can be represented in 2 forms : as java objects and as records in the jBPM database.

The transformations and different forms

Figure 7.1. The transformations and different forms

For more information about the xml representation of process definitions and process archives, see Chapter 16, jBPM Process Definition Language (JPDL).

More information on how to deploy a process archive to the database can be found in Section 16.1.1, “Deploying a process archive”

7.1. The persistence API

7.1.1. Relation to the configuration framework

The persistence API is an integrated with the configuration framework my exposing some convenience persistence methods on the JbpmContext. Persistence API operations can therefor be called inside a jBPM context block like this:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {

  // Invoke persistence operations here

} finally {
  jbpmContext.close();
}

In what follows, we suppose that the configuration includes a persistence service similar to this one (as in the example configuration file src/config.files/jbpm.cfg.xml):

<jbpm-configuration>

  <jbpm-context>
    <service name='persistence' factory='org.jbpm.persistence.db.DbPersistenceServiceFactory' />
    ...
  </jbpm-context>
  ...
</jbpm-configuration>

7.1.2. Convenience methods on JbpmContext

The three most common persistence operations are:

  • Deploying a process
  • Starting a new execution of a process
  • Continuing an execution

First deploying a process definition. Typically, this will be done directly from the graphical process designer or from the deployprocess ant task. But here you can see how this is done programmatically:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  ProcessDefinition processDefinition = ...;
  jbpmContext.deployProcessDefinition(processDefinition);
} finally {
  jbpmContext.close();
}

For the creation of a new process execution, we need to specify of which process definition this execution will be an instance. The most common way to specify this is to refer to the name of the process and let jBPM find the latest version of that process in the database:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  String processName = ...;
  ProcessInstance processInstance = 
      jbpmContext.newProcessInstance(processName);
} finally {
  jbpmContext.close();
}

For continuing a process execution, we need to fetch the process instance, the token or the taskInstance from the database, invoke some methods on the POJO jBPM objects and afterwards save the updates made to the processInstance into the database again.

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  long processInstanceId = ...;
  ProcessInstance processInstance = 
      jbpmContext.loadProcessInstance(processInstanceId);
  processInstance.signal();
  jbpmContext.save(processInstance);
} finally {
  jbpmContext.close();
}

Note that if you use the xxxForUpdate methods in the JbpmContext, an explicit invocation of the jbpmContext.save is not necessary any more because it will then occur automatically during the close of the jbpmContext. E.g. suppose we want to inform jBPM about a taskInstance that has been completed. Note that task instance completion can trigger execution to continue so the processInstance related to the taskInstance must be saved. The most convenient way to do this is to use the loadTaskInstanceForUpdate method:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  long taskInstanceId = ...;
  TaskInstance taskInstance = 
      jbpmContext.loadTaskInstanceForUpdate(taskInstanceId);
  taskInstance.end();
} finally {
  jbpmContext.close();
}

Just as background information, the next part is an explanation of how jBPM manages the persistence and uses hibernate.

The JbpmConfiguration maintains a set of ServiceFactorys. The service factories are configured in the jbpm.cfg.xml as shown above and instantiated lazy. The DbPersistenceServiceFactory is only instantiated the first time when it is needed. After that, service factories are maintained in the JbpmConfiguration. A DbPersistenceServiceFactory manages a hibernate SessionFactory. But also the hibernate session factory is created lazy when requested the first time.

The persistence related classes

Figure 7.2. The persistence related classes

During the invocation of jbpmConfiguration.createJbpmContext(), only the JbpmContext is created. No further persistence related initializations are done at that time. The JbpmContext manages a DbPersistenceService, which is instantiated upon first request. The DbPersistenceService manages the hibernate session. Also the hibernate session inside the DbPersistenceService is created lazy. As a result, a hibernate session will be only be opened when the first operation is invoked that requires persistence and not earlier.

7.1.3. Advanced API usage

The DbPersistenceService maintains a lazy initialized hibernate session. All database access is done through this hibernate session. All queries and updates done by jBPM are exposed by the XxxSession classes like e.g. GraphSession, SchedulerSession, LoggingSession,... These session classes refer to the hibernate queries and all use the same hibernate session underneath.

The XxxxSession classes are accessible via the JbpmContext as well.

7.2. Configuring the persistence service

7.2.1. The hibernate session factory

By default, the DbPersistenceServiceFactory will use the resource hibernate.cfg.xml in the root of the classpath to create the hibernate session factory. Note that the hibernate configuration file resource is mapped in the property 'jbpm.hibernate.cfg.xml' and can be customized in the jbpm.cfg.xml. This is the default configuration:

<jbpm-configuration>
  ...
  <!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
  <string name='resource.hibernate.cfg.xml' 
          value='hibernate.cfg.xml' />
  <!-- <string name='resource.hibernate.properties' 
                  value='hibernate.properties' /> -->
  ...
</jbpm-configuration>

When the property resource.hibernate.properties is specified, the properties in that resource file will overwrite all the properties in the hibernate.cfg.xml. Instead of updating the hibernate.cfg.xml to point to your DB, the hibernate.properties can be used to handle jbpm upgrades conveniently: The hibernate.cfg.xml can then be copied without having to reapply the changes.

7.2.2. Configuring a c3po connection pool

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

7.2.3. The DbPersistenceServiceFactory

The DbPersistenceServiceFactory itself has 3 more configuration properties: isTransactionEnabled, sessionFactoryJndiName and dataSourceJndiName. To specify any of these properties in the jbpm.cfg.xml, you need to specify the service factory as a bean in the factory element like this:

  • isTransactionEnabled: by default, jBPM will begin and end hibernate transactions. To disable transactions and prohibit jBPM from managing transactions with hibernate, configure the isTransactionEnabled property to false as in the example above. For more info about transactions, see Section 7.3, “Hibernate transactions”.
  • isCurrentSessionEnabled: by default, jBPM will begin and close hibernate session. By setting isCurrentSessionEnabled to true, jBPM will use sessionFactory.getCurrentSession() on the specified hibernate session factory instead. This is appropriate in a JTA environment.
  • sessionFactoryJndiName: by default, this is null, meaning that the session factory is not fetched from JNDI. If set and a session factory is needed to create a hibernate session, the session factory will be fetched from jndi using the provided JNDI name.
  • dataSourceJndiName: by default, this is null and creation of JDBC connections will be delegated to hibernate. By specifying a datasource, jBPM will fetch a JDBC connection from the datasource and provide that to hibernate while opening a new session. For user provided JDBC connections, see Section 7.5, “User provided stuff”.

7.3. Hibernate transactions

By default, jBPM will delegate transaction to hibernate and use the session per transaction pattern. jBPM will begin a hibernate transaction when a hibernate session is opened. This will happen the first time when a persistent operation is invoked on the jbpmContext. The transaction will be committed right before the hibernate session is closed. That will happen inside the jbpmContext.close().

Use jbpmContext.setRollbackOnly() to mark a transaction for rollback. In that case, the transaction will be rolled back right before the session is closed inside of the jbpmContext.close().

To prohibit jBPM from invoking any of the transaction methods on the hibernate API, set the isTransactionEnabled property to false as explained in Section 7.2.3, “The DbPersistenceServiceFactory” above.

7.4. JTA transactions

The most common scenario for managed transactions is when using jBPM in a JEE application server like JBoss. The most common scenario to bind your transactions to JTA is the following:

  <jbpm-context>
    <service name="persistence">
      <factory>
        <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">
          <field name="isTransactionEnabled"><false /></field>
          <field name="isCurrentSessionEnabled"><true /></field>
          <field name="sessionFactoryJndiName">
            <string value="java:/myHibSessFactJndiName" />
          </field>
        </bean>
      </factory>
    </service>
    ...
  </jbpm-context>

Then you should specify in your hibernate session factory to use a datasource and bind hibernate to the transaction manager. Make sure that you bind the datasource to an XA datasource in case you're using more then 1 resource. For more information about binding hibernate to your transaction manager, please, refer to paragraph 'Transaction strategy configuration' in the hibernate documentation.

<hibernate-configuration>
  <session-factory>

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

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

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

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

Then make sure that you have configured hibernate to use an XA datasource.

7.5. User provided stuff

You can also programmatically provide JDBC connections, hibernate sessions and hibernate session factories to jBPM.

When such a resource is provided to jBPM, it will use the provided resources rather then the configured ones.

The JbpmContext class contains some convenience methods to inject resources programmatically. For example, to provide a JDBC connectio to jBPM, use the following code:

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  Connection connection = ...;
  jbpmContext.setConnection(connection);
  
  // invoke one or more persistence operations

} finally {
  jbpmContext.close();
}

the JbpmContext class has following convenience methods for providing resource programmatically:

JbpmContext.setConnection(Connection);
JbpmContext.setSession(Session);
JbpmContext.setSessionFactory(SessionFactory);

7.6. Customizing queries

All the HQL queries that jBPM uses are centralized in one configuration file. That resource file is referenced in the hibernate.cfg.xml configuration file like this:

<hibernate-configuration>
  ...
    <!-- hql queries and type defs -->
    <mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
  ...
</hibernate-configuration>

To customize one or more of those queries, take a copy of the original file and put your customized version somewhere on the classpath. Then update the reference 'org/jbpm/db/hibernate.queries.hbm.xml' in the hibernate.cfg.xml to point to your customized version.

7.7. Database compatibility

jBPM runs on any database that is supported by hibernate.

The example configuration files in jBPM (src/config.files) specify the use of the hypersonic in-memory database. That database is ideal during development and for testing. The hypersonic in-memory database keeps all its data in memory and doesn't store it on disk.

7.7.1. Changing the jBPM DB

Following is an indicative list of things to do when changing jBPM to use a different database:

  • put the jdbc-driver library archive in the classpath
  • update the hibernate configuration used by jBPM
  • create the schema in the new database

7.7.2. The jBPM DB schema

The jbpm.db subproject, contains a number of drivers, instructions and scripts to help you getting started on your database of choice. Please, refer to the readme.html in the root of the jbpm.db project for more information.

While jBPM is capable of generating DDL scripts for all database, these schemas are not always optimized. So you might want to have your DBA review the DDL that is generated to optimize the column types and use of indexes.

In development you might be interested in the following hibernate configuration: If you set hibernate configuration property 'hibernate.hbm2ddl.auto' to 'create-drop' (e.g. in the hibernate.cfg.xml), the schema will be automatically created in the database the first time it is used in an application. When the application closes down, the schema will be dropped.

The schema generation can also be invoked programmatically with jbpmConfiguration.createSchema() and jbpmConfiguration.dropSchema().

7.8. Combining your hibernate classes

In your project, you might use hibernate for your persistence. Combining your persistent classes with the jBPM persistent classes is optional. There are two major benefits when combining your hibernate persistence with jBPM's hibernate persistence:

First, session, connection and transaction management become easier. By combining jBPM and your persistence into one hibernate session factory, there is one hibernate session, one jdbc connection that handles both yours and jBPM's persistence. So automatically the jBPM updates are in the same transaction as the updates to your own domain model. This can eliminates the need for using a transaction manager.

Secondly, this enable you to drop your hibernatable persistent object in to the process variables without any further hassle.

The easiest way to integrate your persistent classes with the jBPM persistent classes is by creating one central hibernate.cfg.xml. You can take the jBPM src/config.files/hibernate.cfg.xml as a starting point and add references to your own hibernate mapping files in there.

7.9. Customizing the jBPM hibernate mapping files

To customize any of the jBPM hibernate mapping files, you can proceed as follows:

  • copy the jBPM hibernate mapping file(s) you want to copy from the sources (src/java.jbpm/...) or from inside of the jbpm jar.
  • put the copy anywhere you want on the classpath
  • update the references to the customized mapping files in the hibernate.cfg.xml configuration file

7.10. Second level cache

jBPM uses hibernate's second level cache for keeping the process definitions in memory after loading them once. The process definition classes and collections are configured in the jBPM hibernate mapping files with the cache element like this:

<cache usage="nonstrict-read-write"/>

Since process definitions (should) never change, it is ok to keep them in the second level cache. See also Section 16.1.3, “Changing deployed process definitions”.

The second level cache is an important aspect of the JBoss jBPM implementation. If it weren't for this cache, JBoss jBPM could have a serious drawback in comparison to the other techniques to implement a BPM engine.

The caching strategy is set to nonstrict-read-write. At runtime, the caching strategy could be set to read-only. But in that case, you would need a separate set of hibernate mapping files for deploying a process. That is why we opted for the nonstrict-read-write.

Chapter 8. The jBPM Database

8.1. Switching the Database Backend

Switching the JBoss jBPM database backend is reasonably straightforward. We will step through this process using PostgreSQL as an example. The process is identical for all other supported databases. For a number of these supported databases, a number of JDBC drivers, Hibernate configuration files and Ant build files to generate the database creation scripts are present in the jBPM distribution in the DB subproject. If you cannot find these files for the database you wish to use, you should first make sure if Hibernate supports your database. If this is the case you can have a look at files for one of the databases present in the DB project and mimic this using your own database.

For this document, we will use the JBoss jBPM Starter's Kit distribution. We will assume that this starter's kit was extracted to a location on your machine named ${JBPM_SDK_HOME}. You will find the DB subproject of jBPM in the ${JBPM_SDK_HOME}/jbpm-db.

After installing the database, you will have to run the database creation scripts. These will create the jBPM tables in the database. To make the default webapp running with this new database, we will have to update some configuration files of the server included in the Starter's Kit. For these configuration changes, we will not go into too much detail. If you want to know more about the different configuration settings in the server, we advise you to have a look at the JBoss documentation.

8.1.1. Installing the PostgreSQL Database Manager

To install PostgreSQL or any other database you may be using, we refer to the installation manual of these products. For Windows PostgreSQL installation is pretty straightforward. The installer creates a dedicated Windows user and allows to define the database administrator. PostgreSQL comes with an administration tool called pgAdmin III that we will use to create the jBPM database. A screenshot of this tool right after creating the JbpmDB database with it is shown in the figure below.

The PostgreSQL pgAdmin III tool after creating the JbpmDB database

Figure 8.1. The PostgreSQL pgAdmin III tool after creating the JbpmDB database

After the installation of the database, we can use a database viewer tool like DBVisualizer to look at the contents of the database. Before you can define a database connection with DBVisualizer, you might have to add the PostgreSQL JDBC driver to the driver manager. Select 'Tools->Driver Manager...' to open the driver manager window. Look at the figure below for an example of how to add the PostgreSQL JDBC driver.

Adding the JDBC driver to the driver manager

Figure 8.2. Adding the JDBC driver to the driver manager

Now everything is set to define a database connection in DBVisualizer to our newly created database. We will use this tool further in this document to make sure the creation scripts and process deployment are working as expected. For an example of creating the connection in DBVisualizer we refer to the following figure. As you can see, there are no tables present yet in this database. We will create them in the following section.

Create the connection to the jBPM database

Figure 8.3. Create the connection to the jBPM database

Another thing worth mentioning is the Database URL above : 'jdbc:postgresql://localhost:5432/JbpmDB'. If you created the JbpmDB database with another name, or if PostgreSQL is not running on the localhost machine or on another port, you'll have to adapt your Database URL accordingly.

8.1.2. Creating the JBoss jBPM Database

As already mentioned you will find the database scripts for a lot of the supported databases in the DB subproject. The database scripts for PostgreSQL are found in the folder '${JBPM_SDK_HOME}/jbpm-db/build/postgresql/scripts. The creation script is called 'postgresql.create.sql'. Using DBVisualizer, you can load this script by switching to the 'SQL Commander' tab and then selecting 'File->Load...'. In the following dialog, navigate to the creation script file. The result of doing so is shown in the figure below.

Load the database creation script

Figure 8.4. Load the database creation script

To execution this script with DBVisualizer, you select 'Database->Execute'. After this step all JBoss jBPM tables are created. The situation is illustrated in the figure below.

Running the database creation script

Figure 8.5. Running the database creation script

After these steps, there is not yet any data present in the tables. For the jBPM webapp to work, you should at least create some records in the jbpm_id_user table. In order to have exactly the same entries in this table as the default distribution of the starter's kit running on HSQLDB, we suggest to run the script below.

insert into JBPM_ID_USER (ID_, CLASS_, NAME_, EMAIL_, PASSWORD_) 
       values ('1', 'U', 'cookie monster', 'cookie.monster@sesamestreet.tv', 'crunchcrunch');
insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) 
       values ('2', 'U', 'ernie', 'ernie@sesamestreet.tv', 'canthereyoubert,theresabananainmyear');
insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) 
       values ('3', 'U', 'bert', 'bert@sesamestreet.tv', 'ernie,theresabananainyourear');
insert into JBPM_ID_USER (ID_,CLASS_, NAME_, EMAIL_, PASSWORD_) 
       values ('4', 'U', 'grover', 'grover@sesamestreet.tv', 'mayday mayday');

8.1.3. Update the JBoss jBPM Server Configuration

Before we can really use our newly created database with the JBoss jBPM default webapp we will have to do some updates to the JBoss jBPM configuration. The location of the server is '${JBPM_SDK_HOME}/jbpm-server'. The first thing we will be doing to update this configuration is create a new datasource that points to our JbpmDB database. In a second step, we will make sure that the default webapp is talking to this datasource and not to the HSQLDB datasource anymore.

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

<datasources>
  <local-tx-datasource>
    <jndi-name>JbpmDS</jndi-name>
    <connection-url>jdbc:postgresql://localhost:5432/JbpmDB</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>user</user-name>
    <password>password</password>
    <metadata>
      <type-mapping>PostgreSQL 8.1</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

In order to create a new datasource, you should create a file named e.g. jbpm-ds.xml with the contents shown in the program listing above. Of course it is possible that you have to change some of the values in this file to accommodate for your particular situation. You then simply save this file in the ${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy folder. Congratulations, you just created a new DataSource for your JBoss jBPM server. Well, almost... To make things really work you will have to copy the correct JDBC driver to the ${JBPM_SDK_HOME}/jbpm-server/server/jbpm/lib folder. We already used this JDBC driver above when we were installing it in DBVisualizer to be able to browse our newly created database. The file is named 'postgresql-8.1-*.jdbc3.jar' and it can be found in the jdbc subfolder of your PostgreSQL installation folder.

If you are not using PostgreSQL, you may wonder where you will find the parameters to accomplish this step. For a big amount of databases supported by the JBoss Application Server, you should download a JBoss AS distribution and have a look in the 'docs/examples/jca' folder.

Making the default webapp talk to the correct datasource is again not very difficult. The first step in doing this is simply locate the 'jboss-service.xml' file in the folder '${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy/jbpm.sar/META-INF'. Change the contents of this file with the contents of the listing below. An attentive reader will notice that the only difference is an exchange of the token 'DefaultDS' by 'JbpmDS'.

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

<server>
  <mbean code="org.jbpm.db.jmx.JbpmService" 
         name="jboss.jbpm:name=DefaultJbpm,service=JbpmService"
         description="Default jBPM Service">
    <attribute name="JndiName">java:/jbpm/JbpmConfiguration</attribute>
    <depends>jboss.jca:service=DataSourceBinding,name=JbpmDS</depends>
  </mbean>
</server>

The last thing we have to do to make everything run is a manipulation of the 'jbpm.sar.cfg.jar' file in the '${JBPM_SDK_HOME}/jbpm-server/server/jbpm/deploy/jbpm.sar' folder. You have to extract this file somewhere and open the file named 'hibernate.cfg.xml'. Then replace the section containing the jdbc connection properties. This section should look like shown in the listing below. There are two changes in this file : the hibernate.connection.datasource property should point to the JbpmDS datasource we created as the first step in this section and the hibernate.dialect property should match the PostgreSQL dialect.

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>

    <!-- jdbc connection properties -->
    <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
    <property name="hibernate.connection.datasource">java:/JbpmDS</property>
        
    <!-- other hibernate properties 
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>
    -->
    
    <!-- ############################################ -->
    <!-- # mapping files with external dependencies # -->
    <!-- ############################################ -->

    ...

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

Now we are ready to fire up the server, and look if the webapp works. You will not be able to start any processes yet, as there are no processes deployed yet. To do this we refer to the document on process definition deployment.

8.2. Database upgrades

In the jbpm.db subproject, you can find:

  • An SQL script to create the jBPM 3.0.2 schema (for Hypersonic)
  • An SQL script to create the jBPM 3.1 schema (for Hypersonic)
  • An SQL script to upgrade from a jBPM 3.0.2 schema to a jBPM 3.1 schema (for Hypersonic)
  • The ant scripts to create the schema update

The schema SQL scripts can be found in directory hsqldb/upgrade.scripts.

To run the schema update tool for your database, follow these guidelines:

  • Prerequisite: Make sure you installed the jbpm.db project right besides the jbpm project. In the starters-kit, this is automatically the case. If jbpm is installed in a different location, update the jbpm.3.location dir in build.properties accordingly.
  • Prerequisite: You should have the proper JDBC driver jar for your database.
  • Update the properties in the build.properties in the root of the jbpm.db project:
    • upgrade.hibernate.properties: a properties file that contains the connection properties to your database in hibernate style.
    • upgrade.libdir: the directory containing the jdbc driver jars for your database.
    • upgrade.old.schema.script: the schema generation script to create the old database schema. (if it already exists, you don't need this property.)
  • For creating the old schema and then calculating the differences, run the ant script 'ant upgrade.db.script'
  • For only calculating the update script without first loading the old db schema, run the ant script 'ant upgrade.hibernate.schema.update'
  • After successfull completion, you'll find the upgrade script in build/database.upgrade.sql

For upgrading from jBPM 3.0.2 to jBPM 3.1, the generated upgrade SQL script (for HSQLDB) is illustrated in the listing below:

# New JBPM_MESSAGE table
create table JBPM_MESSAGE (
  ID_ bigint generated by default as identity (start with 1), 
  CLASS_ char(1) not null, 
  DESTINATION_ varchar(255), 
  EXCEPTION_ varchar(255), 
  ISSUSPENDED_ bit, 
  TOKEN_ bigint, 
  TEXT_ varchar(255), 
  ACTION_ bigint, 
  NODE_ bigint, 
  TRANSITIONNAME_ varchar(255), 
  TASKINSTANCE_ bigint, 
  primary key (ID_)
);

# Added columns
alter table JBPM_ACTION add column ACTIONEXPRESSION_ varchar(255);
alter table JBPM_ACTION add column ISASYNC_ bit;
alter table JBPM_COMMENT add column VERSION_ integer;
alter table JBPM_ID_GROUP add column PARENT_ bigint;
alter table JBPM_NODE add column ISASYNC_ bit;
alter table JBPM_NODE add column DECISIONEXPRESSION_ varchar(255);
alter table JBPM_NODE add column ENDTASKS_ bit;
alter table JBPM_PROCESSINSTANCE add column VERSION_ integer;
alter table JBPM_PROCESSINSTANCE add column ISSUSPENDED_ bit;
alter table JBPM_RUNTIMEACTION add column VERSION_ integer;
alter table JBPM_SWIMLANE add column ACTORIDEXPRESSION_ varchar(255);
alter table JBPM_SWIMLANE add column POOLEDACTORSEXPRESSION_ varchar(255);
alter table JBPM_TASK add column ISSIGNALLING_ bit;
alter table JBPM_TASK add column ACTORIDEXPRESSION_ varchar(255);
alter table JBPM_TASK add column POOLEDACTORSEXPRESSION_ varchar(255);
alter table JBPM_TASKINSTANCE add column CLASS_ char(1);
alter table JBPM_TASKINSTANCE add column ISSUSPENDED_ bit;
alter table JBPM_TASKINSTANCE add column ISOPEN_ bit;
alter table JBPM_TIMER add column ISSUSPENDED_ bit;
alter table JBPM_TOKEN add column VERSION_ integer;
alter table JBPM_TOKEN add column ISSUSPENDED_ bit;
alter table JBPM_TOKEN add column SUBPROCESSINSTANCE_ bigint;
alter table JBPM_VARIABLEINSTANCE add column TASKINSTANCE_ bigint;

# Added constraints
alter table JBPM_ID_GROUP add constraint FK_ID_GRP_PARENT foreign key (PARENT_) references JBPM_ID_GROUP;
alter table JBPM_MESSAGE add constraint FK_MSG_TOKEN foreign key (TOKEN_) references JBPM_TOKEN;
alter table JBPM_MESSAGE add constraint FK_CMD_NODE foreign key (NODE_) references JBPM_NODE;
alter table JBPM_MESSAGE add constraint FK_CMD_ACTION foreign key (ACTION_) references JBPM_ACTION;
alter table JBPM_MESSAGE add constraint FK_CMD_TASKINST foreign key (TASKINSTANCE_) references JBPM_TASKINSTANCE;
alter table JBPM_TOKEN add constraint FK_TOKEN_SUBPI foreign key (SUBPROCESSINSTANCE_) references JBPM_PROCESSINSTANCE;
alter table JBPM_VARIABLEINSTANCE add constraint FK_VAR_TSKINST foreign key (TASKINSTANCE_) references JBPM_TASKINSTANCE;

8.3. Starting hsqldb manager on JBoss

Not really crucial for jBPM, but in some situations during development, it can be convenient to open the hypersonic database manager that gives you access to the data in the JBoss hypersonic database.

Start by opening a browser and navigating to the jBPM server JMX console. The URL you should use in your browser for doing this is : http://localhost:8080/jmx-console. Of course this will look slightly different if you are running jBPM on another machine or on another port than the default one. A screenshot of the resulting page is shown in the figure below.

The JBoss jBPM JMX Console

Figure 8.6. The JBoss jBPM JMX Console

If you click on the link 'database=localDB,service=Hypersonic' under the JBoss entries, you will see the JMX MBean view of the HSQLDB database manager. Scrolling a bit down on this page, in the operations section, you will see the 'startDatabaseManager()' operation. This is illustrated in the screenshot below.

The HSQLDB MBean

Figure 8.7. The HSQLDB MBean

Clicking the invoke button will start the HSQLDB Database Manager application. This is a rather harsh database client tool, but it works ok for our purposes of executing this generated script. You may have to ALT-TAB to get to view this application as it may be covered by another window. The figure below shows this application with the above script loaded and ready to execute. Pushing the 'Execute SQL' button will execute the script and effectively update your database.

The HSQLDB Database Manager

Figure 8.8. The HSQLDB Database Manager

Chapter 9. Process Modelling

9.1. Overview

A process definition represents a formal specification of a business process and is based on a directed graph. The graph is composed of nodes and transitions. Every node in the graph is of a specific type. The type of the node defines the runtime behaviour. A process definition has exactly one start state.

A token is one path of execution. A token is the runtime concept that maintains a pointer to a node in the graph.

A process instance is one execution of a process definition. When a process instance is created, a token is created for the main path of execution. This token is called the root token of the process instance and it is positioned in the start state of the process definition.

A signal instructs a token to continue graph execution. When receiving an unnamed signal, the token will leave its current node over the default leaving transition. When a transition-name is specified in the signal, the token will leave its node over the specified transition. A signal given to the process instance is delegated to the root token.

After the token has entered a node, the node is executed. Nodes themselves are responsible for the continuation of the graph execution. Continuation of graph execution is done by making the token leave the node. Each node type can implement a different behaviour for the continuation of the graph execution. A node that does not propagate execution will behave as a state.

Actions are pieces of java code that are executed upon events in the process execution. The graph is an important instrument in the communication about software requirements. But the graph is just one view (projection) of the software being produced. It hides many technical details. Actions are a mechanism to add technical details outside of the graphical representation. Once the graph is put in place, it can be decorated with actions. The main event types are entering a node, leaving a node and taking a transition.

9.2. Process graph

The basis of a process definition is a graph that is made up of nodes and transitions. That information is expressed in an xml file called processdefinition.xml. Each node has a type like e.g. state, decision, fork, join,... Each node has a set of leaving transitions. A name can be given to the transitions that leave a node in order to make them distinct. For example: The following diagram shows a process graph of the jBAY auction process.

The auction process graph

Figure 9.1. The auction process graph

Below is the process graph of the jBAY auction process represented as xml:

<process-definition>

  <start-state>
    <transition to="auction" />
  </start-state>
  
  <state name="auction">
    <transition name="auction ends" to="salefork" />
    <transition name="cancel" to="end" />
  </state>
  
  <fork name="salefork">
    <transition name="shipping" to="send item" />
    <transition name="billing" to="receive money" />
  </fork>
  
  <state name="send item">
    <transition to="receive item" />
  </state>

  <state name="receive item">
    <transition to="salejoin" />
  </state>
  
  <state name="receive money">
    <transition to="send money" />
  </state>

  <state name="send money">
    <transition to="salejoin" />
  </state>
  
  <join name="salejoin">
    <transition to="end" />
  </join>
  
  <end-state name="end" />
  
</process-definition>

9.3. Nodes

A process graph is made up of nodes and transitions. For more information about the graph and its executional model, refer to Chapter 4, Graph Oriented Programming.

Each node has a specific type. The node type determines what will happen when an execution arrives in the node at runtime. jBPM has a set of preimplemented node types that you can use. Alternatively, you can write custom code for implementing your own specific node behaviour.

9.3.1. Node responsibilities

Each node has 2 main responsibilities: First, it can execute plain java code. Typically the plain java code relates to the function of the node. E.g. creating a few task instances, sending a notification, updating a database,... Secondly, a node is responsible for propagating the process execution. Basically, each node has the following options for propagating the process execution:

  • 1. not propagate the execution. In that case the node behaves as a wait state.
  • 2. propagate the execution over one of the leaving transitions of the node. This means that the token that originally arrived in the node is passed over one of the leaving transitions with the API call executionContext.leaveNode(String). The node will now act as an automatic node in the sense it can execute some custom programming logic and then continue process execution automatically without waiting.
  • 3. create new paths of execution. A node can decide to create new tokens. Each new token represents a new path of execution and each new token can be launched over the node's leaving transitions. A good example of this kind of behaviour is the fork node.
  • 4. end paths of execution. A node can decide to end a path of execution. That means that the token is ended and the path of execution is finished.
  • 5. more general, a node can modify the whole runtime structure of the process instance. The runtime structure is a process instance that contains a tree of tokens. Each token represents a path of execution. A node can create and end tokens, put each token in a node of the graph and launch tokens over transitions.

jBPM contains --as any workflow and BPM engine-- a set of preimplemented node types that have a specific documented configuration and behaviour. But the unique thing about jBPM and the Graph Oriented Programming foundation is that we open up the model for developers. Developers can write their own node behaviour very easy and use it in a process.

That is where traditional workflow and BPM systems are much more closed. They usually supply a fixed set of node types (called the process language). Their process language is closed and the executional model is hidden in the runtime environment. Research of workflow patterns has shown that any process language is not powerfull enough. We have decided for a simple model and allow developers to write their own node types. That way the JPDL process language is open ended.

Next, we discuss the most important node types of JPDL.

9.3.2. Nodetype task-node

A task node represents one or more tasks that are to be performed by humans. So when execution arrives in a task node, task instances will be created in the task lists of the workflow participants. After that, the node will behave as a wait state. So when the users perform their task, the task completion will trigger the resuming of the execution. In other words, that leads to a new signal being called on the token.

9.3.3. Nodetype state

A state is a bare-bones wait state. The difference with a task node is that no task instances will be created in any task list. This can be usefull if the process should wait for an external system. E.g. upon entry of the node (via an action on the node-enter event), a message could be sent to the external system. After that, the process will go into a wait state. When the external system send a response message, this can lead to a token.signal(), which triggers resuming of the process execution.

9.3.4. Nodetype decision

Actually there are 2 ways to model a decision. The distinction between the two is based on *who* is making the decision. Should the decision made by the process (read: specified in the process definition). Or should an external entity provide the result of the decision.

When the decision is to be taken by the process, a decision node should be used. There are basically 2 ways to specify the decision criteria. Simplest is by adding condition elements on the transitions. Conditions are beanshell script expressions that return a boolean. At runtime the decision node will loop over its leaving transitions (in the order as specified in the xml), and evaluate each condition. The first transition for which the conditions resolves to 'true' will be taken. Alternatively, an implementation of the DecisionHandler can be specified. Then the decision is calculated in a java class and the selected leaving transition is returned by the decide-method of the DecisionHandler implementation.

When the decision is taken by an external party (meaning: not part of the process definition), you should use multiple transitions leaving a state or wait state node. Then the leaving transition can be provided in the external trigger that resumes execution after the wait state is finished. E.g. Token.signal(String transitionName) and TaskInstance.end(String transitionName).

9.3.5. Nodetype fork

A fork splits one path of execution into multiple concurrent paths of execution. The default fork behaviour is to create a child token for each transition that leaves the fork, creating a parent-child relation between the token that arrives in the fork.

9.3.6. Nodetype join

The default join assumes that all tokens that arrive in the join are children of the same parent. This situation is created when using the fork as mentioned above and when all tokens created by a fork arrive in the same join. A join will end every token that enters the join. Then the join will examine the parent-child relation of the token that enters the join. When all sibling tokens have arrived in the join, the parent token will be propagated over the (unique!) leaving transition. When there are still sibling tokens active, the join will behave as a wait state.

9.3.7. Nodetype node

The type node serves the situation where you want to write your own code in a node. The nodetype node expects one subelement action. The action is executed when the execution arrives in the node. The code you write in the actionhandler can do anything you want but it is also responsible for propagating the execution.

This node can be used if you want to use a JavaAPI to implement some functional logic that is important for the business analyst. By using a node, the node is visible in the graphical representation of the process. For comparison, actions --covered next-- will allow you to add code that is invisible in the graphical representation of the process, in case that logic is not important for the business analyst.

9.4. Transitions

Transitions have a source node and a destination node. The source node is represented with the property from and the destination node is represented by the property to.

A node can optionally have a name. Note that most of the jBPM features depend on the uniqueness of the transition name. If more then one transition has the same name, the first transition with the given name is taken. In case duplicate transition names occur in a node, the method Map getLeavingTransitionsMap() will return less elements then List getLeavingTransitions().

The default transition is the first transition in the list.

9.5. Actions

Actions are pieces of java code that are executed upon events in the process execution. The graph is an important instrument in the communication about software requirements. But the graph is just one view (projection) of the software being produced. It hides many technical details. Actions are a mechanism to add technical details outside of the graphical representation. Once the graph is put in place, it can be decorated with actions. This means that java code can be associated with the graph without changing the structure of the graph. The main event types are entering a node, leaving a node and taking a transition.

Note the difference between an action that is placed in an event versus an action that is placed in a node. Actions that are put in an event are executed when the event fires. Actions on events have no way to influence the flow of control of the process. It is similar to the observer pattern. On the other hand, an action that is put on a node has the responsibility of propagating the execution.

Let's look at an example of an action on an event. Suppose we want to do a database update on a given transition. The database update is technically vital but it is not important to the business analyst.

A database update action

Figure 9.2. A database update action

public class RemoveEmployeeUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // get the fired employee from the process variables.
    String firedEmployee = (String) ctx.getContextInstance().getVariable("fired employee");
    
    // by taking the same database connection as used for the jbpm updates, we 
    // reuse the jbpm transaction for our database update.
    Connection connection = ctx.getProcessInstance().getJbpmSession().getSession().getConnection();
    Statement statement = connection.createStatement();
    statement.execute("DELETE FROM EMPLOYEE WHERE ...");
    statement.execute(); 
    statement.close();
  }
}
<process-definition name="yearly evaluation">

  ...
  <state name="fire employee">
    <transition to="collect badge">
      <action class="com.nomercy.hr.RemoveEmployeeUpdate" />
    </transition>
  </state>
  
  <state name="collect badge">
  ...
  
</process-definition>

9.5.1. Action configuration

For more information about adding configurations to your custom actions and how to specify the configuration in the processdefinition.xml, see Section 16.2.3, “Configuration of delegations”

9.5.2. Action references

Actions can be given a name. Named actions can be referenced from other locations where actions can be specified. Named actions can also be put as child elements in the process definition.

This feature is interesting if you want to limit duplication of action configurations (e.g. when the action has complicated configurations). Another use case is execution or scheduling of runtime actions.

9.5.3. Events

Events specify moments in the execution of the process. The jBPM engine will fire events during graph execution. This occurs when jbpm calculats the next state (read: processing a signal). An event is always relative to an element in the process definition like e.g. the process definition, a node or a transition. Most process elements can fire different types of events. A node for example can fire a node-enter event and a node-leave event. Events are the hooks for actions. Each event has a list of actions. When the jBPM engine fires an event, the list of actions is executed.

9.5.4. Event propagation

Superstates create a parent-child relation in the elements of a process definition. Nodes and transitions contained in a superstate have that superstate as a parent. Top level elements have the process definition as a parent. The process definition does not have a parent. When an event is fired, the event will be propagated up the parent hierarchy. This allows e.g. to capture all transition events in a process and associate actions with these events in a centralized location.

9.5.5. Script

A script is an action that executes a beanshell script. For more information about beanshell, see the beanshell website. By default, all process variables are available as script-variables and no script-variables will be written to the process variables. Also the following script-variables will be available :

  • executionContext
  • token
  • node
  • task
  • taskInstance
<process-definition>
  <event type="node-enter">
    <script>
      System.out.println("this script is entering node "+node);
    </script>
  </event>
  ...
</process-definition>

To customize the default behaviour of loading and storing variables into the script, the variable element can be used as a sub-element of script. In that case, the script expression also has to be put in a subelement of script: expression.

<process-definition>
  <event type="process-end">
    <script>
      <expression>
        a = b + c;
      </expression>
      <variable name='XXX' access='write' mapped-name='a' />
      <variable name='YYY' access='read' mapped-name='b' />
      <variable name='ZZZ' access='read' mapped-name='c' />
    </script>
  </event>
  ...
</process-definition>

Before the script starts, the process variables YYY and ZZZ will be made available to the script as script-variables b and c respectively. After the script is finished, the value of script-variable a is stored into the process variable XXX.

If the access attribute of variable contains 'read', the process variable will be loaded as a script-variable before script evaluation. If the access attribute contains 'write', the script-variable will be stored as a process variable after evaluation. The attribute mapped-name can make the process variable available under another name in the script. This can be handy when your process variable names contain spaces or other invalid script-literal-characters.

9.5.6. Custom events

Note that it's possible to fire your own custom events at will during the execution of a process. Events are uniquely defined by the combination of a graph element (nodes, transitions, process definitions and superstates are graph elements) and an event-type (java.lang.String). jBPM defines a set of events that are fired for nodes, transitions and other graph elements. But as a user, you are free to fire your own events. In actions, in your own custom node implementations, or even outside the execution of a process instance, you can call the GraphElement.fireEvent(String eventType, ExecutionContext executionContext);. The names of the event types can be chosen freely.

9.6. Superstates

A Superstate is a group of nodes. Superstates can be nested recursively. Superstates can be used to bring some hierarchy in the process definition. For example, one application could be to group all the nodes of a process in phases. Actions can be associated with superstate events. A consequence is that a token can be in multiple nested nodes at a given time. This can be convenient to check wether a process execution is e.g. in the start-up phase. In the jBPM model, you are free to group any set of nodes in a superstate.

9.6.1. Superstate transitions

All transitions leaving a superstate can be taken by tokens in nodes contained within the super state. Transitions can also arrive in superstates. In that case, the token will be redirected to the first node in the superstate. Nodes from outside the superstate can have transitions directly to nodes inside the superstate. Also, the other way round, nodes within superstates can have transitions to nodes outside the superstate or to the superstate itself. Superstates also can have self references.

9.6.2. Superstate events

There are 2 events unique to superstates: superstate-enter and superstate-leave. These events will be fired no matter over which transitions the node is entered or left respectively. As long as a token takes transitions within the superstate, these events are not fired.

Note that we have created separate event types for states and superstates. This is to make it easy to distinct between superstate events and node events that are propagated from within the superstate.

9.6.3. Hierarchical names

Node names have to be unique in their scope. The scope of the node is its node-collection. Both the process definintion and the superstate are node collections. To refer to nodes in superstates, you have to specify the relative, slash (/) separated name. The slash separates the node names. Use '..' to refer to an upper level. The next example shows how to reference a node in a superstate:

<process-definition>
  ...
  <state name="preparation">
    <transition to="phase one/invite murphy"/>
  </state>
  <super-state name="phase one">
    <state name="invite murphy"/>
  </super-state>
  ...
</process-definition>

The next example will show how to go up the superstate hierarchy

<process-definition>
  ...
  <super-state name="phase one">
    <state name="preparation">
      <transition to="../phase two/invite murphy"/>
    </state>
  </super-state>
  <super-state name="phase two">
    <state name="invite murphy"/>
  </super-state>
  ...
</process-definition>

9.7. Exception handling

The exception handling mechanism of jBPM only applies to java exceptions. Graph execution on itself cannot result in problems. It is only the execution of delegation classes that can lead to exceptions.

On process-definitions, nodes and transitions, a list of exception-handlers can be specified. Each exception-handler has a list of actions. When an exception occurs in a delegation class, the process element parent hierarchy is serached for an appropriate exception-handler. When it is found, the actions of the exception-handler are executed.

Note that the exception handling mechanism of jBPM is not completely similar to the java exception handling. In java, a caught exception can have an influence on the control flow. In the case of jBPM, control flow cannot be changed by the jBPM exception handling mechanism. The exception is either caught or uncaught. Uncaught exceptions are thrown to the client (e.g. the client that called the token.signal()) or the exception is caught by a jBPM exception-handler. For caught exceptions, the graph execution continues as if no exception has occurred.

Note that in an action that handles an exception, it is possible to put the token in an arbitrary node in the graph with Token.setNode(Node node).

9.8. Process composition

Process composition is supported in jBPM by means of the process-state. The process state is a state that is associated with another process definition. When graph execution arrives in the process state, a new process instance of the sub-process is created and it is associated with the path of execution that arrived in the process state. The path of execution of the super process will wait till the sub process instance has ended. When the sub process instance ends, the path of execution of the super process will leave the process state and continue graph execution in the super process.

<process-definition name="hire">
  <start-state>
    <transition to="initial interview" />
  </start-state>
  <process-state name="initial interview">
    <sub-process name="interview" />
    <variable name="a" access="read,write" mapped-name="aa" />
    <variable name="b" access="read" mapped-name="bb" />
    <transition to="..." />
  </process-state>
  ...
</process-definition>

This 'hire' process contains a process-state that spawns an 'interview' process. When execution arrives in the 'first interview', a new execution (=process instance) for the latest version of the 'interview' process is created. Then variable 'a' from the hire process is copied into variable 'aa' from the interview process. The same way, hire variable 'b' is copied into interview variable 'bb'. When the interview process finishes, only variable 'aa' from the interview process is copied back into the 'a' variable of the hire process.

In general, When a subprocess is started, all variables with read access are read from the super process and fed into the newly created sub process before the signal is given to leave the start state. When the sub process instances is finished, all the variables with write access will be copied from the sub process to the super process. The mapped-name attribute of the variable element allows you to specify the variable name that should be used in the sub process.

9.9. Custom node behaviour

In jBPM, it's quite easy to write your own custom nodes. For creating custom nodes, an implementation of the ActionHandler has to be written. The implementation can execute any business logic, but also has the responsibility to propagate the graph execution. Let's look at an example that will update an ERP-system. We'll read an amout from the ERP-system, add an amount that is stored in the process variables and store the result back in the ERP-system. Based on the size of the amount, we have to leave the node via the 'small amounts' or the 'large amounts' transition.

The update erp example process snippet

Figure 9.3. The update erp example process snippet

public class AmountUpdate implements ActionHandler {
  public void execute(ExecutionContext ctx) throws Exception {
    // business logic
    Float erpAmount = ...get amount from erp-system...;
    Float processAmount = (Float) ctx.getContextInstance().getVariable("amount");
    float result = erpAmount.floatValue() + processAmount.floatValue();
    ...update erp-system with the result...;
    
    // graph execution propagation
    if (result > 5000) {
      ctx.leaveNode(ctx, "big amounts");
    } else {
      ctx.leaveNode(ctx, "small amounts");
    }
  }
}

It is also possible to create and join tokens in custom node implementations. For an example on how to do this, check out the Fork and Join node implementation in the jbpm source code :-).

9.10. Graph execution

The graph execution model of jBPM is based on interpretation of the process definition and the chain of command pattern.

Interpretation of the process definition means that the process definition data is stored in the database. At runtime the process definition information is used during process execution. Note for the concerned : we use hibernate's second level cache to avoid loading of definition information at runtime. Since the process definitions don't change (see process versioning) hibernate can cache the process definitions in memory.

The chain of command pattern means that each node in the graph is responsible for propagating the process execution. If a node does not propagate execution, it behaves as a wait state.

The idea is to start execution on process instances and that the execution continues till it enters a wait state.

A token represents a path of execution. A token has a pointer to a node in the process graph. During waitstates, the tokens can be persisted in the database. Now we are going to look at the algorithm for calculating the execution of a token. Execution starts when a signal is sent to a token. The execution is then passed over the transitions and nodes via the chain of command pattern. These are the relevant methods in a class diagram.

The graph execution related methods

Figure 9.4. The graph execution related methods

When a token is in a node, signals can be sent to the token. Sending a signal is an instruction to start execution. A signal must therefore specify a leaving transition of the token's current node. The first transition is the default. In a signal to a token, the token takes its current node and calls the Node.leave(ExecutionContext,Transition) method. Think of the ExecutionContext as a Token because the main object in an ExecutionContext is a Token. The Node.leave(ExecutionContext,Transition) method will fire the node-leave event and call the Transition.take(ExecutionContext). That method will fire the transition event and call the Node.enter(ExecutionContext) on the destination node of the transition. That method will fire the node-enter event and call the Node.execute(ExecutionContext). Each type of node has its own behaviour that is implementated in the execute method. Each node is responsible for propagating graph execution by calling the Node.leave(ExecutionContext,Transition) again. In summary:

  • Token.signal(Transition)
  • --> Node.leave(ExecutionContext,Transition)
  • --> Transition.take(ExecutionContext)
  • --> Node.enter(ExecutionContext)
  • --> Node.execute(ExecutionContext)

Note that the complete calculation of the next state, including the invocation of the actions is done in the thread of the client. A common misconception is that all calculations *must* be done in the thread of the client. As with any asynchronous invocation, you can use asynchronous messaging (JMS) for that. When the message is sent in the same transaction as the process instance update, all synchronization issues are taken care of. Some workflow systems use asynchronous messaging between all nodes in the graph. But in high throughput environments, this algorithm gives much more control and flexibility for tweaking performance of a business process.

9.11. Transaction demarcation

As explained in Section 9.10, “Graph execution” and Chapter 4, Graph Oriented Programming, jBPM runs the process in the thread of the client and is by nature synchronous. Meaning that the token.signal() or taskInstance.end() will only return when the process has entered a new wait state.

The jPDL feature that we describe here from a modelling perspective is Chapter 13, Asynchronous continuations.

In most situations this is the most straightforward approach because the process execution can easily be bound to server side transactions: the process moves from one state to the next in one transaction.

In some scenarios where in-process calculations take a lot of time, this behaviour might be undesirable. To cope with this, jBPM includes an asynchronous messaging system that allows to continue a process in an asynchronous manner. Of course, in a java enterprise environment, jBPM can be configured to use a JMS message broker instead of the built in messaging system.

In any node, jPDL supports the attribute async="true". Asynchronous nodes will not be executed in the thread of the client. Instead, a message is sent over the asynschronous messaging system and the thread is returned to the client (meaning that the token.signal() or taskInstance.end() will return).

Note that the jbpm client code can now commit the transaction. The sending of the message should be done in the same transaction as the process updates. So the net result of the transaction is that the token has moved to the next node (which has not yet been executed) and a org.jbpm.command.ExecuteNodeCommand-message has been sent on the asynchronous messaging system to the jBPM Command Executor.

The jBPM Command Executor reads commands from the queue and executes them. In the case of the org.jbpm.command.ExecuteNodeCommand, the process will be continued with executing the node. Each command is executed in a separate transaction.

So in order for asynchronous processes to continue, a jBPM Command Executor needs to be running. The simplest way to do that is to configure the CommandExecutionServlet in your web application. Alternatively, you should make sure that the CommandExecutor thread is up and running in any other way.

As a process modeller, you should not really be concerned with all this asynchronous messaging. The main point to remember is transaction demarcation: By default jBPM will operate in the transaction of the client, doing the whole calculation until the process enters a wait state. Use async="true" to demarcate a transaction in the process.

Let's look at an example:

...
<start-state>
  <transition to="one" />
</start-state>
<node async="true" name="one">
  <action class="com...MyAutomaticAction" />
  <transition to="two" />
</node>
<node async="true" name="two">
  <action class="com...MyAutomaticAction" />
  <transition to="three" />
</node>
<node async="true" name="three">
  <action class="com...MyAutomaticAction" />
  <transition to="end" />
</node>
<end-state name="end" />
...

Client code to interact with process executions (starting and resuming) is exactly the same as with normal (synchronous) processes:

...start a transaction...
JbpmContext jbpmContext = jbpmConfiguration.createContext();
try {
  ProcessInstance processInstance = jbpmContext.newProcessInstance("my async process");
  processInstance.signal();
  jbpmContext.save(processInstance);
} finally {
  jbpmContext.close();
}

After this first transaction, the root token of the process instance will point to node one and a ExecuteNodeCommandmessage will have been sent to the command executor.

In a subsequent transaction, the command executor will read the message from the queue and execute node one. The action can decide to propagate the execution or enter a wait state. If the action decides to propagate the execution, the transaction will be ended when the execution arrives at node two. And so on, and so on...

Chapter 10. Context

Context is about process variables. Process variables are key-value pairs that maintain information related to the process instance. Since the context must be storable in a database, some minor limitations apply.

10.1. Accessing variables

org.jbpm.context.exe.ContextInstance serves as the central interface to work with process variables. You can obtain the ContextInstance from a ProcessInstance like this :

ProcessInstance processInstance = ...;
ContextInstance contextInstance = (ContextInstance) processInstance.getInstance(ContextInstance.class);

The most basic operations are

void ContextInstance.setVariable(String variableName, Object value);
void ContextInstance.setVariable(String variableName, Object value, Token token);
Object ContextInstance.getVariable(String variableName);
Object ContextInstance.getVariable(String variableName, Token token);

The variable names are java.lang.String. By default, jBPM supports the following value types:

  • java.lang.String
  • java.lang.Boolean
  • java.lang.Character
  • java.lang.Float
  • java.lang.Double
  • java.lang.Long
  • java.lang.Byte
  • java.lang.Short
  • java.lang.Integer
  • java.util.Date
  • byte[]
  • java.io.Serializable
  • classes that are persistable with hibernate

Also an untyped null value can be stored persistently.

All other types can be stored in the process variables without any problem. But it will cause an exception when you try to save the process instance.

To configure jBPM for storing hibernate persistent objects in the variables, see Storing hibernate persistent objects.

10.2. Variable lifetime

Variables do not have to be declared in the process archive. At runtime, you can just put any java object in the variables. If that variable was not present, it will be created. Just the same as with a plain java.util.Map.

Variables can be deleted with

ContextInstance.deleteVariable(String variableName);
ContextInstance.deleteVariable(String variableName, Token token);

Automatic changing of types is now supported. This means that it is allowed to overwrite a variable with a value of a different type. Of course, you should try to limit the number of type changes since this creates a more db communication then a plain update of a column.

10.3. Variable persistence

The variables are a part of the process instance. Saving the process instance in the database, brings the database in sync with the process instance. The variables are created, updated and deleted from the database as a result of saving (=updating) the process instance in the database. For more information, see Chapter 7, Persistence.

10.4. Variables scopes

Each path of execution (read: token) has its own set of process variables. Requesting a variable is always done on a token. Process instances have a tree of tokens (see graph oriented programming). When requesting a variable without specifying a token, the default token is the root token.

The variable lookup is done recursively over the parents of the given token. The behaviour is similar to the scoping of variables in programming languages.

When a non-existing variable is set on a token, the variable is created on the root-token. This means that each variable has by default process scope. To make a variable token-local, you have to create it explicitely with:

ContextInstance.createVariable(String name, Object value, Token token);

10.4.1. Variables overloading

Variable overloading means that each path of execution can have its own copy of a variable with the same name. They are treated as independant and hence can be of different types. Variable overloading can be interesting if you launch multiple concurrent paths of execution over the same transition. Then the only thing that distinguishes those paths of executions are their respective set of variables.

10.4.2. Variables overriding

Variable overriding means that variables of nested paths of execution override variables in more global paths of execution. Generally, nested paths of execution relate to concurrency : the paths of execution between a fork and a join are children (nested) of the path of execution that arrived in the fork. For example, if you have a variable 'contact' in the process instance scope, you can override this variable in the nested paths of execution 'shipping' and 'billing'.

10.4.3. Task instance variable scope

For more info on task instance variables, see Section 11.4, “Task instance variables”.

10.5. Transient variables

When a process instance is persisted in the database, normal variables are also persisted as part of the process instance. In some situations you might want to use a variable in a delegation class, but you don't want to store it in the database. An example could be a database connection that you want to pass from outside of jBPM to a delegation class. This can be done with transient variables.

The lifetime of transient variables is the same as the ProcessInstance java object.

Because of their nature, transient variables are not related to a token. So there is only one map of transient variables for a process instance object.

The transient variables are accessable with their own set of methods in the context instance, and don't need to be declared in the processdefinition.xml

Object ContextInstance.getTransientVariable(String name);
void ContextInstance.setTransientVariable(String name, Object value);

10.6. Customizing variable persistence

Variables are stored in the database in a 2-step approach :

user-java-object <---> converter <---> variable instance

Variables are stored in VariableInstances. The members of VariableInstances are mapped to fields in the database with hibernate. In the default configuration of jBPM, 6 types of VariableInstances are used:

  • DateInstance (with one java.lang.Date field that is mapped to a Types.TIMESTAMP in the database)

  • DoubleInstance (with one java.lang.Double field that is mapped to a Types.DOUBLE in the database)

  • StringInstance (with one java.lang.String field that is mapped to a Types.VARCHAR in the database)

  • LongInstance (with one java.lang.Long field that is mapped to a Types.BIGINT in the database)

  • HibernateLongInstance (this is used for hibernatable types with a long id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)

  • HibernateStringInstance (this is used for hibernatable types with a string id field. One java.lang.Object field is mapped as a reference to a hibernate entity in the database)

Converters convert between java-user-objects and the java objects that can be stored by the VariableInstances. So when a process variable is set with e.g. ContextInstance.setVariable(String variableName, Object value), the value will optionally be converted with a converter. Then the converted object will be stored in a VariableInstance. Converters are implementations of the following interface:

public interface Converter extends Serializable {
  boolean supports(Object value);
  Object convert(Object o);
  Object revert(Object o);
}

Converters are optional. Converters must be available to the jBPM class loader

The way that user-java-objects are converted and stored in variable instances is configured in the file org/jbpm/context/exe/jbpm.varmapping.properties. To customize this property file, put a modified version in the root of the classpath, as explained in Section 6.2, “Configuration files” Each line of the properties file specifies 2 or 3 class-names separated by spaces : the classname of the user-java-object, optionally the classname of the converter and the classname of the variable instance. When you refer your custom converters, make sure they are in the jBPM class path. When you refer to your custom variable instances, they also have to be in the the jBPM class path and the hibernate mapping file for org/jbpm/context/exe/VariableInstance.hbm.xml has to be updated to include the custom subclass of VariableInstance.

For example, take a look at the following xml snippet in the file org/jbpm/context/exe/jbpm.varmapping.xml.

    <jbpm-type>
      <matcher>
        <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
          <field name="className"><string value="java.lang.Boolean" /></field>
        </bean>
      </matcher>
      <converter class="org.jbpm.context.exe.converter.BooleanToStringConverter" />
      <variable-instance class="org.jbpm.context.exe.variableinstance.StringInstance" />
    </jbpm-type>

This snippet specifies that all objects of type java.lang.Boolean have to be converted with the converter BooleanToStringConverter and that the resulting object (a String) will be stored in a variable instance object of type StringInstance.

If no converter is specified as in

    <jbpm-type>
      <matcher>
        <bean class="org.jbpm.context.exe.matcher.ClassNameMatcher">
          <field name="className"><string value="java.lang.Long" /></field>
        </bean>
      </matcher>
      <variable-instance class="org.jbpm.context.exe.variableinstance.LongInstance" />
    </jbpm-type>

that means that the Long objects that are put in the variables are just stored in a variable instance of type LongInstance without being converted.

Chapter 11. Task management

The core business of jBPM is the ability to persist the execution of a process. A situation in which this feature is extremely useful is the management of tasks and tasklists for people. jBPM allows to specify a piece of software describing an overall process which can have wait states for human tasks.

11.1. Tasks

Tasks are part of the process definition and they define how task instances must be created and assigned during process executions.

Tasks can be defined in task-nodes and in the process-definition. The most common way is to define one or more tasks in a task-node. In that case the task-node represents a task to be done by the user and the process execution should wait until the actor completes the task. When the actor completes the task, process execution should continue. When more tasks are specified in a task-node, the default behaviour is to wait for all the tasks to complete.

Tasks can also be specified on the process-definition. Tasks specified on the process definition can be looked up by name and referenced from within task-nodes or used from inside actions. In fact, all tasks (also in task-nodes) that are given a name can be looked up by name in the process-definition.

Task names must be unique in the whole process definition. Tasks can be given a priority. This priority will be used as the initial priority for each task instance that is created for this task. TaskInstances can change this initial priority afterwards.

11.2. Task instances

A task instance can be assigned to an actorId (java.lang.String). All task instances are stored in one table of the database (JBPM_TASKINSTANCE). By querying this table for all task instances for a given actorId, you get the task list for that perticular user.

The jBPM task list mechanism can combine jBPM tasks with other tasks, even when those tasks are unrelated to a process execution. That way jBPM developers can easily combine jBPM-process-tasks with tasks of other applications in one centralized task-list-repository.

11.2.1. Task instance lifecycle

The task instance lifecycle is straightforward: After creation, task instances can optionally be started. Then, task instances can be ended, which means that the task instance is marked as completed.

Note that for flexibility, assignment is not part of the life cycle. So task instances can be assigned or not assigned. Task instance assignment does not have an influence on the task instance life cycle.

Task instances are typically created by the process execution entering a task-node (with the method TaskMgmtInstance.createTaskInstance(...)). Then, a user interface component will query the database for the tasklists using the TaskMgmtSession.findTaskInstancesByActorId(...). Then, after collecting input from the user, the UI component calls TaskInstance.assign(String), TaskInstance.start() or TaskInstance.end(...).

A task instance maintains it's state by means of date-properties : create, start and end. Those properties can be accessed by their respective getters on the TaskInstance.

Currently, completed task instances are marked with an end date so that they are not fetched with subsequent queries for tasks lists. But they remain in the JBPM_TASKINSTANCE table.

11.2.2. Task instances and graph execution

Task instances are the items in an actor's tasklist. Task instances can be signalling. A signalling task instance is a task instance that, when completed, can send a signal to its token to continue the process execution. Task instances can be blocking, meaning that the related token (=path of execution) is not allowed to leave the task-node before the task instance is completed. By default task instances are signalling and non-blocking.

In case more than one task instance are associated with a task-node, the process developer can specify how completion of the task instances affects continuation of the process. Following is the list of values that can be given to the signal-property of a task-node.

  • last: This is the default. Proceeds execution when the last task instance is completed. When no tasks are created on entrance of this node, execution is continued.
  • last-wait: Proceeds execution when the last task instance is completed. When no tasks are created on entrance of this node, execution waits in the task node till tasks are created.
  • first: Proceeds execution when the first task instance is completed. When no tasks are created on entrance of this node, execution is continued.
  • first-wait: Proceeds execution when the first task instance is completed. When no tasks are created on entrance of this node, execution waits in the task node till tasks are created.
  • unsynchronized: Execution always continues, regardless wether tasks are created or still unfinished.
  • never: Execution never continues, regardless wether tasks are created or still unfinished.

Task instance creation might be based upon a runtime calculation. In that case, add an ActionHandler on the node-enter event of the task-node and set the attribute create-tasks="false". Here is an example of such an action handler implementation:

public class CreateTasks implements ActionHandler {
  public void execute(ExecutionContext executionContext) throws Exception {
    Token token = executionContext.getToken();
    TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
      
    TaskNode taskNode = (TaskNode) executionContext.getNode();
    Task changeNappy = taskNode.getTask("change nappy");

    // now, 2 task instances are created for the same task.
    tmi.createTaskInstance(changeNappy, token);
    tmi.createTaskInstance(changeNappy, token);
  }
}

As shown in the example the tasks to be created can be specified in the task-node. They could also be specified in the process-definition and fetched from the TaskMgmtDefinition. TaskMgmtDefinition extends the ProcessDefinition with task management information.

The API method for marking task instances as completed is TaskInstance.end(). Optionally, you can specify a transition in the end method. In case the completion of this task instance triggers continuation of the execution, the task-node is left over the specified transition.

11.3. Assignment

A process definition contains task nodes. A task-node contains zero or more tasks. Tasks are a static description as part of the process definition. At runtime, tasks result in the creation of task instances. A task instance corresponds to one entry in a person's task list.

With jBPM, push and pull model (see below) of task assignment can be applied in combination. The process can calculate the responsible for a task and push it in his/her tasklist. Or alternatively, a task can be assigned to a pool of actors, in which case each of the actors in the pool can pull the task and put it in the actor's personal tasklist.

11.3.1. Assignment interfaces

Assigning task instances is done via the interface AssignmentHandler:

public interface AssignmentHandler extends Serializable {
  void assign( Assignable assignable, ExecutionContext executionContext );
}

An assignment handler implementation is called when a task instance is created. At that time, the task instance can be assigned to one or more actors. The AssignmentHandler implementation should call the Assignable methods (setActorId or setPooledActors) to assign a task. The Assignable is either a TaskInstance or a SwimlaneInstance (=process role).

public interface Assignable {
  public void setActorId(String actorId);
  public void setPooledActors(String[] pooledActors);
}

Both TaskInstances and SwimlaneInstances can be assigned to a specific user or to a pool of actors. To assign a TaskInstance to a user, call Assignable.setActorId(String actorId). To assign a TaskInstance to a pool of candidate actors, call Assignable.setPooledActors(String[] actorIds).

Each task in the process definition can be associated with an assignment handler implementation to perform the assignment at runtime.

When more then one task in a process should be assigned to the same person or group of actors, consider the usage of a swimlane

To allow for the creation of reusable AssignmentHandlers, each usage of an AssignmentHandler can be configured in the processdefinition.xml. See Section 16.2, “Delegation” for more information on how to add configuration to assignment handlers.

11.3.2. The assignment data model

The datamodel for managing assignments of task instances and swimlane instances to actors is the following. Each TaskInstance has an actorId and a set of pooled actors.

The assignment model class diagram

Figure 11.1. The assignment model class diagram

The actorId is the responsible for the task, while the set of pooled actors represents a collection of candidates that can become responsible if they would take the task. Both actorId and pooledActors are optional and can also be combined.

11.3.3. Push model

The actorId of a task instance denotes the responsible for that given task. The pooled actors for a TaskInstance are the candidate actors for that task. Typically, the actorId of a TaskInstance will reference a user. The pooled actors can reference users and/or groups.

The personal task list of a user are all the TaskInstances that have the given user as actorId. This list can be obtained with TaskMgmtSession.findTaskInstances(String actorId).

11.3.4. Pull model

On the other hand, the tasks of pooled tasks for a given user are the tasks for which the given user is referenced in the pooled actors. Fetching the list of pooled tasks is typically a two step operation : 1) get all the groups for the given user from the identity component and 2) get the list of all pooled tasks for the combined set of the user's actorId and the actorId's that reference the users' groups. Getting the list of pooled tasks that are offered to a given user can be done with the methods TaskMgmtSession.findPooledTaskInstances(String actorId) or TaskMgmtSession.findPooledTaskInstances(List actorIds). These methods will only return task instances for which the actorId is null and one of the given actorIds matches one of the pooled actors.

To prevent multiple users working on the same pooled task, it is sufficient to update the actorId of the TaskInstance with the user's actorId. After that, the task instance will not appear in the list of pooled tasks, but only in the user's personal task list. Setting the actorId of a taskInstance to null will put the task instance back in the pooled tasks.

11.4. Task instance variables

A task instance can have its own set of variables and a task instance can also 'see' the process variables. Task instances are usually created in an execution path (=token). This creates a parent-child relation between the token and the task instance similar to the parent-child relation between the tokens themselves. The normal scoping rules apply between the variables of a task instance and the process variables of the related token. More info about scoping can be found in Section 10.4, “Variables scopes”.

This means that a task instance can 'see' its own variables plus all the variables of its related token.

The controller can be used to create populate and submit variables between the task instance scope and the process scoped variables.

11.5. Task controllers

At creation of a task instance, the task controllers can populate the task instance variables and when the task instance is finished, the task controller can submit the data of the task instance into the process variables.

Note that you are not forced to use task controllers. Task instances also are able to 'see' the process variables related to its token. Use task controllers when you want to:

  • a) create copies of variables in the task instances so that intermediate updates to the task instance variables don't affect the process variables untill the process is finished and the copies are submitted back into the process variables.
  • b) the task instance variables do not relate one-on-one with the process variables. E.g. suppose the process has variables 'sales in januari' 'sales in februari' and 'sales in march'. Then the form for the task instance might need to show the average sales in the 3 months.

Tasks are intended to collect input from users. But there are many user interfaces which could be used to present the tasks to the users. E.g. a web application, a swing application, an instant messenger, an email form,... So the task controllers make the bridge between the process variables (=process context) and the user interface application. The task controllers provide a view of process variables to the user interface application.

The task controller makes the translation (if any) from the process variables to the task variables. When a task instance is created, the task controller is responsible for extracting information from the process variables and creating the task variables. The task variables serve as the input for the user interface form. And the user input can be stored in the task variables. When the user ends the task, the task controller is responsible for updating the process variables based on the task instance data.

The task controllers

Figure 11.2. The task controllers

In a simple scenario, there is a one-on-one mapping between process variables and the form parameters. Task controllers are specified in a task element. In this case, the default jBPM task controller can be used and it takes a list of variable elements inside. The variable elements express how the process variables are copied in the task variables.

The next example shows how you can create separate task instance variable copies based on the process variables:

<task name="clean ceiling">
  <controller>
    <variable name="a" access="read" mapped-name="x" />
    <variable name="b" access="read,write,required" mapped-name="y" />
    <variable name="c" access="read,write" />
  </controller>
</task>

The name attribute refers to the name of the process variable. The mapped-name is optional and refers to the name of the task instance variable. If the mapped-name attribute is omitted, mapped-name defaults to the name. Note that the mapped-name also is used as the label for the fields in the task instance form of the web application.

The access attribute specifies if the variable is copied at task instance creation, will be written back to the process variables at task end and wether it is required. This information can be used by the user interface to generate the proper form controls. The access attribute is optional and the default access is 'read,write'.

A task-node can have many tasks and a start-state can have 1 task.

If the simple one-to-one mapping between process variables and form parameters is too limiting, you can also write your own TaskControllerHandler implementation. Here's the TaskControllerHandler interface:

public interface TaskControllerHandler extends Serializable {
  void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
  void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}

And here's how to configure your custom task controller implementation in a task:

<task name="clean ceiling">
  <controller class="com.yourcom.CleanCeilingTaskControllerHandler">
    -- here goes your task controller handler configuration --
  </controller>
</task>

11.6. Swimlanes

A swimlane is a process role. It is a mechanism to specify that multiple tasks in the process should be done by the same actor. So after the first task instance is created for a given swimlane, the actor should be remembered in the process for all subsequent tasks that are in the same swimlane. A swimlane therefore has one assignment and all tasks that reference a swimlane should not specify an assignment.

When the first task in a given swimlane is created, the AssignmentHandler of the swimlane is called. The Assignable that is passed to the AssignmentHandler will be the SwimlaneInstance. Important to know is that all assignments that are done on the task instances in a given swimlane will propagate to the swimlane instance. This behaviour is implemented as the default because the person that takes a task to fulfilling a certain process role will have the knowledge of that perticular process. So all subsequent assignements of task instances to that swimlane are done automatically to that user.

Swimlane is a terminology borrowed from UML activity diagrams.

11.7. Swimlane in start task

A swimlane can be associated with the start task to capture the process initiator.

A task can be specified in a start-state. That task be associated with a swimlane. When a new task instance is created for such a task, the current authenticated actor will be captured with Authentication.getAuthenticatedActorId() and that actor will be stored in the swimlane of the start task.

For example:

<process-definition>
  <swimlane name='initiator' />
  <start-state>
    <task swimlane='initiator' />
    <transition to='...' />
  </start-state>
  ...
</process-definition>

Also variables can be added to the start task as with any other task to define the form associated with the task. See Section 11.5, “Task controllers”

11.8. Task events

Tasks can have actions associated with them. There are 4 standard event types defined for tasks: task-create, task-assign, task-start and task-end.

task-create is fired when a task instance is created.

task-assign is fired when a task instance is being assigned. Note that in actions that are executed on this event, you can access the previous actor with executionContext.getTaskInstance().getPreviousActorId();

task-start is fired when TaskInstance.start() is called. This can be used to indicate that the user is actually starting to work on this task instance. Starting a task is optional.

task-end is fired when TaskInstance.end(...) is called. This marks the completion of the task. If the task is related to a process execution, this call might trigger the resuming of the process execution.

Since tasks can have events and actions associated with them, also exception handlers can be specified on a task. For more information about exception handling, see Section 9.7, “Exception handling”.

11.9. Task timers

As on nodes, timers can be specified on tasks. See Section 12.1, “Timers”.

The special thing about timers for tasks is that the cancel-event for task timers can be customized. By default, a timer on a task will be cancelled when the task is ended (=completed). But with the cancel-event attribute on the timer, process developers can customize that to e.g. task-assign or task-start. The cancel-event supports multiple events. The cancel-event types can be combined by specifying them in a comma separated list in the attribute.

11.10. Customizing task instances

Task instances can be customized. The easiest way to do this is to create a subclass of TaskInstance. Then create a org.jbpm.taskmgmt.TaskInstanceFactory implementation and configure it by setting the configuration property jbpm.task.instance.factory to the fully qualified class name in the jbpm.cfg.xml. If you use a subclass of TaskInstance, also create a hibernate mapping file for the subclass (using the hibernate extends="org.jbpm.taskmgmt.exe.TaskInstance"). Then add that mapping file to the list of mapping files in the hibernate.cfg.xml

11.11. The identity component

Management of users, groups and permissions is commonly known as identity management. jBPM includes an optional identity component that can be easily replaced by a company's own identity data store.

The jBPM identity management component includes knowledge of the organisational model. Task assignment is typically done with organisational knowledge. So this implies knowledge of an organisational model, describing the users, groups, systems and the relations between them. Optionally, permissions and roles can be included too in an organisational model. Various academic research attempts failed, proving that no generic organisational model can be created that fits every organisation.

The way jBPM handles this is by defining an actor as an actual participant in a process. An actor is identified by its ID called an actorId. jBPM has only knowledge about actorId's and they are represented as java.lang.Strings for maximum flexibility. So any knowledge about the organisational model and the structure of that data is outside the scope of the jBPM core engine.

As an extension to jBPM we will provide (in the future) a component to manage that simple user-roles model. This many to many relation between users and roles is the same model as is defined in the J2EE and the servlet specs and it could serve as a starting point in new developments. People interested in contributing should check the jboss jbpm jira issue tracker for more details.

Note that the user-roles model as it is used in the servlet, ejb and portlet specifications, is not sufficiently powerful for handling task assignments. That model is a many-to-many relation between users and roles. This doesn't include information about the teams and the organisational structure of users involved in a process.

11.11.1. The identity model

The identity model class diagram

Figure 11.3. The identity model class diagram

The classes in yellow are the relevant classes for the expression assignment handler that is discussed next.

A User represents a user or a service. A Group is any kind of group of users. Groups can be nested to model the relation between a team, a business unit and the whole company. Groups have a type to differentiate between the hierarchical groups and e.g. haircolor groups. Memberships represent the many-to-many relation between users and groups. A membership can be used to represent a position in a company. The name of the membership can be used to indicate the role that the user fullfills in the group.

11.11.2. Assignment expressions

The identity component comes with one implementation that evaluates an expression for the calculation of actors during assignment of tasks. Here's an example of using the assignment expression in a process definition:

<process-definition>
  ...
  <task-node name='a'>
    <task name='laundry'>
      <assignment expression='previous --> group(hierarchy) --> member(boss)' />
    </task>
    <transition to='b' />
  </task-node>
  ...

Syntax of the assignment expression is like this:

first-term --> next-term --> next-term --> ... --> next-term

where

first-term ::= previous |
               swimlane(swimlane-name) |
               variable(variable-name) |
               user(user-name) |
               group(group-name)

and 

next-term ::= group(group-type) |
              member(role-name)

11.11.2.1. First terms

An expression is resolved from left to right. The first-term specifies a User or Group in the identity model. Subsequent terms calculate the next term from the intermediate user or group.

previous means the task is assigned to the current authenticated actor. This means the actor that performed the previous step in the process.

swimlane(swimlane-name) means the user or group is taken from the specified swimlane instance.

variable(variable-name) means the user or group is taken from the specified variable instance. The variable instance can contain a java.lang.String, in which case that user or group is fetched from the identity component. Or the variable instance contains a User or Group object.

user(user-name) means the given user is taken from the identity component.

group(group-name) means the given group is taken from the identity component.

11.11.2.2. Next terms

group(group-type) gets the group for a user. Meaning that previous terms must have resulted in a User. It searches for the the group with the given group-type in all the memberships for the user.

member(role-name) gets the user that performs a given role for a group. The previous terms must have resulted in a Group. This term searches for the user with a membership to the group for which the name of the membership matches the given role-name.

11.11.3. Removing the identity component

When you want to use your own datasource for organisational information such as your company's user database or ldap system, you can just rip out the jBPM identity component. The only thing you need to do is make sure that you delete the line ...

<mapping resource="org/jbpm/identity/User.hbm.xml"/>
<mapping resource="org/jbpm/identity/Group.hbm.xml"/>
<mapping resource="org/jbpm/identity/Membership.hbm.xml"/>

from the hibernate.cfg.xml

The ExpressionAssignmentHandler is dependent on the identity component so you will not be able to use it as is. In case you want to reuse the ExpressionAssignmentHandler and bind it to your user data store, you can extend from the ExpressionAssignmentHandler and override the method getExpressionSession.

protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);

Chapter 12. Scheduler

This chapter describes how to work with timers in jBPM.

Upon events in the process, timers can be created. When a timer expires, an action can be executed or a transition can be taken.

12.1. Timers

The easiest way to specify a timer is by adding a timer element to the node.

<state name='catch crooks'>
  <timer name='reminder' 
         duedate='3 business hours' 
         repeat='10 business minutes'
         transition='time-out-transition' >
    <action class='the-remainder-action-class-name' />
  </timer>
</state>

A timer that is specified on a node, is not executed after the node is left. Both the transition and the action are optional. When a timer is executed, the following events occur in sequence :

  • an event is fired of type timer
  • if an action is specified, the action is executed.
  • if a transition is specified, a signal will be sent to resume execution over the given transition.

Every timer must have a unique name. If no name is specified in the timer element, the name of the node is taken as the name of the timer.

The timer action can be any supported action element like e.g. action or script.

Timers are created and cancelled by actions. The 2 action-elements are create-timer and cancel-timer. Actually, the timer element shown above is just a short notation for a create-timer action on node-enter and a cancel-timer action on node-leave.

12.2. Scheduler deployment

Process executions create and cancel timers. The timers are stored in a timer store. A separate timer runner must check the timer store and execute the timers when they are due.

Scheduler components overview

Figure 12.1. Scheduler components overview

The following class diagram shows the classes that are involved in the scheduler deployment. The interfaces SchedulerService and TimerExecutor are specified to make the timer execution mechanism pluggable.

Scheduler classes overview

Figure 12.2. Scheduler classes overview

Chapter 13. Asynchronous continuations

13.1. The concept

jBPM is based on Graph Oriented Programming (GOP). Basically, GOP specifies a simple state machine that can handle concurrent paths of execution. But in the execution algorithm specified in GOP, all state transitions are done in a single operation in the thread of the client. If you're not familiar with the execution algorithm defined in Chapter 4, Graph Oriented Programming, please read that first. By default, this performing state transitions in the thread of the client is a good approach cause it fits naturally with server side transactions. The process execution moves from one wait state to another wait state in one transaction.

But in some situations, a developer might want to fine-tune the transaction demarcation in the process definition. In jPDL, it is possible to specify that the process execution should continue asynchronously with the attribute async="true". async="true" can be specified on all node types and all action types.

13.2. An example

Normally, a node is always executed after a token has entered the node. So the node is executed in the thread of the client. We'll explore asynchronous continuations by looking two examples. The first example is a part of a process with 3 nodes. Node 'a' is a wait state, node 'b' is an automated step and node 'c' is again a wait state. This process does not contain any asynchronous behaviour and it is represented in the picture below.

The first frame, shows the starting situation. The token points to node 'a', meaning that the path of execution is waiting for an external trigger. That trigger must be given by sending a signal to the token. When the signal arrives, the token will be passed from node 'a' over the transition to node 'b'. After the token arrived in node 'b', node 'b' is executed. Recall that node 'b' is an automated step that does not behave as a wait state (e.g. sending an email). So the second frame is a snapshot taken when node 'b' is being executed. Since node 'b' is an automated step in the process, the execute of node 'b' will include the propagation of the token over the transition to node 'c'. Node 'c' is a wait state so the third frame shows the final situation after the signal method returns.

Example 1: Process without asynchronous continuation

Figure 13.1. Example 1: Process without asynchronous continuation

While persistence is not mandatory in jBPM, the most common scenario is that a signal is called within a transaction. Let's have a look at the updates of that transaction. First of all, the token is updated to point to node 'c'. These updates are generated by hibernate as a result of the GraphSession.saveProcessInstance on a JDBC connection. Second, in case the automated action would access and update some transactional resources, those transactional updates should be combined or part of the same transaction.

Now, we are going to look at the second example, the second example is a variant of the first example and introduces an asynchronous continuation in node 'b'. Nodes 'a' and 'c' behave the same as in the first example, namely they behave as wait states. In jPDL, a node is marked as asynchronous by setting the attribute async="true".

The result of adding async="true" to node 'b' is that the process execution will be split up into 2 parts. The first part will execute the process up to the point where node 'b' is to be executed. The second part will execute node 'b' and that execution will stop in wait state 'c'.

The transaction will hence be split up into 2 separate transactions. One transaction for each part. While it requires an external trigger (the invocation of the Token.signal method) to leave node 'a' in the first transaction, jBPM will automatically trigger and perform the second transaction.

Example 2: A process with asynchronous continuations

Figure 13.2. Example 2: A process with asynchronous continuations

For actions, the principle is similar. Actions that are marked with the attribute async="true" are executed outside of the thread that executes the process. If persistence is configured (it is by default), the actions will be executed in a separate transaction.

In jBPM, asynchronous continuations are realized by using an asynchronous messaging system. When the process execution arrives at a point that should be executed asynchronously, jBPM will suspend the execution, produces a command message and send it to the command executor. The command executor is a separate component that, upon receipt of a message, will resume the execution of the process where it got suspended.

jBPM can be configured to use a JMS provider or its built-in asynchronous messaging system. The built-in messaging system is quite limited in functionality, but allowes this feature to be supported on environments where JMS is unavailable.

13.3. The command executor

The command executor is the component that resumes process executions asynchronously. It waits for command messages to arrive over an asynchronous messaging system and executes them. The two commands used for asynchronous continuations are ExecuteNodeCommand and ExecuteActionCommand.

These commands are produced by the process execution. During process execution, for each node that has to be executed asynchronously, an ExecuteNodeCommand (POJO) will be created in the MessageInstance. The message instance is a non-persistent extension of the ProcessInstance and it just collects all the messages that have to be sent.

The messages will be sent as part of the GraphSession.saveProcessInstance. The implementation of that method includes a context builder that acts as an aspect on the saveProcessInstance method. The actual interceptors can be configured in the jbpm.cfg.xml. One of the interceptors, SendMessagesInterceptor, is configured by default and will read the messages from the MessageInstance and send them over the configurable asynchronous messaging system.

The SendMessagesInterceptor uses the interfaces MessageServiceFactory and MessageService to send messages. This is to make the asynchronous messaging implementation configurable (also in jbpm.cfg.xml).

13.4. jBPM's built-in asynchronous messaging

When using jBPM's built-in asynchronous messaging, messages will be send by persisting them to the database. This message persisting can be done in the same transaction/jdbc connection as the jBPM process updates.

The command messages will be stored in the JBPM_MESSAGE table.

The POJO command executor (org.jbpm.msg.command.CommandExecutor) will read the messages from the database table and execute them. So the typical transaction of the POJO command executor looks like this: 1) read next command message 2) execute command message 3) delete command message.

If execution of a command message fails, the transaction will be rolled back. After that, a new transaction will be started that adds the error message to the message in the database. The command executor filters out all messages that contain an exception.

POJO command executor transactions

Figure 13.3. POJO command executor transactions

If for some reason or another, the transaction that adds the exception to the command message would fail, it is rolled back as well. In that case, the message remains in the queue without an exception so it will be retried later.

Limitation: beware that jBPM's built-in asynchronous messaging system does not support multinode locking. So you cannot just deploy the POJO command executor multiple times and have them configured to use the same database.

13.5. JMS for async architectures

The asynchronous continuations feature, opens up a new world of jBPM usage scenarios. Where typically, jBPM is used for modelling business processes, it can now be used from a more technical perspective.

Imagine that you have an application with quite some asynchronous processing. That typically requires quite a bit if difficult set up to bind all the message producing and message consuming pieces of software together. With jBPM it now becomes possible to create a picture of the overall asynchronous architecture, have all your code in POJO's and add transaction demarcation in the overall process file. jBPM will now take care of binding the senders to the receivers without the need for writing all the JMS or MDB code yourself.

13.6. JMS for asynchronous messaging

TODO (not yet implemented)

13.7. Future directions

TODO's: add support for multiple queues. So that it becomes possible to specify a queue for each node or action that is marked as asynchronous. Also it would be great to produce message for a set of queues in a round-robin. Since all of this should be configurable for both the JMS and the built-in messaging systems, this will require some thought on how to do all this configurations. The process definitions should not have to depend on any of the 2 possible implementations.

Chapter 14. Business calendar

This chapter describes the business calendar of jBPM. The business calendar knows about business hours and is used in calculation of due dates for tasks and timers.

The business calendar is able to calculate a date by adding a duration and a date.

14.1. Duration

A duration is specified in absolute or in business hours. Let's look at the syntax:

<quantity> [business] <unit>

Where <quantity> is a piece of text that is parsable with Double.parseDouble(quantity). <unit> is one of {second, seconds, minute, minutes, hour, hours, day, days, week, weeks, month, months, year, years}. And adding the optional indication business means that only business hours should be taken into account for this duration. Without the indication business, the duration will be interpreted as an absolute time period.

14.2. Calendar configuration

The file org/jbpm/calendar/jbpm.business.calendar.properties specifies what business hours are. The configuration file can be customized and a modified copy can be placed in the root of the classpath.

This is the example business hour specification that is shipped by default in jbpm.business.calendar.properties:

hour.format=HH:mm
#weekday ::= [<daypart> [& <daypart>]*]
#daypart ::= <start-hour>-<to-hour>
#start-hour and to-hour must be in the hour.format
#dayparts have to be ordered
weekday.monday=    9:00-12:00 & 12:30-17:00
weekday.thuesday=  9:00-12:00 & 12:30-17:00
weekday.wednesday= 9:00-12:00 & 12:30-17:00
weekday.thursday=  9:00-12:00 & 12:30-17:00
weekday.friday=    9:00-12:00 & 12:30-17:00
weekday.saturday=
weekday.sunday=

day.format=dd/MM/yyyy
# holiday syntax: <holiday>
# holiday period syntax: <start-day>-<end-day>
# below are the belgian official holidays
holiday.1=  01/01/2005 # nieuwjaar
holiday.2=  27/3/2005  # pasen 
holiday.3=  28/3/2005  # paasmaandag 
holiday.4=  1/5/2005   # feest van de arbeid
holiday.5=  5/5/2005   # hemelvaart 
holiday.6=  15/5/2005  # pinksteren 
holiday.7=  16/5/2005  # pinkstermaandag 
holiday.8=  21/7/2005  # my birthday 
holiday.9=  15/8/2005  # moederkesdag 
holiday.10= 1/11/2005  # allerheiligen 
holiday.11= 11/11/2005 # wapenstilstand 
holiday.12= 25/12/2005 # kerstmis 

business.day.expressed.in.hours=             8
business.week.expressed.in.hours=           40
business.month.expressed.in.business.days=  21
business.year.expressed.in.business.days=  220

Chapter 15. Logging

The purpose of logging is to keep track of the history of a process execution. As the runtime data of a process execution changes, all the delta's are stored in the logs.

Process logging, which is covered in this chapter, is not to be confused with software logging. Software logging traces the execution of a software program (usually for debugging purposes). Process logging traces the execution of process instances.

There are various use cases for process logging information. Most obvious is the consulting of the process history by participants of a process execution.

Another use case is Business Activity Monitoring (BAM). BAM will query or analyse the logs of process executions to find usefull statistical information about the business process. E.g. how much time is spend on average in each step of the process ? Where are the bottlenecks in the process ? ... This information is key to implement real business process management in an organisation. Real business process management is about how an organisation manages their processes, how these are supported by information technology *and* how these two improve the other in an iterative process.

Next use case is the undo functionality. Process logs can be used to implement the undo. Since the logs contain the delta's of the runtime information, the logs can be played in reverse order to bring the process back into a previous state.

15.1. Creation of logs

Logs are produced by jBPM modules while they are running process executions. But also users can insert process logs. A log entry is a java object that inherits from org.jbpm.logging.log.ProcessLog. Process log entries are added to the LoggingInstance. The LoggingInstance is an optional extension of the ProcessInstance.

Various kinds of logs are generated by jBPM : graph execution logs, context logs and task management logs. For more information about the specific data contained in those logs, we refer to the javadocs. A good starting point is the class org.jbpm.logging.log.ProcessLog since from that class you can navigate down the inheritance tree.

The LoggingInstance will collect all the log entries. When the ProcessInstance is saved, all the logs in the LoggingInstance will be flushed to the database. The logs-field of a ProcessInstance is not mapped with hibernate to avoid that logs are retrieved from the database in each transactions. Each ProcessLog is made in the context of a path of execution (Token) and hence, the ProcessLog refers to that token. The Token also serves as an index-sequence generator for the index of the ProcessLog in the Token. This will be important for log retrieval. That way, logs that are produced in subsequent transactions will have sequential sequence numbers. (wow, that a lot of seq's in there :-s ).

The API method for adding process logs is the following.

public class LoggingInstance extends ModuleInstance {
  ...
  public void addLog(ProcessLog processLog) {...}
  ...
}

The UML diagram for logging information looks like this:

The jBPM logging information class diagram

Figure 15.1. The jBPM logging information class diagram

A CompositeLog is a special kind of log entry. It serves as a parent log for a number of child logs, thereby creating the means for a hierarchical structure in the logs. The API for inserting a log is the following.

public class LoggingInstance extends ModuleInstance {
  ...
  public void startCompositeLog(CompositeLog compositeLog) {...}
  public void endCompositeLog() {...}
  ...
}

The CompositeLogs should always be called in a try-finally-block to make sure that the hierarchical structure of logs is consistent. For example:

startCompositeLog(new MyCompositeLog());
try {
  ...
} finally {
  endCompositeLog();
}

15.2. Log configurations

For deployments where logs are not important, it suffices to remove the logging line in the jbpm-context section of the jbpm.cfg.xml configuration file:

<service name='logging' factory='org.jbpm.logging.db.DbLoggingServiceFactory' />

In case you want to filter the logs, you need to write a custom implementation of the LoggingService that is a subclass of DbLoggingService. Also you need to create a custom logging ServiceFactory and specify that one in the factory attribute.

15.3. Log retrieval

As said before, logs cannot be retrieved from the database by navigating the LoggingInstance to its logs. Instead, logs of a process instance should always be queried from the database. The LoggingSession has 2 methods that serve this purpose.

The first method retrieves all the logs for a process instance. These logs will be grouped by token in a Map. The map will associate a List of ProcessLogs with every Token in the process instance. The list will contain the ProcessLogs in the same ordered as they were created.

public class LoggingSession {
  ...
  public Map findLogsByProcessInstance(long processInstanceId) {...}
  ...
}

The second method retrieves the logs for a specific Token. The returned list will contain the ProcessLogs in the same ordered as they were created.

public class LoggingSession {
  public List findLogsByToken(long tokenId) {...}
  ...
}

15.4. Database warehousing

Sometimes you may want to apply data warehousing techniques to the jbpm process logs. Data warehousing means that you create a separate database containing the process logs to be used for various purposes.

There may be many reasons why you want to create a data warehouse with the process log information. Sometimes it might be to offload heavy queryies from the 'live' production database. In other situations it might be to do some extensive analysis. Data warehousing even might be done on a modified database schema which is optimized for its purpose.

In this section, we only want to propose the technique of warehousing in the context of jBPM. The purposes are too diverse, preventing a generic solution to be included in jBPM that could cover all those requirements.

Chapter 16. jBPM Process Definition Language (JPDL)

JPDL specifies an xml schema and the mechanism to package all the process definition related files into a process archive.

16.1. The process archive

A process archive is a zip file. The central file in the process archive is processdefinition.xml. The main information in that file is the process graph. The processdefinition.xml also contains information about actions and tasks. A process archive can also contain other process related files such as classes, ui-forms for tasks, ...

16.1.1. Deploying a process archive

Deploying process archives can be done in 3 ways: with the process designer tool, with an ant task or programatically.

Deploying a process archive with the designer tool is still under construction.

Deploying a process archive with an ant task can be done as follows:

<target name="deploy.par">
  <taskdef name="deploypar" classname="org.jbpm.ant.DeployParTask">
    <classpath --make sure the jbpm-[version].jar is in this classpath--/>  
  </taskdef>  
  <deploypar par="build/myprocess.par" /> 
</target>

To deploy more process archives at once, use the nested fileset elements. The file attribute itself is optional. Other attributes of the ant task are:

  • cfg: cfg is optional, the default value is 'hibernate.cfg.xml'. The hibernate configuration file that contains the jdbc connection properties to the database and the mapping files.
  • properties: properties is optional and overwrites *all* hibernate properties as found in the hibernate.cfg.xml
  • createschema: if set to true, the jbpm database schema is created before the processes get deployed.

Process archives can also be deployed programmatically with the class org.jbpm.jpdl.par.ProcessArchiveDeployer

16.1.2. Process versioning

Process definitions should never change because it is extremely difficult (if not, impossible) to predict all possible side effects of process definition changes.

To get around this problem, jBPM has a sophicticated process versioning mechanism. The versioning mechanism allows multiple process definitions of the same name to coexist in the database. A process instance can be started in the latest version available at that time and it will keep on executing in that same process definition for its complete lifetime. When a newer version is deployed, newly created instances will be started in the newest version, while older process instances keep on executing in the older process defintions.

Process definitions are a combination of a declaratively specified process graph and optionally, a set of related java classes. The java classes can be made available to the jBPM runtime environment in 2 ways : by making sure these classes are visible to the jBPM classloader. This usually means that you can put your delegation classes in a .jar file next to the jbpm-[version].jar. The java classes can also be included in the process archive. When you include your delegation classes in the process archive (and they are not visible to the jbpm classloader), jBPM will also apply versioning on these classes. More information about process classloading can be found in Section 16.2, “Delegation”

When a process archive gets deployed, it creates a process definition in the jBPM database. Process definitions can be versioned on the basis of the process definition name. When a named process archive gets deployed, the deployer will assign a version number. To assign this number, the deployer will look up the highest version number for process definitions with the same name and adds 1. Unnamed process definitions will always have version number -1.

16.1.3. Changing deployed process definitions

Changing process definitions after they are deployed into the jBPM database has many potential pitfalls. Therefor, this is highly discouraged.

Actually, there is a whole variety of possible changes that can be made to a process definition. Some of those process definitions are harmless, but some other changes have implications far beyond the expected and desirable.

So please consider migrating process instances to a new definition over this approach.

In case you would consider it, these are the points to take into consideration:

Use hibernate's update: You can just load a process definition, change it and save it with the hibernate session. The hibernate session can be accessed with the method JbpmContext.getSession().

The second level cache: A process definition would need to be removed from the second level cache after you've updated an existing process definition. See also Section 7.10, “Second level cache”

16.1.4. Migrating process instances

An alternative approach to changing process definitions might be to convert the executions to a new process definition. Please take into account that this is not trivial due to the long-lived nature of business processes. Currently, this is an experimental area so for which there are not yet much out-of-the-box support.

As you know there is a clear distinction between process definition data, process instance data (the runtime data) and the logging data. With this approach, you create a separate new process definition in the jBPM database (by e.g. deploying a new version of the same process). Then the runtime information is converted to the new process definition. This might involve a translation cause tokens in the old process might be pointing to nodes that have been removed in the new version. So only new data is created in the database. But one execution of a process is spread over two process instance objects. This might become a bit tricky for the tools and statistics calculations. When resources permit us, we are going to add support for this in the future. E.g. a pointer could be added from one process instance to it's predecessor.

16.1.5. Process conversion

A conversion class has been made available to assist you with converting your jBPM 2.0 process archives into jBPM 3.0 compatible process archives. Create an output directory to hold the converted process archives. Enter the following command line from the build directory of the jBPM 3.0 distribution:

java -jar converter.jar indirectory outdirectory

Substitute the input directory where your jBPM 2.0 process archives reside for "indirectory". Substitute the output directory for the one you created to hold the newly converted process archives for "outdirectory".

16.2. Delegation

Delegation is the mechanism used to include the users' custom code in the execution of processes.

16.2.1. The jBPM class loader

The jBPM class loader is the class loader that loads the jBPM classes. Meaning, the classloader that has the library jbpm-3.x.jar in its classpath. To make classes visible to the jBPM classloader, put them in a jar file and put the jar file besides the jbpm-3.x.jar. E.g. in the WEB-INF/lib folder in the case of webapplications.

16.2.2. The process class loader

Delegation classes are loaded with the process class loader of their respective process definition. The process class loader is a class loader that has the jBPM classloader as a parent. The process class loader adds all the classes of one particular process definition. You can add classes to a process definition by putting them in the /classes folder in the process archive. Note that this is only useful when you want to version the classes that you add to the process definition. If versioning is not necessary, it is much more efficient to make the classes available to the jBPM class loader.

16.2.3. Configuration of delegations

Delegation classes contain user code that is called from within the execution of a process. The most common example is an action. In the case of action, an implementation of the interface ActionHandler can be called on an event in the process. Delegations are specified in the processdefinition.xml. 3 pieces of data can be supplied when specifying a delegation :

  • 1) the class name (required) : the fully qualified class name of the delegation class.
  • 2) configuration type (optional) : specifies the way to instantiate and configure the delegation object. By default the default constructor is used and the configuration information is ignored.
  • 3) configuration (optional) : the configuration of the delegation object in the format as required by the configuration type.

Next is a description of all the configuration types:

16.2.3.1. config-type field

This is the default configuration type. The config-type field will first instantiate an object of the delegation class and then set values in the fields of the object as specified in the configuration. The configuration is xml, where the elementnames have to correspond with the field names of the class. The content text of the element is put in the corresponding field. If necessary and possible, the content text of the element is converted to the field type.

Supported type conversions:

  • String doesn't need converting, of course. But it is trimmed.
  • primitive types such as int, long, float, double, ...
  • and the basic wrapper classes for the primitive types.
  • lists, sets and collections. In that case each element of the xml-content is consitered as an element of the collection and is parsed, recursively applying the conversions. If the type of the elements is different from java.lang.String this can be indicated by specifying a type attribute with the fully qualified type name. For example, following snippet will inject an ArrayList of Strings into field 'numbers':
    <numbers>
      <element>one</element>
      <element>two</element>
      <element>three</element>
    </numbers>

    The text in the elements can be converted to any object that has a String constructor. To use another type then String, specify the element-type in the field element ('numbers' in this case).

    Here's another example of a map:

    <numbers>
      <entry><key>one</key><value>1</value></entry>
      <entry><key>two</key><value>2</value></entry>
      <entry><key>three</key><value>3</value></entry>
    </numbers>
  • maps. In this case, each element of the field-element is expected to have one subelement key and one element value. The key and element are both parsed using the conversion rules recursively. Just the same as with collections, a conversion to java.lang.String is assumed if no type attribute is specified.
  • org.dom4j.Element
  • for any other type, the string constructor is used.

For example in the following class...

public class MyAction implements ActionHandler {
  // access specifiers can be private, default, protected or public
  private String city;
  Integer rounds;
  ...
}

...this is a valid configuration:

...
<action class="org.test.MyAction">
  <city>Atlanta</city>
  <rounds>5</rounds>
</action>
...

16.2.3.2. config-type bean

Same as config-type field but then the properties are set via setter methods, rather then directly on the fields. The same conversions are applied.

16.2.3.3. config-type constructor

This instantiator will take the complete contents of the delegation xml element and passes this as text in the delegation class constructor.

16.2.3.4. config-type configuration-property

First, the default constructor is used, then this instantiator will take the complete contents of the delegation xml element, and pass it as text in method void configure(String);. (as in jBPM 2)

16.3. Expressions

For some of the delegations, there is support for a JSP/JSF EL like expression language. In actions, assignments and decision conditions, you can write an expression like e.g. expression="#{myVar.handler[assignments].assign}"

The basics of this expression language can be found in the J2EE tutorial.

The jPDL expression language is similar to the JSF expression language. Meaning that jPDL EL is based on JSP EL, but it uses #{...} notation and that it includes support for method binding.

Depending on the context, the process variables or task instance variables can be used as starting variables along with the following implicit objects:

  • taskInstance (org.jbpm.taskmgmt.exe.TaskInstance)
  • processInstance (org.jbpm.graph.exe.ProcessInstance)
  • processDefinition (org.jbpm.graph.def.ProcessDefinition)
  • token (org.jbpm.graph.exe.Token)
  • taskMgmtInstance (org.jbpm.taskmgmt.exe.TaskMgmtInstance)
  • contextInstance (org.jbpm.context.exe.ContextInstance)

This feature becomes really powerfull in a JBoss SEAM environment. Because of the integration between jBPM and JBoss SEAM, all of your backed beans, EJB's and other one-kind-of-stuff becomes available right inside of your process definition. Thanks Gavin ! Absolutely awsome ! :-)

16.4. jPDL xml schema

The jPDL schema is the schema used in the file processdefinition.xml in the process archive.

16.4.1. Validation

When parsing a jPDL XML document, jBPM will validate your document against the jPDL schema when two conditions are met: first, the schema has to be referenced in the XML document like this

<process-definition xmlns="urn:jbpm.org:jpdl-3.1">
  ...
</process-definition>

And second, the xerces parser has to be on the classpath.

The jPDL schema can be found in ${jbpm.home}/src/java.jbpm/org/jbpm/jpdl/xml/jpdl-3.1.xsd or at http://jbpm.org/jpdl-3.1.xsd.

16.4.2. process-definition

Table 16.1. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the process
swimlaneelement[0..*]the swimlanes used in this process. The swimlanes represent process roles and they are used for task assignments.
start-stateelement[0..1]the start state of the process. Note that a process without a start-state is valid, but cannot be executed.
{end-state|state|node|task-node|process-state|super-state|fork|join|decision}element[0..*]the nodes of the process definition. Note that a process without nodes is valid, but cannot be executed.
eventelement[0..*]the process events that serve as a container for actions
{action|script|create-timer|cancel-timer}element[0..*]global defined actions that can be referenced from events and transitions. Note that these actions must specify a name in order to be referenced.
taskelement[0..*]global defined tasks that can be used in e.g. actions.
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process definition.

16.4.3. node

Table 16.2. 

NameTypeMultiplicityDescription
{action|script|create-timer|cancel-timer}element1a custom action that represents the behaviour for this node
common node elements  See common node elements

16.4.4. common node elements

Table 16.3. 

NameTypeMultiplicityDescription
nameattributerequiredthe name of the node
asyncattribute{ true | false }, false is the defaultIf set to true, this node will be executed asynchronously. See also Chapter 13, Asynchronous continuations
transitionelement[0..*]the leaving transitions. Each transition leaving a node *must* have a distinct name. A maximum of one of the leaving transitions is allowed to have no name. The first transition that is specifed is called the default transition. The default transition is taken when the node is left without specifying a transition.
eventelement[0..*]supported event types: {node-enter|node-leave}
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process node.
timerelement[0..*]specifies a timer that monitors the duration of an execution in this node.

16.4.5. start-state

Table 16.4. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the node
taskelement[0..1]the task to start a new instance for this process or to capture the process initiator. See Section 11.7, “Swimlane in start task”
eventelement[0..*]supported event types: {node-leave}
transitionelement[0..*]the leaving transitions. Each transition leaving a node *must* have a distinct name.
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process node.

16.4.6. end-state

Table 16.5. 

NameTypeMultiplicityDescription
nameattributerequiredthe name of the end-state
eventelement[0..*]supported event types: {node-enter}
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process node.

16.4.7. state

Table 16.6. 

NameTypeMultiplicityDescription
common node elements  See common node elements

16.4.8. task-node

Table 16.7. 

NameTypeMultiplicityDescription
signalattributeoptional{unsynchronized|never|first|first-wait|last|last-wait}, default is last. signal specifies the effect of task completion on the process execution continuation.
create-tasksattributeoptional{yes|no|true|false}, default is true. can be set to false when a runtime calculation has to determine which of the tasks have to be created. in that case, add an action on node-enter, create the tasks in the action and set create-tasks to false.
end-tasksattributeoptional{yes|no|true|false}, default is false. In case remove-tasks is set to true, on node-leave, all the tasks that are still open are ended.
taskelement[0..*]the tasks that should be created when execution arrives in this task node.
common node elements  See common node elements

16.4.9. process-state

Table 16.8. 

NameTypeMultiplicityDescription
sub-processelement1the sub process that is associated with this node
variableelement[0..*]specifies how data should be copied from the super process to the sub process at the start and from the sub process to the super process upon completion of the sub process.
common node elements  See common node elements

16.4.10. super-state

Table 16.9. 

NameTypeMultiplicityDescription
{end-state|state|node|task-node|process-state|super-state|fork|join|decision}element[0..*]the nodes of the superstate. superstates can be nested.
common node elements  See common node elements

16.4.11. fork

Table 16.10. 

NameTypeMultiplicityDescription
common node elements  See common node elements

16.4.12. join

Table 16.11. 

NameTypeMultiplicityDescription
common node elements  See common node elements

16.4.13. decision

Table 16.12. 

NameTypeMultiplicityDescription
handlerelementeither a 'handler' element or conditions on the transitions should be specifiedthe name of a org.jbpm.jpdl.Def.DecisionHandler implementation
transitionelement[0..*]the leaving transitions. The leaving transitions of a decision can be extended with a condition. The decision will look for the first transition for which the condition evaluates to true. A transition without a condition is considered to evaluate to true (to model the 'otherwise' branch). See the condition element
common node elements  See common node elements

16.4.14. event

Table 16.13. 

NameTypeMultiplicityDescription
typeattributerequiredthe event type that is expressed relative to the element on which the event is placed
{action|script|create-timer|cancel-timer}element[0..*]the list of actions that should be executed on this event

16.4.15. transition

Table 16.14. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the transition. Note that each transition leaving a node *must* have a distinct name.
toattributerequiredthe hierarchical name of the destination node. For more information about hierarchical names, see Section 9.6.3, “Hierarchical names”
{action|script|create-timer|cancel-timer}element[0..*]the actions to be executed upon taking this transition. Note that the actions of a transition do not need to be put in an event (because there is only one)
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process node.

16.4.16. action

Table 16.15. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the action. When actions are given names, they can be looked up from the process definition. This can be useful for runtime actions and declaring actions only once.
classattibuteeither, a ref-name or an expressionthe fully qualified class name of the class that implements the org.jbpm.graph.def.ActionHandler interface.
ref-nameattibuteeither this or classthe name of the referenced action. The content of this action is not processed further if a referenced action is specified.
expressionattibuteeither this, a class or a ref-nameA jPDL expression that resolves to a method. See also Section 16.3, “Expressions”
accept-propagated-eventsattributeoptional{yes|no|true|false}. Default is yes|true. If set to false, the action will only be executed on events that were fired on this action's element. for more information, see Section 9.5.4, “Event propagation”
config-typeattributeoptional{field|bean|constructor|configuration-property}. Specifies how the action-object should be constructed and how the content of this element should be used as configuration information for that action-object.
asyncattibute{true|false}Default is false, which means that the action is executed in the thread of the execution. If set to true, a message will be sent to the command executor and that component will execute the action asynchonously in a separate transaction.
 {content}optionalthe content of the action can be used as configuration information for your custom action implementations. This allows the creation of reusable delegation classes. For more about delegation configuration, see Section 16.2.3, “Configuration of delegations”.

16.4.17. script

Table 16.16. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the script-action. When actions are given names, they can be looked up from the process definition. This can be useful for runtime actions and declaring actions only once.
accept-propagated-eventsattributeoptional [0..*]{yes|no|true|false}. Default is yes|true. If set to false, the action will only be executed on events that were fired on this action's element. for more information, see Section 9.5.4, “Event propagation”
expressionelement[0..1]the beanshell script. If you don't specify variable elements, you can write the expression as the content of the script element (omitting the expression element tag).
variableelement[0..*]in variable for the script. If no in variables are specified, all the variables of the current token will be loaded into the script evaluation. Use the in variables if you want to limit the number of variables loaded into the script evaluation.

16.4.18. expression

Table 16.17. 

NameTypeMultiplicityDescription
 {content} a bean shell script.

16.4.19. variable

Table 16.18. 

NameTypeMultiplicityDescription
nameattributerequiredthe process variable name
accessattributeoptionaldefault is read,write. It is a comma separated list of access specifiers. The only access specifiers used so far are read, write and required.
mapped-nameattributeoptionalthis defaults to the variable name. it specifies a name to which the variable name is mapped. the meaning of the mapped-name is dependent on the context in which this element is used. for a script, this will be the script-variable-name. for a task controller, this will be the label of the task form parameter and for a process-state, this will be the variable name used in the sub-process.

16.4.20. handler

Table 16.19. 

NameTypeMultiplicityDescription
expressionattibuteeither this or a classA jPDL expression. The returned result is transformed to a string with the toString() method. The resulting string should match one of the leaving transitions. See also Section 16.3, “Expressions”.
classattibuteeither this or ref-namethe fully qualified class name of the class that implements the org.jbpm.graph.node.DecisionHandler interface.
config-typeattributeoptional{field|bean|constructor|configuration-property}. Specifies how the action-object should be constructed and how the content of this element should be used as configuration information for that action-object.
 {content}optionalthe content of the handler can be used as configuration information for your custom handler implementations. This allows the creation of reusable delegation classes. For more about delegation configuration, see Section 16.2.3, “Configuration of delegations”.

16.4.21. timer

Table 16.20. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the timer. If no name is specified, the name of the enclosing node is taken. Note that every timer should have a unique name.
duedateattributerequiredthe duration (optionally expressed in business hours) that specifies the time period between the creation of the timer and the execution of the timer. See Section 14.1, “Duration” for the syntax.
repeatattributeoptional{duration | 'yes' | 'true'}after a timer has been executed on the duedate, 'repeat' optionally specifies duration between repeating timer executions until the node is left. If yes or true is specified, the same duration as for the due date is taken for the repeat. See Section 14.1, “Duration” for the syntax.
transitionattributeoptionala transition-name to be taken when the timer executes, after firing the timer event and executing the action (if any).
cancel-eventattributeoptionalthis attribute is only to be used in timers of tasks. it specifies the event on which the timer should be cancelled. by default, this is the task-end event, but it can be set to e.g. task-assign or task-start. The cancel-event types can be combined by specifying them in a comma separated list in the attribute.
{action|script|create-timer|cancel-timer}element[0..1]an action that should be executed when this timer fires

16.4.22. create-timer

Table 16.21. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the timer. The name can be used for cancelling the timer with a cancel-timer action.
duedateattributerequiredthe duration (optionally expressed in business hours) that specifies the the time period between the creation of the timer and the execution of the timer. See Section 14.1, “Duration” for the syntax.
repeatattributeoptional{duration | 'yes' | 'true'}after a timer has been executed on the duedate, 'repeat' optionally specifies duration between repeating timer executions until the node is left. If yes of true is specified, the same duration as for the due date is taken for the repeat. See Section 14.1, “Duration” for the syntax.
transitionattributeoptionala transition-name to be taken when the timer executes, after firing the the timer event and executing the action (if any).

16.4.23. cancel-timer

Table 16.22. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the timer to be cancelled.

16.4.24. task

Table 16.23. 

NameTypeMultiplicityDescription
nameattributeoptionalthe name of the task. Named tasks can be referenced and looked up via the TaskMgmtDefinition
blockingattributeoptional{yes|no|true|false}, default is false. If blocking is set to true, the node cannot be left when the task is not finished. If set to false (default) a signal on the token is allowed to continue execution and leave the node. The default is set to false, because blocking is normally forced by the user interface.
signallingattributeoptional{yes|no|true|false}, default is true. If signalling is set to false, this task will never have the capability of trigering the continuation of the token.
duedateattributeoptionalis a duration expressed in absolute or business hours as explained in Chapter 14, Business calendar
swimlaneattributeoptionalreference to a swimlane. If a swimlane is specified on a task, the assignment is ignored.
priorityattributeoptionalone of {highest, high, normal, low, lowest}. alternatively, any integer number can be specified for the priority. FYI: (highest=1, lowest=5)
assignmentelementoptionaldescribes a delegation that will assign the task to an actor when the task is created.
eventelement[0..*]supported event types: {task-create|task-start|task-assign|task-end}. Especially for the task-assign we have added a non-persisted property previousActorId to the TaskInstance
exception-handlerelement[0..*]a list of exception handlers that applies to all exceptions thrown by delegation classes thrown in this process node.
timerelement[0..*]specifies a timer that monitors the duration of an execution in this task. special for task timers, the cancel-event can be specified. by default the cancel-event is task-end, but it can be customized to e.g. task-assign or task-start.
controllerelement[0..1]specifies how the process variables are transformed into task form parameters. the task form paramaters are used by the user interface to render a task form to the user.

16.4.25. swimlane

Table 16.24. 

NameTypeMultiplicityDescription
nameattributerequiredthe name of the swimlane. Swimlanes can be referenced and looked up via the TaskMgmtDefinition
assignmentelement[1..1]specifies a the assignment of this swimlane. the assignment will be performed when the first task instance is created in this swimlane.

16.4.26. assignment

Table 16.25. 

NameTypeMultiplicityDescription
expressionattributeoptionalFor historical reasons, this attribute expression does not refer to the jPDL expression, but instead, it is an assignment expression for the jBPM identity component. For more information on how to write jBPM identity component expressions, see Section 11.11.2, “Assignment expressions”. Note that this implementation has a dependency on the jbpm identity component.
actor-idattributeoptionalAn actorId. Can be used in conjunction with pooled-actors. The actor-id is resolved as an expression. So you can refer to a fixed actorId like this actor-id="bobthebuilder". Or you can refer to a property or method that returns a String like this: actor-id="myVar.actorId", which will invoke the getActorId method on the task instance variable "myVar".
pooled-actorsattributeoptionalA comma separated list of actorIds. Can be used in conjunction with actor-id. A fixed set of pooled actors can be specified like this: pooled-actors="chicagobulls, pointersisters". The pooled-actors will be resolved as an expression. So you can also refer to a property or method that has to return, a String[], a Collection or a comma separated list of pooled actors.
classattributeoptionalthe fully qualified classname of an implementation of org.jbpm.taskmgmt.def.AssignmentHandler
config-typeattributeoptional{field|bean|constructor|configuration-property}. Specifies how the assignment-handler-object should be constructed and how the content of this element should be used as configuration information for that assignment-handler-object.
 {content}optionalthe content of the assignment-element can be used as configuration information for your AssignmentHandler implementations. This allows the creation of reusable delegation classes. for more about delegation configuration, see Section 16.2.3, “Configuration of delegations”.

16.4.27. controller

Table 16.26. 

NameTypeMultiplicityDescription
classattributeoptionalthe fully qualified classname of an implementation of org.jbpm.taskmgmt.def.TaskControllerHandler
config-typeattributeoptional{field|bean|constructor|configuration-property}. Specifies how the assignment-handler-object should be constructed and how the content of this element should be used as configuration information for that assignment-handler-object.
 {content} either the content of the controller is the configuration of the specified task controller handler (if the class attribute is specified. if no task controller handler is specified, the content must be a list of variable elements.
variableelement[0..*]in case no task controller handler is specified by the class attribute, the content of the controller element must be a list of variables.

16.4.28. sub-process

Table 16.27. 

NameTypeMultiplicityDescription
nameattributerequiredthe name of the sub process. To know how you can test subprocesses, see Section 18.3, “Testing sub processes”
versionattributeoptionalthe version of the sub process. If no version is specified, the latest version of the given process will be taken.

16.4.29. condition

Table 16.28. 

NameTypeMultiplicityDescription
 {content} or attribute expressionrequiredThe contents of the condition element is a jPDL expression that should evaluate to a boolean. A decision takes the first transition (as ordered in the processdefinition.xml) for which the expression resolves to true. If none of the conditions resolve to true, the default leaving transition (== the first one) will be taken.

16.4.30. exception-handler

Table 16.29. 

NameTypeMultiplicityDescription
exception-classattributeoptionalspecifies the fully qualified name of the java throwable class that should match this exception handler. If this attribute is not specified, it matches all exceptions (java.lang.Throwable).
actionelement[1..*]a list of actions to be executed when an exception is being handled by this exception handler.

Chapter 17. Security

Security features of jBPM are still in alpha stage. This chapter documents the pluggable authentication and authorization. And what parts of the framework are finished and what parts not yet.

17.1. Todos

On the framework part, we still need to define a set of permissions that are verified by the jbpm engine while a process is being executed. Currently you can check your own permissions, but there is not yet a jbpm default set of permissions.

Only one default authentication implementation is finished. Other authentication implementations are envisioned, but not yet implemented. Authorization is optional, and there is no authorization implementation yet. Also for authorization, there are a number of authorization implementations envisioned, but they are not yet worked out.

But for both authentication and authorization, the framework is there to plug in your own authentication and authorization mechanism.

17.2. Authentication

Authentication is the process of knowing on who's behalf the code is running. In case of jBPM this information should be made available from the environment to jBPM. Cause jBPM is always executed in a specific environment like a webapp, an EJB, a swing application or some other environment, it is always the surrounding environment that should perform authentication.

In a few situations, jBPM needs to know who is running the code. E.g. to add authentication information in the process logs to know who did what and when. Another example is calculation of an actor based on the current authenticated actor.

In each situation where jBPM needs to know who is running the code, the central method org.jbpm.security.Authentication.getAuthenticatedActorId() is called. That method will delegate to an implementation of org.jbpm.security.authenticator.Authenticator. By specifying an implementation of the authenticator, you can configure how jBPM retrieves the currently authenticated actor from the environment.

The default authenticator is org.jbpm.security.authenticator.JbpmDefaultAutenticator. That implementation will maintain a ThreadLocal stack of authenticated actorId's. Authenticated blocks can be marked with the methods JbpmDefaultAutenticator.pushAuthenticatedActorId(String) and JbpmDefaultAutenticator.popAuthenticatedActorId(). Be sure to always put these demarcations in a try-finally block. For the push and pop methods of this authenticator implementation, there are convenience methods supplied on the base Authentication class. The reason that the JbpmDefaultAutenticator maintains a stack of actorIds instead of just one actorId is simple: it allows the jBPM code to distinct between code that is executed on behalf of the user and code that is executed on behalf of the jbpm engine.

See the javadocs for more information.

17.3. Authorization

Authorization is validating if an authenticated user is allowed to perform a secured operation.

The jBPM engine and user code can verify if a user is allowed to perform a given operation with the API method org.jbpm.security.Authorization.checkPermission(Permission).

The Authrorization class will also delegate that call to a configurable implementation. The interface for pluggin in different authorization strategies is org.jbpm.security.authorizer.Authorizer.

In the package org.jbpm.security.authorizer there are some examples that show intentions of authorizer implementations. Most are not fully implemented and none of them are tested.

Also still todo is the definition of a set of jBPM permissions and the verification of those permissions by the jBPM engine. An example could be verifying that the current authenticated user has sufficient privileges to end a task by calling Authorization.checkPermission(new TaskPermission("end", Long.toString(id))) in the TaskInstance.end() method.

Chapter 18. TDD for workflow

18.1. Introducing TDD for workflow

Since developing process oriented software is no different from developing any other software, we believe that process definitions should be easily testable. This chapter shows how you can use plain JUnit without any extensions to unit test the process definitions that you author.

The development cycle should be kept as short as possible. Changes made to the sources of software should be immediately verifiable. Preferably, without any intermediate build steps. The examples given below will show you how to develop and test jBPM processes without intermediate steps.

Mostly the unit tests of process definitions are execution scenarios. Each scenario is executed in one JUnit testmethod and will feed in the external triggers (read: signals) into a process execution and verifies after each signal if the process is in the expected state.

Let's look at an example of such a test. We take a simplified version of the auction process with the following graphical representation:

The auction test process

Figure 18.1. The auction test process

Now, let's write a test that executes the main scenario:

public class AuctionTest extends TestCase {

  // parse the process definition
  static ProcessDefinition auctionProcess = 
      ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par");

  // get the nodes for easy asserting
  static StartState start = auctionProcess.getStartState();
  static State auction = (State) auctionProcess.getNode("auction");
  static EndState end = (EndState) auctionProcess.getNode("end");

  // the process instance
  ProcessInstance processInstance;

  // the main path of execution
  Token token;

  public void setUp() {
    // create a new process instance for the given process definition
    processInstance = new ProcessInstance(auctionProcess);

    // the main path of execution is the root token
    token = processInstance.getRootToken();
  }
  
  public void testMainScenario() {
    // after process instance creation, the main path of 
    // execution is positioned in the start state.
    assertSame(start, token.getNode());
    
    token.signal();
    
    // after the signal, the main path of execution has 
    // moved to the auction state
    assertSame(auction, token.getNode());
    
    token.signal();
    
    // after the signal, the main path of execution has 
    // moved to the end state and the process has ended
    assertSame(end, token.getNode());
    assertTrue(processInstance.hasEnded());
  }
}

18.2. XML sources

Before you can start writing execution scenario's, you need a ProcessDefinition. The easiest way to get a ProcessDefinition object is by parsing xml. If you have code completion, type ProcessDefinition.parse and activate code completion. Then you get the various parsing methods. There are basically 3 ways to write xml that can be parsed to a ProcessDefinition object:

18.2.1. Parsing a process archive

A process archive is a zip file that contains the process xml in a file called processdefinition.xml. The jBPM process designer reads and writes process archives. For example:

...
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseParResource("org/jbpm/tdd/auction.par");
...

18.2.2. Parsing an xml file

In other situations, you might want to write the processdefinition.xml file by hand and later package the zip file with e.g. an ant script. In that case, you can use the JpdlXmlReader

...
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseXmlResource("org/jbpm/tdd/auction.xml");
...

18.2.3. Parsing an xml String

The simplest option is to parse the xml in the unit test inline from a plain String.

...
static ProcessDefinition auctionProcess = 
    ProcessDefinition.parseXmlString(
  "<process-definition>" + 
  "  <start-state name='start'>" + 
  "    <transition to='auction'/>" + 
  "  </start-state>" + 
  "  <state name='auction'>" + 
  "    <transition to='end'/>" + 
  "  </state>" + 
  "  <end-state name='end'/>" + 
  "</process-definition>");
...

18.3. Testing sub processes

TODO (see test/java/org/jbpm/graph/exe/ProcessStateTest.java)

Chapter 19. Pluggable architecture

The functionality of jBPM is split into modules. Each module has a definition and an execution (or runtime) part. The central module is the graph module, made up of the ProcessDefinition and the ProcessInstance. The process definition contains a graph and the process instance represents one execution of the graph. All other functionalities of jBPM are grouped into optional modules. Optional modules can extend the graph module with extra features like context (process variables), task management, timers, ...

The pluggable architecture

Figure 19.1. The pluggable architecture

The pluggable architecture in jBPM is also a unique mechanism to add custom capabilities to the jBPM engine. Custom process definition information can be added by adding a ModuleDefinition implementation to the process definition. When the ProcessInstance is created, it will create an instance for every ModuleDefinition in the ProcessDefinition. The ModuleDefinition is used as a factory for ModuleInstances.

The most integrated way to extend the process definition information is by adding the information to the process archive and implementing a ProcessArchiveParser. The ProcessArchiveParser can parse the information added to the process archive, create your custom ModuleDefinition and add it to the ProcessDefinition.

public interface ProcessArchiveParser {

  void writeToArchive(ProcessDefinition processDefinition, ProcessArchive archive);
  ProcessDefinition readFromArchive(ProcessArchive archive, ProcessDefinition processDefinition);

}

To do its work, the custom ModuleInstance must be notified of relevant events during process execution. The custom ModuleDefinition might add ActionHandler implementations upon events in the process that serve as callback handlers for these process events.

Alternatively, a custom module might use AOP to bind the custom instance into the process execution. JBoss AOP is very well suited for this job since it is mature, easy to learn and also part of the JBoss stack.