Top Banner
Chapter 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.5.1. Event listener example 6.5.2. Event propagation 6.6. Asynchronous continuations 6.6.1. Async activity 6.6.2. Async fork 6.7. User code 6.7.1. User code configuration 6.7.2. User code classloading This chapter will explain the jPDL file format for describing process definitions. jPDL is the prominent process language of jBPM. The goal of jPDL is to be as concise and developer- friendly as possible, while offering every feature you'd expect from a BPM process language. The jPDL schema file contains more attributes and elements then this documentation. This part of the documentation explains the stable and supported part of jPDL. Experimental/not supported jPDL features can be found in the developers guide. An example jPDL process file looks like this: <?xml version="1.0" encoding="UTF-8"?> <process name="Purchase order" xmlns="http://jbpm.org/4.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>
46
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: JPDL

Chapter 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.5.1. Event listener example

6.5.2. Event propagation

6.6. Asynchronous continuations

6.6.1. Async activity

6.6.2. Async fork

6.7. User code

6.7.1. User code configuration

6.7.2. User code classloading

This chapter will explain the jPDL file format for describing process definitions. jPDL is the

prominent process language of jBPM. The goal of jPDL is to be as concise and developer-

friendly as possible, while offering every feature you'd expect from a BPM process language.

The jPDL schema file contains more attributes and elements then this documentation. This

part of the documentation explains the stable and supported part of jPDL. Experimental/not

supported jPDL features can be found in the developers guide.

An example jPDL process file looks like this:

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

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

Page 2: JPDL

<decision name="Check supplier data">

<transition name="nok" to="Error" />

<transition name="ok" to="Completed" />

</decision>

<end name="Completed" />

<end name="Error" />

</process>

6.1. process

The top level element representing one process definition.

Table 6.1. process attributes:

Attribute Type Default Required? Description

name any text required

name or label of the process

used to display to the process

name in user interactions.

key alpha numeric

characters and

underscores

if omitted, the key

will be generated

based on the name by

replacing all non-

alpha-numeric

characters with

underscores

optional

identification to distinct

different process definitions.

Multiple versions of a process

with the same key can be

deployed. The key:name

combination must remain

exactly the same for all

deployed versions.

version integer

one higher then

highest version

number starting with

1 if no other process

is deployed with the

same name/key.

optional version number of this process

Table 6.2. process elements:

Element Multiplicity Description

description 0..1 description text

activities 1..* a list of any activity type can be placed here. At least one start

activity must be present.

6.2. Control flow activities

6.2.1. start

Page 3: JPDL

Indicates where an execution for this process starts. Typically there is exactly one start

activity in a process. A process has to have at least one start activity. A start activity must

have exactly one outgoing transition and that transition is taken when a process execution

starts.

Known limitation: for now, a process can not have more then one start.

Table 6.3. start attributes:

Attribute Type Default Required? Description

name any

text optional

name of the activity. Since a start activity cannot have

incoming transitions, the name is optional.

Table 6.4. start elements:

Element Multiplicity Description

transition 1 the outgoing transition

6.2.2. state

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.

6.2.2.1. state sequence

Let's look at an example which shows states connected with transitions as a sequence

Figure 6.1. A sequence of states

<process name="StateSequence" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="a" />

</start>

<state name="a">

<transition to="b" />

</state>

<state name="b">

<transition to="c" />

</state>

Page 4: JPDL

<state name="c" />

</process>

After you start an execution like this:

ProcessInstance processInstance =

executionService.startProcessInstanceByKey("StateSequence");

the created process instance will be positioned in state a. Providing an external trigger can be

done with the signalExecution methods.

Execution executionInA = processInstance.findActiveExecutionIn("a");

assertNotNull(executionInA);

processInstance =

executionService.signalExecutionById(executionInA.getId());

Execution executionInB = processInstance.findActiveExecutionIn("b");

assertNotNull(executionInB);

processInstance =

executionService.signalExecutionById(executionInB.getId());

