JBoss.orgCommunity Documentation

jBPM User Guide


1. Introduction
1.1. License and EULA
1.2. Downloads
1.3. Sources
1.4. What is it
1.5. Contents of this userguide
1.6. Migration from jBPM 3
2. Installation
2.1. The distribution
2.2. Required software
2.3. Getting started quickly
2.4. Ant scripts
2.5. JBoss
2.6. Database
2.7. Tomcat
2.8. Configuration files
2.9. Graphical Process Designer (GPD)
2.9.1. Get Eclipse
2.9.2. Add the update site gpd/jbpm-gpd-site.zip
2.9.3. Define the jBPM User Library
2.9.4. Adding jPDL 4 schema to the catalog
2.9.5. Importing the Examples
2.9.6. Adding deployment with ant
3. Graphical Process Designer (GPD)
3.1. Creating a new process file
3.2. Editing the process source
4. Deploying business archives
4.1. Deploying process files and process resources
4.2. Deploying classes
5. Services
5.1. Process definition, process instance and executions
5.2. ProcessEngine
5.3. Deploying a process
5.4. Undeploying deployments
5.5. Deleting a deployment
5.6. Starting a new process instance
5.6.1. In latest
5.6.2. Specific process version
5.6.3. With a key
5.6.4. With variables
5.7. Signalling a waiting execution
5.8. TaskService
5.9. HistoryService
5.10. ManagementService
6. jPDL
6.1. process
6.2. Control flow activities
6.2.1. start
6.2.2. state
6.2.3. decision
6.2.4. concurrency
6.2.5. end
6.2.6. task
6.2.7. sub-process
6.2.8. custom
6.3. Automatic activities
6.3.1. java
6.3.2. script
6.3.3. hql
6.3.4. sql
6.3.5. mail
6.4. Common activity contents
6.5. Events
6.6. Asynchronous continuations
6.6.1. Async activity
6.6.2. Async fork
6.7. timer
6.7.1. Duedate expressions
6.7.2. Business calendar
6.7.3. Timer transition
6.7.4. Timer event
6.7.5. Timer business time
6.7.6. Timer repeat
6.8. User code
7. Variables
8. Scripting
9. Identity
10. JBoss Integration
10.1. Packaging process archives
10.2. Deploying processes archives to a JBoss instance
10.3. Process deployments and versioning
10.4. ProcessEngine and J2EE/JEE programming models
11. Mail Support
11.1. Producers
11.1.1. Default Producer
11.2. Templates
11.3. Servers
11.3.1. Multiple Servers
11.4. Extension Points
11.4.1. Custom Producers

This documentation is best viewed in firefox. There are some known issues with internet explorer.

This chapter describes how to install jBPM in different application environments.

Eclipse is used as the platform to host the jPDL graphical process designer. This section will describe how to obtain and install Eclipse and how to install the GPD plugin in Eclipse.

You'll need Eclipse 3.4.2.

Use the demo setup or download eclipse manually: Eclipse IDE for Java EE Developers (163 MB).

The classic version of eclipse will not be sufficient as it does not have an XML editor. Eclipse for Java developers should also work.

This chapter will explain how to work with the Graphical Process Designer. After installing the GPD and setting up the examples, you'll see that the jPDL process files will get a special icon. Double clicking such a file in the package view will open up the jPDL process in the GPD.


A business archive is a collection of files assembled in a jar formatted file. The files in a business archive can be jPDL process files, forms, process image and other process resources.

Process files and process resources have to be deployed in the process repository which is stored in the database.

There is a jBPM ant task to deploy business archives (org.jbpm.pvm.internal.ant.JbpmDeployTask) The JbpmDeployTask can deploy individual process files and business archives. They are deployed directly to the database over a JDBC connection. So it is a requirement that the database is up and running before you can deploy processes.

An example of creating and deploying a business archive can be found in the examples directory of the distribution. Let's look at the relevant parts. First a path is declared that includes the jbpm.jar and all its dependencies.

<path id="jbpm.libs.incl.dependencies">
  <pathelement location="${jbpm.home}/examples/target/classes" />
  <fileset dir="${jbpm.home}">
    <include name="jbpm.jar" />
  </fileset>
  <fileset dir="${jbpm.home}/lib" />
</path>

The JDBC driver jar(s) for your database should also be included in the path. MySql, Postgresql and hsqldb are in the distribution. But the oracle driver you have to download separately from the oracle site since we're not allowed to redistribute that file.

When a business archive is deployed, jBPM scans for all the files with the .jpdl.xml extension in the business archive. All those files will be parsed as jPDL processes and made available to the runtime engine. All other resources in the business archive will also be stored as resources in that deployment and made accessible through InputStream getResourceAsStream(long deploymentDbid, String resourceName); in class RepositoryService

For creating a business archives, the jar task can be used.

<jar destfile="${jbpm.home}/examples/target/examples.bar">
      <fileset dir="${jbpm.home}/examples/src">
        <include name="**/*.jpdl.xml" />
        ...
      </fileset>
    </jar>

Before the jbpm-deploy task can be used it need to be declared like this:

<taskdef name="jbpm-deploy"
           classname="org.jbpm.pvm.internal.ant.JbpmDeployTask"
         classpathref="jbpm.libs.incl.dependencies" />

Then the ant task can be used like this

  <jbpm-deploy file="${jbpm.home}/examples/target/examples.bar" />


Interacting with jBPM occurs through services. The service interfaces can be obtained from the ProcessEngine which is build from a Configuration.

A ProcessEngine is thread safe and can be stored in a static member field or even better in JNDI or some other central location. One ProcessEngine object can be used by all requests and threads in an application. Here's how you can obtain a ProcessEngine

