JBoss.orgCommunity Documentation

jBPM User Guide


1. Introduction
1.1. License and EULA
1.2. Downloads
1.3. Sources
1.4. JVM version
1.5. What is it
1.6. Contents of this userguide
1.7. Process definition, process instance and executions
2. Installation
2.1. Running the installer
2.2. Libraries
2.3. Tomcat xxx
2.4. JBoss xxx
2.5. Database
2.5.1. Creating the DB tables
2.6. Configuration files
2.7. Graphical Process Designer (GPD)
2.7.1. Download Eclipse
2.7.2. Add the update site gpd/jbpm-gpd-site.zip
2.7.3. Define the jBPM User Library
2.7.4. Adding jPDL 4 schema to the catalog
2.7.5. Importing the Examples
3. GPD
3.1. Creating a new process file
3.2. Editing the process source
4. Services
4.1. ProcessEngine
4.2. Deploying a process
4.3. Undeploying deployments
4.4. Deleting a deployment
4.5. Starting a new process instance
4.5.1. In latest
4.5.2. Specific process version
4.5.3. With a key
4.5.4. With variables
4.6. Signalling a waiting execution
4.7. TaskService
4.8. HistoryService
4.9. ManagementService
5. jPDL
5.1. process
5.2. Activities
5.2.1. start
5.2.2. state
5.2.3. exclusive
5.2.4. concurrency
5.2.5. end
5.2.6. java
5.2.7. task
5.2.8. script
5.2.9. esb
5.2.10. hql
5.2.11. sql
5.2.12. Common activity contents
5.3. User code
6. Variables
7. Scripting
8. Identity
9. JBoss Integration
9.1. Packaging process archives
9.2. Deploying processes archives to a JBoss instance
9.3. Process deployments and versioning
9.4. ProcessEngine and J2EE/JEE programming models

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 Eclipse, install it the GPD.

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.


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:


A process definition describes what must be done in terms of activities. Each activity in a process is either to be performed by the process system or by an external participant. When an activity is to be performed by an external participant, then the execution must wait until the external participant notifies the process system that the activity is completed. So an execution is either executing or waiting on an external participant. Typically, you'll see that the processes are mostly waiting for external participations. Especially humans tend to be slow :-) The time consumed by the process system between two wait states is typically very small.

A state is the basic activity that represents something has to be done by an external participant and the execution must wait until a signal (aka external trigger) is given.

When an execution is in a wait state, it can be given an external trigger with one of the signal methods. The recommended way to reference an execution is by using the process definition and execution key. In the next code snippet, ICL refers to the process definition key and 82436 refers to the execution key.

executionService.signalExecutionByKey("ICL", "82436");

Alternatively, the execution that must be signaled can be referenced by a unique execution id. In the next snippet, ICL.82436 refers to the executionId.

executionService.signalExecutionById("ICL.82436");

Optionally some data can be passed along with a signal: a signalName and parameters. How the signalName gets used depends on the execution's current activity. The parameters get stored as process variables.

Map<String,Object> parameters = new HashMap<String,Object>();
parameters.put("quality", "a+"); 
parameters.put("target", "profit"); 

executionService.signalExecutionById("ICL/82436", "Accept", parameters);

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

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

  <end name="Completed" />

  <end name="Error" />

</process>

(BPMN note: when we mention activities here, we are not only refering to BPMN activities, but also to BPMN events and BPMN gateways.)

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. An exclusive activity has multiple outgoing transitions and when an execution arrives in an exclusive activity, an automatic evaluation will decide which outgoing transition is taken.

An exclusive activity should be configured in one of the three following ways:

An exclusive handler is a java class that implements the ExclusiveHandler interface. The exclusive handler will be responsible for selecting the name of the outgoing transition.

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

The handler is specified as a sub element of the exclusive


Here's an example process of an exclusive using an ExclusiveHandler:


<process name="ExclusiveHandler">

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

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

  <state name="submit document" />

  <state name="try again" />

  <state name="give up" />

</process>

The ContentEvaluation class looks like this

public class ContentEvaluation implements ExclusiveHandler {

  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.

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/jpdl">

  <start>
    <transition to="invoke java method" />
  </start>

  <java name="invoke java method" 
        class="org.jbpm.examples.java.JohnDoe"
        method="hello"
        var="answer">
        
    <field name="state"><string value="fine"/></field>
    <field name="session"><env type="org.hibernate.Session"/></field>

    <arg><string value="Hi, how are you?"/></arg>
    
    <transition to="wait" />
  </java>
  
  <state name="wait">

</process>
      

The java task 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. Let's look at the class JohnDoe below.

package org.jbpm.examples.java;

import org.hibernate.Session;

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

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

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"));
Execution execution = executionService
    .startProcessInstanceByKey("TaskAssignee", variables);

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

List<Task> taskList = taskService.findAssignedTasks("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 takable 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 takable tasks for users johndoe and joesmoe

taskService.findTakableTasks("johndoe");
taskService.findTakableTasks("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 takable 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 takable 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 5.3, “User code”

Let's look at the task assignment example process.


<process name="TaskAssignmentHandler" xmlns="http://jbpm.org/4/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/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.

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

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.