JBoss.orgCommunity Documentation

jBPM Reference Guide

Guide to using and developing with jBPM 3

Abstract

This is the User Guide for jBPM 3. jBPM is a flexible Business Process Management (BPM) Suite that bridges the gap between non-technical business users and developers by offering both BPM and workflow features.


1. Introduction
1.1. Overview
1.2. The jPDL Suite
1.3. The jPDL Graphical Process Designer
1.4. The jBPM Console Web Application
1.5. The jBPM Core Library
1.6. The Identity Component
1.7. The JBoss jBPM Job Executor
1.8. Conclusion
2. Getting started
2.1. Downloading and installing jBPM
2.2. The JBoss jBPM cummunity page
3. Tutorial
3.1. "Hello World" Example
3.2. Database Example
3.3. Contextual Example: Process Variables
3.4. Task Assignment Example
3.5. Example of a Custom Action
4. Deployment
4.1. jBPM libraries
4.2. Java runtime environment
4.3. Third party libraries
4.4. Deployment in JBoss
4.4.1. The jbpm directory
4.4.2. The enterprise bundle
4.4.3. Configuring the logs in the suite server
4.4.4. Debugging a process in the suite
5. Configuration
5.1. Customizing Factories
5.2. Configuration Properties
5.3. Other Configuration Files
5.4. Logging Optimistic Concurrency Exceptions
5.5. Object Factory
6. Persistence
6.1. The Persistence Application Programming Interface
6.1.1. Relationship with the Configuration Framework
6.1.2. Convenience Methods on JbpmContext
6.2. Configuring the Persistence Service
6.2.1. The DbPersistenceServiceFactory
6.2.2. Hibernate Transactions
6.2.3. JTA Transactions
6.2.4. Customizing Queries
6.2.5. Database Compatibility
7. The jBPM Database
7.1. Switching the Database Backend
7.1.1. Isolation level
7.1.2. Installing the PostgreSQL Database Manager
7.1.3. Installing the MySQL Database Manager
7.1.4. Creating the JBoss jBPM Database with your new PostgreSQL or MySQL
7.1.5. Last Steps
7.1.6. Update the JBoss jBPM Server Configuration
7.2. Database upgrades
7.3. Starting hsqldb manager on JBoss
8. Java EE Application Server Facilities
8.1. Enterprise Beans
8.2. jBPM Enterprise Configuration
8.3. Hibernate Enterprise Configuration
8.4. Client Components
8.5. Conclusion
9. Process Modeling
9.1. Some Helpful Definitions
9.2. Process Graph
9.3. Nodes
9.3.1. Node Responsibilities
9.3.2. Node Type: Task Node
9.3.3. Node Type: State
9.3.4. Node Type: Decision
9.3.5. Node Type: Fork
9.3.6. Node Type: Join
9.3.7. Node Type: Node
9.4. Transitions
9.5. Actions
9.5.1. Action References
9.5.2. Events
9.5.3. Passing On Events
9.5.4. Scripts
9.5.5. Custom Events
9.6. Super-States
9.6.1. Super-State Transitions
9.6.2. Super-State Events
9.6.3. Hierarchical Names
9.7. Exception Handling
9.8. Process Composition
9.9. Custom Node Behavior
9.10. Graph Execution
9.11. Transaction Demarcation
10. The Context
10.1. Accessing Process Variables
10.2. Lifes of Variables
10.3. Variable Persistence
10.4. Variable Scopes
10.4.1. Variable Overloading
10.4.2. Variable Over-Riding
10.4.3. Task Instance Variable Scope
10.5. Transient Variables
11. Task Management
11.1. Tasks
11.2. Task Instances
11.2.1. Task Instance Life-Cycle
11.2.2. Task Instances and Graph Executions
11.3. Assignment
11.3.1. Assignment Interfaces
11.3.2. The Assignment Data Model
11.3.3. The Personal Task List
11.3.4. The Group Task List
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.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 Job Executor
13.4. jBPM's built-in asynchronous messaging
14. Business Calendar
14.1. Due Date
14.1.1. Duration
14.1.2. Base Date
14.1.3. Due Date Examples
14.2. Calendar Configuration
14.3. Examples
15. Email Support
15.1. Mail in JPDL
15.1.1. Mail Action
15.1.2. Mail Node
15.1.3. "Task Assigned" Email
15.1.4. "Task Reminder" Email
15.2. Expressions in Mail
15.3. Specifying Email Recipients
15.3.1. Multiple Recipients
15.3.2. Sending Email to a BCC Address
15.3.3. Address Resolving
15.4. Email Templates
15.5. Mail Server Configuration
15.6. Email Authentication
15.6.1. Email authentication configuration
15.6.2. Email authentication logic
15.7. "From" Address Configuration
15.8. Customizing Email Support
16. Logging
16.1. Log Creation
16.2. Log Configurations
16.3. Log Retrieval
17. jBPM Process Definition Language
17.1. Process Archive
17.1.1. Deploying a Process Archive
17.1.2. Process Versioning
17.1.3. Changing Deployed Process Definitions
17.1.4. Migrating Process Instances
17.2. Delegation
17.2.1. jBPM Class Loader
17.2.2. Process Class Loader
17.2.3. Configuring Delegations
17.3. Expressions
17.4. jPDL XML Schema
17.4.1. Validation
17.4.2. process-definition
17.4.3. node
17.4.4. common node elements
17.4.5. start-state
17.4.6. end-state
17.4.7. state
17.4.8. task-node
17.4.9. process-state
17.4.10. super-state
17.4.11. fork
17.4.12. join
17.4.13. decision
17.4.14. event
17.4.15. transition
17.4.16. action
17.4.17. script
17.4.18. expression
17.4.19. variable
17.4.20. handler
17.4.21. timer
17.4.22. create-timer
17.4.23. cancel-timer
17.4.24. task
17.4.25. swimlane
17.4.26. assignment
17.4.27. Controller
17.4.28. sub-process
17.4.29. condition
17.4.30. exception-handler
18. Security
18.1. Todos
18.2. Authentication
18.3. Authorization
19. Test Driven Development for Workflow
19.1. Introducing Test Driven Development for Workflow
19.2. XML Sources
19.2.1. Parsing a Process Archive
19.2.2. Parsing an XML File
19.2.3. Parsing an XML String
19.3. Testing sub processes
20. Pluggable architecture
A. GNU Lesser General Public License version 3
B. Revision History

This Guide has been written for developers and administrators. Read on in order to learn how to use jBPM and JPDL in your corporate setting. Note that this book not only teaches how to use the software but explains, in significant detail, how it works.

Note

This Guide contains a lot of terminology. Definitions for the key terms can be found in Section 9.1, “ Some Helpful Definitions ”.

The JBoss Business Process Manager (jBPM) is a flexible and extensible scaffolding for process languages. The jBPM Process Definition Language (JPDL) is one of the process languages that is built on top of this framework. It is an intuitive language, designed to enable the user to express business processes graphically. It does so by representing tasks, wait states (for asynchronous communication), timers and automated actions. To bind these operations together, the language has a powerful and extensible control flow mechanism.

The JPDL has few dependencies, making it is as easy to install as a Java library. To do so, deploy it on a J2EE clustered application server. One will find it particularly useful in environments in which extreme throughput is a crucial requirement.

Note

The JPDL can be configured for use with any database. It can also be deployed on any application server.

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.

To get the latest release jBPM 3 version, go to the jBPM jPDL 3 package on Sourceforge.net and download the latest installer.

The jBPM installer creates a runtime installation and it can also download and install the eclipse designer and a jboss server. You can use jBPM also without application server, but all of these components are preconfigured to interoperate out-of-the-box to get you started with jBPM quickly. To launch the installer, open a command line and go to the directory where you downloaded it. Then type:

java -jar jbpm-installer-{version}.jar

Step through the instructions. Any supported version of JBoss and the exact version of eclipse can optionally be downloaded by the installer.

When installing jBPM into JBoss, this will create a jBPM directory into a server configuration's deploy directory. All jBPM files are centralized inside this deploy/jbpm directory. No other files of your JBoss installation will be touched.

