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

Graphical Notation

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.

multi instance
XML Representation

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:

multi instance types

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.

collection source user task
Set Collection from Expression

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()}
Set Collection from Process Variable

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 (String) or a list of users (objects of the User class or class implementing UserDetails interface).

Besides, the only Assignee source is available in this case — Expression. If the collection named usernames with items having the type of String, the expression would be like this:

${usernames_item}

And if the collection named users with items of the User class or UserDetails, the expression would be

${users_items.username}

You can edit the expression manually but do this with care.

Set collection from UserProvider

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.

local variables multi

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:

process example multi user task

Then, we can use outcome-based like "Everyone said Yes":

sequence flow with outcome based condition

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.

multi instance error

Compensation

It may happen that the multi-instance task requires compensation. Your first idea would be like this:

multi instance compensation bad

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.

multi instance compensation good

Execution Listeners

There is an issue when using execution listeners in combination with multi-instance.

See the Execution Listeners section.