The code snippets in this section and the next section about process deployments are taken from example org.jbpm.examples.services.ServicesTest

ProcessEngine processEngine = new Configuration()
      .buildProcessEngine();

The previous code snippet shows how to build a ProcessEngine from the default configuration file jbpm.cfg.xml which is expected in the root of the classpath. If you want to specify another resource location, use the setResource method like this:

ProcessEngine processEngine = new Configuration()
      .setResource("my-own-configuration-file.xml")
      .buildProcessEngine();

There are other setXxxx methods that allow to specify the configuration content as an InputStream, an xmlString, InputSource, URL or File.

From a ProcessEngine the following services can be obtained:

RepositoryService repositoryService = processEngine.getRepositoryService();
ExecutionService executionService = processEngine.getExecutionService();
TaskService taskService = processEngine.getTaskService();
HistoryService historyService = processEngine.getHistoryService();
ManagementService managementService = processEngine.getManagementService();

Process engine objects defined in the configuration can also be retrieved by type (processEngine.get(Class<T>)) or by name (processEngine.get(String))

The RepositoryService groups all methods to manage the repository of deployments. In this first example, we'll deploy one process resource from the classpath with the RepositoryService:

long deploymentDbid = repositoryService.createDeployment()
    .addResourceFromClasspath("org/jbpm/examples/services/Order.jpdl.xml")
    .deploy();

Analogue to the addResourceFromClasspath method above, the source of the processes definitions XML can be picked up from a file, url, string, input stream or zip input stream.

Each deployment is composed of a set of named resources. The content of each resource is a byte array. jPDL process files are recognized by their extension .jpdl.xml. Other resource types are task forms and java classes.

A deployment works with a set of named resources and can potentially contain multiple process descriptions and multiple other artifact types. The jPDL deployer will recognise process files based on the .jpdl.xml extension automatically.

During deployment, an id is assigned to the process definitions. The id will have format {key}-{version} with a dash between key and version

If key is not provided, it is generated automatically based on the name. All non alpha numeric characters in the name will be replaced by underscores to generate the key.

The same name can only be associated to one key and vice verca.

If version is not provided, a version will be automatically be assigned. For version assignment, the versions of all deployed process definitions with the same name will be taken into account. The assigned version will be one higher then the highest version number of deployed process definitions with the same key. If no process definitions with a similar key have been deployed, version number 1 is assigned.

In this first example, we'll supply a name and nothing else.

<process name="Insurance claim">
...
</process>

Let's assume that this is the first time that this process gets deployed. Then it will get the following properties:


And as a second example, we'll show how you can get shorter ids by specifying a process key:

<process name="Insurance claim" key="ICL">
...
</process>

Then the process definition properties look like this:


When using a state activity, the execution (or process instance) will wait when it arrives in the state, waiting for a signal (aka external trigger). Methods signalExecution can be used for that. Executions are referenced by an execution id (String).

In some cases, the execution that arrives in a state will be the process instance itself. But that is not always the case. In case of timers or concurrency, a process is the root execution of a tree of executions. So you have to make sure that you signal the right path of execution.

The preferred way to capture the right execution is by associating an event listener to the state activity like this:

<state name="wait">
  <on event="start">
    <event-listener class="org.jbpm.examples.StartExternalWork" />
  </on>
  ...
</state>

In event listener StartExternalWork you can kick off what needs to be done externally. In that event listener you can also obtain the exact execution id with execution.getId(). It's that executionId that you'll need to provide the signal later on when the external work is done:

executionService.signalExecutionById(executionId);

There is an alternatively (less preferrable) way to obtain the executionId when the execution arrives in the state activity. It's only possible to obtain the execution id this way if you know after which jBPM API call the execution will have entered the state activity:

// assume that we know that after the next call
// the process instance will arrive in state external work

ProcessInstance processInstance = 
  executionService.startProcessInstanceById(processDefinitionId);
// or ProcessInstance processInstance = 
//  executionService.signalProcessInstanceById(executionId);

Execution execution = processInstance.findActiveExecutionIn("external work");
String executionId = execution.getId();

This chapter will explain the jPDL file format for describing process definitions. The schemadocs can also serve as a quick reference for this information.

An example jPDL process file looks like this:

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

<process name="Purchase order" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="Verify supplier" />
  </start>

  <state name="Verify supplier">
    <transition name="Supplier ok" to="Check supplier data" />
    <transition name="Supplier not ok" to="Error" />
  </state>

  <decision name="Check supplier data">
    <transition name="nok" to="Error" />
    <transition name="ok" to="Completed" />
  </decision>

  <end name="Completed" />

  <end name="Error" />

</process>

A wait state. Process execution will wait until an external trigger is provided through the API. Apart from the common activity content, state doesn't have any extra attributes or elements.

Takes one path of many alternatives. Also known as a decision. A decision activity has multiple outgoing transitions and when an execution arrives in a decision activity, an automatic evaluation will decide which outgoing transition is taken.

A decision activity should be configured in one of the three following ways:

A decision handler is a java class that implements the DecisionHandler interface. The decision handler will be responsible for selecting the name of the outgoing transition.

public interface DecisionHandler {
  String select(OpenExecution execution);
}

The handler is specified as a sub element of the decision


Here's an example process of a decision using a DecisionHandler:


<process name="DecisionHandler">

  <start>
    <transition to="evaluate document" />
  </start>

  <decision name="evaluate document">
    <handler class="org.jbpm.examples.decision.handler.ContentEvaluation" />
    <transition name="good" to="submit document" />
    <transition name="bad" to="try again" />
    <transition name="ugly" to="give up" />
  </decision>

  <state name="submit document" />

  <state name="try again" />

  <state name="give up" />