You can use your own eclipse (if it is version 3.4+) or you can use the eclipse that the installer downloaded. To install the graphical process designer in eclipse, just use the eclipse update mechanism (Help --> Software Updates --> ...) and point it to the file designer/jbpm-jpdl-designer-site.zip.

The jBPM Community Page provides all the details about where to find forums, wiki, issue tracker, downloads, mailing lists and the source repositories.

Study the following tutorial to learn how to use basic process constructs in the JPDL. The tutorial also demonstrates ways in which to manage run-time executions via the application programming interface.

Each of the extensively-commented examples in the tutorial can be found in the jBPM download package, located in the src/java.examples sub-directory.

Note

Red Hat recommends creating a project at this point. One can then freely experiment and create variations of each of the examples in turn.

To get started first, download and install jBPM. Section 2.1, “Downloading and installing jBPM”

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 the Downloadables Overview section.. You don't need the graphical designer tool to complete this tutorial.

A process definition is a directed graph, made up of nodes and transitions. The Hello World process definition has three of these nodes. (It is best to learn how the pieces fit together by studying this simple process without using the Designer Tool.) The following diagram presents a graphical representation of the Hello World process:


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());
}

One of the jBPM's basic features is the ability to make the execution of database processes persist whilst they are in a wait state. The next example demonstrates this ability, storing a process instance in the jBPM database.

It works by creating separate methods for different pieces of user code. For instance, a piece of user code in a web application starts a process and "persists" the execution in the database. Later, a message-driven bean loads that process instance and resumes the execution of it.

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 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();
    }
  }
}

Whilst processes are executed, the context information is held in process variables. These are similar to java.util.Map classes, in that they map variable names to values, the latter being Java objects. (The process variables are "persisted" as part of the process instance.)

// 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"));

The next example demonstrates how to assign a task to a user. Because of the separation between the jBPM workflow engine and the organizational model, expression languages will always be too limited to use to calculate actors. Instead, specify an implementation of AssignmentHandler and use it to include 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());
}

Actions are mechanisms designed to bind custom Java code to jBPM processes. They can be associated with their own nodes (if these are relevant to the graphical representation of the process.) Alternatively, actions can be "placed on" events (for instance, when taking a transition, or entering or leaving a node.) If they are placed on events, the actions are not treated as part of the graphical representation (but they are still run when the events are "fired" during a run-time process execution.)

Firstly, look at the action handler implementation to be used in the next example: MyActionHandler. It is not particularly impressive of itself: it merely sets the Boolean variable isExecuted to true. Note that this variable is static so one can access it from within the action handler (and from the action itself) to verify its value.

// 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;
  }
}

Important

Prior to each test, 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;
  }

The first example illustrates 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 now being placed on both the enter-node and leave-node events. Note that a node has more than one event type. This is in contrast to a transition, which has only one event. Hence, when placing actions on a node, always put them 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);

jPDL is an embeddable BPM engine, which means that you can take the jPDL libraries 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.

All the libraries on which jPDL might have a dependency, are located in the lib directory. The actual version of those libraries might depend on the JBoss server that you've selected in the installer.

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.


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.


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'll get a log message saying that jbpm couldn't load the Script class and hence, the script element won't be available.


The installer deploys jBPM into JBoss. This section walks you through the deployed components.

First of all, in case you're just starting to develop a new process, it is much easier to use plain JUnit tests and run the process in memory like explained in Chapter 3, Tutorial .

But if you want to run the process in the console and debug it there here are the 2 steps that you need to do:

1) in jboss-{version}/server/bin/run.bat, somewhere at the end, there is a line like this:

rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%

For backup reasons, just start by making a copy of that line, then remove the first 'rem' and change suspend=y to suspend=n. Then you get something like

rem set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=y %JAVA_OPTS%
set JAVA_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n %JAVA_OPTS%

2) In your IDE debug by connecting to a remote Java application on localhost on port 8787. Then you can start adding break points and run through the processes with the console until the breakpoint is hit.

For more info about configuring logging of optimistic locking failures, see Section 5.4, “ Logging Optimistic Concurrency Exceptions ”

Read this chapter and studied the examples to learn how to configure the jBPM.

The simplest way to configure the Business Process Manager is by putting the jbpm.cfg.xml configuration file into the root of the classpath. If the file is not available for use as a resource, the default minimal configuration will be used instead. This minimal configuration is included in the jBPM library (org/jbpm/default.jbpm.cfg.xml.) If a jBPM configuration file is provided, the values it contains will be used as the defaults. Hence, one only needs to specify the values that are to be different from those in the default configuration file.

The jBPM configuration is represented by a Java class called org.jbpm.JbpmConfiguration. Obtain it by making use of the singleton instance method (JbpmConfiguration.getInstance().)

Note

Use the JbpmConfiguration.parseXxxx methods to load a configuration from another source.

static JbpmConfinguration jbpmConfiguration = JbpmConfinguration.parseResource("my.jbpm.cfg.xml");

The JbpmConfiguration is "thread safe" and, hence, can be kept in a static member.

Every thread can use a JbpmConfiguration as a factory for JbpmContext objects. A JbpmContext will usually represent one transaction. They make services available inside context blocks which 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 both a set of services and the configuration settings available to the Business Process Manager. The services are configured by the values in the jbpm.cfg.xml file. They make it possible for the jBPM to run in any Java environment, using whatever services are available within said environment.

Here are the default configuration settings for the JbpmContext:

<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' />
  <string name='resource.mail.templates' 
    value='jbpm.mail.templates.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' />
    
    <string name='jbpm.mail.smtp.host' value='localhost' />
    
    <bean name='jbpm.mail.address.resolver' 
        class='org.jbpm.identity.mail.IdentityAddressResolver' 
        singleton='true' />
    <string name='jbpm.mail.from.address' value='jbpm@noreply' />

    <bean name='jbpm.job.executor' 
        class='org.jbpm.job.executor.JobExecutor'>
      <field name='jbpmConfiguration'><ref bean='jbpmConfiguration' />
      </field>
      <field name='name'><string value='JbpmJobExecutor' /></field>
      <field name='nbrOfThreads'><int value='1' /></field>
      <field name='idleInterval'><int value='60000' /></field>
      <field name='retryInterval'><int value='4000' /></field>
      <!-- 1 hour -->
      <field name='maxIdleInterval'><int value='3600000' /></field> 
      <field name='historyMaxSize'><int value='20' /></field>
      <!-- 10 minutes -->
      <field name='maxLockTime'><int value='600000' /></field> 
      <!-- 1 minute -->
      <field name='lockMonitorInterval'><int value='60000' /></field> 
       <!-- 5 seconds -->
      <field name='lockBufferTime'><int value='5000' /></field>
    </bean>
</jbpm-configuration>

The above file contains three parts:

  1. a set of service implementations which configure the JbpmContext. (The possible configuration options are detailed in the chapters that cover specific service implementations.)

  2. all of the mappings linking references to configuration resources. If one wishes to customize one of the configuration files, update these mappings. To do so, always back up the default configuration file (jbpm-3.x.jar) to another location on the classpath first. Then, update the reference in this file, pointing it to the customized version that the jBPM is to use.

  3. miscellaneous configurations for use by the jBPM. (These are described in the chapters that cover the specific topics in question.)

The default configuration has been optimized for a simple web application environment which has minimal dependencies. The persistence service obtains a JDBC connection which is used by all of the other services. Hence, all of the workflow operations are centralized as they are placed in a single transaction on a JDBC connection (without the need for a transaction manager.)

JbpmContext contains convenience methods for most of the common process operations. They are demonstrated in this code sample:

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

There is no need to call any of the save methods explicitly because the XxxForUpdate methods are designed to register the loaded object for "auto-save."

It is possible to specify multiple jbpm-contexts. To do so, make sure that each of them is given a unique name attribute. (Retrieve named contexts by using JbpmConfiguration.createContext(String name);.)

A service element specifies its own name and associated service factory. The service will only be created when requested to do so by JbpmContext.getServices().getService(String name).

Note

One can also specfy the factories as elements instead of attributes. This is necessary when injecting some configuration information into factory objects.

Note that the component responsible for creating and wiring the objects and parsing the XML is called the object factory.