Execution executionInC = processInstance.findActiveExecutionIn("c");

assertNotNull(executionInC);

6.2.2.2. state choice

In this second example with states, we'll show how you can use a state can be used to feed in

an external choice of the path to take.

Figure 6.2. A choice between state

<process name="StateChoice" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="wait for response" />

</start>

<state name="wait for response">

<transition name="accept" to="submit document" />

<transition name="reject" to="try again" />

</state>

<state name="submit document" />

Page 5: JPDL

<state name="try again" />

</process>

Let's start a new process instance for this process definition:

ProcessInstance processInstance = executionService

.startProcessInstanceByKey("StateChoice");

Now, the execution has arrived in the wait for response. The execution will wait there

until an external trigger is given. In case a state has multiple outgoing transitions, the

signalName given in the external trigger will be matched against the name of the outgoing

transition to take. So when we provide signalName accept like this:

String executionId = processInstance

.findActiveExecutionIn("wait for response")

.getId();

processInstance = executionService.signalExecutionById(executionId,

"accept");

assertTrue(processInstance.isActive("submit document"));

Then the execution will continue over the outgoing transition named accept. Analogue, when

signalName reject is given in the signalExecutionXxx methods, the execution will continue

over the outgoing transition named reject.

6.2.3. decision

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:

6.2.3.1. Decision conditions

A decision with conditions on the transitions evaluates the condition in each transition. The

first transition for which the nested condition expression resolves to true or which does not

have a condition is taken.

Table 6.5. decision.transition.condition attributes:

Attribute Type Default Required? Description

expr expression required

script that will be

evaluated in the

specified expression

language.

lang expression

language

the default-expression-

language taken from the optional

the language in which

expr is to be evaluated.

Page 6: JPDL

Attribute Type Default Required? Description

script-manager

configuration

Example:

Figure 6.3. The decision conditions example process

<process name="DecisionConditions" >

<start>

<transition to="evaluate document" />

</start>

<decision name="evaluate document">

<transition to="submit document">

<condition expr="#{content=="good"}" />

</transition>

<transition to="try again">

<condition expr="#{content=="not so good"}" />

</transition>

<transition to="give up" />

</decision>

<state name="submit document" />

<state name="try again" />

<state name="give up" />

</process>

After starting a process instance with good content

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("content", "good");

ProcessInstance processInstance =

executionService.startProcessInstanceByKey("DecisionConditions",

variables);

Page 7: JPDL

The activity submit document will be active

assertTrue(processInstance.isActive("submit document"));

See the example unit test for more scenarios.

6.2.3.2. Decision expression

A decision expression evaluates to a String representing the name of an outgoing transition.

Table 6.6. decision attributes:

Attribute Type Default Required? Description

expr expression required

script that will be

evaluated in the

specified expression

language.

lang expression

language

the default-expression-

language taken from the script-manager configuration

optional the language in which

expr is to be evaluated.

Example:

Figure 6.4. The decision expression example process

<process name="DecisionExpression" xmlns="http://jbpm.org/4.4/jpdl">

<start >

<transition to="evaluate document"/>

</start>

<decision name="evaluate document" expr="#{content}" >

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

When you start an new process instance with good content like this

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("content", "good");

Page 8: JPDL

ProcessInstance processInstance =

executionService.startProcessInstanceByKey("DecisionExpression",

variables);

then the new execution will go to activity submit document.

See the example unit test for the other scenarios.

6.2.3.3. Decision handler

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 decide(OpenExecution execution);

}

The handler is specified as a sub element of the decision. The configuration attributes and

content of a decision handler element can be found in Section 6.7, “User code”.

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

Figure 6.5. The decision handler example process