</process>

The ContentEvaluation class looks like this

public class ContentEvaluation implements DecisionHandler {

  public String select(OpenExecution execution) {
    String content = (String) execution.getVariable("content");
    if (content.equals("you're great")) {
      return "good";
    }
    if (content.equals("you gotta improve")) {
      return "bad";
    }
    return "ugly";
  }
}

Now, when we start a process instance and supply value you're great for variable content, then the ContentEvaluation will return String good and the process instance will arrive in activity Submit document.

Ends the execution.

Creates a task for a person in the task component.

A simple task that will be assigned to a specific user



<process name="TaskAssignee">

  <start>
    <transition to="review" />
  </start>

  <task name="review" 
        assignee="#{order.owner}">
 
     <transition to="wait" />
  </task>
  
  <state name="wait" />

</process>

This process shows 2 aspects of task assignment. First, that the attribute assignee is used to indicate the user that is responsible for completing the task. The assignee is a String property of a task and refers to a user.

Secondly, this attribute is by default evaluated as an expression. In this case the task is assigned to #{order.owner}. Which means that first an object is searched for with name order. One of the places where this object is looked up is the process variables associated to the task. Then the getOwner() getter will be used to get the userId that references the user that is responsible for completing this task.

Here's the Order class used in our example:

public class Order implements Serializable {
  
  String owner;

  public Order(String owner) {
    this.owner = owner;
  }

  public String getOwner() {
    return owner;
  }

  public void setOwner(String owner) {
    this.owner = owner;
  }
}

Next a new process instance is created with an order as a process variable.

Map<String, Object> variables = new HashMap<String, Object>(); 
variables.put("order", new Order("johndoe"));
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("TaskAssignee", variables);

Then the task list for johndoe can be obtained like this.

List<Task> taskList = taskService.findPersonalTasks("johndoe");

Note that it is also possible to put plain text like assignee="johndoe". In that case the task will be assigned to johndoe.

A task that will be offered to a group of users. One of the users should then take the task in order to complete it.



Here's an example process using task candidates:

<process name="TaskCandidates">

  <start>
    <transition to="review" />
  </start>

  <task name="review" 
        candidate-groups="sales-dept">
 
     <transition to="wait" />
  </task>
  
  <state name="wait"/>

</process>
        

After starting, a task will be created. The task will not show up in anyone's personal task list. Following task lists will be empty.

taskService.getAssignedTasks("johndoe");
taskService.getAssignedTasks("joesmoe");

But the task will show up in the group task list of all members of the sales-dept group.

The in our example, the sales-dept has two members: johndoe and joesmoe

identityService.createGroup("sales-dept");

identityService.createUser("johndoe", "johndoe", "John", "Doe");
identityService.createMembership("johndoe", "sales-dept");

identityService.createUser("joesmoe", "joesmoe", "Joe", "Smoe");
identityService.createMembership("joesmoe", "sales-dept"); 

So after the process is created, the task will appear in both the group tasks for users johndoe and joesmoe

taskService.findGroupTasks("johndoe");
taskService.findGroupTasks("joesmoe");

Candidates must take a task before they can work on it. This will prevent that two candides start working on the same task. The user interface must only offer the action 'Take' for the tasks in the group task list.

taskService.takeTask(task.getDbid(), "johndoe");

When a user takes a task, the assignee of that task will be set to the given user. The task will disappear from all the candidate's group task list and it will appear in the user's assigned tasks.

Users are only allowed to work on tasks in their personal task list. This should be enforced by the user interface.

Similarly, the attribute candidate-users can be used that resolves to a comma separated list of userIds. The candidate-users attribute can be used in combination with other assignment options.

An AssignmentHandler can be used to calculate the assignee and the candidates for a task programmatically.

public interface AssignmentHandler extends Serializable {

  /** sets the actorId and candidates for the given assignable. */
  void assign(Assignable assignable, OpenExecution execution) throws Exception;
}

Assignable is a common interface for Tasks and Swimlanes. So AssignmentHandlers can be used for tasks as well as swimlanes (see later).

assignment-handler is a sub element of the task element. It specifies a user code object. So the attributes and elements of assignment-handler are documented in Section 6.8, “User code”

Let's look at the task assignment example process.


<process name="TaskAssignmentHandler" xmlns="http://jbpm.org/4.0/jpdl">

  <start g="20,20,48,48">
    <transition to="review" />
  </start>
  
  <task name="review" g="96,16,127,52">
    <assignment-handler class="org.jbpm.examples.task.assignmenthandler.AssignTask">
      <field name="assignee">
        <string value="johndoe" />
      </field>
    </assignment-handler>
    <transition to="wait" />
  </task>

  <state name="wait" g="255,16,88,52" />
  
</process>

The referenced class AssignTask looks like this:

public class AssignTask implements AssignmentHandler {

  String assignee;

  public void assign(Assignable assignable, OpenExecution execution) {
    assignable.setAssignee(assignee);
  }
}

Please note that potentially, AssignmentHandler implementations can use the process variables and any other Java API to access resources like your application database to calculate the assignee and candidate users and groups.

Starting a new process instance of the TaskAssignmentHandler process will immediately bring the new execution to the task activity. A new review task is created and at that point, the AssignTask assignment handler is called. That will set johndoe as the assignee. So John Doe will find the task in his personal task list.

Multiple tasks in a process should be assigned to the same user or candidates. Multiple tasks in a process can be associated to a single swimlane. The process instance will remember the candidates and user that performed the first task in the swimlane. And subsequent tasks in the same swimlane will be assigned to those user and candidates.

A swimlane can also be considered as a process role. In some cases, this might boil down to authorization roles in the identity component. But bare in mind that it is not always the same thing.