There are a number of configuration files in the jBPM which can be customized:

The Object Factory can build objects to the specification contained in a "beans-like" XML configuration file. This file dictates how objects are to be created, configured and wired together to form a complete object graph. Also use the Object Factory to inject configurations and other beans into a single bean.

In its most elementary form, the Object Factory is able to create both basic types and Java beans from such a configuration, as shown in the following examples:

<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"));]]>

This code shows how to configure lists:

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

This code demonstrates how to configure 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>

Use direct field injection and property setter methods to configure beans:

<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, an integer or any other kind. Here is some code that demonstrates this capability:

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

Beans can be built with any constructor, as this code shows:

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

Beans can be constructed using a factory method:

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

Beans can be constructed using 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>

Use the attribute singleton="true" to mark each named object as a singleton. Doing so will ensure that a given object factory always returns the same object for each request.

The singleton feature causes differentiation between the methods named getObject and getNewObject. Normally, one should use getNewObject as this clears the object factory's object cache before the new object graph is constructed.

During construction of the object graph, the non-singleton objects are stored in the object factory's cache. This allows references to one object to be shared. Bear in mind that the singleton object cache is different from the plain object cache. The singleton cache is never cleared, whilst the plain one is cleared every time a getNewObject method is started.

Having studied this chapter, one now has a thorough knowledge of the many ways in which the jBPM can be configured.

This chapter provides the reader with detailed insight into the Business Process Manager's "persistence" functionality.

Most of the time, the jBPM is used to execute processes that span several transactions. The main purpose of the persistence functionality is to store process executions when wait states occur. It is helpful to think of the process executions as state machines. The intention is to move the process execution state machine from one state to the next within a single transaction.

A process definition can be represented in any of three different forms, namely XML, Java object or a jBPM database record. (Run-time data and log information can also be represented in either of the latter two formats.)


Note

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

The persistence application programming interface is integrated with the configuration framework, (see Chapter 5, Configuration .) This has been achieved by the exposure of some of the convenience persistence methods on the JbpmContext, allowing the jBPM context block to call persistence API operations.

JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
  // Invoke persistence operations here
} finally {
  jbpmContext.close();
}

The three most commonly-performed persistence operations are:

Process deployment is normally undertaken directly from the Graphical Process Designer or from the deployprocess ant task. However, to do it directly from Java, use this code:

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

Create a new process execution by specifying the process definition of which it will be an instance. The most common way to do this is by referring to the name of the process. The jBPM will then find the latest version of that process in the database. Here is some demonstration code:

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

To continue a process execution, fetch the process instance, the token or the taskInstance from the database and invoke some methods on the POJO (Plain Old Java Object) jBPM objects. Afterwards, save the updates made to the processInstance into the database.

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

Note that it is not necessary to explicitly invoke the jbpmContext.save method if the ForUpdate methods are used in the JbpmContext class. This is because the save process will run automatically when the jbpmContext class is closed. For example, one may wish to inform the jBPM that a taskInstance has completed. This can cause an execution to continue, so the processInstance related to the taskInstance must be saved. The most convenient way to do this is by using the loadTaskInstanceForUpdate method:

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

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

The DbPersistenceServiceFactory class has three more configuration properties: isTransactionEnabled, sessionFactoryJndiName, and dataSourceJndiName. To specify any of these properties in the jbpm.cfg.xml file, specify the Service Factory as a bean within the factory element. This sample code demonstrates how to do so:

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

Important

Do not mix the short and long notation for configuring the factories. (See also Section 5.1, “ Customizing Factories ”.) If the factory is just a new instance of a class, use the factory attribute to refer to its factory class name but if properties in a factory require configuration, the long notation must be used and, furthermore, the factory and the bean must be combined as nested elements.

isTransactionEnabled

By default, jBPM will begin a Hibernate transaction when the session is retrieved for the first time and, if the jbpmContext is closed, the Hibernate transaction will be ended. The transaction is then committed or rolled back depending on whether or not jbpmContext.setRollbackOnly was called. (The isRollbackOnly property is maintained in the TxService.) To disable transactions and prohibit jBPM from managing them with Hibernate, set the isTransactionEnabled property value to false. (This property only controls the behaviour of the jbpmContext; the DbPersistenceService.beginTransaction() can still be called directly directly with the application programming interface, which ignores the isTransactionEnabled setting.) To learn more about transactions, please study Section 6.2.2, “ Hibernate Transactions ”.

sessionFactoryJndiName

By default, this is null, which means that the session factory will not be fetched from JNDI. If it is set and a session factory is needed in order to create a Hibernate session, it will be fetched from JNDI.

dataSourceJndiName

By default, this is null, resulting in creation of JDBC connections being delegated to Hibernate. By specifying a data-source, one makes the Business Process Manager fetch a JDBC connection from the data-source and provide it to Hibernate whilst opening a new session.

To learn how to configure jBPM with JBossCache, read http://wiki.jboss.org/wiki/Wiki.jsp?page=JbpmConfiguration

To learn how to configure a cache provider to work with Hibernate, study http://www.hibernate.org/hib_docs/reference/en/html/performance.html#performance-cache.

The hibernate.cfg.xml file that ships with jBPM includes the following line:

<property name="hibernate.cache.provider_class">
    org.hibernate.cache.HashtableCacheProvider
</property>

This is provided so that users do not have to concern themselves with configuring classpaths.

Warning

Do not use Hibernate's HashtableCacheProvider in a production environment.

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

Managed transactions are most commonly found when jBPM is used in conjuction with the JBoss Application Server. The following code sample shows a common way in which transactions are bound to JTA:

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

Next, configure the Hibernate session factory to use a data-source and bind Hibernate itself to the Transaction Manager. If using more than one datasource, bind them to an XA datasource.

<hibernate-configuration>
    <session-factory>

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

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

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

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

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

Next, configure Hibernate to use an XA datasource.

These configurations allow the enterprise beans to use CMT whilst the web console uses BMT. (This is why jta.UserTransaction is also specified.)

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

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

jBPM uses Hibernate's second level cache to keep the process definitions in memory after loading they have been loaded once. The process definition classes and collections are configured in the Hibernate mapping files so that the cache element looks like this:

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

Since process definitions will never change, it is acceptable to keep them in the second level cache. (See also Section 17.1.3, “ Changing Deployed Process Definitions ”.)

The default caching strategy is set to nonstrict-read-write. During run-time execution, the process definitions remain static, allowing maximum caching to be achieved. In theory, setting the caching strategy read-only would be even better for run-time execution but, that setting would not permit the deployment of new process definitions.

Having read this chapter, you have learned a great deal of theoretical information and practical advice relating to the topic of persistence in jBPM, including how to utilize Hibernate to its fullest potential.

Switching the JBoss jBPM database backend is reasonably straightforward. We will step through this process using PostgreSQL and MySQL 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 jBPM jPDL installer. Download and install as described in Section 2.1, “Downloading and installing jBPM”. We will assume that this installation was done to a location on your machine named ${jbpm-jpdl-home}. You will find the DB subproject of jBPM in the ${jbpm-jpdl-home}/db.

After installing the database of your choice, you will have to run the database creation scripts to create the jBPM tables. Note that in the hsqldb inside jboss this is done automatically during installation.

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.


After the installation of the database, we can use the pgAdmin III Query tool to look at the contents of the database.

Before we do, we have to define a database connection in pgAdmin 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 pgAdmin we refer to the following figure. As you will see, there are no tables present yet in this database. We will create them in the following section.


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.

In order to get the proper database scripts for your database, you should look int the directory ${jbpm-jpdl-home}/db. Using your database admin console, navigate to the database and then open and execute the create script we just referenced. Below are screen shots doing this for PostgreSQL and MySQL under their respective admin consoles

Before we can really use our newly created database with the JBoss jBPM default web app we will have to do some updates to the JBoss jBPM configuration. The location of the jbpm server configuration is ${jboss-home}/server/default/deploy/jbpm.

First we create a new datasource in JBoss that binds to our database. In the default installation, this is the done in the file jbpm-hsqldb-ds.xml. That hypersonic database configuration file can be removed and should be replaced by the a file that ends with -ds.xml like e.g. jbpm-postgres-ds.xml