<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 decide(OpenExecution execution) {

String content = (String) execution.getVariable("content");

Page 9: JPDL

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.

6.2.4. concurrency

Concurrent paths of executions can be modeled with the fork and join activities. The next

table describes the join attributes; fork has no specific attributes.

Table 6.7. join attributes:

Attribute Type Default Required? Description

multiplicity integer or

expression

nbr of

incoming

transitions

optional

The number of executions that

should arrive in this join before

the join activates and push an

execution out the single

outgoing transition of the join.

lockmode

{none, read,

upgrade,

upgrade_nowait,

write}

upgrade optional

the hibernate lock mode

applied on the parent execution

to prevent that 2 concurrent

transactions see each other as

not yet arrived at the join,

causing a process deadlock.

6.2.4.1. Parallel split with fork

The fork activity allows a single path of execution to be split into two or more branches

which can execute activities concurrently.

Page 10: JPDL

Figure 6.6. Parallel split example process

<process name="ConcurrencyGraphBased" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="fork"/>

</start>

<fork name="fork">

<transition to="send invoice" />

<transition to="load truck"/>

<transition to="print shipping documents" />

</fork>

<state name="send invoice" >

<transition to="final join" />

</state>

<state name="load truck" >

<transition to="shipping join" />

</state>

<state name="print shipping documents">

<transition to="shipping join" />

</state>

<join name="shipping join" >

<transition to="drive truck to destination" />

</join>

<state name="drive truck to destination" >

<transition to="final join" />

</state>

<join name="final join" >

<transition to="end"/>

</join>

<end name="end" />

</process>

Page 11: JPDL

6.2.5. end

Ends the execution.

6.2.5.1. end process instance

By default, an end activity will end the complete process instance. In case multiple concurrent

executions are still active within the same process instance, all of them will be ended.

Figure 6.7. The end event

<process name="EndProcessInstance" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="end" />

</start>

<end name="end" />

</process>

When a new process instance is created, it immediately ends.

6.2.5.2. end execution

Only the execution that arrives in the end activity will be ended and other concurrent

executions should be left active. To get this behaviour, set attribute ends="execution"

Table 6.8. end execution attributes:

Attribute Type Default Required? Description

ends {processinstance|execution} processinstance optional

specifies if the whole

process instance should

be ended or just the path

of execution that arrives

in the end activity.

6.2.5.3. end multiple

A process can have multiple end events. This can be handy to indicate different outcomes of a

process instance. For example

Page 12: JPDL

Figure 6.8. Multiple end events

<process name="EndMultiple" xmlns="http://;jbpm.org/4/jpdl">

<start>

<transition to="get return code" />

</start>

<state name="get return code">

<transition name="200" to="ok"/>

<transition name="400" to="bad request"/>

<transition name="500" to="internal server error"/>

</state>

<end name="ok"/>

<end name="bad request"/>

<end name="internal server error"/>

</process>

Now if we would start an execution and signal it to move out of the get return code wait

state with the following code, the execution would end with the bad request end event.

ProcessInstance processInstance =

executionService.startProcessInstanceByKey("EndMultiple");

String pid = processInstance.getId();

processInstance = executionService.signalExecutionById(pid, "400");

Likewise, using the value 200 or 500 would cause the execution to end with the ok or with the

internal server error end events respectively.

6.2.5.4. end state

An execution can also end with different states. It is another way to specify the outcome of a

process. It is indicated by the state attribute of the end event or by the end-cancel and end-

error shortcut notations.

Table 6.9. end execution attributes:

Page 13: JPDL

Attribute Type Default Required? Description

state String optional the state assigned to the execution.

Take for example the following process.

Figure 6.9. Different end states

<process name="EndState" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="get return code"/>

</start>

<state name="get return code">

<transition name="200" to="ok"/>

<transition name="400" to="bad request" />

<transition name="500" to="internal server error"/>

</state>

<end name="ok" state="completed"/>

<end-cancel name="bad request"/>

<end-error name="internal server error"/>

</process>

This time, if we would start an execution and signal it to move out of the get return code

wait state with the following code, the execution would end with the cancel state.

Similarly, using the value 200 or 500 would cause the execution to end with the completed or

with the error states respectively.

6.2.6. task

Creates a task for a person in the task component.

6.2.6.1. task assignee

Page 14: JPDL

A simple task that will be assigned to a specific user

Table 6.10. task attributes:

Attribute Type Default Required? Description

assignee expression optional userId referring to the person that is responsible

for completing this task.

Figure 6.10. The task assignee example process

<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() {

Page 15: JPDL

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.

6.2.6.2. task candidates

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.

Table 6.11. task attributes:

Attribute Type Default Required? Description

candidate-

groups expression optional

resolves to a comma separated list of

groupIds. All the people in the groups will be

candidates for this task.

candidate-

users expression optional resolves to a comma separated list of userIds.

All the users will be candidates for this task.

Figure 6.11. The task candidates example process

Here's an example process using task candidates:

<process name="TaskCandidates">

<start>

<transition to="review" />

</start>

Page 16: JPDL

<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.getPersonalTasks("johndoe");

taskService.getPersonalTasks("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.

6.2.6.3. task assignment handler

Page 17: JPDL

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.7, “User

code”

Let's look at the task assignment example process.

Figure 6.12. The task assignment handler example process

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

Page 18: JPDL

}

}

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.

6.2.6.4. task swimlanes

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.

Table 6.12. task attributes:

Attribute Type Default Required? Description

swimlane swimlane

(string) optional

refers to a swimlane that is declared in the

process

Swimlanes can be declared inside a process element:

Table 6.13. swimlane attributes:

Attribute Type Default Required? Description

name swimlane

(string) required

Name for this swimlane. This is the name

that will be referenced by task swimlane

attributes.

assignee expression optional userId referring to the person that is

responsible for completing this task.

candidate-

groups expression optional

resolves to a comma separated list of

groupIds. All the people in the groups will

be candidates for this the tasks in this

swimlane.

candidate-

users expression optional

resolves to a comma separated list of

userIds. All the users will be candidates for

the tasks in this swimlane.

Page 19: JPDL

Figure 6.13. The task swimlane example process

The task swimlane example has the following process file :

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

Page 20: JPDL

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.

6.2.6.5. task variables

Tasks can read and update process variables. Later tasks will have the option to declare task-

local process variables. Task variables are an important part of the task forms. Task forms

typically show data that comes from the task and the process instance. Then input from the

user is translated in setting task variables.

Getting task variables can be done like this:

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

Task task = taskList.get(0);

long taskDbid = task.getDbid();

Set<String> variableNames = taskService.getVariableNames(taskDbid);

Map<String, Object> variables = taskService.getVariables(taskDbid,

variableNames);

And setting task variables can be done like this:

variables = new HashMap<String, Object>();

variables.put("category", "small");

variables.put("lires", 923874893);

taskService.setVariables(taskDbid, variables);

6.2.6.6. e-mail support in tasks

It is possible to provide assignees with notifications when a task is added to their list, as well

as reminders at specific intervals. Every email message is produced from a template.

Templates may be specified inline or in the process-engine-context section of the

configuration file.

Table 6.14. task elements

Element Multiplicity Description

notification 0..1

Sends a notification message when a task is assigned. If no template

is referenced or supplied inline, mail support falls back on the

template named task-notification.

reminder 0..1

Sends a reminder message at specific intervals. If no template is

referenced or supplied inline, mail support falls back on the

template named task-reminder.

Page 21: JPDL

Table 6.15. notification attributes:

Attribute Type Default Required? Description

continue {sync | async |

exclusive} sync optional

Specifies if an asynchronous continuation

should be introduced right before sending

this notification email.

Table 6.16. reminder attributes:

Attribute Type Default Required? Description

duedate duration (plain string

or containing

expression)

required Delay before a reminder email should

be send.

repeat duration (plain string

or containing

expression)

optional Delay after a subsequent reminder

email should be send

continue {sync | async |

exclusive} sync optional

Specifies if an asynchronous

continuation should be introduced right

before sending this notification email.

Here is a basic example that accepts the default templates.

<task name="review"

assignee="#{order.owner}"

<notification/>

<reminder duedate="2 days" repeat="1 day"/>

</task>

6.2.7. sub-process

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.

Table 6.17. sub-process attributes:

Attribute Type Default Required? Description

sub-

process-

id

string or

expression

either this or

sub-process-key

is required

Identifies the sub process by the id. This

means that a specific version of a process

definition is referenced. Sub process id can

be specified as simple text or EL

expression.

sub-

process-

key

string or

expression

either this or

sub-process-key

is required

Identifies the sub process by the key. This

means that the latest version of the process

definition with the given key is referenced.

The latest version of the process is looked

Page 22: JPDL

Attribute Type Default Required? Description

up each time the activity executes. Sub

process key can be specified as simple text

or EL expression.

outcome expression

required when

transitions have outcome-

value's

specified

Expression that is evaluated when the sub

process instance ends. The value is then

used for outcome transition mapping. Add

outcome-value elements to the outgoing

transitions of this sub-process activity.

Table 6.18. sub-process elements:

Element Multiplicity Description

parameter-

in 0..* Declares a variable that is passed to the sub process instance

when it is created.

parameter-

out 0..* Declares a variable that will be set in the super process

execution when the sub process ends.

Table 6.19. parameter-in attributes:

Attribute Type Default Required? Description

subvar string required The name of the sub process variable in

which the value is set.

var string

exactly one of {'var',

'expr'} is required to

specify the value

The name of the variable in the super

process execution context.

expr string

exactly one of {'var',

'expr'} is required to

specify the value

An expression that will be resolved in the

super process execution context. The

resulting value will be set in the sub

process variable.

lang string juel optional The scripting language in which the

expression should be resolved.

Table 6.20. parameter-out attributes:

Attribute Type Default Required? Description

var string required

The name of the variable in the super

process execution context in which the

value will be set.

subvar string

exactly one of {'subvar',

'expr'} is required to

specify the value

The name of the sub process variable from

which the value will be taken.

Page 23: JPDL

Attribute Type Default Required? Description

expr string

exactly one of {'subvar',

'expr'} is required to

specify the value

An expression that will be resolved in the

sub process execution context. The

resulting value will be set in the super

process variable.

lang string juel optional The scripting language in which the

expression should be resolved.

Table 6.21. Extra transition elements in case of outcome variable mappings:

Element Multiplicity Description

outcome-

value 0..1 If the outcome matches the value, this transition is taken after the

sub-process ended. The value is specified with one child element.

6.2.7.1. sub-process variables

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.

Figure 6.14. The subprocess document example process

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

Page 24: JPDL

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

Figure 6.15. The subprocess review example process

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

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("result", "accept");

taskService.setVariables(task.getDbid(), variables);

Page 25: JPDL

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.

6.2.7.2. sub-process outcome value

In the SubProcessOutcomeValueTest example, the value of a sub process variable is used to

select the outgoing transition of the sub-process activity.

Figure 6.16. The subprocess document example process

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

Page 26: JPDL

Figure 6.17. The subprocess review example process for outcome value

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

Map<String, Object> variables = new HashMap<String, Object>();

variables.put("result", "ok");

taskService.setVariables(task.getId(), variables);

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.

6.2.7.3. sub-process outcome activity

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.

Page 27: JPDL

Figure 6.18. The subprocess document example process for outcome 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:

Figure 6.19. The subprocess review example process for outcome activity

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

<start>

<transition to="get approval"/>

</start>

Page 28: JPDL

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

6.2.8. custom

Invokes user code that implements custom behaviour of an activity.

A custom activity refers to user code. See Section 6.7, “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.4/jpdl">

<start >

<transition to="print dots" />

</start>

<custom name="print dots"

class="org.jbpm.examples.custom.PrintDots">

<transition to="end" />

</custom>

<end name="end" />

Page 29: JPDL

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

}

}