Swimlanes can be declared inside a process element:



The task swimlane example has the following process file :

<process name="TaskSwimlane" xmlns="http://jbpm.org/4.0/jpdl">

  <swimlane name="sales representative"
            candidate-groups="sales-dept" />

  <start>
    <transition to="enter order data" />
  </start>
  
  <task name="enter order data"
        swimlane="sales representative">

    <transition to="calculate quote"/>
  </task>

  <task 
      name="calculate quote" 
      swimlane="sales representative">
  </task>

</process>

In this example we create the following information in the identity component:

identityService.createGroup("sales-dept");

identityService.createUser("johndoe", "johndoe", "John", "Doe");
identityService.createMembership("johndoe", "sales-dept");

After starting a new process instance, user johndoe will be a candidate for task enter order data. Again like in the previous task candidates example, John Doe can now take this task like this:

taskService.takeTask(taskDbid, "johndoe");

Taking the task will make Litjohndoe the assignee for the task. And since this task is coupled to the swimlane sales representative, assignee johndoe will also be propagated as the assignee in the swimlane.

Next, John Doe can complete the task like this:

taskService.completeTask(taskDbid);

Completing the task will bring the process execution to the next task, which is calculate quote. Also this task is linked to the swimlane. Therefore, the task will be assigned to johndoe. Also the candidate users and candidate groups of the initial assignment will be copied from the swimlane to the task. This is relevant in case user johndoe would release the task and offer it back to the other candidates.

Creates a sub process instance and waits till it is completed. When the sub process instance completes, then the execution in the sub-process will continue.






The SubProcessVariables example scenario will show the basic workings of the sub-process activity, how to feed information in the sub process when it starts and how to extract information out of the subprocess when it ends.

The parent process involves a document that needs to be reviewed.


<process name="SubProcessDocument" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="review" />
  </start>

  <sub-process name="review"
               sub-process-key="SubProcessReview">
               
    <parameter-in var="document" subvar="document" />
    <parameter-out var="reviewResult" subvar="result" />
    
    <transition to="wait" />
  </sub-process>
  
  <state name="wait"/>

</process>

The review process is a reusable process for all kinds of reviews.


<process name="SubProcessReview" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="get approval"/>
  </start>

  <task name="get approval"
        assignee="johndoe">
               
    <transition to="end"/>
  </task>
  
  <end name="end" />

</process>

The document process is started with a document variable:

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("document", "This document describes how we can make more money...");
    
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument", variables);

Then the parent process execution will arrive in the sub process activity. A sub process instance is created and linked with the super process execution. When the SubProcessReview process instance starts, it arrives in the task. A task will be created for johndoe.

List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);

We can see that the document has been passed from the super process instance to the sub process instance:

String document = (String) taskService.getVariable(task.getDbid(), "document");
assertEquals("This document describes how we can make more money...", document);

Then we set a variable on the task. This is typically done through a form. But here we'll show how it is done programmatically.

taskService.setVariable(task.getDbid(), "result", "accept");

Completing this task, will cause the sub process instance to end.

taskService.completeTask(task.getDbid());

When the sub process ends, the super process execution will get signalled(=notified). First the result variable from the sub process instance will be copied into the reviewResult variable in the super process execution. Then the super process execution will continue and leave the review activity.

In the SubProcessOutcomeValueTest example, the value of a sub process variable is used to select the outgoing transition of the sub-process activity.


<process name="SubProcessDocument">

  <start>
    <transition to="review" />
  </start>

  <sub-process name="review"
               sub-process-key="SubProcessReview"
               outcome="#{result}">
               
    <transition name="ok" to="next step" />
    <transition name="nok" to="update" />
    <transition name="reject" to="close" />
  </sub-process>
  
  <state name="next step" />
  <state name="update" />
  <state name="close" />

</process>

The SubProcessReview is the same as above in the subprocess variables example:


<process name="SubProcessReview" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="get approval"/>
  </start>

  <task name="get approval"
        assignee="johndoe">
               
    <transition to="end"/>
  </task>
  
  <end name="end" />

</process>

A new document process instance is started like usual:

ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument");

Then task is fetched from johndoe's task list

List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
        

Then the result variable is set and the task is completed.

taskService.setVariable(task.getDbid(), "result", "ok");
taskService.completeTask(task.getDbid());
        

In this scenario, the ok transition is taken in the parent process out of the sub-process review activity. The example test case also shows other scenarios.

A process can have many end activities. In the SubProcessOutcomeActivityTest example, the resulting end activity is used to select the outgoing transition of the sub-process activity.


<process name="SubProcessDocument">

  <start>
    <transition to="review" />
  </start>

  <sub-process name="review"
               sub-process-key="SubProcessReview">
               
    <transition name="ok" to="next step" />
    <transition name="nok" to="update" />
    <transition name="reject" to="close" />
  </sub-process>
  
  <state name="next step" />
  <state name="update" />
  <state name="close" />

</process>

The SubProcessReview now has multiple end activities:


<process name="SubProcessReview" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="get approval"/>
  </start>

  <task name="get approval"
        assignee="johndoe">
               
    <transition name="ok" to="ok"/>
    <transition name="nok" to="nok"/>
    <transition name="reject" to="reject"/>
  </task>
  
  <end name="ok" />
  <end name="nok" />
  <end name="reject" />
  
</process>

A new document process instance is started like usual:

ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument");

Then task is fetched from johndoe's task list

List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
        

Then the task is completed with outcome ok.

taskService.completeTask(task.getDbid(), "ok");
        

This will cause the sub process to end in end activity ok. The super process execution will then take outgoing transition ok to next step.

The example test case also shows the other scenarios.