<?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>

For MySQL, the datasource definition would look as follows:

<datasources>
  <local-tx-datasource>
    <jndi-name>JbpmDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/jbpmdb</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>root</password>
    <metadata>
      <type-mapping>MySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

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 ${jboss-home}/server/default/deploy/jbpm 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 ${jboss.home}/server/default/lib folder. The file is named postgresql-8.1-*.jdbc3.jar and it can be found in the jdbc subdirectory of your PostgreSQL installation folder.

For MySQL, copy the jdbc driver installed from the MySQL ConnectorJ package. The version you need to use is currently the MySQL Connector/J 3.1 available from http://www.mysql.com/products/connector/j/

The last thing we have to do to make everything run is to update the hibernate configuration file hibernate.cfg.xml. That file is located in directory ${jboss.home}/server/default/deploy/jbpm-service.sar. 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 or MySQL dialect.

Below is a sample of the 2 changes required, comment out the version of the dialect you don't need depending on the database you are using. You can get a list of supported database Dialect types from here http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html#configuration-optional-dialects

<hibernate-configuration>
  <session-factory>

    <!-- jdbc connection properties -->
    <!-- comment out the dialect not needed! -->
    <property name="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</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 web app 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.

Read this chapter to learn about the facilities offered by the jBPM to that can be used to leverage the Java EE infrastructure.

The CommandServiceBean is a stateless session bean that runs Business Process Manager commands by calling its execute method within a separate jBPM context. The available environment entries and customizable resources are summarized in the following table:


The CommandListenerBean is a message-driven bean that listens to the CommandQueue for command messages. It delegates command execution to the CommandServiceBean.

The body of the message must be a Java object that can implement the org.jbpm.Command interface. (The message properties, if any, are ignored.) If the message is not of the expected format, it is forwarded to the DeadLetterQueue and will not be processed any further. The message will also be rejected if the destination reference is absent.

If a received message specifies a replyTo destination, the command execution result will be wrapped in an object message and sent there.

The command connection factory environment reference points to the resource manager being used to supply Java Message Service connections.

Conversely, JobListenerBean is a message-driven bean that listens to the JbpmJobQueue for job messages, in order to support asynchronous continuations.

Note

Be aware that the message must have a property called jobId of type long. This property must contain references to a pending Job in the database. The message body, if it exists, is ignored.

This bean extends the CommandListenerBean. It inherits the latter's environmental entries and those resource references that can be customized.


The TimerEntityBean is used by the Enterprise Java Bean timer service for scheduling. When the bean expires, timer execution is delegated to the command service bean.

The TimerEntityBean requires access to the Business Process Manager's data source. The Enterprise Java Bean deployment descriptor does not define how an entity bean is to map to a database. (This is left to the container provider.) In the JBoss Application Server, the jbosscmp-jdbc.xml descriptor defines the data source's JNDI name and relational mapping data (such as the table and column names.)

Note

The JBoss CMP (container-managed persistence) descriptor uses a global JNDI name (java:JbpmDS), as opposed to a resource manager reference (java:comp/env/jdbc/JbpmDataSource).

Note

Earlier versions of the Business Process Manager used a stateless session bean called TimerServiceBean to interact with the Enterprise Java Bean timer service. The session approach had to be abandoned because it caused an unavoidable bottleneck for the cancellation methods. Because session beans have no identity, the timer service was forced to iterate through all the timers to find the ones it had to cancel.

The bean is still available for backwards compatibility purposes. It works in the same environment as the TimerEntityBean, so migration is easy.


The following configuration items are included in jbpm.cfg.xml:

<jbpm-context>
  <service name="persistence"
    factory="org.jbpm.persistence.jta.JtaDbPersistenceServiceFactory" />
  <service name="message"
    factory="org.jbpm.msg.jms.JmsMessageServiceFactory" />
  <service name="scheduler"
    factory="org.jbpm.scheduler.ejbtimer.EntitySchedulerServiceFactory" />
</jbpm-context>

The JtaDbPersistenceServiceFactory allows the Business Process Manager to participate in JTA transactions. If an existing transaction is underway, the JTA persistence service "clings" to it; otherwise it starts a new transaction. The Business Process Manager's enterprise beans are configured to delegate transaction management to the container. However, a new one will be started automatically if one creates a JbpmContext in an environment in which no transaction is active (such as a web application.) The JTA persistence service factory contains the configurable fields described below.

isCurrentSessionEnabled

