BProc add-on is used for modeling and running business processes. Processes are described using BPMN 2.0 notation. The add-on uses Flowable as a business process engine and bpmn-js rendering toolkit for process modeling.
This manual mostly describes features added by the add-on, not features provided by underlying frameworks, so you must be familiar with the Flowable engine.
Also, see the video tutorial on the CUBA YouTube channel .
bproc_ModelerScreen
screen ID has been changed to
bproc_BpmnModelerScreen
. If you have security roles that grant permissions to this screen, you have to update the roles.
modeler
UI component has been renamed to
bpmnModeler
. If you have screens with the
modeler
component, you have to change the component name.
The BPMN Viewer UI component has been introduced. It can be used to display a read-only BPMN process diagram. The component has methods to highlight any model element. An example of its usage can be found in the Process Instance Editor screen. A new tab Diagram displays a process model with the highlighted active node.
bproc-process-admin - grants access to all BProc screens and entities, including modeler and administration screens.
bproc-process-actor - grants access to BProc screens and entities required for starting the process and working with user tasks.
Launch CUBA Studio . You can download it from the CUBA website. Please ensure that you have Studio version 13.2 or newer.
Go to CUBA → Marketplace in the main menu.
Use this way of installation in case you build your project from the command line or your subscription does not include Studio Premium Subscription.
Adding Premium Repository
Open
build.gradle
file and add one more repository:
If the main repository is
repo.cuba-platform.com
, add
https://repo.cuba-platform.com/content/groups/premium
buildscript {
// ...
repositories {
// ...
maven {
url 'https://repo.cuba-platform.com/content/groups/premium'
credentials {
username(rootProject.hasProperty('premiumRepoUser') ?
rootProject['premiumRepoUser'] : System.getenv('CUBA_PREMIUM_USER'))
password(rootProject.hasProperty('premiumRepoPass') ?
rootProject['premiumRepoPass'] : System.getenv('CUBA_PREMIUM_PASSWORD'))
url 'https://cuba-platform.bintray.com/premium'
credentials {
username(rootProject.hasProperty('bintrayPremiumRepoUser') ?
rootProject['bintrayPremiumRepoUser'] : System.getenv('CUBA_PREMIUM_USER'))
password(rootProject.hasProperty('premiumRepoPass') ?
rootProject['premiumRepoPass'] : System.getenv('CUBA_PREMIUM_PASSWORD'))
Your license key consists of two parts: the first part before the dash is a repository user name, the part after the dash is a password. For example, if your key is 111111222222-abcdefabcdef
, then the user name is 111111222222
and the password is abcdefabcdef
. In case of Bintray, the user name must be followed by @cuba-platform
.
You can provide the credentials in one of the following ways:
The recommended way is to create a ~/.gradle/gradle.properties
file in your user home directory and set properties in it:
premiumRepoUser=111111222222
bintrayPremiumRepoUser=111111222222@cuba-platform
premiumRepoPass=abcdefabcdef
Alternatively, you can specify the credentials in the CUBA_PREMIUM_USER and CUBA_PREMIUM_PASSWORD environment variables.
When you run Gradle tasks from the command line, you can also pass the properties as command-line arguments with the -P
prefix, for example:
gradlew assemble -PpremiumRepoUser=111111222222 -PpremiumRepoPass=abcdefabcdef
In the build.gradle
file specify the component artifacts in the dependencies section as follows:
com.haulmont.addon.bproc:bproc-global:<add-on version>
where <add-on version>
is compatible with the used version of the CUBA platform.
Specify the add-on identifier com.haulmont.addon.bproc
in the web.xml
files of the core
and web
modules in the appComponents
context parameter:
<context-param>
<param-name>appComponents</param-name>
<param-value>com.haulmont.cuba com.haulmont.addon.bproc</param-value>
</context-param>
For projects created on CUBA 7.2+ BProc provides design-time roles: bproc-process-admin (grants access to all BProc screens and entities, including modeler and administration screens) and bproc-process-actor (grants access to BProc screens and entities required for starting the process and working with user tasks). BProc → Modeler screen will be available to the admin user and to users with the bproc-process-admin role.
Click on the StartEvent
element on the canvas. Properties of the StartEvent
element will be displayed on the properties panel. The Form section is used to describe the form that will be displayed to users when they start the process. There are several values available under the Form type drop-down list:
Input dialog — form fields are defined in the process model. The form is generated in the runtime when the user starts a process or completes a user task.
CUBA screen — a reference to a standard framework screen (see the CUBA Screen Process Form section for details).
Custom — used if you need to render a form in a non-standard way (see the Custom Process Form section for details).
Let’s specify the Input dialog as a start process form. Select the Input dialog form type.
Create a new input dialog parameter. In the Input Dialog Parameter Editor specify the following properties:
Note that the Process variable lookup field allows you to enter the value in it. If you want to associate the dialog parameter with the process variable that hasn’t been used before, then type the variable name and press Enter. If you want to display an existing variable, select its name from the drop-down list (we’ll see it shortly).
Create three more input dialog parameters:
After that create an input dialog parameter manager
with the User
type (it must be required and editable). For the User
type, additional fields are displayed in the editor: they are used for specifying a UI component for user selection (lookup field or picker field) and an optional JPQL query for filling the lookup field or a screen for selecting users in case of picker fields. We’ll use the LookupField
component and will leave the JPQL query field blank — that means that the default query select e from sec$User e
will be used.
On the bottom of the panel there is a Business key lookup field. A business key is a domain-specific identifier of a process instance. Select the requestNumber
field.
When a process is started input dialog parameters will be written into corresponding process variables.
After you’ve specified the start form, create the Approval user task. If the start event node is selected on the canvas, you can click the Append task icon in the pop-up menu:
Type a task name: Approval
. You can do that both right on the element node or in the Name field of the properties panel.
On the properties panel specify a task id: approval
.
There are several ways to specify task assignee (see Single User Assignment). In this current case, we’ll use the Process variable
assignee source. After you select this source a Process variable
lookup field will be displayed. All form fields and process variables with the type User
will be displayed there. We have only one such field, it is manager
defined in the start event process form. Select it.
For the Approval
task we also must specify a process form. It will be an input dialog again. The dialog will display process variables from the start event form and one more new parameter.
Create a new input dialog parameter and in the Process variable drop-down list select the requestNumber
variable.
In the same way create input dialog parameters for expenseAmount
, expenseDate
and document
variables. They all must be non-editable.
Add a new dialog parameter with the comment
process variable name and the Multiline string
type. The field must be editable and non-required. This field will be used by the manager if the manager wants to make some notes when approving or rejecting the refund request.
At the Approval
step, the manager has two options: to Approve
and to Reject
the request. An easy way to support this is to use outcomes (for details see User Task Outcomes). For each outcome a button will be displayed on the form and the result will be stored in a special process variable.
Create a new outcome using the corresponding button of the Outcomes section. In the outcome editor specify an outcome id approve
, a caption Approve
and select an icon CHECK
in the icons list.
Then create a second outcome: the id is reject
, the caption is Reject
, the icon is BAN
.
After the Approval
task is completed, the process must decide which path to chose using the outcome value. Let’s create an exclusive gateway. Click the Approval task element and click the Append Gateway
action of the context menu.
For the Payment
task, we will implement the following behavior: the task should be displayed for all accountants and any of them will be able to take this task for himself. To achieve this we should not specify a particular assignee for the user task but should specify Candidate groups or Candidate users. We’ll use the Candidate groups option but first, we must create a user group for accountants. User groups are managed using the BProc → User Groups screen (read more about user groups here: User Groups). Open the user groups screen and create a new group called Accountants
. The group code is accountants
. Change group type to Users
and add several users to the group. Commit the screen and go back to the modeler.
Configure an input dialog for the Payment
task. Add existing requestNumber
, expenseAmount
, expenseDate
, document
and comment
fields. Make them all non-editable.
The last thing we need do is to specify who can start this process. Click on the free space on the canvas to display process properties. Similar to the Payment
task we’ll define Candidate groups in the Starter candidates section.
If we want the process to be started by any user we may create and use a special user group. Open the User groups
screen and create a new group called All users
. Set its Type to All users
, this will mean that this group will automatically include every user. In the modeler select the All users
group for process starter candidates.
To start the process open the BProc → Start Process screen. This screen displays process definitions available for starting by the current user. Double click on the Refund
process line or select it and click the Start process button.
A start form will appear. Fill the fields, select the manager and click the Start process button.
Some fields on this form are read-only as we configured in the modeler and there are two buttons for outcomes: Approve and Reject. Enter the comment and click the Approve button.
Log in on behalf of any user who is a member of the Accountants
user group. Open the BProc → My Tasks screen. You’ll see that the table is empty, but in the filter, there is an indicator that the user has a group task that can be claimed. Select the Group tasks type and click the Apply button.
All users of the Accountants
group will see the Payment
task among their group tasks until any of accountants claims it. Open the task form. The form is read-only - you cannot do anything with the task until you claim it. On the bottom of the form there are two buttons: Claim and resume and Claim and close.
Claim and resume button will remove the task from group task lists of other users and will leave the task form on the screen. The form will become editable and buttons for completing the task will be displayed.
Claim and close will remove the task from other users task lists, the process form will be closed. The task will appear in the Assigned tasks list for the current user. Click the Claim and close button.
Select the Payment tasks node from the Assigned tasks group. Open the task form and complete the task using the default Complete task button (when we don’t specify task outcomes in the model, this default button is displayed).
3.1. Input Dialog Process Form
Parameters of input dialogs are defined right in the process model. If you click on StartEvent
or UserTask
model element, then on the properties panel you will be able to configure a related process variable, datatype, parameters ordering, etc. When users start processes or complete tasks, input dialogs are automatically rendered based on the configuration you provided in the model.
The input dialog parameter editor looks as follows:
Depending on the selected type, additional components can be displayed. For example, for Entity
and Entity list
types you should select an entity class from the drop-down list, a UI component (LookupField or PickerField) and optionally a lookup screen or JPQL query (default values will be used if these fields are left blank).
User
and Users list
types work like Entity
and Entity list
types with the sec$User
meta class. An additional piece of functionality in the modeler related to these field types is that you will be able to select these parameters as assignee value of UserTask
elements.
3.2. CUBA Screen Process Form
In many cases you’ll probably need a process form with a complex layout and behavior. In this case, instead of input dialog forms (which are defined right in a process model) you can use an existing CUBA screen. To be used as a process form, the screen controller should be annotated with the @ProcessForm
annotation.
The @ProcessForm
annotation indicates that the screen must appear in the process forms lookup in the modeler.
3.2.1. Process Variables in CUBA Screen Form
A @ProcessVariable
annotation may be placed on injected UI components or on regular class fields.
This annotation has two effects:
The value of the process variable will be written to this field when the process form is opened. In case of the UI component, the value of the process variable will be set to the UI component.
If you configure the ProcessFormContext with the saveInjectedProcessVariables()
method, then the values of annotated fields will be saved as process variables when the process is started or the user task is completed.
The ProcessFormContext
contains information about a process definition to be started (when the form is used for starting the process) or a user task to be completed. The ProcessFormContext
also contains methods for starting the process and task completion. Without the ProcessFormContext
the process form is barely useful.
You will use different methods of the ProcessFormContext
when working with start process forms and task process forms.
An example of how to start the process:
processFormContext.processStarting() (1)
.withBusinessKey("order-123") (2)
.addProcessVariable("order", order) (3)
.addProcessVariable("approver", order.getManager())
.start(); (4)
processFormContext.taskCompletion() (1)
.withOutcome("reject") (2)
.saveInjectedProcessVariables() (3)
.complete(); (4)
Indicates that values of class fields annotated with the @ProcessVariables
should be collected and saved as process variables.
Completes the actual task.
3.2.3. Declare Task Outcomes for the Modeler
In the modeler, for the sequence flow element, we may define a condition by selecting a user task and its outcome from the drop-down list. To fill this list for a user task that uses a CUBA screen process form you may declare a list of possible outcomes in the form controller. Use the outcomes
attribute of the @ProcessForm
annotation for that:
TaskApprovalForm.java
@ProcessForm(
outcomes = {
@Outcome(id = "approve"),
@Outcome(id = "reject")
public class TaskApprovalForm extends Screen {
//...
3.2.4. CUBA Screen Process Form Parameters
CUBA screen process forms can accept external parameters defined in the modeler. For example, you need a form for the next process actor selection. The form must display a lookup field with users and save the result into some process variable. We want to use the form for selecting different actors at different process steps, so the form should have two parameters:
You can provide a direct param value (value source is Direct value
) or use one of the existing process variables as a parameter value (value source is Process variable
).
Inside the process form controller use the @ProcessFormParam
annotation on class fields to get parameter values:
@ProcessFormParam(name = "variableName")
private String variableNameParam;
@ProcessFormParam
private String lookupCaption;
Like the @ProcessVariable
annotation, the @ProcessFormParam
supports an optional name
attribute. If the attribute is not defined, then a field name is used as a parameter name.
Another way to get a full list of process form parameters is to get them from the ProcessFormContext
object:
List<FormParam> formParams = processFormContext.getFormData().getFormParams();
The source code of the actor selection form:
actor-selection-form.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.haulmont.bproc.ref.web.screens.forms.actorselection">
<collection id="usersDc" class="com.haulmont.cuba.security.entity.User" view="_minimal">
<loader id="usersDl">
<query><![CDATA[select u from sec$User u order by u.name]]></query>
</loader>
</collection>
</data>
<layout spacing="true">
<form width="400px">
<lookupField id="userLookup" optionsContainer="usersDc" width="100%"/>
</form>
<hbox spacing="true">
<button id="completeTaskBtn" icon="font-icon:CHECK" caption="msg://completeTask"/>
</hbox>
</layout>
</window>
ActorSelectionForm.java
@UiController("ref_ActorSelectionForm")
@UiDescriptor("actor-selection-form.xml")
@LoadDataBeforeShow
@ProcessForm(
params = { (1)
@Param(name = "variableName"),
@Param(name = "lookupCaption")
public class ActorSelectionForm extends Screen {
@Inject
private ProcessFormContext processFormContext;
@Inject
private LookupField<User> userLookup;
@ProcessFormParam (2)
private String variableName;
@ProcessFormParam (2)
private String lookupCaption;
@Subscribe
private void onBeforeShow(BeforeShowEvent event) {
userLookup.setCaption(lookupCaption); (3)
@Subscribe("completeTaskBtn")
private void onCompleteTaskBtnClick(Button.ClickEvent event) {
processFormContext.taskCompletion()
.addProcessVariable(variableName, userLookup.getValue()) (4)
.complete();
closeWithDefaultAction();
Indicates that the parameter value that was defined in the process model should be injected into this class field.
Updates the lookup field caption with the value defined in the process model.
Saves the value of the users lookup field to the process variable which name was defined in the form param in the modeler.
processFormContext.taskCompletion()
.addProcessVariable("variableName", "variableValue")
.complete();
When you model the process, it may be useful to know which variables are set by the form in order to reuse them later in the process model. A way to achieve this is to use the outputVariabes
attribute of the @ProcessForm
annotation:
@ProcessForm(
outputVariables = {
@OutputVariable(name = "nextProcessActor", type = User.class),
@OutputVariable(name = "comment", type = String.class)
Often there are cases when a process variable is set only when the task is completed using a particular outcome. To declare this, place the outputVariables
annotation attribute to the @Outcome
annotation.
@ProcessForm(
outcomes = {
@Outcome(
id = "approve",
outputVariables = {
@OutputVariable(name = "nextActor", type = User.class) (1)
@Outcome(
id = "reject",
outputVariables = {
@OutputVariable(name = "rejectionReason", type = String.class) (2)
outputVariables = {
@OutputVariable(name = "comment", type = String.class) (3)
The rejectionReason
variable may be set when the task is completed with the reject
outcome.
The comment
variable may be set in any case.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.company.qs.web.screens.form.startapproval">
<collection id="usersDc" class="com.haulmont.cuba.security.entity.User" view="_minimal">
<loader>
<query>select u from sec$User u order by u.name</query>
</loader>
</collection>
</data>
<layout expand="actionsPanel" spacing="true">
<textField id="orderNumber" caption="msg://orderNumber"/>
<lookupField id="managerLookup" optionsContainer="usersDc" caption="msg://manager"/>
</form>
<hbox id="actionsPanel" spacing="true">
<button id="startProcessBtn" caption="msg://startProcess" icon="font-icon:PLAY"/>
</hbox>
</layout>
</window>
Screen controller:
StartApprovalForm.java
@UiController("qs_StartApprovalForm")
@UiDescriptor("start-approval-form.xml")
@LoadDataBeforeShow
@ProcessForm (1)
public class StartApprovalForm extends Screen {
@Inject
@ProcessVariable (2)
private TextField<String> orderNumber;
@Inject
@ProcessVariable(name = "manager") (3)
private LookupField<User> managerLookup;
@Inject
private ProcessFormContext processFormContext; (4)
@Subscribe("startProcessBtn")
protected void onStartProcessBtnClick(Button.ClickEvent event) {
processFormContext.processStarting()
.withBusinessKey(orderNumber.getValue()) (5)
.saveInjectedProcessVariables() (6)
.start();
closeWithDefaultAction();
The @ProcessForm
annotation indicates that this screen is a process form and the screen will be available in the modeler.
We declare that the injected UI component (the orderNumber text field) is a process variable. Since we develop a start process form, the variable has no value yet, but the annotation will be used on process start (we’ll see it later).
The same as 2, but here the process variable name (manager) differs from the field name (managerLookup).
ProcessFormContext
is the object that we’ll use to start the process.
When we start the process, we can pass an optional process instance business key. We use the order number here.
The saveInjectedProcessVariables()
indicates that values of the fields annotated with the @ProcessVariables
should be saved as process variables on process start.
processFormContext.processStarting()
.withBusinessKey(orderNumber.getValue())
.addProcessVariable("orderNumber", orderNumber.getValue())
.addProcessVariable("manager", managerLookup.getValue())
.start();
There will be two buttons (Approve and Reject) that complete the user task with the corresponding outcome.
task-approval-form.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://caption"
messagesPack="com.company.qs.web.screens.form.taskapproval">
<layout expand="actionsPanel" spacing="true">
<textField id="orderNumber" caption="msg://orderNumber" editable="false"/>
<textField id="commentField" caption="msg://comment"/>
</form>
<hbox id="actionsPanel" spacing="true">
<button id="approveBtn" caption="msg://approve" icon="font-icon:CHECK"/>
<button id="rejectBtn" caption="msg://reject" icon="font-icon:BAN"/>
</hbox>
</layout>
</window>
TaskApprovalForm.java
@UiController("qs_TaskApprovalForm")
@UiDescriptor("task-approval-form.xml")
@ProcessForm(
outcomes = { (1)
@Outcome(id = "approve"),
@Outcome(id = "reject")
public class TaskApprovalForm extends Screen {
@Inject
@ProcessVariable (2)
private TextField<String> orderNumber;
@Inject
@ProcessVariable(name = "comment") (3)
private TextField<String> commentField;
@Inject
private ProcessFormContext processFormContext;
@Subscribe("approveBtn")
protected void onApproveBtnClick(Button.ClickEvent event) {
processFormContext.taskCompletion()
.withOutcome("approve")
.saveInjectedProcessVariables() (4)
.complete();
closeWithDefaultAction();
@Subscribe("rejectBtn")
protected void onRejectBtnClick(Button.ClickEvent event) {
processFormContext.taskCompletion()
.withOutcome("reject")
.addProcessVariable("comment", commentField.getValue()) (5)
.complete();
closeWithDefaultAction();
The form defines two possible outcomes that may be used in sequence flow node condition in the modeler. This information is used by the modeler only.
The orderNumber
variable has been already set on process start. Because of the @ProcessVariable
annotation, the value of the orderNumber
process variables will be set to the orderNumber
text field when the form is displayed.
The comment
variable is not set yet, but the @ProcessVariable
annotation will be taken into account when we complete the task in the button click listener.
Values of all field annotated with the @ProcessVariable
will be saved as process variables on task completion.
An alternative way to define process variables. Instead of using the saveInjectedProcessVariables()
method you may define process variables directly.
3.2.7.3. StandardEditor Process Form Example
This example demonstrates how to use a StandardEditor
as a process form. This may be useful in a case when you store some entity in a process variable and want to view or edit entity fields using the task process form.
Let’s suppose that we added a Start process button to the standard Order
entity editor. The Start process button starts the process programmatically and puts the edited Order
entity instance to the process variables.
@Subscribe("startProcessBtn")
protected void onStartProcessBtnClick(Button.ClickEvent event) {
Order order = getEditedEntity();
Map<String, Object> processVariables = new HashMap<>();
processVariables.put("order", order); (1)
bprocRuntimeService.startProcessInstanceByKey("order-approval",
order.getNumber(),
processVariables);
The XML descriptor of the process form that is defined for the next user task may look like this:
order-edit-task-form.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/screen/window.xsd"
caption="msg://editorCaption"
focusComponent="form"
messagesPack="com.company.qs.web.screens.form.orderedittaskform">
<instance id="orderDc"
class="com.company.qs.entity.Order"
view="order-edit">
<loader/>
</instance>
</data>
<dialogMode height="600"
width="800"/>
<layout expand="taskActions" spacing="true">
<form id="form" dataContainer="orderDc">
<column width="250px">
<textField id="numberField" property="number"/>
<dateField id="dateField" property="date"/>
<pickerField id="managerField" property="manager">
<actions>
<action id="lookup" type="picker_lookup"/>
<action id="clear" type="picker_clear"/>
</actions>
</pickerField>
</column>
</form>
<hbox id="taskActions" spacing="true">
<button id="completeTaskBtn" caption="msg://completeTask"/> (1)
</hbox>
</layout>
</window>
@UiController("qs_OrderEditTaskForm")
@UiDescriptor("order-edit-task-form.xml")
@EditedEntityContainer("orderDc")
@LoadDataBeforeShow
@ProcessForm (1)
public class OrderEditTaskForm extends StandardEditor<Order> {
@ProcessVariable
protected Order order; (2)
@Inject
protected ProcessFormContext processFormContext;
@Subscribe
protected void onInit(InitEvent event) {
setEntityToEdit(order); (3)
@Subscribe("completeTaskBtn")
protected void onCompleteTaskBtnClick(Button.ClickEvent event) {
commitChanges() (4)
.then(() -> {
processFormContext.taskCompletion()
.complete();
closeWithDefaultAction();
By the time the InitEvent
listener is fired, values of the @ProcessVariable
fields must be already set. We invoke the setEntityToEdit()
method of the StandardEditor
class - this method reloads the order
entity with the view required for the editor screen and sets the entity to the data container.
When the Complete task button is clicked, the editor is commited, and if the commit was successful (all required fields are filled, etc.), the task completion is performed.
@Component("samples_MyCustomProcessFormScreenCreator")
public class MyCustomProcessFormScreenCreator implements ProcessFormScreenCreator {
@Inject
private ScreenBuilders screenBuilders;
@Override
public Screen createStartProcessScreen(CreationContext creationContext) {
return screenBuilders.screen(creationContext.getFrameOwner())
.withScreenId(creationContext.getFormData().getScreenId())
.withOpenMode(OpenMode.DIALOG)
.build();
@Override
public Screen createUserTaskScreen(CreationContext creationContext) {
return screenBuilders.screen(creationContext.getFrameOwner())
.withScreenId(creationContext.getFormData().getScreenId())
.withOpenMode(OpenMode.DIALOG)
.build();
Then this ProcessFormScreenCreator
must be registered as a renderer creator of forms with type Custom
. This may be done on application startup using the registerScreenCreator
method of the ProcessFormScreenCreators
bean:
@Component("samples_CustomFormScreenCreatorRegister")
public class CustomFormScreenCreatorRegister {
@Inject
private ProcessFormScreenCreators processFormScreenCreators;
@Inject
private MyCustomProcessFormScreenCreator myCustomProcessFormScreenCreator;
@Inject
private Logger log;
@EventListener
public void appStarted(AppContextStartedEvent event) {
processFormScreenCreators.registerScreenCreator("custom", myCustomProcessFormScreenCreator);
log.info("MyCustomProcessFormScreenCreator registered");
Expression
You will need to write an expression that evaluates an id of the User who must be an assignee. If you know that a user entity is stored in a process variable called manager
then the expression will be ${manager.id}
. You also may invoke Spring bean method that returns a user id: ${demo_MyBean.evaluateManager(methodParam1, 'methodParam2')}
Process variable
If you select the Process variable
assignee source then the LookupField component will be displayed in the element properties panel. This lookup field will contain all process variables with the User
type (input dialog parameters with the User
type and process variables with the User
type defined in the Start event properties panel). A process may be started programmatically without using the start process form and process variables values may be passed using the API. In this case, if you need to assign a user task to the person from one of the process variables, you can either write an explicit expression for the assignee or add an info about the process variable to the Process variables section.
User provider
If the assignee is evaluated in runtime you may use a user provider. User providers are Spring beans that implement the UserProvider
interface. An interface has a User get(String executionId)
method. The method returns a User who should become a task assignee. Here is an example of a user provider that reads a process variable with the user login and finds a user with this login in the database.
@Component("ref_MyUserProvider")
public class MyUserProvider implements UserProvider {
@Inject
private DataManager dataManager;
@Inject
private BprocRuntimeService bprocRuntimeService;
@Override
public User get(String executionId) {
String userLogin = (String) bprocRuntimeService.getVariable(executionId, "userLogin");
return dataManager.load(User.class)
.query("select u from sec$User u where u.login = :login")
.parameter("login", userLogin)
.one();
4.2. Multi-instance Tasks
A user task may be assigned not to a single user, but to multiple users as well. For example, you may need several people to approve the contract. Approvers may do their work in parallel or one after another. See Multi-instance (for each) section of Flowable documentation for basic information.
Let’s look at the user tasks element in process XML:
<userTask id="someTask" name="Some Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
flowable:collection="${collectionVariable}" flowable:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>
In the modeler, you’ll have to fill the expression for the Collection field of the multiInstanceLoopCharacteristics element and the elementVariable field. After that use the elementVariable
value as an assignee
for the user task element.
BProc modeler helps you to fill all these attributes in case you have a list of users in the form field or in the process variable. A user task model element (as long as ServiceTask, Sub-process, and some other elements) has a Multi instance section in the properties panel. When you change the multi-instance type from None to Parallel or Sequential, new fields appear on the panel. Let’s suppose that in the start process form you defined a field lawyers
with the User list
type. This field value holds a collection of users. For the current user task you may select a Process variable collection source and then select a lawyers
field in the Process variable lookup.
4.3. Task Candidate Users and Groups
For the user task you may specify a list of potential task assignees (see the User assignment section of the Flowable documentation). A task with candidates appears in the Group tasks list for each candidate. After any of the candidates claims the task, the task moves to the Assigned tasks list of the user who claimed the task and disappears from the Group tasks list of other candidates.
Candidates are defined using Candidate groups or Candidate users fields on the user task properties panel.
In the Candidate groups edit dialog, the Group source should be defined. The possible group source values are:
User groups. A list of UserGroup
should be selected. See the User Groups section for the information about user groups.
User groups provider. Use this type if the groups should be evaluated programmatically. A Spring bean that implements the UserGroupListProvider
interface should be selected. The bean must implement the List<UserGroup> get(String executionId)
method that evaluates and returns a list of user groups. The bean must be located in the core module.
Expression. Explicitly provide an expression used by the process engine. The expression should return a list o user group ids. It will be written to the flowable:candidateGroups
attribute of the <userTask>
element.
Users provider. Use this type if the users list should be evaluated programmatically. A Spring bean that implements the UserListProvider
interface should be selected. The bean must implement the List<User> get(String executionId)
method that evaluates and returns a list of users. The bean must be located in the core module.
Expression. Explicitly provide an expression used by the process engine. The expression should return a list of user ids. It will be written to the flowable:candidateUsers
attribute of the <userTask>
element.
4.4. User Task Outcomes
When users complete user tasks they often have to make some decision (approve or reject, yes or no, etc.). After they do it, the process should go one or another way. You may use task outcomes for modeling such cases. For Input dialog process forms outcomes are defined in the Outcomes panel in the Form properties section, for CUBA screen forms the outcomes are defined in the form controller.
When the user clicks on one of the outcome buttons, the task is completed and the decision is written to a special process variable. The process variable name is built according to the following name pattern: <user task id>_result
. The variable value holds information about users who completed the task and outcomes they selected.
As an example of how to use outcomes let’s take the process:
The Application approval task has two outcomes: approve
and reject
. We need to specify conditions for sequence flows after the exclusive gateway element.
The first option is to write a condition expression. The second one is to select an outcome from the drop-down list.
Change the Condition source to the Task outcome and select the task and its outcome in drop-down lists:
If the user task is a multi-instance task then you’ll need to specify an additional parameter for a sequence flow condition:
This parameter is Condition type. It has several possible values:
Using pools and lanes element you can define assignees for the all user tasks on the lane. Every user task on the lane without an explicitly defined assignee will inherit the assignee value from the lane.
To display the properties panel for the lane select the lane on the canvas. In the example below, the manager process variable is assigned to the selected lane Manager:
Roles. For the group of Roles type a set of security roles should be defined. Users are considered to be a member of the user group if they have one of the selected roles.
All users. Group of this type will automatically contain all users.
7.1. Spring Bean Service Task
BProc add-on adds one more way to define a service task. It allows you to select a Spring bean, the bean method and provide parameter values for the selected method. Bean name and methods are selected from drop-down lists. After a method is selected a panel for entering method argument values is displayed.
Pay attention to the is var check box. It makes sense mostly for string parameters. If the check box is not selected then the argument value will be written to the resulting expression in apostrophes. If the check box is selected the no apostrophes will be added and a variable with a provided name will be passed to the method.
${ref_MyBean.someMethod('description')}
- this expression will use the string value description to the method.
${ref_MyBean.someMethod(description)}
- this expression will use the value of the variable named description to the method.
7.2. Java Delegate Service Task
If you select the JavaDelegate class
in the Type lookup then the JavaDelegate class drop down list field will appear. The list will contain a list of classes that implement the org.flowable.engine.delegate.JavaDelegate
interface. See Java Service Task Implementation section of the Flowable documentation.
If the selected JavaDelegate class contains fields of org.flowable.common.engine.api.delegate.Expression
type (see Field Injection in Flowable documentation) then field names will appear in the Fields
table.
Here is an example of a Java class that sends an email:
SendEmailJavaDelegate.java
public class SendEmailJavaDelegate implements JavaDelegate {
private Expression addressee; (1)
private Expression emailSubject;
private Expression emailBody;
private static Logger log = LoggerFactory.getLogger(SendEmailJavaDelegate.class);
@Override
public void execute(DelegateExecution execution) { (2)
User addresseeValue = (User) addressee.getValue(execution); (3)
if (addresseeValue == null || Strings.isNullOrEmpty(addresseeValue.getEmail())) {
log.error("Cannot evaluate addressee email");
return;
String emailSubjectValue = (String) emailSubject.getValue(execution);
String emailBodyValue = (String) emailBody.getValue(execution);
EmailService emailService = AppBeans.get(EmailService.class);
emailService.sendEmailAsync(new EmailInfo(addresseeValue.getEmail(),
emailSubjectValue,
emailBodyValue));
When you click the Deploy process button in the model editor, the process will be deployed to the Flowable engine and will be available for starting. If you don’t want it to be deployed and just want to save the process to get back to it later, then you can do it use the Save draft button. To open a draft use the Open draft button.
When you need to get notified of some process engine event (task assigned, activity completed etc.) there are several approaches.
First, you may define an execution listener or a task listener for the particular process node. See the Execution Listener section of Flowable documentation for details. In the execution listener editor, the drop-down list for selecting a class contains classes that implement the org.flowable.engine.delegate.ExecutionListener
interface. In case of task listener there are classes implementing org.flowable.engine.delegate.TaskListener
there. If the class contains injected fields then these fields will be automatically added to the Fields section of the listener editor.
Second, you can declare global event listeners provided by the Flowable framework (see the Event handlers in Flowable documentation). You’ll find the Event listeners section on the project properties panel.
BProc add-on additionally introduces a set of Spring application events (see the Events section in CUBA documentation). The following events are published:
Here is an example of event listener that sends an email notification each time a user task is assigned to a user:
TaskAssignedNotificationSender
@Component(TaskAssignedNotificationSender.NAME)
public class TaskAssignedNotificationSender {
public static final String NAME = "ref_TaskAssignedNotificationSender";
@Inject
protected EmailService emailService;
@EventListener (1)
protected void onTaskAssigned(UserTaskAssignedEvent event) { (2)
User user = event.getUser(); (3)
TaskData taskData = event.getTaskData(); (4)
String emailTitle = "New process task " + taskData.getName();
String emailBodyTemplatePath = "com/haulmont/bproc/ref/notification/task-email-body.template";
Map<String, Serializable> templateParameters = new HashMap<>();
templateParameters.put("user", user);
templateParameters.put("taskData", taskData);
EmailInfo emailInfo = new EmailInfo(
user.getEmail(),
emailTitle,
null,
emailBodyTemplatePath,
templateParameters
emailService.sendEmailAsync(emailInfo); (5)
The UserTaskAssignedEvent
contains an information about the user who is assigned to the task.
The UserTaskAssignedEvent
contains a TaskData
object which holds an information about the user task.
See the Email Sending Guide for details.
@EventListener
protected void onOtherProcessTaskAssigned(UserTaskAssignedEvent event) {
if ("some-process".equals(event.getProcessDefinitionData().getKey())) {
// ...
or you may define a SpEL (Spring Expression Language) expression that should match in order to handle the event:
@EventListener(condition = "#event.processDefinitionData.key == 'some-process'")
protected void onOtherProcessTaskAssigned(UserTaskAssignedEvent event) {
// ...
10.1. Process Definitions Screen
Process definition editor displays process definition entity properties, BPMN XML, active process instances of the selected process definition version. To change the process definition version use the Version lookup field.
From the editor, you also can suspend the process definition (the process will not be available for starting until you activate it). In the suspend process definition dialog, you have an option to suspend running process instances as well. In this case, user tasks of suspended instances won’t be displayed in the tasks list, all timers and jobs won’t be executed.
10.2. Process Instances Screen
Process instances browser screen may display both active and completed process instances. To switch between them, change the value of the State lookup in the filter and click the Apply button.
In the process instance editor, using actions buttons, you can migrate an instance to a new process definition version, suspend an instance or remove it at all.
The Runtime tab is available only for active process instances and contains information about current process state: active user tasks, execution tree, variable values, failed jobs.
Selecting items in executions tree will modify content of Variables and Tasks tabs. If you select a particular execution, only objects related to this execution will be displayed .
Using the Variables tab You can create, modify and remove process variables. Note, that when you create a new process variable it will be put to the selected execution (if selected).
Using the User tasks tab you can view a list of active tasks and reassign tasks to other users.
The Failed jobs tab displays the list of jobs that failed with an error. For example, if some service task method threw an exception, this service task will appear in the failed jobs list. From the UI you will be able to see the exception stacktrace and restore the failed job after the cause of the error is fixed.
10.3. User Tasks Screen
The screen displays all active user tasks. On the top of the screen, you can filter tasks using various criteria: process definition, task assignee, task candidate, etc.
With the help of this screen you can, for example, find all tasks assigned to a user and reassign them all to another person.
After a process model is modified and a new process definition version is deployed, all existing process instances continue using an older version of the process definition. If necessary you can migrate them to the new process definition version.
If you need to migrate all process instances, open the process definition editor, select a required version in the lookup field and click the Migrate all instances button. Before the migration is performed, all instances are validated, and if any instance cannot be migrated, information window is displayed.
To migrate a single instance, find it in the Process Instances screen, open the Process Instance Editor and click the Migrate button.
For more details about the process instance migration see the Process Instance Migration section of the Flowable documentation.
The Flowable engine is added as a dependency to the core module of BProc add-on (see Application Modules of CUBA Manual). This means that the code in the core module can use the Flowable API directly.
In the web module the Flowable API is not available. If you need to work with a process from the web module, e.g. from a screen controller, you should use the BProc API (this API is also available in the core module, so it can be used everywhere). BProc API is very similar to the Flowable API, many of its methods repeat contracts of Flowable services and invoke Flowable services under the hood.
BProc provides the following services:
12.1. Starting the Process Programmatically
You may want to start processes not from the Start Process screen, but from the regular entity editor. Let’s say, we have the Order
entity editor. The Order
entity has the manager
field of the User
type. In the process we’re going to start, some user task should be assigned to the order manager, so we must store the manager in a proper process variable. To start the process use the BprocRuntimeService
.
If the order editor screen has a button with an id startProcessBtn
, then a button click listener that starts the process will look like this:
@Subscribe("startProcessBtn")
protected void onStartProcessBtnClick(Button.ClickEvent event) {
Order order = getEditedEntity();
Map<String, Object> processVariables = new HashMap<>();
processVariables.put("orderId", order.getId()); (1)
processVariables.put("approver", order.getManager()); (2)
bprocRuntimeService.startProcessInstanceByKey( (3)
"order-approval", (4)
order.getNumber(), (5)
processVariables); (6)
We put an edited entity id to the process variable with the orderId
name. Later we will be able to find the related process instance and user tasks using this variable value.
The approver
variable is used for the user task assignment.
To start the process we use the BprocRuntimeService
.
order-approval
is the process definition key.
The order number is used as an optional process instance business key. It is not required to use business keys in general case.
The third method argument is a process variables map (contains an order id and the approver user).
12.2. Getting a List of Process Instances
The next snippet can be used inside the order entity editor to get a list of process instances of order-approval
definition related to the edited order entity. The orderId
variable should store the order id. It may be passed, for example, on process start (see the Starting the Process Programmatically section).
Order order = getEditedEntity();
List<ProcessInstanceData> processInstances = bprocRuntimeService.createProcessInstanceDataQuery()
.processDefinitionKey("order-approval")
.variableValueEquals("orderId", order.getId())
.list();
Order order = getEditedEntity();
List<TaskData> tasks = bprocTaskService.createTaskDataQuery()
.processDefinitionKey("order-approval")
.taskAssignee(userSession.getCurrentOrSubstitutedUser())
.processVariableValueEquals("orderId", order.getId())
.orderByTaskCreateTime()
.desc()
.list();
bprocTaskService.completeWithOutcome(tasksData, // TaskData instance
"approve", // outcome
processVariables); // process variables map
This means that all files with names ending with .dmn.xml
and located under the bproc/model/dmn
package of the core module will be deployed when the application is started.
Only files that were modified since last application start will be deployed.
Using the action buttons panel you can deploy the decision table to the DMN engine or save the table definition draft if you wish to continue working on it later.
In the input entries expressions the ==
operator may be omitted. Also, don’t forget to wrap string values in input entries expressions in double quotes.
The decision table result will be stored in the corresponding process variable. Properties panel of sequence flow element in the BPMN modeler has an interface for working with decision table results.
@EventListener (1)
public void onBeforeProcessEngineInit(BeforeProcessEngineInitEvent event) {
ProcessEngineConfiguration processEngineConfiguration = event.getProcessEngineConfiguration(); (2)
((ProcessEngineConfigurationImpl) processEngineConfiguration).setAsyncExecutorNumberOfRetries(1); (3)
A listener for the BeforeProcessEngineInitEvent
event. See Event handling in beans in CUBA documentation.
Get the ProcessEngineConfiguration
from the event instance.
Configure the process engine. E.g. set the number of times a job will be retried before it is moved to the deadletter table.