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>
<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
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>
<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" />
<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.
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);
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");
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");
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.
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>
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
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:
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
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() {
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>
<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
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);
}
}
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.
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);
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.
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
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.
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>
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);
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:
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.
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>
<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" />
</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:
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 {
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.
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"/>
</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
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.
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.
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
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
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.
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:
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.
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
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:
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
}
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
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
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.
Top Related