When this is set to true, the Business Process Manager will use the "current" Hibernate session associated with the ongoing JTA transaction. This is the default setting. (See http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture.html#architecture-current-session for more information.)

Use the same session as by jBPM in other parts of the application by taking advantage of the contextual session mechanism. Do so through a call to SessionFactory.getCurrentSession(). Alternatively, supply a Hibernate session to jBPM by setting isCurrentSessionEnabled to false and injecting the session via the JbpmContext.setSession(session) method. This also ensures that jBPM uses the same Hibernate session as other parts of the application.

Note

The Hibernate session can be injected into a stateless session bean (via a persistence context, for example.)

isTransactionEnabled

When this is set to true, jBPM will begin a transaction through Hibernate's transaction API, using the JbpmConfiguration.createJbpmContext() method to commit it. (The Hibernate session is closed when JbpmContext.close() is called.)

Warning

This is not the desired behavior when the Business Process Manager is deployed as an EAR and hence isTransactionEnabled is set to false by default. (See http://www.hibernate.org/hib_docs/v3/reference/en/html/transactions.html#transactions-demarcation for more details.)

JmsMessageServiceFactory delivers asynchronous continuation messages to the JobListenerBean by leveraging the reliable communication infrastructure exposed through the Java Message Service interfaces. The JmsMessageServiceFactory exposes the following configurable fields:

connectionFactoryJndiName

This is the name of the JMS connection factory in the JNDI initial context. It defaults to java:comp/env/jms/JbpmConnectionFactory.

destinationJndiName

This is the name of the JMS destination to which job messages will be sent. It must match the destination from which JobListenerBean receives messages. It defaults to java:comp/env/jms/JobQueue.

isCommitEnabled

This specifies whether the Business Process Manager should commit the Java Message Service session upon JbpmContext.close(). Messages produced by the JMS message service are never meant to be received before the current transaction commits; hence the sessions created by the service are always transacted. The default value is false, which is appropriate when the connection factory in use is XA-capable, as the messages produced by the Java Message Service session will be controlled by the overall JTA transaction. This field should be set to true if the JMS connection factory is not XA-capable so that the Business Process Manager explicitly commits the JMS session's local transaction.

The EntitySchedulerServiceFactory is used to schedule business process timers. It does so by building upon on the transactional notification service for timed events provided by the Enterprise Java Bean container. The EJB scheduler service factory has the configurable field described below.

timerEntityHomeJndiName

This is the name of the TimerEntityBean's local home interface in the JNDI initial context. The default value is java:comp/env/ejb/TimerEntityBean.

The hibernate.cfg.xml file includes the following configuration items. Modify them to support other databases or application servers.

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

<property name="hibernate.cache.provider_class">
  org.hibernate.cache.HashtableCacheProvider
</property>

<!-- DataSource properties (begin) -->
<property name="hibernate.connection.datasource">
    java:comp/env/jdbc/JbpmDataSource
</property>
<!-- DataSource properties (end) -->

<!-- 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>
<!-- JTA transaction properties (end) -->

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

Replace the hibernate.dialect setting with that which is appropriate for one's database management system. (For more information, read http://www.hibernate.org/hib_docs/v3/reference/en/html/session-configuration.html#configuration-optional-dialects.)

The HashtableCacheProvider can be replaced with other supported cache providers. (Refer to http://www.hibernate.org/hib_docs/v3/reference/en/html/performance.html#performance-cache for more information.)

Out of the box, jBPM is configured to use the JTATransactionFactory. If an existing transaction is underway, the JTA transaction factory uses it; otherwise it creates a new transaction. The jBPM enterprise beans are configured to delegate transaction management to the container. However, if the jBPM APIs are being used in a context in which no transaction is active (such as a web application), one will be started automatically.

To prevent unintended transaction creations when using container-managed transactions, switch to the CMTTransactionFactory. This setting ensures that Hibernate will always look for an existing transaction and will report a problem if none is found.

Ensure that the appropriate environmental references are in place for deployment descriptors for client components written directly against those Business Process Manager APIs that can leverage the enterprise services. The descriptor below can be regarded as typical for a client session bean:

<session>

  <ejb-name>MyClientBean</ejb-name>
  <home>org.example.RemoteClientHome</home>
  <remote>org.example.RemoteClient</remote>
  <local-home>org.example.LocalClientHome</local-home>
  <local>org.example.LocalClient</local>
  <ejb-class>org.example.ClientBean</ejb-class>
  <session-type>Stateless</session-type>
  <transaction-type>Container</transaction-type>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home>
    <local>org.jbpm.ejb.LocalTimerEntity</local>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <res-type>javax.jms.ConnnectionFactory</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <message-destination-type>javax.jms.Queue</message-destination-type>
    <message-destination-usage>Produces</message-destination-usage>
  </message-destination-ref>

</session>

The environmental references above can be bound to resources in the target operational environment as follows. Note that the JNDI names match the values used by the Business Process Manager enterprise beans.

<session>

  <ejb-name>MyClientBean</ejb-name>
  <jndi-name>ejb/MyClientBean</jndi-name>
  <local-jndi-name>java:ejb/MyClientBean</local-jndi-name>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <jndi-name>java:JbpmDS</jndi-name>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <jndi-name>java:JmsXA</jndi-name>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <jndi-name>queue/JbpmJobQueue</jndi-name>
  </message-destination-ref>

</session>

If the client component is a web application, as opposed to an enterprise bean, the deployment descriptor shall look like this:

<web-app>

  <servlet>
    <servlet-name>MyClientServlet</servlet-name>
    <servlet-class>org.example.ClientServlet</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>MyClientServlet</servlet-name>
    <url-pattern>/client/servlet</url-pattern>
  </servlet-mapping>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <ejb-ref-type>Entity</ejb-ref-type>
    <local-home>org.jbpm.ejb.LocalTimerEntityHome</local-home>
    <local>org.jbpm.ejb.LocalTimerEntity</local>
    <ejb-link>TimerEntityBean</ejb-link>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <res-type>javax.jms.ConnectionFactory</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <message-destination-type>javax.jms.Queue</message-destination-type>
    <message-destination-usage>Produces</message-destination-usage>
    <message-destination-link>JobQueue</message-destination-link>
  </message-destination-ref>

</web-app>

The above environmental references can also be bound to resources in the target operational environment, as per this code sample:

<jboss-web>

  <ejb-local-ref>
    <ejb-ref-name>ejb/TimerEntityBean</ejb-ref-name>
    <local-jndi-name>java:ejb/TimerEntityBean</local-jndi-name>
  </ejb-local-ref>

  <resource-ref>
    <res-ref-name>jdbc/JbpmDataSource</res-ref-name>
    <jndi-name>java:JbpmDS</jndi-name>
  </resource-ref>

  <resource-ref>
    <res-ref-name>jms/JbpmConnectionFactory</res-ref-name>
    <jndi-name>java:JmsXA</jndi-name>
  </resource-ref>

  <message-destination-ref>
    <message-destination-ref-name>
        jms/JobQueue
    </message-destination-ref-name>
    <jndi-name>queue/JbpmJobQueue</jndi-name>
  </message-destination-ref>

</jboss-web>

Read this section to learn the terminology that you will find used throughout the rest of this book.

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 node type defines the run-time behavior. A process definition only has 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 generated 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 to execute the graph. When it receives 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 making the graph execution continue. Continuation of graph execution is achieved by making the token leave the node. Each type of node can implement a different behavior for the continuation of the graph execution. A node that does not pass on the execution will behave as a state.

Actions are pieces of Java code that are executed upon events during the process execution. The graph is an important instrument in the communication of software requirements but it is just one view (projection) of the software being produced. It hides many technical details. Actions are a mechanism one uses to add technical details beyond those 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.

Having learned these definitions, read on to find out how process modelling works.

A process graph is made up of nodes and transitions. Each node is of a specific type. The node type determines what will happen when an execution arrives in the node at run-time. The Business Process Manager provides a set of node types to use. Alternatively, one can write custom code to implement a specific node behavior.

Each node has two main responsibilities: firstly, it can execute plain Java code, code which will normally relate to the function of the node. Its second responsibility is to pass on the process execution.

A node may face the following options when it attempts to pass the process execution on. It will follow that course which is most applicable:

The Business Process Manager contains a set of pre-implemented node types, each of which has a specific configuration and behavior. However, one can also write one's own node behavior and use it in a process.

There are two ways in which one can model a decision, the choice as to which to use being left to the discretion of the user. The options are:

When the decision is to be undertaken by the process, use a decision node. Specify the decision criteria in one of two ways, the simplest being to add condition elements to the transitions. (Conditions are EL expressions or beanshell scripts that return a Boolean value.)

At run-time, the decision node will, firstly, loop over those leaving transitions on which conditions have been specified. It will evaluate those transitions first in the order specified in the XML. The first transition for which the condition resolves to true will be taken. If the conditions for all transitions resolve to false, the default transition, (the first in the XML), will taken instead.

The second approach is to use an expression that returns the name of the transition to take. Use the expression attribute to specify an expression on the decision. This will need to resolve to one of the decision node's leaving transitions.

One can also use the handler element on the decision, as this element can be used to specify an implementation of the DecisionHandler interface that can be specified on the decision node. In this scenario, the decision is calculated by a Java class and the selected leaving transition is returned by the decide method, which belongs to the DecisionHandler implementation.

When the decision is undertaken by an external party, always use multiple transitions that will leave a state or wait state node. The leaving transition can then be provided in the external trigger that resumes execution after the wait state is finished (these might, for example, be Token.signal(String transitionName) or TaskInstance.end(String transitionName).)

Use this node to avoid writing custom code. It expects only one sub-element action, which will be run when the execution arrives in the node. Custom code written in actionhandler can do anything but be aware that it is also responsible for passing on the execution. (See Section 9.3.1, “ Node Responsibilities ” for more information.)

This node can also be used when one is utilizing a Java API to implement some functional logic for a corporate business analyst. It is advantageous to do so this way because the node remains visible in the graphical representation of the process. (Use actions to add code that is invisible in the graphical representation of the process.)

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.

Read this section to study an example of an action on an event. It demonstrates how to undertake a database update on a given transition. (The database update is technically vital but it is not of importance to the business analyst.)


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>

A script is an action that executes a Beanshell script. (For more information about Beanshell, see http://www.beanshell.org/.) By default, all process variables are available as script variables but no script variables will be written to the process variables. The following script-variables are 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 behavior of loading and storing variables into the script, use the variable element as a sub-element of script. If doing so, also place the script expression into the script as a sub-element: 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 variable's access attribute contains read, the process variable will be loaded as a script variable before the script is evaluated. If the access attribute contains write, the script variable will be stored as a process variable after evaluation. The mapped-name attribute can make the process variable available under another name in the script. Use this when the process variable names contain spaces or other invalid characters.

A super-state is a group of nodes. They can be nested recursively and are used to add a hierarchy to the process definition. For example, this functionality is useful to group the nodes belonging to a process in phases.

Actions can be associated with super-state events. Events fired by tokens in nested nodes bubble up the super-state hierarchy up to the process definition. The token therefore acts as being in every node in the hierarchy at the same time. This can be convenient when checking if a process execution is in, for example, the start-up phase.

The Business Process Manager supports process composition by means of the process-state. This is a state that is associated with another process definition. When graph execution arrives in the process-state, a new instance of the sub-process is created. This sub-process is then associated with the path of execution that arrived in the process state. The super-process' path of execution will wait until the sub-process has ended and then 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>

In the example above, the hire process contains a process-state that spawns an interview process. When execution arrives in the first interview, a new execution (that is, process instance) of the interview process is created. If a version is not explicitly specified, the latest version of the sub-process is used. To make the Business Process Manager instantiate a specific version, specify the optional version attribute. To postpone binding the specified or latest version until the sub-process is actually created, set the optional binding attribute to late.

Next, hire process variable a is copied into interview process variable aa. In the same way, hire variable b is copied into interview variable bb. When the interview process finishes, only variable aa is copied back into the a variable.

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

The Business Process Manager's graph execution model is based on an interpretation of the process definition and the "chain of command" pattern.

The process definition data is stored in the database and is used during process execution.

The "chain of command pattern" makes each node in the graph responsible for passing on the process execution. If a node does not pass it on, it behaves as though it were a wait state.

Let the execution start on process instances and it will continue until it enters a wait state.

A token represents a path of execution. It has a pointer to a node in the process graph. During wait state, the tokens can be made to persist in the database.

This algorithm is used to calculate the execution of a token. Execution starts when a signal is sent to the tokenand it is then passed over the transitions and nodes via the chain of command pattern. These are the relevant methods:


When a token is in a node, signals can be sent to it. A signal is treated as an instruction to start execution and must, therefore, specify a leaving transition from the token's current node. The first transition is the default. In a signal to a token, it takes its current node and calls the Node.leave(ExecutionContext,Transition) method. (It is best to think of the ExecutionContext as a token because the main object in it is a token.) The Node.leave(ExecutionContext,Transition) method will fire the node-leave event and call the Transition.take(ExecutionContext). That method will then run the transition event and call the Node.enter(ExecutionContext) on the transition's destination node. That method will then fire the node-enter event and call the Node.execute(ExecutionContext).

Every type of node has its own behaviour, these being implemented via the execute method. Each node is responsible for passing on the 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

The next state, including the invocation of the actions, is calculated via the client's thread. A common misconception is that all calculations must be undertaken in this way. Rather, as is the case with any asynchronous invocation, one can use asynchronous messaging (via Java Message Service) for that. When the message is sent in the same transaction as the process instance update, all synchronization issues are handled correctly. 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 to those wishing to maximise business process performance.

As explained in Section 9.10, “ Graph Execution ”, the Business Process Manager runs the process in the thread of the client and is, by nature, synchronous. In practice, this means that the token.signal() or taskInstance.end() will only return when the process has entered a new wait state.

Note

To learn more about the jPDL feature being described in this section, read Chapter 13, Asynchronous Continuations .

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

Sometimes, in-process calculations take a lot of time, so this behavior might be undesirable. To cope with this issue, the Business Process Manager includes an asynchronous messaging system that allows it to continue a process in a manner, which is, as the name implies, asynchronous. (Of course, in a Java enterprise environment, jBPM can be configured to use a Java Message Service broker instead of the in-built messaging system.)

jPDL supports the async="true" attribute in every node. Asynchronous nodes will not be executed in the thread of the client. Instead, a message is sent over the asynchronous messaging system and the thread is returned to the client (in other words, token.signal() or taskInstance.end() will be returned.)

The Business Process Manager's client code can now commit the transaction. Send messages in the same transaction as that containing the process updates. (The overall result of such a transaction will be that the token is moved to the next node (which has not yet been executed) and a org.jbpm.command.ExecuteNodeCommand message will be sent from the asynchronous messaging system to the jBPM Command Executor. This reads the commands from the queue and executes them. In the case of the org.jbpm.command.ExecuteNodeCommand, the process will be continued when the node is executed. (Each command is executed in a separate transaction.)

Important

Ensure that a jBPM Command Executor is running so that asynchronous processes can continue. Do so by configuring the web application's CommandExecutionServlet.

Note

Process modelers do not need to be excessively concerned with asynchronous messaging. The main point to remember is transaction demarcation: by default, the Business Process Manager will operate in the client transaction, undertaking the whole calculation until the process enters a wait state. (Use async="true" to demarcate a transaction in the process.)

Here is 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" />
...

The client code needed to both start and resume process executions is exactly the same as that needed for 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 occurs, the process execution's root token will point to node one and an ExecuteNodeCommand message is 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 pass the execution on or enter a wait state. If it chooses to pass it on, the transaction will be ended when the execution arrives at node two.

Read this chapter to learn about process variables. Process variables are key-value pairs that maintain process instance-related information.

Note

Since one must be able to store the context in a database, some minor limitations apply.

Each path of execution (also known as a token) has its own set of process variables. Variables are always requested on a path of execution. Process instances have a tree of these paths. If a variable is requested but no path is specified, the root token will be used by default.

The variable look-up occurs recursively. It runs over the parents of the given path of execution. (This is similar to the way in which variables are scoped in programming languages.)

When a non-existent variable is set on a path of execution, the variable is created on the root token. (Hence, each variable has, by default, a process scope.) To make a variable token "local", create it explicitly, as per this example:

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

The jBPM's core role is to persist the execution of a process. This feature is extremely useful when one is seeking to manage tasks and task-lists for people. The jBPM allows one to specify a piece of software that describes an overall process. Such a piece of software can have wait states for human tasks.

It is possible to assign a task instance to an actorId (java.lang.String). Every task instance is stored in one table (JBPM_TASKINSTANCE.) Query this table for every task instances for a given actorId, in order to obtain the task list for that particular user.

Use the jBPM task list mechanism to combine jBPM tasks with other tasks, even when those other tasks are unrelated to a process execution. In this way, one can easily combine jBPM-process-tasks with other application's tasks in one centralised repository.

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

If more than one task instance is associated with a task-node, the process developer can specify the way in which completion of the task instances affects continuation of the process. Give any of these values to the task-node's signal-property:

Task instance creation can be based upon a run-time calculation. In these cases, add an ActionHandler to the task-node's node-enter event and set create-tasks="false". Here is an example:

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);
  }
}

Here, the tasks to be created are specified in the task-node. They could also be specified in the process-definition and fetched from the TaskMgmtDefinition. (TaskMgmtDefinition extends the process definition by adding task management information.)

The TaskInstance.end() method is used to mark task instances as completed. One can optionally 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.

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

With the jBPM, one can apply the push (personal task list) and pull (group task list) models of task assignment in combination. The process determines those responsible for a task and push it to their task lists. A task can also be assigned to a pool of actors, in which case each of the actors pull the task and put it in their personal task lists. (Refer to Section 11.3.3, “ The Personal Task List ” and Section 11.3.4, “ The Group Task List ” for more information.)

The pooled actors are the group of candidates to whom the task is offered. One candidate has to accept it. Users can not start working on tasks immediately as that would, potentially, result in a conflict if many people commenced work on the same task. To prevent this, users can only take task instances from the group task list and move these into their personal task lists. It is only when a task is placed on the user's personal task list that her or she can commence working on it.

To put a taskInstance in someone's group task list, add the user's actorId or one of the user's groupIds to the pooledActorIds. To specify the pooled actors, use one of the following methods:

To fetch the group task list for a given user, make a collection that includes the user's actorId and those of all the groups to which the user belongs. Use TaskMgmtSession.findPooledTaskInstances(String actorId) or TaskMgmtSession.findPooledTaskInstances(List actorIds) to search for task instances that are not in a personal task list (actorId==null) and for which there is a match amongst the pooled actorId.

The actorId always overrides pooled actors. Hence, a taskInstance that has an actorId and a list of pooledActorIds will only show up in the actor's personal task list. Retain the pooledActorIds in order to put a task instance back into the group by simply setting the taskInstance's actorId property to null.

When task instances are created, one can use task controllers populate the task instance variables. When the task instances terminate, one can use task controllers to submit the data belonging to them to the process variables.

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

When a task instance is created, the task controller translates process variables, if there are any, into task variables. The task variables serve as the input for the user interface form. The user input itself is stored in the task variables. When the user ends the task, the task controller updates the process variables based on the task instance data.


In a simple scenario, there is a one-on-one mapping between process variables and the form parameters. Specify task controllers in a task element. In this case, the default JBPM task controller can be used. It takes a list of variable elementswhich express how the process variables are copied in the task variables.

The next example demonstrates how to create separate copies of task instance variable, 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 is also used as the label for the fields in the web application's task instance form.

Use the access attribute to specify as to whether or not the variable copied at task instance creation, will be written back to the process variables at task instance conclusion. (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 whilst a start-state has one task.

If the simple one-to-one mapping between process variables and form parameters is too limiting, create a custom TaskControllerHandler implementation. Here is the interface for it:.

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

This code sample demonstrates how to configure it:

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

A swimlane is a process role. Use this mechanism to specify that multiple tasks in the process are to be be undertaken by the same actor. After the first task instance for a given swimlane is created, the actor is "remembered" for every subsequent task in the same swimlane. A swimlane therefore has one assignment. Study Section 11.3, “ Assignment ” to learn more.

When the first task in a given swimlane is created, the AssignmentHandler is called. The Assignable item that is passed to the AssignmentHandler is SwimlaneInstance. Every assignment undertaken on the task instances in a given swimlane will propagate to the swimlane instance. This is the default behaviour because the person that takes a task will have a knowledge of that particular process. Hence, ever subsequent task instances in that swimlane is automatically assigned to that user.

One can specify timers on tasks. See Section 12.1, “ Timers ”.

It is possible to customise cancel-event for task timers. By default, a task timer cancels when the task is ended but with the cancel-event attribute on the timer, one can customise that to task-assign or task-start. The cancel-event supports multiple events. To combine cancel-event types, specify them in a comma-separated list in the attribute.

Management of users, groups and permissions is termed identity management. The jBPM includes an optional identity component. One can easily replace it with one's company's own data store.

The jBPM identity management component holds knowledge of the organisational model and uses this to assign tasks. This model describes the users, groups, systems and the relationships between these. Optionally, permissions and roles can also be included.

The jBPM handles this by defining an actor as an actual participant in a process. An actor is identified by its ID called an actorId. The jBPM has only knowledge about actorIds and they are represented as java.lang.Strings for maximum flexibility. So any knowledge about the organizational model and the structure of that data is outside the scope of the jBPM's 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.

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 organizational structure of users involved in a process.

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>
      
      <para>Syntax of the assignment expression is like this:</para>
      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)
</programlisting>

Read this chapter to learn about the role of timers in the Business Process Manager.

Timers can be created upon events in the process. Set them to trigger either action executions or event transitions.

Normally, a node is always executed after a token has entered it. Hence, the node is executed in the client's thread. One will explore asynchronous continuations by looking at two examples. The first example is part of a process with three 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 behavior and it is represented in the diagram 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.


Whilst "persistence" is not mandatory in jBPM, most commonly a signal will be called within a transaction. Look at the updates of that transaction. Initially, 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. Secondly, in case the automated action accesses and updates some transactional resources, such updates should be combined or made part of the same transaction.

The second example is a variant of the first 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 into two parts. The first of these will execute the process up to the point at which node 'b' is to be executed. The second part will execute node 'b.' That execution will stop in wait state 'c'.

The transaction will hence be split into two separate transactions, one 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.


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 allows this feature to be supported on environments where JMS is unavailable.

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

These job messages are produced by the process execution. During process execution, for each node or action that has to be executed asynchronously, a Job (Plain Old Java Object) will be dispatched to the MessageService. The message service is associated with the JbpmContext and it just collects all the messages that have to be sent.

The messages will be sent as part of JbpmContext.close(). That method cascades the close() invocation to all of the associated services. The actual services can be configured in jbpm.cfg.xml. One of the services, DbMessageService, is configured by default and will notify the job executor that new job messages are available.

The graph execution mechanism uses the interfaces MessageServiceFactory and MessageService to send messages. This is to make the asynchronous messaging service configurable (also in jbpm.cfg.xml). In Java EE environments, the DbMessageService can be replaced with the JmsMessageService to leverage the application server's capabilities.

The following is a brief summary of the way in which the job executor works.

"Jobs" are records in the database. Furthermore, they are objects and can be executed. Both timers and asynchronous messages are jobs. For asynchronous messages, the dueDate is simply set to the current time when they are inserted. The job executor must execute the jobs. This is done in two phases.

Acquiring a job and executing the job are done in 2 separate transactions. The dispatcher thread acquires jobs from the database on behalf of all the executor threads on this node. When the executor thread takes the job, it adds its name into the owner field of the job. Each thread has a unique name based on IP address and sequence number.

A thread could die between acquisition and execution of a job. To clean-up after those situations, there is one lock-monitor thread per job executor that checks the lock times. The lock monitor thread will unlock any jobs that have been locked for more than 10 minutes, so that they can be executed by another job executor thread.

The isolation level must be set to REPEATABLE_READ for Hibernate's optimistic locking to work correctly. REPEATABLE_READ guarantees that this query will only update one row in exactly one of the competing transactions.

update JBPM_JOB job
set job.version = 2
    job.lockOwner = '192.168.1.3:2'
where 
    job.version = 1

Non-Repeatable Reads can lead to the following anomaly. A transaction re-reads data it has previously read and finds that data has been modified by another transaction, one that has been committed since the transaction's previous read.

Non-Repeatable reads are a problem for optimistic locking and therefore, isolation level READ_COMMITTED is not enough because it allows for Non-Repeatable reads to occur. So REPEATABLE_READ is required if you configure more than one job executor thread.

Configuration properties related to the job executor are:

Read this chapter to learn about the Business Process Manager's calendar functionality, which is used to calculate due dates for tasks and timers.

It does so by adding or subtracting a duration with a base date. (If the base date is omitted, the current date is used by default.)

The due date is comprised of a duration and a base date. The formula used is: duedate ::= [<basedate> +/-] <duration>

The following examples demonstrate different ways in which it can be used:

<timer name="daysBeforeHoliday" duedate="5 business days">...</timer>

<timer name="pensionDate" duedate="#{dateOfBirth} + 65 years" >...</timer>

<timer name="pensionReminder" duedate="#{dateOfPension} - 1 year" >...</timer>

<timer name="fireWorks" duedate="#{chineseNewYear} repeat="1 year" >...</timer>

<reminder name="hitBoss" duedate="#{payRaiseDay} + 3 days" repeat="1 week" />
hour.format=HH:mm
#weekday ::= [<daypart> [&amp; <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 &amp; 12:30-17:00
weekday.tuesday=   9:00-12:00 &amp; 12:30-17:00
weekday.wednesday= 9:00-12:00 &amp; 12:30-17:00
weekday.thursday=  9:00-12:00 &amp; 12:30-17:00
weekday.friday=    9:00-12:00 &amp; 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

Having studied this chapter, the reader now understands how the Business Calendar works.

This chapter describes the "out-of-the-box" e. mail support available in Business Process Manager JPDL. Read this information to learn how to configure different aspects of the mail functionality.

There are four ways in which one can specify the point in time at which e. mails are to be sent from a process. Each shall be examined in turn.

Use a mail action if there is a reason not to show the e. mail as a node in the process graph.

&lt;mail actors="#{president}" subject="readmylips" text="nomoretaxes" /&gt;

Specify the subject and text attributes as an element like this:

<mail actors="#{president}" >
    <subject>readmylips</subject>
    <text>nomoretaxes</text>
</mail>

Each of the fields can contain JSF-like expressions:

<mail
    to='#{initiator}'
    subject='websale'
    text='your websale of #{quantity} #{item} was approved' />

Two attribute specify the recipients: actors and to. The to attribute should "resolve" to a semi-colon separated list of e. mail addresses. The actors attribute should resolve to a semi-colon separated list of actorIds. These actorIds will, in turn, resolve to e. mail addresses. (Refer to Section 15.3.3, “ Address Resolving ” for more details.)

<mail 
    to='admin@mycompany.com' 
    subject='urgent'
    text='the mailserver is down :-)' />

Note

To learn how to specify recipients, read Section 15.3, “ Specifying Email Recipients ”

Emails can be defined by the use of templates. Overwrite template properties in this way:

<mail template='sillystatement' actors="#{president}" />

Note

Learn more about templates by reading Section 15.4, “ Email Templates ”

The fields to, recipients, subject and text can contain JSF-like expressions. (For more information about expressions, see Section 17.3, “ Expressions ”.)

One can use the following variables in expressions: swimlanes, process variables and transient variables beans. Configure them via the jbpm.cfg.xml file.

Expressions can be combined with address resolving functionality. (Refer to Section 15.3.3, “ Address Resolving ”. for more information.)

This example pre-supposes the existence of a swimlane called president:

<mail   actors="#{president}" 
        subject="readmylips" 
        text="nomoretaxes" />

The code will send an e. mail to the person that acts as the president for that particular process execution.

Read this chapter to learn about the logging functionality present in the Business Process Manager and the various ways in which it can be utilized.

The purpose of logging is to record the history of a process execution. As the run-time data of each process execution alters, the changes are stored in the logs.

Note

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 the purpose of debugging it.) Process logging, by contast, traces the execution of process instances.