Invokes user code that implements custom behaviour of an activity.

A custom activity refers to user code. See Section 6.8, “User code” for more details on the specific attributes and elements. Let's look at the example:

<process name="Custom" xmlns="http://jbpm.org/4.0/jpdl">

  <start >
    <transition to="print dots" />
  </start>

  <custom name="print dots" 
        class="org.jbpm.examples.custom.PrintDots">
        
    <transition to="end" />
  </custom>
  
  <end name="end" />

</process>

The custom activity behaviour class PrintDots shows that it's possible to control the flow when implementing custom activity behaviours. In this case the PrintDots acitivity implementation will after printing dots wait in the activity until a signal is given.

public class PrintDots implements ExternalActivityBehaviour {

  private static final long serialVersionUID = 1L;

  public void execute(ActivityExecution execution) {
    String executionId = execution.getId();
    
    String dots = ...;

    System.out.println(dots);
    
    execution.waitForSignal();
  }

  public void signal(ActivityExecution execution, 
                     String signalName, 
                     Map<String, ?> parameters) {
    execution.take(signalName);
  }
}

The Java task. A process execution will execute the method of the class that is configured in this activity.



Consider the following example.


<process name="Java" xmlns="http://jbpm.org/4.0/jpdl">

  <start >
    <transition to="greet" />
  </start>

  <java name="greet" 
        class="org.jbpm.examples.java.JohnDoe"
        method="hello"
        var="answer"
        >
        
    <field name="state"><string value="fine"/></field>
    <arg><string value="Hi, how are you?"/></arg>
    
    <transition to="shake hand" />
  </java>
  
  <java name="shake hand" 
        expr="#{hand}"
        method="shake"
        var="hand"
        >
        
    <arg><object expr="#{joesmoe.handshakes.force}"/></arg>
    <arg><object expr="#{joesmoe.handshakes.duration}"/></arg>
    
    <transition to="wait" />
  </java>

  <state name="wait" />

</process>
      

Classes involved:

public class JohnDoe {
  
  String state;
  Session session;
  
  public String hello(String msg) {
    if ( (msg.indexOf("how are you?")!=-1)
         && (session.isOpen())
       ) {
      return "I'm "+state+", thank you.";
    }
    return null;
  }
}
public class JoeSmoe implements Serializable {

  static Map<String, Integer> handshakes = new HashMap<String, Integer>();
  {
    handshakes.put("force", 5);
    handshakes.put("duration", 12);
  }
  
  public Map<String, Integer> getHandshakes() {
    return handshakes;
  }
}
public class Hand implements Serializable {

  private boolean isShaken;

  public Hand shake(Integer force, Integer duration) {
    if (force>3 && duration>7) {
      isShaken = true;
    }
    
    return this;
  }

  public boolean isShaken() {
    return isShaken;
  }
}

The first java activity greet specifies that during its execution an instance of the class org.jbpm.examples.java.JohnDoe will be instantiated and the method hello of this class will be invoked on the resulting object. The variable named answer will contain the result of the invocation.

The class above reveals that it contains two fields named state and session and that the method hello accepts one argument. During the execution the values specified in the field and arg configuration elements will be used. The expected result of creating a process instance is that the process variable answer contains the string I'm fine, thank you..

The second java activity is named shake hand. It will resolve expression #{hand} and capture the resulting object as the target object. On that object, the method shake will be invoked. The two arguments will be calculated by resolving the respective expressions #{joesmoe.handshakes.force} and #{joesmoe.handshakes.duration}. The resulting object is a mofied version of the hand and var="hand" will cause the modified hand to overwrite the old hand variable value.

A script activity evaluates a script. Scripts can be specified in any language for which there is a JSR-223 compliant scripting engine. Configuration of scripting engines is explained below.

There are 2 ways of specifying a script:

The script is provided with the expr attribute. This is for short expressions that are easier expressed in an attribute then in a text element. If no lang is specified, the default-expression-language is used.


In the next example, we'll see how a script activity with an expression and how the result is stored in a variable.


<process name="ScriptExpression" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="invoke script" />
  </start>

  <script name="invoke script" 
          expr="Send packet to #{person.address}"
          var="text">

    <transition to="wait" />
  </script>
  
  <state name="wait"/>

</process>

This example uses a Person class that looks like this.

public class Person implements Serializable {

  String address;
  
  public Person(String address) {
    this.address = address;
  }
  
  public String getAddress() {
    return address;
  }
  
  public void setAddress(String address) {
    this.address = address;
  }
}

When starting a process instance for this process, we supply a person with a given address property as variable person.

Map<String, Object> variables = new HashMap<String, Object>();
variables.put("person", new Person("Honolulu"));
   
executionService.startProcessInstanceByKey("ScriptText", variables);

After the execution of the script activity, variable text will contain 'Send packet to Honolulu'.

Execution of this process is exactly the same as with the script expression above.

The sql activity is exactly the same as the hql activity, with the only difference that session.createSQLQuery(...) is used.

Events specify points in a process on which a list of event listeners can be registered. When an execution passes that point in the process, the event listeners are notified. The events and listeners are not shown in the graphical view of the process. An event is fired by an element in the process definition like e.g. the process definition, an activity or a transition.

The EventListener interface looks like this:

public interface EventListener extends Serializable {
  
  void notify(EventListenerExecution execution) throws Exception;

}

All automatic activities can be used as event listeners as well.

To associate a list of event listeners with a process or an activity, use the on element to group the event listeners and specifiy the event. on can be nested as a subelement of process or any activity.

To associate a list of event listeners with a transition take event, just include the event listeners directly in the transition element.



Let's look at an example process with event listeners:


<process name="EventListener" xmlns="http://jbpm.org/4.0/jpdl">

  <on event="start">
    <event-listener class="org.jbpm.examples.eventlistener.LogListener"/>
  </on>

  <on event="end">
    <event-listener class="org.jbpm.examples.eventlistener.LogListener"/>
  </on>


  <start >
    <transition to="wait"/>
  </start>

  <state name="wait" >
    <on event="start">
      <event-listener class="org.jbpm.examples.eventlistener.LogListener"/>
    </on>
    <on event="end">
      <event-listener class="org.jbpm.examples.eventlistener.LogListener"/>
    </on>
    <transition to="park">
      <event-listener class="org.jbpm.examples.eventlistener.LogListener"/>
    </transition>
  </state>
  
  <state name="park" />

</process>

LogListener will maintain a list of logs in a static member field:

public class LogListener implements EventListener {

  public void notify(EventListenerExecution execution) {
    List<String> logs = (List<String>) execution.getVariable("logs");
    if (logs==null) {
      logs = new ArrayList<String>();
      execution.setVariable("logs", logs);
    }
    
    logs.add(execution.getEvent()+" on "+execution.getEventSource());

    execution.setVariable("logs", logs);
  }
}

Next, we start a new process instance.

ProcessInstance processInstance = executionService.startProcessInstanceByKey("EventListener");

Then the process instance executes up to the wait activity. So we provide a signal and that will cause it to execute till the end.

Execution execution = processInstance.findActiveExecutionIn("wait");
executionService.signalExecutionById(execution.getId());

The list of log messages will now look like this:

[event(start) on process(EventListener),
 event(start) on activity(wait),
 event(end) on activity(wait),
 event(take) on (wait)-->(park)]

Each invocation of ExecutionService.startProcessInstanceById(...) or ExecutionService.signalProcessInstanceById(...) will cause the process to be executed in the thread of the client. In other words, those methods will only return after the process execution has arrived in a wait state.

This default behaviour has a couple of advantages: user application transactions can be easily propagated to jBPM to that jBPM's DB updates are done in the user's transaction context. Secondly, it's possible for a client to get an exception in case something goes wrong during execution of the process. Usually, the automatic work that has to be done as part of the process inbetween two wait states is relatively small. E.g. < 1 second, even if multiple automatic activities are executed inbetween 2 wait states. So in most situations, it's good to do all that work in a single transaction. This explains that the default behaviour of jPDL is to perform all work of the process synchronously in the thread of client.

For those cases where it is needed, jPDL allows for very fine grained control over transaction boundaries. On various places in the process, asynchronous continuations can be introduced. Asynchronous continuations are placed on places where logically the jBPM engine remains in control for executing a series of automatic steps.

Upon an asynchronous continuation, an asynchronous message will be sent as part of the currently ongoing transaction. And then the originally invoked method like e.g. startProcessInstanceById(...) or signalProcessInstanceById(...) will return. When the asynchronous message is committed and then processed, it will start a new transaction and resume execution where it left off.


  • sync (default) keep executing the element as part of the ongoing transaction.
  • async introduces an asynchronous continuation (aka safe point). The ongoing transaction is committed and the element is executed in a new transaction. Transactional asynchronous messaging is used by the jBPM implementation to achieve this.
  • exclusive introduces a asynchronous continuation (aka safe point). The ongoing transaction is committed and the element is executed in a new transaction. Transactional asynchronous messaging is used by the jBPM implementation to achieve this. Exclusive messages will not be processed concurrently. The JobExecutor(s) will serialize all exclusive job executions. This can be used to prevent optimistic locking failures in case multiple, potentially conflicting jobs are scheduled in the same transaction.

Let's look at a couple of examples.


<process name="AsyncActivity" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="generate pdf"/>
  </start>

  <java name="generate pdf" 
        continue="async" 
        class="org.jbpm.examples.async.activity.Application"
        method="generatePdf" >
    <transition to="calculate primes"/>
  </java>

  <java name="calculate primes" 
        continue="async" 
        class="org.jbpm.examples.async.activity.Application"
        method="calculatePrimes">
    <transition to="end"/>
  </java>

  <end name="end"/>

</process>
public class Application {

  public void generatePdf() {
    // assume long automatic calculations here
  }

  public void calculatePrimes() {
    // assume long automatic calculations here
  }
}
ProcessInstance processInstance = 
     executionService.startProcessInstanceByKey("AsyncActivity");
String processInstanceId = processInstance.getId();

Without the asynchronous continuations, this would be an all automatic process and the process would execute all the way up to the end in method startProcessInstanceByKey

But with continue="async" the execution only goes untill it is about to execute activity generate pdf. Then an asynchronous continuation message is send and the startProcessInstanceByKey method returns.

In a normal configuration, the job executor will automatically pick up the message and execute it. But for testing scenarios and for these examples we want to control when messages are executed so the job executor is not configured. Therefor we have to execute the jobs manually like this:

Job job = managementService.createJobQuery()
  .processInstanceId(processInstanceId)
  .uniqueResult();
managementService.executeJob(job.getDbid());

That will bring the process until it's about to execute activity calculate primes and again an asynchronous message is send.

Then the message can be looked up again and when that message is executed, that transaction will run the execution till the end.


<process name="AsyncFork" xmlns="http://jbpm.org/4.0/jpdl">

  <start >
    <transition to="fork"/>
  </start>

  <fork >
    <on event="end" continue="exclusive" />
    <transition />
    <transition />
  </fork>

  <java class="org.jbpm.examples.async.fork.Application" >
    <transition />
  </java>

  <java class="org.jbpm.examples.async.fork.Application" >
    <transition />
  </java>

  <join >
    <transition to="end"/>
  </join>
   
  <end />

</process>
public class Application {