6.3. Automatic activities

6.3.1. java

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

this activity.

Table 6.22. java attributes:

Attribute Type Default Required? Description

class classname

either 'class' or

'expr' has to be

specified

The fully qualified classname. See

Section 6.7.2, “User code classloading” for

classloading information. The user code

object will be lazy initialized and cached

as part of the process definition.

expr expression

either 'expr' or

'class' has to be

specified

An expression that returns the target object

on which the method should be invoked.

method methodname required The name of the method to invoke

var variablename optional The name of the variable in which the

return value should be stored.

Table 6.23. java elements:

Page 30: JPDL

Element Multiplicity Description

field 0..* describes a configuration value to inject in a memberfield before the

method is invoked.

arg 0..* method parameters

Consider the following example.

Figure 6.20. A java task

<process name="Java" xmlns="http://jbpm.org/4.4/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 {

Page 31: JPDL

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.

Page 32: JPDL

6.3.2. script

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:

6.3.2.1. script expression

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.

Table 6.24. script expression attributes:

Attribute Type Default Required? Description

expr text required the expression text to

evaluate.

lang scripting language

name as defined in

Chapter 8, Scripting

the default expression

language as defined in

Chapter 8, Scripting

optional

the language in which

the expression is

specified.

var variablename optional

name of the variable in