There are many ways in which process logging information can be useful. Most obvious of these is the consulting of the process history by process execution participants.

Another use case is that of Business Activity Monitoring (BAM). This can be used to query or analyze the logs of process executions to find useful statistical information about the business process. This information is key to implementing "real" business process management in an organization. (Real business process management is about how an organization manages its processes, how these processes are supported by information technology and how these two can be usede improve each other in an iterative process.)

Process logs can also be used to implement "undos." Since the logs contain a record of all run-time information changes, they can be "played" in reverse order to bring a process back into a previous state.

Business Process Manager modules produce logs when they run 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, which is an optional extension of the ProcessInstance.

The Business Process Manager generates many different kinds of log, these being graph execution logs, context logs and task management logs. A good starting point is org.jbpm.logging.log.ProcessLog since one can use that to navigate down the inheritance tree.

The LoggingInstance collects all log entries. When the ProcessInstance is saved, they are flushed from here to the database. (The ProcessInstance's logs field is not mapped to Hibernate. This is so as to avoid those logs that are retrieved from the database in each transaction.)

Each ProcessInstance is made in the context of a path of execution and hence, the ProcessLog refers to that token, which also serves as an index sequence generator it. (This is important for log retrieval as it means that logs produced in subsequent transactions shall have sequential sequence numbers.

Use this API method to add process logs:

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

This is the UML diagram for information logging:


A CompositeLog is a special case. It serves as the parent log for a number of children, thereby creating the means for a hierarchical structure to be applied. The following application programming interface is used to insert a log:

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 is consistent. For example:

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

The jBPM Process Definition Language (jPDL) is the notation to define business processes recognized by the jBPM framework and expressed as an XML schema. Process definitions often require support files in addition to the jPDL document. All these files are packaged into a process archive for deployment.

The process archive is just a ZIP archive with a specific content layout. The central file in the process archive is called processdefinition.xml This file defines the business process in the jPDL notation and provides information about automated actions and human tasks. The process archive also contains other files related to the process, such as action handler classes and user interface task forms.

One can deploy a process archive in any of three different ways:

To deploy a process archive with the Process Designer Tool, right-click on the process archive folder and select the Deploy process archive option.

The jBPM application server integration modules include the gpd-deployer web application, which has a servlet to upload process archives, called GPD Deployer Servlet. This servlet is capable of receiving process archives and deploying them to the configured database.

To deploy a process archive with an ant task, define and call the task as follows.

<target name="deploy-process">
  <taskdef name="deployproc" classname="org.jbpm.ant.DeployProcessTask">
    <classpath location="jbpm-jpdl.jar" />
  </taskdef> 
  <deployproc process="build/myprocess.par" />
</target>

To deploy more process archives at once, use nested fileset elements. Here are the DeployProcessTask attributes.


To deploy process archives programmatically, use one of the parseXXX methods of the org.jbpm.graph.def.ProcessDefinition class.

Process instances always execute on the same process definition as that in which they were started. However, the jBPM allows multiple process definitions of the same name to co-exist in the database. Typically, a process instance is 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 definitions.

If the process includes references to Java classes, these can be made available to the jBPM runtime environment in one of two ways:

When a process archive is deployed, a process definition is created in the jBPM database. Version process definitions on the basis of their names. When a named process archive is deployed, the deployer assigns it a version number. It does so by searching for the highest number assigned to a process definition of the same name and then adds one to that value. (Unnamed process definitions will always be versioned as -1.)

Use the delegation mechanism to include custom code in process executions.

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

Here are descriptions of every type of configuration:

This is the default configuration type. The config-type field first instantiates an object of the delegation class and then set values in those object fields specified in the configuration. The configuration is stored in an XML file. In this file, the element names have to correspond to the class' field names. The element's content text is put in the corresponding field. If both necessary and possible to do, the element's content text is converted to the field type.

These are the supported type conversions:

Look at this 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 for that class:

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

There is limited support for a JSP/JSF EL-like expression language. In actions, assignments and decision conditions, one can write this kind of expression: expression="#{myVar.handler[assignments].assign}"

The jPDL and JSF expression languages are similar. jPDL EL is based on JSF EL but, in contrast to the latter, it employs #{...} notation and includes support for method-binding.

Depending on the context, the process and task instance variables can be used as starting variables, as can the 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 powerful when used in a JBoss SEAM environment (http://www.jboss.com/products/seam). Because of the integration between the jBPM and SEAM, every backed bean, Enterprise Java Bean and so forth becomes accessible from within one's process definition.

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

Table 17.16. Action Schema

NameTypeMultiplicityDescription
nameattributeoptionalThis is the 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 expressionThis is the fully-qualified class name of the class that implements the org.jbpm.graph.def.ActionHandler interface.
ref-nameattibuteeither this or classThis is the 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-nameThis is a jPDL expression that resolves to a method. See also Section 17.3, “ Expressions ”
accept-propagated-eventsattributeoptionalThe options are {yes|no|true|false}. The 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, read Section 9.5.3, “ Passing On Events ”
config-typeattributeoptionalThe options are {field|bean|constructor|configuration-property}. This 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.
asyncattribute{true|false} 'async="true" is only supported in action when it is triggered in an event. The default value 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 asynchronously in a separate transaction.
 {content}optionalThe action's content can be used as the configuration information for custom action implementations. This allows one to create reusable delegation classes. For more about delegation configuration, read ???.

Table 17.24. Task Schema

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.

Table 17.26. Assignment Schema

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

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.

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.JbpmDefaultAuthenticator. That implementation will maintain a ThreadLocal stack of authenticated actorId's. Authenticated blocks can be marked with the methods JbpmDefaultAuthenticator.pushAuthenticatedActorId(String) and JbpmDefaultAuthenticator.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 JbpmDefaultAuthenticator 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.

Since developing process-oriented programs is no different from developing any other software, Red Hat believes that one should be able to test process definitions easily. Read this chapter to learn how to use JUnit without any extensions to unit test custom process definitions.

Keep the development cycle as short as possible. Verify all changes to software source code immediately, (preferably, without any intermediate build steps.) The following examples demonstrate how to develop and test jBPM processes in this way.

Most process definition unit tests are execution-based. Each scenario is executed in one JUnit test method and this feeds the external triggers (signals) into a process execution. It then verifies after each signal to confirm that the process is in the expected state.

Here is an example graphical representation of such a test. It takes a simplified version of the auction process:


Next, 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());
  }
}

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

Version 3, 29 June 2007

Copyright 2007 Free Software Foundation, Inc. http://fsf.org/

Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below.

0. Additional Definitions.

As used herein, this License refers to version 3 of the GNU Lesser General Public License, and the GNU GPL refers to version 3 of the GNU General Public License.

The Library refers to a covered work governed by this License, other than an Application or a Combined Work as defined below.

An Application is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library.

A Combined Work is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the Linked Version.

The Minimal Corresponding Source for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version.

The Corresponding Application Code for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work.

1. Exception to Section 3 of the GNU GPL.

You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL.

2. Conveying Modified Versions.

If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version:

3. Object Code Incorporating Material from Library Header Files.

The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following:

4. Combined Works.

You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following:

  1. Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License.

  2. Accompany the Combined Work with a copy of the GNU GPL and this license document.

  3. For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document.

  4. Do one of the following:

  5. Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.)

5. Combined Libraries.

You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following:

6. Revised Versions of the GNU Lesser General Public License.

The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.

Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License or any later version applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation.

If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxys public statement of acceptance of any version is permanent authorization for you to choose that version for the Library.

Revision History
Revision 0Tue May 11 2010David Le Sage
Initial creation of book by publican