  public void shipGoods() {
    // assume automatic calculations here
  }

  public void sendBill() {
    // assume automatic calculations here
  }
}

By placing the asynchronous continuation on the end event of the fork (<on event="end" continue="exclusive" />), each forked execution that takes a transition out of the fork will be continued asynchronously.

Value exclusive was selected to serialize the executions of the 2 asynchonous continuation jobs resulting from the fork. The respective transactions that will execute activities ship goods and send bill will both arrive at the join. At the join, both transactions will synchronize on the same execution (read: update the same execution row in the DB), resulting in a potential optimistic locking failure.

ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncFork");
String processInstanceId = processInstance.getId();

List<Job> jobs = managementService.createJobQuery()
  .processInstanceId(processInstanceId)
  .list();

assertEquals(2, jobs.size());

Job job = jobs.get(0);

// here we simulate execution of the job, 
// which is normally done by the job executor
managementService.executeJob(job.getDbid());

job = jobs.get(1);

// here we simulate execution of the job, 
// which is normally done by the job executor
managementService.executeJob(job.getDbid());

Date endTime = historyService
  .createHistoryProcessInstanceQuery()
  .processInstanceId(processInstance.getId())
  .uniqueResult()
  .getEndTime();

assertNotNull(endTime);

A timer can be specified in the transition element in wait state activities such as states, tasks, sub-processes and groups. When such a timer fires, that transition is taken.

A timer can also be specified in custom events in wait state activities such as states, tasks, sub-processes and groups. The timer element should then be the first element in the on element representing the event. In that case the event fires upon the duedate of the timer.

Timers are created when the activity is entered. The timer can fire when the execution remains in the activity until the duedate. When the execution leaves the activity, the timer is cancelled.


Example TimerTransitionTest shows how to put a timer on a transition.


<process name="TimerTransition" xmlns="http://jbpm.org/4.0/jpdl">

  <start>
    <transition to="guardedWait" />
  </start>

  <state name="guardedWait">
    <transition name="go on" to="next step" />
    <transition name="timeout" to="escalation">
      <timer duedate="10 minutes" />
    </transition>
  </state>
  
  <state name="next step" />
  <state name="escalation" />

</process>

When an process instance for this process is started, it arrives immediately in the guardedWait state. At that time, a timer is created that will fire after 10 minutes.

Execution processInstance = executionService
      .startProcessInstanceByKey("TimerTransition");

With the following query, we can query for the timers related to the newly created processInstance. We know that there should be exactly one such timer.

Job job = managementService.createJobQuery()
      .timers()
      .processInstanceId(processInstance.getId())
      .uniqueResult();
      

In a unit test, we won't use the JobExecutor to execute the timer. Instead, we execute timers directly in the thread of the unit test. That way it is easy to simulate one scenario though an execution.

So as the next step, we assume that the timer will fire. We simulate this by executing the timer programmatically:

managementService.executeJob(job.getDbid());

After that the process instance will have taken the timeout transition and moved to the escalation state.

processInstance = executionService.findExecutionById(processInstance.getId());
assertEquals("escalation", processInstance.getActivityName());

The second scenario in TimerTransitionTest shows that the timer is cancelled in case the signal go on is given before the timer fires. In that case the execution ends up in the next step.

TODO

Scripting in jBPM is based on JSR 223: Scripting for the JavaTM Platform. Scripting engines can be configured like this:

<script-manager default-expression-language="juel"
                    default-script-language="juel"
                    read-contexts="execution, environment, process-engine"
                    write-context="">
  <script-language name="juel" factory="com.sun.script.juel.JuelScriptEngineFactory" />
</script-manager>

A jPDL process definition can contain scripts and expressions. All of the configured scripting engines can be used in each situation. But scripts and expressions each have their own default.

The default jBPM identity component is based on JBoss IDM. Configuration is like this:

<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">

  <process-engine-context>
    ...
    <identity-service />
    ...
  </process-engine-context>

  <transaction-context>
    ...
    <identity-session realm="realm://jbpm-identity" />
  </transaction-context>

</jbpm-configuration>

To replace the identity component, keep the identity-service declaration, implement org.jbpm.session.IdentitySession and configure your identity session in the transaction context like this:

<jbpm-configuration xmlns="http://jbpm.org/xsd/cfg">
  ...
  <transaction-context>
    ...
    <object class="your.package.YourIdentitySession" />
  </transaction-context>

</jbpm-configuration>

jBPM provides integration with JBoss 4.2.x and JBoss 5.0.0.GA. As part of the installation, the ProcessEngine and a deployer for jBPM archives will be installed as a JBoss service.

After a successful installation you should see that the ProcessEngine has been started and bound to JNDI:

    [...]
    14:12:09,301 INFO  [JBPMService] jBPM 4 - Integration JBoss 4
    14:12:09,301 INFO  [JBPMService] 4.0.0.Beta1
    14:12:09,301 INFO  [JBPMService] ProcessEngine bound to: java:/ProcessEngine
  

As described above the ProcessEngine will be installed as JBoss service and bound to JNDI. This means that any EE component (i.e. servlet, ejb) can access it doing a JNDI lookup:

    private ProcessEngine processEngine;
    [...]

    try
    {
      InitialContext ctx = new InitialContext();
      this.processEngine = (ProcessEngine)ctx.lookup("java:/ProcessEngine");
    }
    catch (Exception e)
    {
      throw new RuntimeException("Failed to lookup process engine");
    }
    