which the return value

should be stored.

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

stored in a variable.

Figure 6.21. The script.expression example process

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

Page 33: JPDL

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

6.3.2.2. script text

The second way of specifying a script is with a text element. This is convenient when the

script text spans multiple lines.

Table 6.25. script text attributes:

Attribute Type Default Required? Description

lang scripting language

name as defined in

Chapter 8, Scripting

the default scripting

language as defined

in Chapter 8,

Scripting

optional the language in which

the script is specified.

var variablename optional

name of the variable in

which the return value

should be stored.

Table 6.26. script text elements:

Element Multiplicity Description

Page 34: JPDL

Element Multiplicity Description

text 1 contains the script text

For example

Figure 6.22. The script.text example process

<process name="ScriptText" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="invoke script" />

</start>

<script name="invoke script"

var="text">

<text>

Send packet to #{person.address}

</text>

<transition to="wait" />

</script>

<state name="wait"/>

</process>

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

6.3.3. hql

With the hql activity, a HQL query can be performed on the database and the result is stored

in a process variable.

Table 6.27. hql attributes:

Attribute Type Default Required? Description

var variablename required the name of the variable in which the result is

stored.

unique {true, false} false optional

a value of true means that the result from the

hibernate query should be obtained with

method uniqueResult(). The default is false

