Multi-instance Activities
Overview
A multi-instance activity is a way of defining repetition for a certain step in a business process.
In programming concepts, a multi-instance is equivalent to the for each
construct: it allows you to execute a certain step,
or even a complete subprocess, for each item in a given collection, sequentially or in parallel.
A multi-instance is a regular activity that has extra properties defined (named 'multi-instance characteristics') that will cause the activity to be executed multiple times at runtime. The following activities can become a multi-instance activity:
-
User Task
-
Script Task
-
Service Task
-
Embedded Subprocess
-
Call Activity
Multi-instance activities are visually represented as regular activities provided with parallel or sequential marker in the center-bottom of the task or subprocess element. Three vertical lines indicate that the instances will be executed in parallel, while three horizontal lines indicate sequential execution.
To make an activity multi-instance, the activity XML element must have a multiInstanceLoopCharacteristics
child element.
<multiInstanceLoopCharacteristics isSequential="false|true">
...
</multiInstanceLoopCharacteristics>
The isSequential attribute indicates if the instances of that activity are executed sequentially or in parallel.
By default, this attribute is omitted and its value is considered false
.
Special Variables
As required by the BPMN 2.0 specification, each parent execution of the created executions for each instance will have the following variables:
nrOfInstances |
The total number of instances. |
nrOfActiveInstances |
The number of currently active (not yet finished) instances. For a sequential multi-instance, this will always be 1. |
nrOfCompletedInstances |
The number of already completed instances. |
These values can be retrieved by calling the execution.getVariable(x) method.
Additionally, each of the created executions will have an execution-local variable (not visible for the other executions, and not stored on process instance level):
loopCounter |
It indicates the index in the for-each loop of that particular instance. |
Properties
To make an activity multi-instance, select it on the canvas and go to the BPMN Navigator panel. Choose a desired instance type:
Number of Instances
The number of instances can be defined by two ways:
-
Directly — by setting a Cardinality parameter.
-
By collection — it’d be equal a collection size.
When setting cardinality, you can use expressions that resolve to a positive number, like this:
${nrOfOrders-nrOfCancellations}
- Set collection source and collection
-
— there are various sources available — expression, process variable, and user provider. In this case, process engine creates as many activities as collection size is.
Also, you can use expression to pass a collection in multi-instance activity. The expression may contain a call of a bean method returning a list of objects:
${smpl_OrderService.getOrders()}
The simplest way is to use process variable as a collection source. It can be a list of entities, strings, numbers or any objects you need.
For user tasks, it must be a list of usernames ( Besides, the only Assignee source is available in this case — Expression.
If the collection named ${usernames_item} And if the collection named ${users_items.username} You can edit the expression manually but do this with care. |
In the third variant, you can implement a UserListProvider and use a list of usernames in multi-instance activity.
See User List Provider in the User Task section.
When the collection is set, the system automatically creates an Element variable.
It plays the role of the iteration variable in the for each
construct.
You can rename the element variable as you wish.
Completion Conditions
As a common, multi-instance activity ends when all instances are completed. However, it is possible to specify an expression evaluated every time an instance ends.
When this expression evaluates to true, all remaining instances are destroyed and the multi-instance activity ends, continuing the process. Such an expression must be defined in the completionCondition child element.
For example,
${nrOfCompletedInstances > nrOfActiveInstances}
It means when the number of completed tasks becomes greater than the number of active instances, all remaining tasks would be dropped.
Also, it is possible to call Spring bean methods in condition expression:
${smpl_ErrorService.failure()}
Using Local Variables
Suppose, we want to build a process in which multiple performers will write a value of the variable. For example, several teachers set score to the student’s work. Or it can be a service task, writing some value.
In this case, you should use local variables in the multi-instance activity. Otherwise, each instance will override the value of the outer scope variable.
Outcomes-based Conditions
When a multi-instance is a user task having outcomes, it is possible to arrange some kind of "voting".
In this case, the system stores the decision made by each user in the so-called outcomes container — a process variable of the type OutcomesContainer
.
After completion of all instances, you can see all results in this container.
Then, you can use completion conditions of those types on the sequence flows outgoing from exclusive or inclusive gateway:
-
Anyone completed with the outcome
-
Everyone completed with the outcome
-
No one completed with the outcome
For example, we have a process with multi-instance user task having two outcomes, Yes
and No
:
Then, we can use outcome-based like "Everyone said Yes":
Boundary Events
Since a multi-instance is a regular activity, it is possible to define a boundary event on its boundary. In the case of an interrupting boundary event, when the event is caught, all instances that are still active will be destroyed.
Compensation
It may happen that the multi-instance task requires compensation. Your first idea would be like this:
Yes, this can work, but not in all cases. It’s OK for sequential synchronous tasks but can cause errors in other cases.
Better wrap your task with compensation with embedded subprocess, this pattern works in any case.
Execution Listeners
There is an issue when using execution listeners in combination with multi-instance.
See the Execution Listeners section.