Once you obtained an instance of the ProcessEngine you can invoke on it as described in chapter services

    UserTransaction tx = (UserTransaction)ctx.lookup("UserTransaction");        (1)
    Environment env = ((EnvironmentFactory)processEngine).openEnvironment();

    try
    {

      ExecutionService execService = (ExecutionService)
              this.processEngine.get(ExecutionService.class);

      // begin transaction
      tx.begin();

      // invoke on process engine
      executionService.signalExecutionById("ICL.82436");

      // commit transaction
      tx.commit();
      
    }
    catch (Exception e)
    {
      if(tx!=null)
      {
        try
        {
          tx.rollback();
        }
        catch (SystemException e1) {}
      }

      throw new RuntimeException("...", e);

    }
    finally
    {
      env.close();
    }
    

(1) Wrapping the call in a UserTransaction is not necessary if the invocation comes a CMT component, i.e. an EJB.

jBPM 4 takes advantage of the JavaMail API to make high-level email services available to business process authors.

Producers are responsible for creating email messages within jBPM. All mail producers implement the org.jbpm.pvm.internal.email.spi.MailProducer interface. A default mail producer is available out of the box to address typical email needs.

The default mail producer is capable of creating email messages with text, HTML and attachments from a template. Templates can be provided inline or in the process-engine-context section of the jBPM configuration. Templates may contain expressions which are evaluated through the script manager. Refer to Scripting for details.

The following listing presents a mail activity with an inline template.

<mail name="rectify" language="juel">                             (1)
  <from addresses='winston@minitrue' />                           (2)
  <to addresses='julia@minitrue, obrien@miniluv'/>                (3)
  <cc users='bigbrother'/>
  <bcc groups='thinkpol, innerparty'/>
  <subject>Part ${part} Chapter ${chapter}</subject>              (4)
  <text>times ${date} reporting bb dayorder doubleplusungood      (5)
    refs ${unpersons} rewrite fullwise upsub antefiling</text>
  <html><table><tr><td>times</td><td>${date}</td>                 (6)
    <td>reporting bb dayorder doubleplusungood 
    refs ${unpersons} rewrite fullwise upsub antefiling</td>
    </tr></table></html>
  <attachments>                                                   (7)
    <attachment url='http://www.george-orwell.org/1984/3.html'/>
    <attachment resource='org/example/pic.jpg'/>
    <attachment file='${user.home}/.face'/>
  </attachments>
</mail>
  1. Expressions within the template are written in the scripting language indicated here. If not specified, the default expression language will be assumed.

  2. List of message senders. Senders are either identified directly by their email addresses or appointed by means of the identity model.

  3. Lists of message recipients, categorized as follows: To (primary), CC (carbon copy) and BCC (blind carbon copy). Like senders, recipients are directly identified by their email addresses or appointed by means of the identity model.

  4. Character data contained in element subject are used as the message subject.

  5. Character data contained in element text are used as the plain text content of the message.

  6. Nodes contained in element html are used as the HTML content of the message.

  7. Attachments can be specified as absolute URLs, classpath resources or local files.

Note that every section of the template is amenable to expression evaluation.

For complex emails or custom generation of attachments, see: Extension Points: Custom Emails.

Mail templates are available to externalize commonly used messages from process definitions. Templates are placed in the process-engine-context section of your configuration file. All elements available to inline templates, as described in the previous section are available to external templates. Consider the fragment below.

<jbpm-configuration>
<process-engine-context>
  <mail-template name="rectify-template">
    <!-- same elements as inline template -->
  </mail-template>
</process-engine-context>
</jbpm-configuration>

Each template must have an unique name. Mail activities may reference the template through the template attribute, as follows.

<mail name="rectify" template="rectify-template />

Mail servers are declared in the configuration file. The mail-server element describes an SMTP mail server capable of sending email messages. Because jBPM uses JavaMail to send mail, all properties supported by JavaMail are also exposed to jBPM. Within the session-properties child element, the SMTP properties must be provided as shown in the example below.

See the Sun JavaMail API for more information on supported properties: Sun SMTP Properties.

<jbpm-configuration>
<transaction-context>
  <mail-session>
    <mail-server>
      <session-properties>
        <property name="mail.smtp.host" value="localhost" />
        <property name="mail.smtp.port" value="2525" />
        <property name="mail.from" value="noreply@jbpm.org" />
      </session-properties>
    </mail-server>
  </mail-session>
</transaction-context>
</jbpm-configuration>

If the "From" attribute is not present in an outgoing message, the value of the mail.from property will be used instead.

Multiple SMTP server support has been added to jBPM 4 to accommodate a wider variety of organizational server structures. For example, this is useful for companies that have both internal and external SMTP servers.

To setup multiple SMTP mail servers, declare multiple mail servers within the configuration file, as described below. The tag address-filter exists to define which domains are serviced by each mail server. The address filter consists of regular expressions that determine whether an address will be processed by a given server.

See the Sun Pattern API for more information on supported regular expressions: Sun Regex Patterns.

<jbpm-configuration>
<transaction-context>
  <mail-session>
    <mail-server>
      <address-filter>
        <include>.+@jbpm.org</include>
      </address-filter>
      <session-properties>
        <property name="mail.smtp.host" value="int.smtp.jbpm.org" />
        <property name="mail.from" value="noreply@jbpm.org" />
      </session-properties>
    </mail-server>
    <mail-server>
      <address-filter>
        <exclude>.+@jbpm.org</exclude>
      </address-filter>
      <session-properties>
        <property name="mail.smtp.host" value="ext.smtp.jbpm.org" />
        <property name="mail.from" value="noreply@jbpm.org" />
      </session-properties>
    </mail-server>
  </mail-session>
</transaction-context>
</jbpm-configuration>

Address filters follow the logic below to accept an address.

  • Address is accepted if it is included and not excluded.

  • Absence of includes implies the address is included.

  • Absence of excludes implies the address is not excluded.