and in that case the list() method will be

used to get the result.

Page 35: JPDL

Table 6.28. hql elements:

Element Multiplicity Description

query 1 The HQL query.

parameter 0..* The query parameters

For example:

Figure 6.23. The hql example process

<process name="Hql" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="get process names" />

</start>

<hql name="get process names"

var="activities with o">

<query>

select activity.name

from org.jbpm.pvm.internal.model.ActivityImpl as activity

where activity.name like :activityName

</query>

<parameters>

<string name="activityName" value="%o%" />

</parameters>

<transition to="count activities" />

</hql>

<hql name="count activities"

var="activities"

unique="true">

<query>

select count(*)

from org.jbpm.pvm.internal.model.ActivityImpl

</query>

<transition to="wait" />

</hql>

<state name="wait"/>

</process>

6.3.4. sql

The sql activity is exactly the same as the hql activity, with the only difference that

session.createSQLQuery(...) is used.

Page 36: JPDL

6.3.5. mail

Through the mail activity, process authors are able to specify the content of an email message

to be sent to multiple recipients at once. Every email message is produced from a template.

Templates may be specified inline or in the process-engine-context section of the

configuration file.

Table 6.29. mail attributes

Attribute Type Default Required? Description

template string no Reference to a mail-template element in the

configuration file. If absent, the template must be

specified inline using the child elements.

Table 6.30. mail elements

Element Multiplicity Description

from 0..1 list of sender(s)

to 1 list of primary recipients

cc 0..1 list of carbon copy recipients

bcc 0..1 list of blind carbon copy recipients

subject 1 text content of this element becomes the message subject

text 0..1 text content of this element becomes the message text content

html 0..1 text content of this element becomes the message HTML content

attachments 0..1 each attachment is configured in a separate subelement

Table 6.31. attachment attributes

Attribute Type Default Required? Description

name string no, unless

expression is present

File name associated with this attachment.

If absent, data sources that encapsulate

files such as resource, file and url

provide a reasonable fallback value.

description string no Descriptive information associated with

this attachment.

expression string one of expression,

file, url or

resource must be

present

Expression that evaluates to a

representation of the attachment data in the

form of a Java object. Useful to extract

content from process variables.

file string Path to the attachment data in the file

system. The denoted file must exist.

url string Location of the attachment data in the

Page 37: JPDL

Attribute Type Default Required? Description

worldwide web. The pointed resource

must exist.

resource string

Name of the resource containing the

attachment data in the class path. The

denoted resource must exist.

mime-type string no, unless

expression is present

MIME type of the object returned by the

expression.

Example usage:

<process name="InlineMail" xmlns="http://jbpm.org/4.4/jpdl">

<start>

<transition to="send birthday reminder note" />

</start>

<mail name="send birthday reminder note">

<to addresses="[email protected]" />

<subject>Reminder: ${person} celebrates his birthday!</subject>

<text>Do not forget: ${date} is the birthday of ${person} </text>

<attachments>

<attachment resource="org/example/birthday_card.png"/>

<attachment name="picture.jpg" expression="${picture}" mime-

type="image/jpeg"/>

</attachments>

<transition to="end" />

</mail>

<state name="end"/>

</process>

6.4. Common activity contents

Unless specified otherwise above, all activities also include this content model:

Table 6.32. Common activity attributes:

Attribute Type Default Required? Description

name any text required name of the activity

Table 6.33. Common activity elements:

Element Multiplicity Description

transition 0..* the outgoing transitions

6.5. Events

Page 38: JPDL

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, which makes them very

interesting for implementing technical details. 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.

Table 6.34. on attributes:

Attribute Type Default Required? Description

event {start | end} required name name of the event

Table 6.35. on elements:

Element Multiplicity Description

event-listener 0..* An event listener implementation object.

any automatic activity 0..*

Table 6.36. event listener attributes:

event-listener is user code so it can be configured like described in Section 6.7, “User

code”.

Any automatic activities (including event-listener) that are placed on events can specify

following additional attributes:

Attribute Type Default Required? Description

propagation {enabled |

disabled | true |

false | on | off}

disabled optional indicates if the event listener should also

be invoked for propagating events.

Page 39: JPDL

Attribute Type Default Required? Description

continue {sync | async |

exclusive} sync optional

indicates if the execution should be

continued asynchronously right before

the event listener is executed. @see also

Section 6.6, “Asynchronous

continuations”

6.5.1. Event listener example

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

Figure 6.24. The event listener example process

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

<on event="start">

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

<field name="msg"><string value="start on process

definition"/></field>

</event-listener>

</on>

<start>

<transition to="wait"/>

</start>

<state name="wait">

<on event="start">

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

<field name="msg"><string value="start on activity wait"/></field>

</event-listener>

</on>

<on event="end">

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

<field name="msg"><string value="end on activity wait"/></field>

</event-listener>

</on>

<transition to="park">

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

<field name="msg"><string value="take transition"/></field>

</event-listener>

</transition>

</state>

<state name="park"/>

</process>

LogListener will maintain a list of logs as a process variable:

Page 40: JPDL

public class LogListener implements EventListener {

// value gets injected from process definition

String msg;

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

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:

[start on process definition,

start on activity wait,

end on activity wait,

take transition]

6.5.2. Event propagation

Events are propagated from activities and transitions to outer activities and eventually to the

process definition.

By default, event listeners are only invoked for events that are fired on the elements on which

the event listeners are subscribed. But by specifying propagation="enabled", the event

listener will also be invoked for all events that are fired on contained elements.

6.6. Asynchronous continuations

Each invocation of ExecutionService.startProcessInstanceById(...) or

ExecutionService.signalProcessInstanceById(...) will cause the process to be

executed in the thread it was called from (=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.

Page 41: JPDL

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. 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 you don't want the call to jBPM to be blocking until all the automatic

work is done, jPDL allows for very fine grained control over transaction boundaries. On

various places in the process, asynchronous continuations can be introduced. Asynchronous

continuations cause the transaction to commit and the jBPM method invocation will return.

jBPM will then start a new transaction in a new thread and continue the rest of the automatic

process work asynchronously. jBPM uses asynchronous messaging internally to accomplish

this.

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.

Table 6.37. Attribute of any activity, transition or on:

Attribute Type Default Required? Description

continue {sync | async |

exclusive} sync optional

indicates if an asynchronous continuation

should be performed before the element is

executed.

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

sure that exclusive jobs for the same process instance are not executed concurrently,

even if your jBPM configuration has multiple asynchronous message processors (like

the JobExecutor) running on different systems. 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.

6.6.1. Async activity

Page 42: JPDL

Figure 6.25. The async activity example process

<process name="AsyncActivity" xmlns="http://jbpm.org/4.4/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. Therefore we have to execute the jobs

manually like this:

Page 43: JPDL

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.

6.6.2. Async fork

Figure 6.26. The async fork example process

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

Page 44: JPDL

}

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

6.7. User code

Various elements in the jPDL process language refer to a an object on which an interface

method will be invoked. This section describes the common attributes and elements for the

instantiation and configuration of such user code objects.

custom event-listener

assignment-handler in task

Page 45: JPDL

handler in decision

condition in transition

6.7.1. User code configuration

Table 6.38. attributes:

Attribute Type Default Required? Description

class classname

one of

{class|expr} is

required

The fully qualified classname. Instantiation is

done only once and the user object is cached

as part of the process definition.

expr expression

one of

{class|expr} is

required

Expression for which the resulting value will

be taken as the target object. Expressions will

be evaluated for every usage. In other words,

the resulting value of the evaluation will not

be cached.

Table 6.39. user code configuration elements:

Element Multiplicity Description

field 0..* describes a configuration value to be injected directly in a

memberfield before this user class is used.

property 0..* describes a configuration value to injected through a setter method

before this user object is used.

Table 6.40. field and property attributes:

Attribute Type Default Required? Description

name string required the name of the field or property.

Table 6.41. field and property contained element:

field and property elements have exactly one child element that represents the value that

will be injected.

Element Multiplicity Description

string 0..1 a java.lang.String

int 0..1 a java.lang.Integer

long 0..1 a java.lang.Long

float 0..1 a java.lang.Float

double 0..1 a java.lang.Double

Page 46: JPDL

Element Multiplicity Description

true 0..1 Boolean.TRUE

false 0..1 Boolean.FALSE

object 0..1 a object that will be instantiated with reflection

Table 6.42. Attribute for basic type string, int, long, floatand double:

Attribute Type Default Required? Description

value text required text value that will be parsed to the respective type

6.7.2. User code classloading

Process definitions are cached. By default, all user code objects are cached as part of those

process definitions. For all objects that are referenced by a class name, will be instantiated

during parsing time. Which implies that the objects aren't allowed to store non-stateless data

(ie which can change). This is typically OK since those objects are in practice almost always

immutable. If you do need to use 'dynamic' data in your user code, you can always fall back to

process variables (or Environment.get(xxx) calls).

Objects that are referenced by an expression are calculated dynamically.

The devguide also explains an unsupported attribute to prevent that user objects are cached.