JBoss.orgCommunity Documentation

jBPM Documentation

Version 6.5.0.Final


I. Getting Started
1. Overview
1.1. What is jBPM?
1.2. Overview
1.3. Core Engine
1.4. Process Designer
1.5. Data Modeler
1.6. Form Modeler
1.7. Process Instance and Task Management
1.8. Business Activity Monitoring
1.9. Workbench
1.10. Eclipse Developer Tools
2. Getting Started
2.1. Downloads
2.2. Getting Started
2.3. Community
2.4. Sources
2.4.1. License
2.4.2. Source code
2.4.3. Building from source
2.5. Getting Involved
2.5.1. Sign up to jboss.org
2.5.2. Sign the Contributor Agreement
2.5.3. Submitting issues via JIRA
2.5.4. Fork GitHub
2.5.5. Writing Tests
2.5.6. Commit with Correct Conventions
2.5.7. Submit Pull Requests
2.6. What to do if I encounter problems or have questions?
3. jBPM Installer
3.1. Prerequisites
3.2. Downloading the Installer
3.3. Demo Setup
3.4. 10-Minute Tutorial using the Workbench
3.5. 10-Minute Tutorial using Eclipse
3.6. Configuration
3.6.1. Playgrounds
3.6.2. Workbench Authentication
3.6.3. Using your own database with the jBPM installer
3.6.4. jBPM database schema scripts (DDL scripts)
3.6.5. jBPM installer script
3.7. Frequently Asked Questions
4. Examples
4.1. Introduction
4.2. Importing Projects through Git
4.3. Human Resources Example
4.3.1. The Kie Project: human-resources
4.3.2. Building the Human Resources Example
4.3.3. Create a new Process Instance
4.4. Examples zip
II. jBPM Core
5. Core Engine API
5.1. Overview
5.2. KieBase
5.3. KieSession
5.3.1. ProcessRuntime
5.3.2. Event Listeners
5.3.3. Correlation Keys
5.3.4. Threads
5.4. RuntimeManager
5.4.1. Overview
5.4.2. Strategies
5.4.3. Usage
5.4.4. Configuration
5.5. Services
5.5.1. Deployment Service
5.5.2. Definition Service
5.5.3. Process Service
5.5.4. Runtime Data Service
5.5.5. User Task Service
5.5.6. QueryService
5.5.7. ProcessInstanceMigrationService
5.5.8. Working with deployments
5.6. Configuration
6. Processes
6.1. What is BPMN 2.0
6.2. Process
6.2.1. Creating a process
6.3. Activities
6.3.1. Script task
6.3.2. Service task
6.3.3. User task
6.3.4. Reusable sub-process
6.3.5. Business rule task
6.3.6. Embedded sub-process
6.3.7. Multi-instance sub-process
6.4. Events
6.4.1. Start event
6.4.2. End events
6.4.3. Intermediate events
6.5. Gateways
6.5.1. Diverging gateway
6.5.2. Converging gateway
6.6. Others
6.6.1. Variables
6.6.2. Scripts
6.6.3. Constraints
6.6.4. Timers
6.7. Process Fluent API
6.7.1. Example
6.8. Testing
6.8.1. Unit testing
7. Human Tasks
7.1. Introduction
7.2. Using User Tasks in our Processes
7.2.1. Swimlanes
7.3. Data Mappings
7.4. Task Lifecycle
7.5. Task Permissions
7.5.1. Task Permissions Matrix
7.6. Task Service and The Process Engine
7.7. Task Service API
7.7.1. Task event listener
7.7.2. Data model of task service
7.8. Interacting with the Task Service
8. Persistence and Transactions
8.1. Process Instance State
8.1.1. Runtime State
8.2. Audit Log
8.2.1. The jBPM Audit data model
8.2.2. Storing Process Events in a Database
8.2.3. Storing Process Events in a JMS queue for further processing
8.2.4. Variables auditing
8.3. Transactions
8.3.1. Container managed transactions
8.4. Configuration
8.4.1. Adding dependencies
8.4.2. Manually configuring the engine to use persistence
8.4.3. Configuring the engine to use persistence using JBPMHelper - for tests only
III. Workbench
9. Workbench (General)
9.1. Installation
9.1.1. War installation
9.1.2. Workbench data
9.1.3. System properties
9.1.4. Trouble shooting
9.2. Quick Start
9.2.1. Add repository
9.2.2. Add project
9.2.3. Define Data Model
9.2.4. Define Rule
9.2.5. Build and Deploy
9.3. Administration
9.3.1. Administration overview
9.3.2. Organizational unit
9.3.3. Repositories
9.4. Configuration
9.4.1. Basic user management
9.4.2. Roles
9.4.3. Restricting access to repositories
9.4.4. Command line config tool
9.5. Introduction
9.5.1. Log in and log out
9.5.2. Home screen
9.5.3. Workbench concepts
9.5.4. Initial layout
9.6. Changing the layout
9.6.1. Resizing
9.6.2. Repositioning
9.7. Authoring (General)
9.7.1. Artifact Repository
9.7.2. Asset Editor
9.7.3. Tags Editor
9.7.4. Project Explorer
9.7.5. Project Editor
9.7.6. Validation
9.7.7. Data Modeller
9.7.8. Data Sets
9.8. User and group management
9.8.1. Introduction
9.8.2. Security management providers
9.8.3. Installation and setup
9.8.4. Usage
9.9. Embedding Workbench In Your Application
9.10. Asset Management
9.10.1. Asset Management Overview
9.10.2. Managed vs Unmanaged Repositories
9.10.3. Asset Management Processes
9.10.4. Usage Flow
9.10.5. Repository Structure
9.10.6. Managed Repositories Operations
9.11. Execution Server Management UI
9.11.1. Server Templates
9.11.2. Container
9.11.3. Remote Server
10. Workbench Integration
10.1. REST
10.1.1. Job calls
10.1.2. Repository calls
10.1.3. Organizational unit calls
10.1.4. Maven calls
10.1.5. REST summary
10.2. Keycloak SSO integration
10.2.1. Scenario
10.2.2. Install and setup a Keycloak server
10.2.3. Create and setup the demo realm
10.2.4. Install and setup jBPM Workbench
10.2.5. Securing workbench remote services via Keycloak
10.2.6. Securing workbench's file system services via Keycloak
10.2.7. Execution server
10.2.8. Consuming remote services
11. Workbench High Availability
11.1.
11.1.1. VFS clustering
11.1.2. jBPM clustering
12. Designer
12.1. Designer UI Explained
12.2. Getting started with Modelling
12.3. Designer Toolbar
13. Forms
13.1. Configure process and human tasks
13.2. Generate forms from task definitions
13.3. Edit forms
13.3.1. Form generated description
13.3.2. Customizing form
13.3.3. Field types
13.4. Document attachments
13.4.1. Process and forms configuration
13.4.2. Marshalling strategy and deployment configuration
13.5. Using forms on client applications
13.5.1. What does the API provides?
13.5.2. Sample usage
14. Runtime Management
14.1. Deployments
14.1.1. Deployment descriptors
14.2. Process Deployments
14.3. Jobs
15. Process and Task Management
15.1. Process Management
15.1.1. Process Definitions
15.1.2. Process Instances
15.2. Tasks
15.2.1. Task List
15.2.2. New Task (Ad-Hoc Task)
16. Business Activity Monitoring
16.1. Overview
16.2. Business Dashboards
16.3. Process Dashboard
16.3.1. Task Dashboard
17. Remote API
17.1. Remote Java API
17.1.1. Remote REST Java API Client Configuration
17.1.2. Remote JMS Java API Client Configuration
17.1.3. Remote CommandWebService Java API Client Configuration
17.1.4. Supported methods
17.2. REST
17.2.1. REST permissions
17.2.2. Runtime calls
17.2.3. Task calls
17.2.4. Deployment Calls
17.2.5. Deployment call details
17.2.6. Execute calls
17.2.7. REST summary
17.3. REST Query API
17.3.1. Query URL layout
17.3.2. Query Parameters
17.3.3. Parameter Table
17.3.4. Parameter examples
17.3.5. Query Output Format
17.4. JMS
17.4.1. JMS Queue setup
17.4.2. Using the remote Java API
17.4.3. Example JMS usage
17.5. Additional Information
17.5.1. REST Serialization: JAXB or JSON
17.5.2. Sending and receiving user class instances
17.5.3. Including the deployment id
17.5.4. REST Pagination
17.5.5. REST Map query parameters
17.5.6. REST Number query parameters
17.5.7. Runtime strategies
IV. Eclipse
18. jBPM Eclipse Plugin
18.1. jBPM Eclipse Plugin
18.1.1. Installation
18.1.2. jBPM Project Wizard
18.1.3. New BPMN2 Process Wizard
18.1.4. jBPM Runtime
18.1.5. jBPM Maven Project Wizard
18.1.6. Drools Eclipse plugin
18.1.7. Kie Navigator View
18.2. Debugging
18.2.1. The Process Instances View
18.2.2. The Audit View
18.3. Synchronizing with Workbench Repositories
18.3.1. Importing a workbench repository
18.3.2. Committing changes to the workbench
18.3.3. Updating from to the workbench
18.3.4. Working on individual projects
19. Eclipse BPMN 2.0 Modeler
19.1. Overview
19.2. Installation
19.3. Documentation
V. Integration
20. Integration
20.1. Maven
20.1.1. Maven artifacts as deployment units
20.1.2. Use Maven for dependency management
20.2. CDI
20.2.1. Overview
20.2.2. Configuring CDI integration
20.2.3. RuntimeManager as CDI bean
20.3. Spring
20.3.1. Direct use of Runtime Manager API
20.3.2. jBPM services with Spring
20.4. Ejb
20.4.1. Ejb services implementation
20.4.2. Local interface
20.4.3. Remote interface
20.5. OSGi
VI. Advanced Topics
21. Domain-specific Processes
21.1. Introduction
21.2. Overview
21.2.1. Work Item Definitions
21.2.2. Work Item Handlers
21.3. Example: Notifications
21.3.1. The Notification Work Item Definition
21.3.2. The NotificationWorkItemHandler
21.4. Service Repository
21.4.1. Public jBPM service repository
21.4.2. Setting up your own service repository
21.4.3. Programatically interacting with the service repository
21.4.4. Defining extended service configuration with JSON
21.4.5. Installing services from the service repositoryy
22. Exception Management
22.1. Overview
22.2. Introduction
22.3.
22.3.1. Technical Exceptions
22.3.2. Technical Exception Examples
22.4.
22.4.1. Business Exceptions
23. Flexible Processes
24. Concurrency and asynchronous execution
24.1. Concurrency
24.1.1. Engine execution
24.1.2. Multiple knowledge sessions and persistence
24.2. Asynchronous execution
24.2.1. Asynchronous handlers
24.2.2. jbpm executor
25. Release Notes
25.1. jBPM 6.5
25.1.1. New and Noteworthy in jBPM 6.5.0
25.1.2. New and Noteworthy in KIE Workbench 6.5.0
25.2. jBPM 6.4
25.2.1. New and Noteworthy in jBPM 6.4.0
25.2.2. New and Noteworthy in KIE Workbench 6.4.0
25.3. jBPM 6.3
25.3.1. New and Noteworthy in jBPM 6.3.0
25.3.2. New and Noteworthy in KIE Workbench 6.3.0
25.4. jBPM 6.2
25.4.1. New and Noteworthy in jBPM 6.2.0
25.4.2. New and Noteworthy in KIE Workbench 6.2.0
25.5. jBPM 6.1
25.5.1. New and Noteworthy in jBPM 6.1.0
25.5.2. New and Noteworthy in KIE Workbench 6.1.0
25.6. jBPM 6.0
25.6.1. New and Noteworthy in KIE API 6.0.0
25.6.2. New and Noteworthy in jBPM 6.0.0
25.6.3. New and Noteworthy in KIE Workbench 6.0.0
25.6.4. New and Noteworthy in Integration 6.0.0

Introduction and getting started with jBPM

jBPM is a flexible Business Process Management (BPM) Suite. It is light-weight, fully open-source (distributed under Apache license) and written in Java. It allows you to model, execute, and monitor business processes throughout their life cycle.

A business process allows you to model your business goals by describing the steps that need to be executed to achieve those goals, and the order of those goals are depicted using a flow chart. This process greatly improves the visibility and agility of your business logic. jBPM focuses on executable business processes, which are business processes that contain enough detail so they can actually be executed on a BPM engine. Executable business processes bridge the gap between business users and developers as they are higher-level and use domain-specific concepts that are understood by business users but can also be executed directly.

Business processes need to be supported throughout their entire life cycle: authoring, deployment, process management and task lists, and dashboards and reporting.

The core of jBPM is a light-weight, extensible workflow engine written in pure Java that allows you to execute business processes using the latest BPMN 2.0 specification. It can run in any Java environment, embedded in your application or as a service.

On top of the core engine, a lot of features and tools are offered to support business processes throughout their entire life cycle:

BPM creates the bridge between business analysts, developers and end users by offering process management features and tools in a way that both business users and developers like. Domain-specific nodes can be plugged into the palette, making the processes more easily understood by business users.

jBPM supports adaptive and dynamic processes that require flexibility to model complex, real-life situations that cannot easily be described using a rigid process. We bring control back to the end users by allowing them to control which parts of the process should be executed; this allows dynamic deviation from the process.

jBPM is not just an isolated process engine. Complex business logic can be modeled as a combination of business processes with business rules and complex event processing. jBPM can be combined with the Drools project to support one unified environment that integrates these paradigms where you model your business logic as a combination of processes, rules and events.


This figure gives an overview of the different components of the jBPM project.

  • The core engine is the heart of the project and allows you to execute business processes in a flexible manner. It is a pure Java component that you can choose to embed as part of your application or deploy it as a service and connect to it through the web-based UI or remote APIs.
    • An optional core service is the human task service that will take care of the human task life cycle if human actors participate in the process.
    • Another optional core service is runtime persistence; this will persist the state of all your process instances and log audit information about everything that is happening at runtime.
    • Applications can connect to the core engine by through its Java API or as a set of CDI services, but also remotely through a REST and JMS API.
  • Web-based tools allows you to model, simulate and deploy your processes and other related artifacts (like data models, forms, rules, etc.):
    • The process designer allows business users to design and simulate business processes in a web-based environment.
    • The data modeler allows non-technical users to view, modify and create data models for use in your processes.
    • A web-based form modeler also allows you to create, generate or edit forms related to your processes (to start the process or to complete one of the user tasks).
    • Rule authoring allows you to specify different types of business rules (decision tables, guided rules, etc.) for combination with your processes.
    • All assets are stored and managed on the Guvnor repository (exposed through Git) and can be managed (versioning), built and deployed.
  • The web-based management console allows business users to manage their runtime (manage business processes like start new processes, inspect running instances, etc.), to manage their task list and to perform Business Activity Monitoring (BAM) and see reports.
  • The Eclipse-based developer tools are an extension to the Eclipse IDE, targeted towards developers, and allows you to create business processes using drag and drop, test and debug your processes, etc.

Each of the components are described in more detail below.

The core jBPM engine is the heart of the project. It's a light-weight workflow engine that executes your business processes. It can be embedded as part of your application or deployed as a service (possibly on the cloud). Its most important features are the following:

The core engine can also be integrated with a few other (independent) core services:

All releases can be downloaded from SourceForge. Select the version you want to download and then select which artifact you want:

  • bin: all the jBPM binaries (JARs) and their dependencies

  • src: the sources of the core components

  • docs: the documentation

  • examples: some jBPM examples, can be imported into Eclipse

  • installer: the jbpm-installer, downloads and installs a demo setup of jBPM

  • installer-full: the jbpm-installer, downloads and installs a demo setup of jBPM, already contains a number of dependencies prepackages (so they don't need to be downloaded separately)

Here are a lot of useful links part of the jBPM community:

Please feel free to join us in our IRC channel at chat.freenode.net #jbpm. This is where most of the real-time discussion about the project takes place and where you can find most of the developers most of their time as well. Don't have an IRC client installed? Simply go to http://webchat.freenode.net/, input your desired nickname, and specify #jbpm. Then click login to join the fun.

We are often asked "How do I get involved". Luckily the answer is simple, just write some code and submit it :) There are no hoops you have to jump through or secret handshakes. We have a very minimal "overhead" that we do request to allow for scalable project development. Below we provide a general overview of the tools and "workflow" we request, along with some general advice.

If you contribute some good work, don't forget to blog about it :)

This script assumes you have Java JDK 1.6+ (set as JAVA_HOME), and Ant 1.7+ installed. If you don't, use the following links to download and install them:

Java: http://java.sun.com/javase/downloads/index.jsp

Ant: http://ant.apache.org/bindownload.cgi

Tip

To check whether Java and Ant are installed correctly, type the following commands inside a command prompt:

java -version

ant -version

This should return information about which version of Java and Ant you are currently using.

First of all, you need to download the installer and unzip it to your local file system. There are two versions

  • full installer - which already contains a lot of the dependencies that are necessary during the installation

  • minimal installer - which only contains the installer and will download all dependencies

In general, it is probably best to download the full installer: jBPM-{version}-installer-full.zip

You can also find the latest snapshot release here (only minimal installer) here:

https://hudson.jboss.org/jenkins/job/jBPM/lastSuccessfulBuild/artifact/jbpm-distribution/target/

The easiest way to get started is to simply run the installation script to install the demo setup. The demo install will setup all the web tooling (on top of WildFly) and Eclipse tooling in a pre-configured setup. Go into the jbpm-installer folder where you unzipped the installer and (from a command prompt) run:

ant install.demo

This will:

Running this command could take a while (REALLY, not kidding, we are for example downloading an Eclipse installation, even if you downloaded the full installer, specifically for your operating system).

Once the demo setup has finished, you can start playing with the various components by starting the demo setup:

ant start.demo

This will:

Now wait until the process management console comes up:

http://localhost:8080/jbpm-console

Note

It could take a minute to start up the application server and web application. If the web page doesn't show up after a while, make sure you don't have a firewall blocking that port, or another application already using the port 8080. You can always take a look at the server log jbpm-installer/wildfly-8.1.0.Final/standalone/log/server.log

Finally, if you also want to use the DashBuilder for reporting (which is implemented as a separate war), you can now also install this:

ant install.dashboard.into.jboss

Once everything is started, you can start playing with the Eclipse and web tooling, as explained in the following sections.

If you only want to try out the web tooling and do not wish to download and install the Eclipse tooling, you can use these alternative commands:

ant install.demo.noeclipse
ant start.demo.noeclipse

Similarly, if you only want to try out the Eclipse tooling and do not wish to download and install the web tooling, you can use these alternative commands:

ant install.demo.eclipse
ant start.demo.eclipse

Now continue with the 10-minute tutorials. Once you're done playing and you want to shut down the demo setup, you can use:

ant stop.demo

If at any point in time would like to start over with a clean demo setup - meaning all changes you did inside the web tooling and/or saved in the database will be lost, you can run the following command (after which you can run the installer again from scratch, note that this cannot be undone):

ant clean.demo

Open up the process management console:

http://localhost:8080/jbpm-console

Note

It could take a minute to start up the AS and web application. If the web page doesn't show up after a while, make sure you don't have a firewall blocking that port, or another application already using the port 8080. You can always take a look at the server log jbpm-installer/jboss-as-7.1.1.Final/standalone/log/server.log

Log in, using krisv / krisv as username / password.

Using a prebuilt Evaluation example, the following screencast gives an overview of how to manage your process instances. It shows you:

  • How to build and deploy a process
  • How to start a new process instance
  • How to look up the current status of a running process instance
  • How to look up your tasks
  • How to complete a task
  • How to generate reports to monitor your process execution

Figure 3.1. 


The workbench supports the entire life cycle of your business processes: authoring, deployment, process management, tasks and dashboards.

  • The project authoring perspective allows you to look at existing repositories, where each project can contain business processes (but also business rules, data models, forms, etc.). By default, the workbench will download two sample playground repositories, containing examples to look at.
    • In this screencast, the Evaluation project inside the jbpm-playground repository is used.
  • The project explorer shows all available artefacts:
    • evaluation: business process describing the evaluation process as a sequence of tasks
    • evaluation-taskform: process form to start the evaluation process
    • PerformanceEvaluation-taskform: task form to perform the evaluation tasks
  • To make a process available for execution, you need to successfully build and deploy it first. To do so, open up the Project Editor (from the Tools menu) and click Build & Deploy.
  • To manage your process definitions and instances, click on the "Process Management" menu option at the top menu bar an select one of available options depending on you interest:
    • Process Definitions - lists all available process definitions
    • Process Instances - lists all active process instances (allows to show completed, aborted as well by changing filter criteria)
  • Process definitions panel allow you to start a new process instance by clicking on the "Play" button. The process form (as defined in the project) will be shown, where you need to fill in the necessary information to start the process. In this case, you need to fill the user you want to start an evaluation for (in this case use "krisv") and a reason for the request, after which you can complete the form. Some details about the process instance that was just started will be shown in the process instance details panel. From there you can access additional details:
    • Process model - to visualize current state of the process
    • Process variables - to see current values of process variables
    The process instance that you just started is first requiring a self-evaluation of the user and is waiting until the user has completed this task.
  • To see the tasks that have been assigned to you, choose the "Tasks" menu option on the top bar and select "Task List" (you may need to click refresh to update your task view). The personal tasks table should show a "Performance Evaluation" task reserved for you. After starting the task, you can complete the task, which will open up the task form related to this task. You can fill in the necessary data and then complete the form and close the window. After completing the task, you could check the "Process Instances" once more to check the progress of your process instance. You should be able to see that the process is now waiting for your HR manager and project manager to also perform an evaluation. You could log in as "john" / "john" and "mary" / "mary" to complete these tasks.
  • After starting and/or completing a few process instances and human tasks, you can generate a report of what has happened so far. Under "Dashboards", select "Process & Task Dashboard". This is a set of see predefined charts that allow users to spot what is going on in the system. Charts can be fully customized as well, as explained in the Business Activity Monitoring chapter.

The following screencast gives an overview of how to use the Eclipse tooling. It shows you:

  • How to import and execute the evaluation sample project
    • Import the evaluation project (included in the jbpm-installer)
    • Open the Evaluation.bpmn process
    • Open the com.sample.ProcessTest Java class
    • Execute the ProcessTest class to run the process
  • How to create a new jBPM project (including sample process and JUnit test)

Figure 3.2. 


You can import the evaluation project - a sample included in the jbpm-installer - by selecting "File -> Import ...", select "Existing Projects into Workspace" and browse to the jbpm-installer/sample/evaluation folder and click "Finish". You can open up the evaluation process and the ProcessTest class. To execute the class, right-click on it and select "Run as ... - Java Application". The console should show how the process was started and how the different actors in the process completed the tasks assigned to them, to complete the process instance.

You could also create a new project using the jBPM project wizard. The sample projects contain a process and an associated Java file to start the process. Select "File - New ... - Project ..." and under the "jBPM" category, select "jBPM project" and click "Next". Give the project a name and click "Next". You can choose from a simple HelloWorld example or a slightly more advanced example using persistence and human tasks. If you select the latter and click Finish, you should see a new project containing a "sample.bpmn" process and a "com.sample.ProcessTest" JUnit test class. You can open the BPMN2 process by double-clicking it. To execute the process, right-click on ProcessTest.java and select "Run As - Java Application".

The workbench web application is using the pre-installed other security domain for authenticating and authorizing users (as specified in the WEB-INF/jboss-web.xml inside the WARs).

The application server uses by default property files based realms - Please note that this configuration is intended only for demo purposes (users, roles and passwords are stored in simple property files on the filesystem).

Authentication is configured in the standalone.xml file as follows:


    <security-domain name="other" cache-type="default">
      <authentication>
        <login-module code="Remoting" flag="optional">
          <module-option name="password-stacking" value="useFirstPass"/>
        </login-module>
        <login-module code="RealmDirect" flag="required">
          <module-option name="password-stacking" value="useFirstPass"/>
        </login-module>
      </authentication>
    </security-domain>
      

    <security-realm name="ApplicationRealm">
      <authentication>
        <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
        <properties path="users.properties" relative-to="jboss.server.config.dir"/>
      </authentication>
      <authorization>
        <properties path="roles.properties" relative-to="jboss.server.config.dir"/>
      </authorization>
    </security-realm>
      

These are the default users:


Authentication can be customized by using any of the following options:

  • The users and groups management screens on the workbench web application.

    Navigate into the workbench web application and use the menu Home -> User management / Group management entries.

  • The add-user script that comes by default on Wildfly/EAP.

    Example for Linux platforms - run the following command and follow the script instructions:

    /bin/sh $JBOSS_HOME/bin/add-user.sh
              --user-properties $JBOSS_HOME/standalone/configuration/users.properties
              --group-properties $JBOSS_HOME/standalone/configuration/roles.properties
              --realm ApplicationRealm

The following files define the persistence settings for the jbpm-installer demo:

Do the following:

If you decide to use a different database with this demo, you need to remember the following when going through the steps above:

jBPM installer ant script performs most of the work automatically and usually does not require additional attention but in case it does, here is a list of available targets that might be needed to perform some of the steps manually.

Table 3.3. jBPM installer available targets

TargetDescription
clean.dbcleans up database used by jBPM demo (applies only to H2 database)
clean.democleans up entire installation so new installation can be performed
clean.demo.noeclipsesame as clean.demo but does not remove Eclipse
clean.eclipseremoves Eclipse and its workspace
clean.generated.ddlremoves DDL scripts generated if any
clean.jbossremoves application server with all its deployments
clean.jboss.repositoryremoves repository content for demo setup (guvnor Maven repo, niogit, etc)
download.dashboarddownloads jBPM dashboard component (BAM)
download.db.driverdownloads DB driver configured in build.properties
download.ddl.dependenciesdownloads all dependencies required to run DDL script generation tool
download.droolsjbpm.eclipsedownloads Drools and jBPM Eclipse plugin
download.eclipsedownloads Eclipse distribution
download.jbossdownloads JBoss Application Server
download.jBPM.bindownloads jBPM binary distribution (jBPM libs and its dependencies)
download.jBPM.consoledownloads jBPM console for JBoss AS
install.dashboard.into.jbossinstalls jBPM dashboard into JBoss AS
install.db.filesinstalls DB driver as JBoss module
install.demoinstalls complete demo environment
install.demo.eclipseinstalls Eclipse with all jBPM plugins, no server installation
install.demo.noeclipsesimilar to install.demo but skips Eclipse installation
install.dependenciesinstalls custom libraries (such as work item handlers, etc) into the jBPM console
install.droolsjbpm-eclipse.into.eclipseinstalls droolsjbpm Eclipse plugin into Eclipse
install.eclipseinstall Eclipse IDE
install.jbossinstalls JBoss AS
install.jBPM-console.into.jbossinstalls jBPM console application into JBoss AS

Some common issues are explained below.

Q: What if the installer complains it cannot download component X?

A: Are you connected to the Internet? Do you have a firewall turned on? Do you require a proxy? It might be possible that one of the locations we're downloading the components from is temporarily offline. Try downloading the components manually (possibly from alternate locations) and put them in the jbpm-installer/lib folder.

Q: What if the installer complains it cannot extract / unzip a certain JAR/WAR/zip?

A: If your download failed while downloading a component, it is possible that the installer is trying to use an incomplete file. Try deleting the component in question from the jbpm-installer/lib folder and reinstall, so it will be downloaded again.

Q: What if I have been changing my installation (and it no longer works) and I want to start over again with a clean installation?

A: You can use ant clean.demo to remove all the installed components, so you end up with a fresh installation again.

Q: I sometimes see exceptions when trying to stop or restart certain services, what should I do?

A: If you see errors during shutdown, are you sure the services were still running? If you see exceptions during restart, are you sure the service you started earlier was successfully shutdown? Maybe try killing the services manually if necessary.

Q: Something seems to be going wrong when running Eclipse but I have no idea what. What can I do?

A: Always check the consoles for output like error messages or stack traces. You can also check the Eclipse Error Log for exceptions. Try adding an audit logger to your session to figure out what's happening at runtime, or try debugging your application.

Q: Something seems to be going wrong when running the a web-based application like the jbpm-console. What can I do?

A: You can check the server log for possible exceptions: jbpm-installer/jboss-as-{version}/standalone/log/server.log (for JBoss AS7).

For all other questions, try contacting the jBPM community as described in the Getting Started chapter.

The Human Resource Example's use case can be described as follows: A company wants to hire new developers. In this process, three departments (that is the Human resources, IT, and Accounting) are involved. These departments are represented by three users: Katy, Jack, and John respectively.


Note that only four out of the six defined activities within the business process are User Tasks. User Tasks require human interaction. The other two tasks are Service Tasks, which are automated and connected to other systems.

Each instance of the process will follow certain actions:

  • The human resources team performs the initial interview with the candidate.

  • The IT department team performs the technical interview.

  • Based on the output from the previous two steps, the accounting team creates a job proposal.

  • When the proposal has been drafted, it is automatically sent to the candidate via email.

  • If the candidate accepts the proposal, a new meeting to sign the contract is scheduled.

  • Finally, if the candidate accepts the proposal, the system posts a message about the new hire using Twitter service connector.

Note, that Jack, John, and Katy represent any employee within the company with appropriate role assigned.

Using the jBPM Core Engine

Table of Contents

5. Core Engine API
5.1. Overview
5.2. KieBase
5.3. KieSession
5.3.1. ProcessRuntime
5.3.2. Event Listeners
5.3.3. Correlation Keys
5.3.4. Threads
5.4. RuntimeManager
5.4.1. Overview
5.4.2. Strategies
5.4.3. Usage
5.4.4. Configuration
5.5. Services
5.5.1. Deployment Service
5.5.2. Definition Service
5.5.3. Process Service
5.5.4. Runtime Data Service
5.5.5. User Task Service
5.5.6. QueryService
5.5.7. ProcessInstanceMigrationService
5.5.8. Working with deployments
5.6. Configuration
6. Processes
6.1. What is BPMN 2.0
6.2. Process
6.2.1. Creating a process
6.3. Activities
6.3.1. Script task
6.3.2. Service task
6.3.3. User task
6.3.4. Reusable sub-process
6.3.5. Business rule task
6.3.6. Embedded sub-process
6.3.7. Multi-instance sub-process
6.4. Events
6.4.1. Start event
6.4.2. End events
6.4.3. Intermediate events
6.5. Gateways
6.5.1. Diverging gateway
6.5.2. Converging gateway
6.6. Others
6.6.1. Variables
6.6.2. Scripts
6.6.3. Constraints
6.6.4. Timers
6.7. Process Fluent API
6.7.1. Example
6.8. Testing
6.8.1. Unit testing
7. Human Tasks
7.1. Introduction
7.2. Using User Tasks in our Processes
7.2.1. Swimlanes
7.3. Data Mappings
7.4. Task Lifecycle
7.5. Task Permissions
7.5.1. Task Permissions Matrix
7.6. Task Service and The Process Engine
7.7. Task Service API
7.7.1. Task event listener
7.7.2. Data model of task service
7.8. Interacting with the Task Service
8. Persistence and Transactions
8.1. Process Instance State
8.1.1. Runtime State
8.2. Audit Log
8.2.1. The jBPM Audit data model
8.2.2. Storing Process Events in a Database
8.2.3. Storing Process Events in a JMS queue for further processing
8.2.4. Variables auditing
8.3. Transactions
8.3.1. Container managed transactions
8.4. Configuration
8.4.1. Adding dependencies
8.4.2. Manually configuring the engine to use persistence
8.4.3. Configuring the engine to use persistence using JBPMHelper - for tests only

This chapter introduces the API you need to load processes and execute them. For more detail on how to define the processes themselves, check out the chapter on BPMN 2.0.

To interact with the process engine (for example, to start a process), you need to set up a session. This session will be used to communicate with the process engine. A session needs to have a reference to a knowledge base, which contains a reference to all the relevant process definitions. This knowledge base is used to look up the process definitions whenever necessary. To create a session, you first need to create a knowledge base, load all the necessary process definitions (this can be from various sources, like from classpath, file system or process repository) and then instantiate a session.

Once you have set up a session, you can use it to start executing processes. Whenever a process is started, a new process instance is created (for that process definition) that maintains the state of that specific instance of the process.

For example, imagine you are writing an application to process sales orders. You could then define one or more process definitions that define how the order should be processed. When starting up your application, you first need to create a knowledge base that contains those process definitions. You can then create a session based on this knowledge base so that, whenever a new sales order comes in, a new process instance is started for that sales order. That process instance contains the state of the process for that specific sales request.

A knowledge base can be shared across sessions and usually is only created once, at the start of the application (as creating a knowledge base can be rather heavy-weight as it involves parsing and compiling the process definitions). Knowledge bases can be dynamically changed (so you can add or remove processes at runtime).

Sessions can be created based on a knowledge base and are used to execute processes and interact with the engine. You can create as many independent session as you need and creating a session is considered relatively lightweight. How many sessions you create is up to you. In general, most simple cases start out with creating one session that is then called from various places in your application. You could decide to create multiple sessions if for example you want to have multiple independent processing units (for example, if you want all processes from one customer to be completely independent from processes for another customer, you could create an independent session for each customer) or if you need multiple sessions for scalability reasons. If you don't know what to do, simply start by having one knowledge base that contains all your process definitions and create one session that you then use to execute all your processes.

The jBPM project has a clear separation between the API the users should be interacting with and the actual implementation classes. The public API exposes most of the features we believe "normal" users can safely use and should remain rather stable across releases. Expert users can still access internal classes but should be aware that they should know what they are doing and that the internal API might still change in the future.

As explained above, the jBPM API should thus be used to (1) create a knowledge base that contains your process definitions, and to (2) create a session to start new process instances, signal existing ones, register listeners, etc.

Once you've loaded your knowledge base, you should create a session to interact with the engine. This session can then be used to start new processes, signal events, etc. The following code snippet shows how easy it is to create a session based on the previously created knowledge base, and to start a process (by id).


KieSession ksession = kbase.newKieSession();
ProcessInstance processInstance = ksession.startProcess("com.sample.MyProcess");

The ProcessRuntime interface defines all the session methods for interacting with processes, as shown below.


  /**
	 * Start a new process instance.  The process (definition) that should
	 * be used is referenced by the given process id.
	 * 
	 * @param processId  The id of the process that should be started
	 * @return the ProcessInstance that represents the instance of the process that was started
	 */
    ProcessInstance startProcess(String processId);

    /**
	 * Start a new process instance.  The process (definition) that should
	 * be used is referenced by the given process id.  Parameters can be passed
	 * to the process instance (as name-value pairs), and these will be set
	 * as variables of the process instance. 
     * 
	 * @param processId  the id of the process that should be started
     * @param parameters  the process variables that should be set when starting the process instance 
	 * @return the ProcessInstance that represents the instance of the process that was started
     */
    ProcessInstance startProcess(String processId,
                                 Map<String, Object> parameters);

    /**
     * Signals the engine that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All process instances that are listening to this type
     * of (external) event will be notified.  For performance reasons, this type of event
     * signaling should only be used if one process instance should be able to notify
     * other process instances. For internal event within one process instance, use the
     * signalEvent method that also include the processInstanceId of the process instance
     * in question. 
     * 
     * @param type the type of event
     * @param event the data associated with this event
     */
    void signalEvent(String type,
                     Object event);

    /**
     * Signals the process instance that an event has occurred. The type parameter defines
     * which type of event and the event parameter can contain additional information
     * related to the event.  All node instances inside the given process instance that
     * are listening to this type of (internal) event will be notified.  Note that the event
     * will only be processed inside the given process instance.  All other process instances
     * waiting for this type of event will not be notified.
     * 
     * @param type the type of event
     * @param event the data associated with this event
     * @param processInstanceId the id of the process instance that should be signaled
     */
    void signalEvent(String type,
                     Object event,
                     long processInstanceId);

    /**
     * Returns a collection of currently active process instances.  Note that only process
     * instances that are currently loaded and active inside the engine will be returned.
     * When using persistence, it is likely not all running process instances will be loaded
     * as their state will be stored persistently.  It is recommended not to use this
     * method to collect information about the state of your process instances but to use
     * a history log for that purpose.
     * 
     * @return a collection of process instances currently active in the session
     */
    Collection<ProcessInstance> getProcessInstances();

    /**
     * Returns the process instance with the given id.  Note that only active process instances
     * will be returned.  If a process instance has been completed already, this method will return
     * null.
     * 
     * @param id the id of the process instance
     * @return the process instance with the given id or null if it cannot be found
     */
    ProcessInstance getProcessInstance(long processInstanceId);

    /**
     * Aborts the process instance with the given id.  If the process instance has been completed
     * (or aborted), or the process instance cannot be found, this method will throw an
     * IllegalArgumentException.
     * 
     * @param id the id of the process instance
     */
    void abortProcessInstance(long processInstanceId);

    /**
     * Returns the WorkItemManager related to this session.  This can be used to
     * register new WorkItemHandlers or to complete (or abort) WorkItems.
     * 
     * @return the WorkItemManager related to this session
     */
    WorkItemManager getWorkItemManager();

The session provides methods for registering and removing listeners. A ProcessEventListener can be used to listen to process-related events, like starting or completing a process, entering and leaving a node, etc. Below, the different methods of the ProcessEventListener class are shown. An event object provides access to related information, like the process instance and node instance linked to the event. You can use this API to register your own event listeners.

public interface ProcessEventListener {

  void beforeProcessStarted( ProcessStartedEvent event );
  void afterProcessStarted( ProcessStartedEvent event );
  void beforeProcessCompleted( ProcessCompletedEvent event );
  void afterProcessCompleted( ProcessCompletedEvent event );
  void beforeNodeTriggered( ProcessNodeTriggeredEvent event );
  void afterNodeTriggered( ProcessNodeTriggeredEvent event );
  void beforeNodeLeft( ProcessNodeLeftEvent event );
  void afterNodeLeft( ProcessNodeLeftEvent event );
  void beforeVariableChanged(ProcessVariableChangedEvent event);
  void afterVariableChanged(ProcessVariableChangedEvent event);

}

A note about before and after events: these events typically act like a stack, which means that any events that occur as a direct result of the previous event, will occur between the before and the after of that event. For example, if a subsequent node is triggered as result of leaving a node, the node triggered events will occur inbetween the beforeNodeLeftEvent and the afterNodeLeftEvent of the node that is left (as the triggering of the second node is a direct result of leaving the first node). Doing that allows us to derive cause relationships between events more easily. Similarly, all node triggered and node left events that are the direct result of starting a process will occur between the beforeProcessStarted and afterProcessStarted events. In general, if you just want to be notified when a particular event occurs, you should be looking at the before events only (as they occur immediately before the event actually occurs). When only looking at the after events, one might get the impression that the events are fired in the wrong order, but because the after events are triggered as a stack (after events will only fire when all events that were triggered as a result of this event have already fired). After events should only be used if you want to make sure that all processing related to this has ended (for example, when you want to be notified when starting of a particular process instance has ended.

Also note that not all nodes always generate node triggered and/or node left events. Depending on the type of node, some nodes might only generate node left events, others might only generate node triggered events. Catching intermediate events for example are not generating triggered events (they are only generating left events, as they are not really triggered by another node, rather activated from outside). Similarly, throwing intermediate events are not generating left events (they are only generating triggered events, as they are not really left, as they have no outgoing connection).

jBPM out-of-the-box provides a listener that can be used to create an audit log (either to the console or the a file on the file system). This audit log contains all the different events that occurred at runtime so it's easy to figure out what happened. Note that these loggers should only be used for debugging purposes. The following logger implementations are supported by default:

The KieServices lets you add a KieRuntimeLogger to your session, as shown below. When creating a console logger, the knowledge session for which the logger needs to be created must be passed as an argument. The file logger also requires the name of the log file to be created, and the threaded file logger requires the interval (in milliseconds) after which the events should be saved. You should always close the logger at the end of your application.


  import org.kie.api.KieServices;
  import org.kie.api.logger.KieRuntimeLogger;
  ...
  KieRuntimeLogger logger = KieServices.Factory.get().getLoggers().newFileLogger(ksession, "test");
  // add invocations to the process engine here,
  // e.g. ksession.startProcess(processId);
  ...
  logger.close();

The log file that is created by the file-based loggers contains an XML-based overview of all the events that occurred at runtime. It can be opened in Eclipse, using the Audit View in the Drools Eclipse plugin, where the events are visualized as a tree. Events that occur between the before and after event are shown as children of that event. The following screenshot shows a simple example, where a process is started, resulting in the activation of the Start node, an Action node and an End node, after which the process was completed.

A common requirement when working with processes is ability to assign a given process instance some sort of business identifier that can be later on referenced without knowing the actual (generated) id of the process instance. To provide such capabilities, jBPM allows to use CorrelationKey that is composed of CorrelationProperties. CorrelationKey can have either single property describing it (which is in most cases) but it can be represented as multi valued properties set.

Correlation capabilities are provided as part of interface

CorrelationAwareProcessRuntime

that exposes following methods:


      /**
      * Start a new process instance.  The process (definition) that should
      * be used is referenced by the given process id.  Parameters can be passed
      * to the process instance (as name-value pairs), and these will be set
      * as variables of the process instance.
      *
      * @param processId  the id of the process that should be started
      * @param correlationKey custom correlation key that can be used to identify process instance
      * @param parameters  the process variables that should be set when starting the process instance
      * @return the ProcessInstance that represents the instance of the process that was started
      */
      ProcessInstance startProcess(String processId, CorrelationKey correlationKey, Map<String, Object> parameters);

      /**
      * Creates a new process instance (but does not yet start it).  The process
      * (definition) that should be used is referenced by the given process id.
      * Parameters can be passed to the process instance (as name-value pairs),
      * and these will be set as variables of the process instance.  You should only
      * use this method if you need a reference to the process instance before actually
      * starting it.  Otherwise, use startProcess.
      *
      * @param processId  the id of the process that should be started
      * @param correlationKey custom correlation key that can be used to identify process instance
      * @param parameters  the process variables that should be set when creating the process instance
      * @return the ProcessInstance that represents the instance of the process that was created (but not yet started)
      */
      ProcessInstance createProcessInstance(String processId, CorrelationKey correlationKey, Map<String, Object> parameters);

      /**
      * Returns the process instance with the given correlationKey.  Note that only active process instances
      * will be returned.  If a process instance has been completed already, this method will return
      * null.
      *
      * @param correlationKey the custom correlation key assigned when process instance was created
      * @return the process instance with the given id or null if it cannot be found
      */
      ProcessInstance getProcessInstance(CorrelationKey correlationKey);
    

Correlation is usually used with long running processes and thus require persistence to be enabled to be able to permanently store correlation information.

In the following text, we will refer to two types of "multi-threading": logical and technical. Technical multi-threading is what happens when multiple threads or processes are started on a computer, for example by a Java or C program. Logical multi-threading is what we see in a BPM process after the process reaches a parallel gateway, for example. From a functional standpoint, the original process will then split into two processes that are executed in a parallel fashion.

Of course, the jBPM engine supports logical multi-threading: for example, processes that include a parallel gateway. We've chosen to implement logical multi-threading using one thread: a jBPM process that includes logical multi-threading will only be executed in one technical thread. The main reason for doing this is that multiple (technical) threads need to be be able to communicate state information with each other if they are working on the same process. This requirement brings with it a number of complications. While it might seem that multi-threading would bring performance benefits with it, the extra logic needed to make sure the different threads work together well means that this is not guaranteed. There is also the extra overhead incurred because we need to avoid race conditions and deadlocks.

In general, the jBPM engine executes actions in serial. For example, when the engine encounters a script task in a process, it will synchronously execute that script and wait for it to complete before continuing execution. Similarly, if a process encounters a parallel gateway, it will sequentially trigger each of the outgoing branches, one after the other. This is possible since execution is almost always instantaneous, meaning that it is extremely fast and produces almost no overhead. As a result, the user will usually not even notice this. Similarly, action scripts in a process are also synchronously executed, and the engine will wait for them to finish before continuing the process. For example, doing a Thread.sleep(...) as part of a script will not make the engine continue execution elsewhere but will block the engine thread during that period.

The same principle applies to service tasks. When a service task is reached in a process, the engine will also invoke the handler of this service synchronously. The engine will wait for the completeWorkItem(...) method to return before continuing execution. It is important that your service handler executes your service asynchronously if its execution is not instantaneous.

An example of this would be a service task that invokes an external service. Since the delay in invoking this service remotely and waiting for the results might be too long, it might be a good idea to invoke this service asynchronously. This means that the handler will only invoke the service and will notify the engine later when the results are available. In the mean time, the process engine then continues execution of the process.

Human tasks are a typical example of a service that needs to be invoked asynchronously, as we don't want the engine to wait until a human actor has responded to the request. The human task handler will only create a new task (on the task list of the assigned actor) when the human task node is triggered. The engine will then be able to continue execution on the rest of the process (if necessary) and the handler will notify the engine asynchronously when the user has completed the task.

RuntimeManager has been introduced to simplify and empower usage of knowledge API especially in context of processes. It provides configurable strategies that control actual runtime execution (how KieSessions are provided) and by default provides following:

Runtime Manager is primary responsible for managing and delivering instances of RuntimeEngine to the caller. In turn, RuntimeEngine encapsulates two the most important elements of jBPM engine:

Both of these components are already configured to work with each other smoothly without additional configuration from end user. No more need to register human task handler or keeping track if it's connected to the service or not.

public interface RuntimeManager {

	/**
	 * Returns <code>RuntimeEngine</code> instance that is fully initialized:
	 * <ul>
	 * 	<li>KiseSession is created or loaded depending on the strategy</li>
	 * 	<li>TaskService is initialized and attached to ksession (via listener)</li>
	 * 	<li>WorkItemHandlers are initialized and registered on ksession</li>
	 * 	<li>EventListeners (process, agenda, working memory) are initialized and added to ksession</li>
	 * </ul>
	 * @param context the concrete implementation of the context that is supported by given <code>RuntimeManager</code>
	 * @return instance of the <code>RuntimeEngine</code>
	 */
    RuntimeEngine getRuntimeEngine(Context<?> context);
    
    /**
     * Unique identifier of the <code>RuntimeManager</code>
     * @return
     */
    String getIdentifier();
   
    /**
     * Disposes <code>RuntimeEngine</code> and notifies all listeners about that fact.
     * This method should always be used to dispose <code>RuntimeEngine</code> that is not needed
     * anymore. <br/>
     * ksession.dispose() shall never be used with RuntimeManager as it will break the internal
     * mechanisms of the manager responsible for clear and efficient disposal.<br/>
     * Dispose is not needed if <code>RuntimeEngine</code> was obtained within active JTA transaction, 
     * this means that when getRuntimeEngine method was invoked during active JTA transaction then dispose of
     * the runtime engine will happen automatically on transaction completion.
     * @param runtime
     */
    void disposeRuntimeEngine(RuntimeEngine runtime);
    
    /**
     * Closes <code>RuntimeManager</code> and releases its resources. Shall always be called when
     * runtime manager is not needed any more. Otherwise it will still be active and operational.
     */
    void close();
    
}

RuntimeEngine interface provides the most important methods to get access to engine components:

public interface RuntimeEngine {

	/**
	 * Returns <code>KieSession</code> configured for this <code>RuntimeEngine</code>
	 * @return
	 */
    KieSession getKieSession();
    
    /**
	 * Returns <code>TaskService</code> configured for this <code>RuntimeEngine</code>
	 * @return
	 */
    TaskService getTaskService();   
}

RuntimeManager will ensure that regardless of the strategy it will provide same capabilities when it comes to initialization and configuration of the RuntimeEngine. That means

On the other hand, RuntimeManager maintains the engine disposal as well by providing dedicated methods to dispose RuntimeEngine when it's no more needed to release any resources it might have acquired.

Singleton strategy - instructs RuntimeManager to maintain single instance of RuntimeEngine (and in turn single instance of KieSession and TaskService). Access to the RuntimeEngine is synchronized and by that thread safe although it comes with a performance penalty due to synchronization. This strategy is similar to what was available by default in jBPM version 5.x and it's considered easiest strategy and recommended to start with.

It has following characteristics that are important to evaluate while considering it for given scenario:

Per request strategy - instructs RuntimeManager to provide new instance of RuntimeEngine for every request. As request RuntimeManager will consider one or more invocations within single transaction. It must return same instance of RuntimeEngine within single transaction to ensure correctness of state as otherwise operation done in one call would not be visible in the other. This is sort of "stateless" strategy that provides only request scope state and once request is completed RuntimeEngine will be permanently destroyed - KieSession information will be removed from the database in case persistence was used.

It has following characteristics:

Per process instance strategy - instructs RuntimeManager to maintain a strict relationship between KieSession and ProcessInstance. That means that KieSession will be available as long as the ProcessInstance that it belongs to is active. This strategy provides the most flexible approach to use advanced capabilities of the engine like rule evaluation in isolation (for given process instance only), maximum performance and reduction of potential bottlenecks intriduced by synchronization; and at the same time reduces number of KieSessions to the actual number of process instances rather than number of requests (in contrast to per request strategy).

It has following characteristics:

Regular usage scenario for RuntimeManager is:

Here is how you can build RuntimeManager and get RuntimeEngine (that encapsulates KieSession and TaskService) from it:


    // first configure environment that will be used by RuntimeManager
    RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
    .newDefaultInMemoryBuilder()
    .addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
    .get();

    // next create RuntimeManager - in this case singleton strategy is chosen
    RuntimeManager manager = RuntimeManagerFactory.Factory.get().newSingletonRuntimeManager(environment);

    // then get RuntimeEngine out of manager - using empty context as singleton does not keep track
    // of runtime engine as there is only one
    RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get());

    // get KieSession from runtime runtimeEngine - already initialized with all handlers, listeners, etc that were configured
    // on the environment
    KieSession ksession = runtimeEngine.getKieSession();

    // add invocations to the process engine here,
    // e.g. ksession.startProcess(processId);

    // and last dispose the runtime engine
    manager.disposeRuntimeEngine(runtimeEngine);
  

This example provides simplest (minimal) way of using RuntimeManager and RuntimeEngine although it provides few quite valuable information:

The complexity of knowing when to create, dispose, register handlers, etc is taken away from the end user and moved to the runtime manager that knows when/how to perform such operations but still allows to have a fine grained control over this process by providing comprehensive configuration of the RuntimeEnvironment.


  public interface RuntimeEnvironment {

	/**
	 * Returns <code>KieBase</code> that shall be used by the manager
	 * @return
	 */
    KieBase getKieBase();
    
    /**
     * KieSession environment that shall be used to create instances of <code>KieSession</code>
     * @return
     */
    Environment getEnvironment();
    
    /**
     * KieSession configuration that shall be used to create instances of <code>KieSession</code>
     * @return
     */
    KieSessionConfiguration getConfiguration();
    
    /**
     * Indicates if persistence shall be used for the KieSession instances
     * @return
     */
    boolean usePersistence();
    
    /**
     * Delivers concrete implementation of <code>RegisterableItemsFactory</code> to obtain handlers and listeners
     * that shall be registered on instances of <code>KieSession</code>
     * @return
     */
    RegisterableItemsFactory getRegisterableItemsFactory();
    
    /**
     * Delivers concrete implementation of <code>UserGroupCallback</code> that shall be registered on instances 
     * of <code>TaskService</code> for managing users and groups.
     * @return
     */
    UserGroupCallback getUserGroupCallback();
    
    /**
     * Delivers custom class loader that shall be used by the process engine and task service instances
     * @return
     */
    ClassLoader getClassLoader();
    
    /**
     * Closes the environment allowing to close all depending components such as ksession factories, etc 
     */
    void close();
  

While RuntimeEnvironment interface provides mostly access to data kept as part of the environment and will be used by the RuntimeManager, users should take advantage of builder style class that provides fluent API to configure RuntimeEnvironment with predefined settings.

public interface RuntimeEnvironmentBuilder {

	public RuntimeEnvironmentBuilder persistence(boolean persistenceEnabled);

	public RuntimeEnvironmentBuilder entityManagerFactory(Object emf);

	public RuntimeEnvironmentBuilder addAsset(Resource asset, ResourceType type);

	public RuntimeEnvironmentBuilder addEnvironmentEntry(String name, Object value);

	public RuntimeEnvironmentBuilder addConfiguration(String name, String value);

	public RuntimeEnvironmentBuilder knowledgeBase(KieBase kbase);

	public RuntimeEnvironmentBuilder userGroupCallback(UserGroupCallback callback);

	public RuntimeEnvironmentBuilder registerableItemsFactory(RegisterableItemsFactory factory);

	public RuntimeEnvironment get();

	public RuntimeEnvironmentBuilder classLoader(ClassLoader cl);
	
	public RuntimeEnvironmentBuilder schedulerService(Object globalScheduler); 
    
  

Instances of the RuntimeEnvironmentBuilder can be obtained via RuntimeEnvironmentBuilderFactory that provides preconfigured sets of builder to simplify and help users to build the environment for the RuntimeManager.

public interface RuntimeEnvironmentBuilderFactory {

	/**
     * Provides completely empty <code>RuntimeEnvironmentBuilder</code> instance that allows to manually
     * set all required components instead of relying on any defaults.
     * @return new instance of <code>RuntimeEnvironmentBuilder</code>
     */
    public RuntimeEnvironmentBuilder newEmptyBuilder();
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultBuilder();
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * but it does not have persistence for process engine configured so it will only store process instances in memory
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultInMemoryBuilder();
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * This one is tailored to works smoothly with kjars as the notion of kbase and ksessions
     * @param groupId group id of kjar
     * @param artifactId artifact id of kjar
     * @param version version number of kjar
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultBuilder(String groupId, String artifactId, String version);
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * This one is tailored to works smoothly with kjars as the notion of kbase and ksessions
     * @param groupId group id of kjar
     * @param artifactId artifact id of kjar
     * @param version version number of kjar
     * @param kbaseName name of the kbase defined in kmodule.xml stored in kjar
     * @param ksessionName name of the ksession define in kmodule.xml stored in kjar
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultBuilder(String groupId, String artifactId, String version, String kbaseName, String ksessionName);
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * This one is tailored to works smoothly with kjars as the notion of kbase and ksessions
     * @param releaseId <code>ReleaseId</code> that described the kjar
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultBuilder(ReleaseId releaseId);
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * This one is tailored to works smoothly with kjars as the notion of kbase and ksessions
     * @param releaseId <code>ReleaseId</code> that described the kjar
     * @param kbaseName name of the kbase defined in kmodule.xml stored in kjar
     * @param ksessionName name of the ksession define in kmodule.xml stored in kjar
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newDefaultBuilder(ReleaseId releaseId, String kbaseName, String ksessionName);
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * It relies on KieClasspathContainer that requires to have kmodule.xml present in META-INF folder which 
     * defines the kjar itself.
     * Expects to use default kbase and ksession from kmodule.
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newClasspathKmoduleDefaultBuilder();
    
    /**
     * Provides default configuration of <code>RuntimeEnvironmentBuilder</code> that is based on:
     * <ul>
     * 	<li>DefaultRuntimeEnvironment</li>
     * </ul>
     * It relies on KieClasspathContainer that requires to have kmodule.xml present in META-INF folder which 
     * defines the kjar itself.
     * @param kbaseName name of the kbase defined in kmodule.xml
     * @param ksessionName name of the ksession define in kmodule.xml   
     * @return new instance of <code>RuntimeEnvironmentBuilder</code> that is already preconfigured with defaults
     * 
     * @see DefaultRuntimeEnvironment
     */
    public RuntimeEnvironmentBuilder newClasspathKmoduleDefaultBuilder(String kbaseName, String ksessionName);

Besides KieSession Runtime Manager provides access to TaskService too as integrated component of a RuntimeEngine that will always be configured and ready for communication between process engine and task service.

Since the default builder was used, it will already come with predefined set of elements that consists of:

To extend it with your own handlers or listeners a dedicated mechanism is provided that comes as implementation of RegisterableItemsFactory

	/**
	 * Returns new instances of <code>WorkItemHandler</code> that will be registered on <code>RuntimeEngine</code>
	 * @param runtime provides <code>RuntimeEngine</code> in case handler need to make use of it internally
	 * @return map of handlers to be registered - in case of no handlers empty map shall be returned.
	 */
    Map<String, WorkItemHandler> getWorkItemHandlers(RuntimeEngine runtime);
    
    /**
	 * Returns new instances of <code>ProcessEventListener</code> that will be registered on <code>RuntimeEngine</code>
	 * @param runtime provides <code>RuntimeEngine</code> in case listeners need to make use of it internally
	 * @return list of listeners to be registered - in case of no listeners empty list shall be returned.
	 */
    List<ProcessEventListener> getProcessEventListeners(RuntimeEngine runtime);
    
    /**
	 * Returns new instances of <code>AgendaEventListener</code> that will be registered on <code>RuntimeEngine</code>
	 * @param runtime provides <code>RuntimeEngine</code> in case listeners need to make use of it internally
	 * @return list of listeners to be registered - in case of no listeners empty list shall be returned.
	 */
    List<AgendaEventListener> getAgendaEventListeners(RuntimeEngine runtime);
    
    /**
	 * Returns new instances of <code>WorkingMemoryEventListener</code> that will be registered on <code>RuntimeEngine</code>
	 * @param runtime provides <code>RuntimeEngine</code> in case listeners need to make use of it internally
	 * @return list of listeners to be registered - in case of no listeners empty list shall be returned.
	 */
    List<WorkingMemoryEventListener> getWorkingMemoryEventListeners(RuntimeEngine runtime);

A best practice is to just extend those that come out of the box and just add your own. Extensions are not always needed as the default implementations of RegisterableItemsFactory provides possibility to define custom handlers and listeners. Following is a list of available implementations that might be useful (they are ordered in the hierarchy of inheritance):

Alternatively, simple (stateless or requiring only KieSession) work item handlers might be registered in the well known way - defined as part of CustomWorkItem.conf file that shall be placed on class path. To use this approach do following:

And that's it, now all these work item handlers will be registered for any KieSession created by that application, regardless if it uses RuntimeManager or not.

When using RuntimeManager in CDI environment there are dedicated interfaces that can be used to provide custom WorkItemHandlers and EventListeners to the RuntimeEngine.

public interface WorkItemHandlerProducer {

    /**
     * Returns map of (key = work item name, value work item handler instance) of work items 
     * to be registered on KieSession
     * <br/>
     * Parameters that might be given are as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * 
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return map of work item handler instances (recommendation is to always return new instances when this method is invoked)
     */
    Map<String, WorkItemHandler> getWorkItemHandlers(String identifier, Map<String, Object> params);
}

Event listener producer shall be annotated with proper qualifier to indicate what type of listeners they provide, so pick one of following to indicate they type:

public interface EventListenerProducer<T> {

    /**
     * Returns list of instances for given (T) type of listeners
     * <br/>
     * Parameters that might be given are as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return list of listener instances (recommendation is to always return new instances when this method is invoked)
     */
    List<T> getEventListeners(String identifier, Map<String, Object>  params);
}

Implementations of these interfaces shall be packaged as bean archive (includes beans.xml inside META-INF) and placed on application classpath (e.g. WEB-INF/lib for web application). THat is enough for CDI based RuntimeManager to discover them and register on every KieSession that is created or loaded from data store.

Some parameters are provided to the producers to allow handlers/listeners to be more stateful and be able to do more advanced things with the engine - like signal of the engine or process instance in case of an error. Thus all components are provided:

In addition, some filtering can be applied based on identifier (that is given as argument to the methods) to decide if given RuntimeManager shall receive handlers/listeners or not.

On top of RuntimeManager API a set of high level services has been provided from jBPM version 6.2. These services are meant to be the easiest way to embed (j)BPM capabilities into custom application. A complete set of modules are delivered as part of these services. They are partitioned into several modules to ease thier adoptions in various environments.

Service modules are grouped with its framework dependencies, so developers are free to choose which one is suitable for them and use only that.

Upon deployment, every process definition is scanned using definition service that parses the process and extracts valuable information out of it. These information can provide valuable input to the system to inform users about what is expected. Definition service provides information about:

So definition service can be seen as sort of supporting service that provides quite a few information about process definition that are extracted directly from BPMN2.


String processId = "org.jbpm.writedocument";
         
Collection<UserTaskDefinition> processTasks = 
bpmn2Service.getTasksDefinitions(deploymentUnit.getIdentifier(), processId);
         
Map<String, String> processData = 
bpmn2Service.getProcessVariables(deploymentUnit.getIdentifier(), processId);
         
Map<String, String> taskInputMappings = 
bpmn2Service.getTaskInputMappings(deploymentUnit.getIdentifier(), processId, "Write a Document" );

While it usually is used with combination of other services (like deployment service) it can be used standalone as well to get details about process definition that do not come from kjar. This can be achieved by using buildProcessDefinition method of definition service.


public interface DefinitionService {
	
    ProcessDefinition buildProcessDefinition(String deploymentId, String bpmn2Content,
			ClassLoader classLoader, boolean cache) throws IllegalArgumentException;

    ProcessDefinition getProcessDefinition(String deploymentId, String processId);
    
    Collection<String> getReusableSubProcesses(String deploymentId, String processId);
    
    Map<String, String> getProcessVariables(String deploymentId, String processId);
    
    Map<String, String> getServiceTasks(String deploymentId, String processId);
    
    Map<String, Collection<String>> getAssociatedEntities(String deploymentId, String processId);
    
    Collection<UserTaskDefinition> getTasksDefinitions(String deploymentId, String processId);
    
    Map<String, String> getTaskInputMappings(String deploymentId, String processId, String taskName);
    
    Map<String, String> getTaskOutputMappings(String deploymentId, String processId, String taskName);
	
}

Process service is the one that usually is of the most interest. Once the deployment and definition service was already used to feed the system with something that can be executed. Process service provides access to execution environment that allows:

At the same time process service is a command executor so it allows to execute commands (essentially on ksession) to extend its capabilities.

Important to note is that process service is focused on runtime operations so use it whenever there is a need to alter (signal, change variables, etc) process instance and not for read operations like show available process instances by looping though given list and invoking getProcessInstance method. For that there is dedicated runtime data service that is described below.

An example on how to deploy and run process can be done as follows:


KModuleDeploymentUnit deploymentUnit = new KModuleDeploymentUnit(GROUP_ID, ARTIFACT_ID, VERSION);
         
deploymentService.deploy(deploymentUnit);
 
long processInstanceId = processService.startProcess(deploymentUnit.getIdentifier(), "customtask");
      
ProcessInstance pi = processService.getProcessInstance(processInstanceId);   

As you can see start process expects deploymentId as first argument. This is extremely powerful to enable service to easily work with various deployments, even with same processes but coming from different versions - kjar versions.


public interface ProcessService {
	
    Long startProcess(String deploymentId, String processId);

    Long startProcess(String deploymentId, String processId, Map<String, Object> params);

    void abortProcessInstance(Long processInstanceId);
    
    void abortProcessInstances(List<Long> processInstanceIds);

    void signalProcessInstance(Long processInstanceId, String signalName, Object event);
    
    void signalProcessInstances(List<Long> processInstanceIds, String signalName, Object event);
    
    ProcessInstance getProcessInstance(Long processInstanceId);

    void setProcessVariable(Long processInstanceId, String variableId, Object value);
    
    void setProcessVariables(Long processInstanceId, Map<String, Object> variables);
    
    Object getProcessInstanceVariable(Long processInstanceId, String variableName);

    Map<String, Object> getProcessInstanceVariables(Long processInstanceId);
    
    Collection<String> getAvailableSignals(Long processInstanceId);
    
    void completeWorkItem(Long id, Map<String, Object> results);

    void abortWorkItem(Long id);
    
    WorkItem getWorkItem(Long id);

    List<WorkItem> getWorkItemByProcessInstance(Long processInstanceId);
    
    public <T> T execute(String deploymentId, Command<T> command);
    
    public <T> T execute(String deploymentId, Context<?> context, Command<T> command);

}

Runtime data service as name suggests, deals with all that refers to runtime information:

Use this service as main source of information whenever building list based UI - to show process definitions, process instances, tasks for given user, etc. This service was designed to be as efficient as possible and still provide all required information.

Some examples:

There are two important arguments that the runtime data service operations supports:

These provide capabilities for efficient management result set like pagination, sorting and ordering (QueryContext). Moreover additional filtering can be applied to task queries to provide more advanced capabilities when searching for user tasks.


public interface RuntimeDataService {
  
    // Process instance information
    
    Collection<ProcessInstanceDesc> getProcessInstances(QueryContext queryContext);
   
    Collection<ProcessInstanceDesc> getProcessInstances(List<Integer> states, String initiator, QueryContext queryContext);
   
    Collection<ProcessInstanceDesc> getProcessInstancesByProcessId(List<Integer> states, String processId, String initiator, QueryContext queryContext);
   
    Collection<ProcessInstanceDesc> getProcessInstancesByProcessName(List<Integer> states, String processName, String initiator, QueryContext queryContext);
    
    Collection<ProcessInstanceDesc> getProcessInstancesByDeploymentId(String deploymentId, List<Integer> states, QueryContext queryContext);
    
    ProcessInstanceDesc getProcessInstanceById(long processInstanceId);
    
    Collection<ProcessInstanceDesc> getProcessInstancesByProcessDefinition(String processDefId, QueryContext queryContext);
    
    Collection<ProcessInstanceDesc> getProcessInstancesByProcessDefinition(String processDefId, List<Integer> states, QueryContext queryContext);

    
    // Node and Variable instance information
   
    NodeInstanceDesc getNodeInstanceForWorkItem(Long workItemId);

    Collection<NodeInstanceDesc> getProcessInstanceHistoryActive(long processInstanceId, QueryContext queryContext);

    Collection<NodeInstanceDesc> getProcessInstanceHistoryCompleted(long processInstanceId, QueryContext queryContext);

    Collection<NodeInstanceDesc> getProcessInstanceFullHistory(long processInstanceId, QueryContext queryContext);
    
    Collection<NodeInstanceDesc> getProcessInstanceFullHistoryByType(long processInstanceId, EntryType type, QueryContext queryContext);

    Collection<VariableDesc> getVariablesCurrentState(long processInstanceId);

    Collection<VariableDesc> getVariableHistory(long processInstanceId, String variableId, QueryContext queryContext);

    
    // Process information
  
    Collection<ProcessDefinition> getProcessesByDeploymentId(String deploymentId, QueryContext queryContext);   
    
    Collection<ProcessDefinition> getProcessesByFilter(String filter, QueryContext queryContext);

    Collection<ProcessDefinition> getProcesses(QueryContext queryContext);
   
    Collection<String> getProcessIds(String deploymentId, QueryContext queryContext);
   
    ProcessDefinition getProcessById(String processId);
  
    ProcessDefinition getProcessesByDeploymentIdProcessId(String deploymentId, String processId);
    
	// user task query operations

    UserTaskInstanceDesc getTaskByWorkItemId(Long workItemId);

    UserTaskInstanceDesc getTaskById(Long taskId);

    List<TaskSummary> getTasksAssignedAsBusinessAdministrator(String userId, QueryFilter filter);
	
    List<TaskSummary> getTasksAssignedAsBusinessAdministratorByStatus(String userId, List<Status> statuses, QueryFilter filter);

    List<TaskSummary> getTasksAssignedAsPotentialOwner(String userId, QueryFilter filter);
	
    List<TaskSummary> getTasksAssignedAsPotentialOwner(String userId, List<String> groupIds, QueryFilter filter);

    List<TaskSummary> getTasksAssignedAsPotentialOwnerByStatus(String userId, List<Status> status, QueryFilter filter);
	
    List<TaskSummary> getTasksAssignedAsPotentialOwner(String userId, List<String> groupIds, List<Status> status, QueryFilter filter);
	
    List<TaskSummary> getTasksAssignedAsPotentialOwnerByExpirationDateOptional(String userId, List<Status> status, Date from, QueryFilter filter);
	
    List<TaskSummary> getTasksOwnedByExpirationDateOptional(String userId, List<Status> strStatuses, Date from, QueryFilter filter);

    List<TaskSummary> getTasksOwned(String userId, QueryFilter filter);

    List<TaskSummary> getTasksOwnedByStatus(String userId, List<Status> status, QueryFilter filter);

    List<Long> getTasksByProcessInstanceId(Long processInstanceId);

    List<TaskSummary> getTasksByStatusByProcessInstanceId(Long processInstanceId, List<Status> status, QueryFilter filter);
        
    List<AuditTask> getAllAuditTask(String userId, QueryFilter filter);
	    
}

QueryService provides advanced search capabilities that are based on Dashbuilder DataSets. The concept behind it is that users are given control over how to retrieve data from underlying data store. This includes complex joins with external tables such as JPA entities tables, custom systems data base tables etc.

QueryService is build around two parts:

DashBuilder DataSets provide support for multiple data sources (CSV, SQL, elastic search, etc) while jBPM - since its backend is RDBMS based - focuses on SQL based data sets. So jBPM QueryService is a subset of DashBuilder DataSets capabilities to allow efficient queries with simple API.

Terminology

While QueryDefinition and QueryParam is rather straight forward, QueryParamBuilder and QueryResultMapper is bit more advanced and require slightly more attention to make use of it in right way, and by that take advantage of their capabilities.

QueryResultMapper

Mapper as the name suggest, maps data taken out from data base (from data set) into object representation. Much like ORM providers such as hibernate maps tables to entities. Obviously there might be many object types that could be used for representing data set results so it's almost impossible to provide them out of the box. Mappers are rather powerful and thus are pluggable, you can implement your own that will transform the result into whatever type you like. jBPM comes with following mappers out of the box:

Each QueryResultMapper is registered under given name to allow simple look up by name instead of referencing its class name - especially important when using EJB remote flavor of services where we want to reduce number of dependencies and thus not relying on implementation on client side. So to be able to reference QueryResultMapper by name, NamedQueryMapper should be used which is part of jbpm-services-api. That acts as delegate (lazy delegate) as it will look up the actual mapper when the query is actually performed.


queryService.query("my query def", new NamedQueryMapper<Collection<ProcessInstanceDesc>>("ProcessInstances"), new QueryContext());

QueryParamBuilder

QueryParamBuilder that provides more advanced way of building filters for our data sets. By default when using query method of QueryService that accepts zero or more QueryParam instances (as we have seen in above examples) all of these params will be joined with AND operator meaning all of them must match. But that's not always the case so that's why QueryParamBuilder has been introduced for users to build their on builders which will provide filters at the time the query is issued.

There is one QueryParamBuilder available out of the box and it is used to cover default QueryParams that are based on so called core functions. These core functions are SQL based conditions and includes following

QueryParamBuilder is simple interface that is invoked as long as its build method returns non null value before query is performed. So you can build up a complex filter options that could not be simply expressed by list of QueryParams. Here is basic implementation of QueryParamBuilder to give you a jump start to implement your own - note that it relies on DashBuilder Dataset API.


public class TestQueryParamBuilder implements QueryParamBuilder<ColumnFilter> {
 
    private Map<String, Object> parameters;
    private boolean built = false;
    public TestQueryParamBuilder(Map<String, Object> parameters) {
        this.parameters = parameters;
    }
     
    @Override
    public ColumnFilter build() {
        // return null if it was already invoked
        if (built) {
            return null;
        }
         
        String columnName = "processInstanceId";
         
        ColumnFilter filter = FilterFactory.OR(
                FilterFactory.greaterOrEqualsTo((Long)parameters.get("min")),
                FilterFactory.lowerOrEqualsTo((Long)parameters.get("max")));
        filter.setColumnId(columnName);
        
        built = true;
        return filter;
    }
 
}

Once you have query param builder implemented you simply use its instance when performing query via QueryService


queryService.query("my query def", ProcessInstanceQueryMapper.get(), new QueryContext(), paramBuilder);

Typical usage scenario

First thing user needs to do is to define data set - view of the data you want to work with - so called QueryDefinition in services api.


SqlQueryDefinition query = new SqlQueryDefinition("getAllProcessInstances", "java:jboss/datasources/ExampleDS");
query.setExpression("select * from processinstancelog");
       

This is the simplest possible query definition as it can be:

Once we have the sql query definition we can register it so it can be used later for actual queries.


queryService.registerQuery(query);

From now on, this query definition can be used to perform actual queries (or data look ups to use terminology from data sets). Following is the basic one that collects data as is, without any filtering


Collection<ProcessInstanceDesc> instances = queryService.query("getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext());

Above query was very simple and used defaults from QueryContext - paging and sorting. So let's take a look at one that changes the defaults of the paging and sorting


QueryContext ctx = new QueryContext(0, 100, "start_date", true);
         
Collection<ProcessInstanceDesc> instances = queryService.query("getAllProcessInstances", ProcessInstanceQueryMapper.get(), ctx);

Now let's take a look at how to do data filtering


// single filter param
Collection<ProcessInstanceDesc> instances = queryService.query("getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext(), QueryParam.likeTo(COLUMN_PROCESSID, true, "org.jbpm%"));
 
// multiple filter params (AND)
Collection<ProcessInstanceDesc> instances = queryService.query("getAllProcessInstances", ProcessInstanceQueryMapper.get(), new QueryContext(),
 QueryParam.likeTo(COLUMN_PROCESSID, true, "org.jbpm%"),
 QueryParam.in(COLUMN_STATUS, 1, 3));

With that end user is put in driver seat to define what data and how they should be fetched. Not being limited by JPA provider nor anything else. Moreover this promotes use of tailored queries for your environment as in most of the case there will be single data base used and thus specific features of that data base can be used to increase performance.

Further examples can be found here.

ProcessInstanceMigrationService provides administrative utility to move given process instance(s) from one deployment to another or one process definition to another. It’s main responsibility is to allow basic upgrade of process definition behind given process instance. That might include mapping of currently active nodes to other nodes in new definition.

Migration does not deal with process or task variables, they are not affected by migration. Essentially process instance migration means a change of underlying process definition process engine uses to move on with process instance.

Even though process instance migration is available it’s recommended to let active process instances finish and then start new instances with new version whenever possible. In case that approach can’t be used, migration of active process instance needs to be carefully planned before its execution as it might lead to unexpected issues.Most important to take into account is:

Answers to these questions might save a lot of headache and production problems after migration. Best is to always stick with backward compatible processes - like extending process definition rather than removing nodes. Though that’s not always possible and in some cases there is a need to remove certain nodes from process definition. In that situation, migration needs to be instructed how to map nodes that were removed in new definition in case active process instance is at the moment in such a node.

Node mapping is given as a map of node ids (UniqueIds that are set in the definition) where key is the source node id (from process definition used by process instance) to target node id (in new process definition).

Again, process or task variables are not affected by process instance migration at the moment.

ProcessInstanceMigrationService comes with several flavors of migrate operation:


public interface ProcessInstanceMigrationService {
/**
* Migrates given process instance that belongs to source deployment, into target process id that belongs to target deployment.
* Following rules are enforced:
* <ul>
* <li>source deployment id must be there</li>
* <li>process instance id must point to existing and active process instance</li>
* <li>target deployment must exist</li>
* <li>target process id must exist in target deployment</li>
* </ul>
* Migration returns migration report regardless of migration being successful or not that needs to be examined for migration outcome.
* @param sourceDeploymentId deployment that process instance to be migrated belongs to
* @param processInstanceId id of the process instance to be migrated
* @param targetDeploymentId id of deployment that target process belongs to
* @param targetProcessId id of the process process instance should be migrated to
* @return returns complete migration report
*/
MigrationReport migrate(String sourceDeploymentId, Long processInstanceId, String targetDeploymentId, String targetProcessId);
/**
* Migrates given process instance (with node mapping) that belongs to source deployment, into target process id that belongs to target deployment.
* Following rules are enforced:
* <ul>
* <li>source deployment id must be there</li>
* <li>process instance id must point to existing and active process instance</li>
* <li>target deployment must exist</li>
* <li>target process id must exist in target deployment</li>
* </ul>
* Migration returns migration report regardless of migration being successful or not that needs to be examined for migration outcome.
* @param sourceDeploymentId deployment that process instance to be migrated belongs to
* @param processInstanceId id of the process instance to be migrated
* @param targetDeploymentId id of deployment that target process belongs to
* @param targetProcessId id of the process process instance should be migrated to
* @param nodeMapping node mapping - source and target unique ids of nodes to be mapped - from process instance active nodes to new process nodes
* @return returns complete migration report
*/
MigrationReport migrate(String sourceDeploymentId, Long processInstanceId, String targetDeploymentId, String targetProcessId, Map<String, String> nodeMapping);
/**
* Migrates given process instances that belong to source deployment, into target process id that belongs to target deployment.
* Following rules are enforced:
* <ul>
* <li>source deployment id must be there</li>
* <li>process instance id must point to existing and active process instance</li>
* <li>target deployment must exist</li>
* <li>target process id must exist in target deployment</li>
* </ul>
* Migration returns list of migration report - one per process instance, regardless of migration being successful or not that needs to be examined for migration outcome.
* @param sourceDeploymentId deployment that process instance to be migrated belongs to
* @param processInstanceIds list of process instance id to be migrated
* @param targetDeploymentId id of deployment that target process belongs to
* @param targetProcessId id of the process process instance should be migrated to
* @return returns complete migration report
*/
List<MigrationReport> migrate(String sourceDeploymentId, List<Long> processInstanceIds, String targetDeploymentId, String targetProcessId);
 /**
 * Migrates given process instances (with node mapping) that belong to source deployment, into target process id that belongs to target deployment.
 * Following rules are enforced:
 * <ul>
 * <li>source deployment id must be there</li>
 * <li>process instance id must point to existing and active process instance</li>
 * <li>target deployment must exist</li>
 * <li>target process id must exist in target deployment</li>
 * </ul>
 * Migration returns list of migration report - one per process instance, regardless of migration being successful or not that needs to be examined for migration outcome.
 * @param sourceDeploymentId deployment that process instance to be migrated belongs to
 * @param processInstanceIds list of process instance id to be migrated
 * @param targetDeploymentId id of deployment that target process belongs to
 * @param targetProcessId id of the process process instance should be migrated to
 * @param nodeMapping node mapping - source and target unique ids of nodes to be mapped - from process instance active nodes to new process nodes
 * @return returns list of migration reports one per each process instance
 */
 List<MigrationReport> migrate(String sourceDeploymentId, List<Long> processInstanceIds, String targetDeploymentId, String targetProcessId, Map<String, String> nodeMapping);
}

Migration can either be performed for single process instance or multiple process instances at the same time. Multiple process instances migration is a utility method on top of single instance, instead of calling it multiple times, users call it once and then service will take care of the migration of individual process instances.

Deployment Service provides convinient way to put business assets to an execution environment but there are cases that requires some additional management to make them available in right context.

Activation and Deactivation of deployments

Imagine situation where there are number of processes already running of given deployment and then new version of these processes comes into the runtime environment. With that administrator can decide that new instances of given process definition should be using new version only while already active instances should continue with the previous version.

To help with that deployment service has been equipped with following methods:

This feature allows smooth transition between project versions whitout need of process instance migration.

Deployment synchronization

Prior to jBPM 6.2, jbpm services did not have deployment store by default. When embedded in jbpm-console/kie-wb they utilized sistem.git VFS repository to preserve deployed units across server restarts. While that works fine, it comes with some drawbacks:

With version 6.2 jbpm services come with deployment synchronizer that stores available deployments into data base, including its deployment descriptor. At the same time it constantly monitors that table to keep it in sync with other installations that might be using same data source. This is especially important when running in cluster or when jbpm console runs next to custom application and both should be able to operate on the same artifacts.

By default synchronization must be configured (when runing as core services while it is automatically enabled for ejb and cdi extensions). To configure synchronization following needs to be configured:


TransactionalCommandService commandService = new TransactionalCommandService(emf);

DeploymentStore store = new DeploymentStore();
store.setCommandService(commandService);

DeploymentSynchronizer sync = new DeploymentSynchronizer();
sync.setDeploymentService(deploymentService);
sync.setDeploymentStore(store);

DeploymentSyncInvoker invoker = new DeploymentSyncInvoker(sync, 2L, 3L, TimeUnit.SECONDS);
invoker.start();
....
invoker.stop();

With this, deployments will be synchronized every 3 seconds with initial delay of two seconds.

Invoking latest version of project's processes

In case there is a need to always work with latest version of project's process, services allow to interact with various operations using deployment id with latest keyword. Let's go over an example to better understand the feature.

Initially deployed unit is org.jbpm:HR:1.0 which has the first version of an hiring process. After several weeks, new version is developed and deployed to the execution server - org.jbpm:HR.2.0 with version 2 of the hiring process.

To allow callers of the services to interact without being worried if they work with latest version, they can use following deployment id:

org.jbpm.HR:latest

this will alwyas find out latest available version of project that is identified by:

version comparizon is based on Maven version numbers and relies on Maen based algorithm to find the latest one.

Here is a complete example with deployment of multiple versions and interacting always with the latest:


KModuleDeploymentUnit deploymentUnitV1 = new KModuleDeploymentUnit("org.jbpm", "HR", "1.0");
deploymentService.deploy(deploymentUnitV1);

long processInstanceId = processService.startProcess("org.jbpm:HR:LATEST", "customtask");
ProcessInstanceDesc piDesc = runtimeDataService.getProcessInstanceById(processInstanceId); 

// we have started process with project's version 1
assertEquals(deploymentUnitV1.getIdentifier(), piDesc.getDeploymentId());

// next we deploy version 1
KModuleDeploymentUnit deploymentUnitV2 = new KModuleDeploymentUnit("org.jbpm", "HR", "2.0");
deploymentService.deploy(deploymentUnitV2);

processInstanceId = processService.startProcess("org.jbpm:HR:LATEST", "customtask");
piDesc = runtimeDataService.getProcessInstanceById(processInstanceId); 

// this time we have started process with project's version 2
assertEquals(deploymentUnitV2.getIdentifier(), piDesc.getDeploymentId());

As illustrated this provides very powerful feature when interacting with frequently chaning environment that allows to always be up to date when it comes to use of process definitions.

There are several control parameters available to alter engine default behavior. This allows to fine tune the execution for the environment needs and actual requirements. All of these parameters are set as JVM system properties, usually with -D when starting program e.g. application server.

Table 5.1. Control parameters

NamePossible valuesDefault valueDescription 
jbpm.ut.jndi.lookupString Alternative JNDI name to be used when there is no access to the default one (java:comp/UserTransaction) 
jbpm.enable.multi.contrue|falsefalseEnables multiple incoming/outgoing sequence flows support for activities 
jbpm.business.calendar.propertiesString/jbpm.business.calendar.propertiesAllows to provide alternative classpath location of business calendar configuration file 
jbpm.overdue.timer.delayLong2000Specifies delay for overdue timers to allow proper initialization, in milliseconds 
jbpm.process.name.comparatorString Allows to provide alternative comparator class to empower start process by name feature, if not set NumberVersionComparator is used 
jbpm.loop.level.disabledtrue|falsetrueAllows to enable or disable loop iteration tracking, to allow advanced loop support when using XOR gateways 
org.kie.mail.sessionStringmail/jbpmMailSessionAllows to provide alternative JNDI name for mail session used by Task Deadlines 
jbpm.usergroup.callback.propertiesString/jbpm.usergroup.callback.propertiesAllows to provide alternative classpath location for user group callback implementation (LDAP, DB) 
jbpm.user.group.mappingString${jboss.server.config.dir}/roles.propertiesAllows to provide alternative location of roles.properties for JBossUserGroupCallbackImpl 
jbpm.user.info.propertiesString/jbpm.user.info.propertiesAllows to provide alternative classpath location of user info configuration (used by LDAPUserInfoImpl) 
org.jbpm.ht.user.separatorString,Allows to provide alternative separator of actors and groups for user tasks, default is comma (,) 
org.quartz.propertiesString Allows to provide location of the quartz config file to activate quartz based timer service 
jbpm.data.dirString${jboss.server.data.dir} is available otherwise ${java.io.tmpdir}Allows to provide location where data files produced by jbpm should be stored 
org.kie.executor.pool.sizeInteger1Allows to provide thread pool size for jbpm executor 
org.kie.executor.retry.countInteger3Allows to provide number of retries attempted in case of error by jbpm executor 
org.kie.executor.intervalInteger3Allows to provide frequency used to check for pending jobs by jbpm executor, in seconds 
org.kie.executor.disabledtrue|falsetrueEnables or disable jbpm executor 
org.kie.store.services.classStringorg.drools.persistence.jpa.KnowledgeStoreServiceImplFully qualified name of the class that implements KieStoreServices that will be responsible for bootstraping KieSession instances 

The Business Process Model and Notation (BPMN) 2.0 specification is an OMG specification that not only defines a standard on how to graphically represent a business process (like BPMN 1.x), but now also includes execution semantics for the elements defined, and an XML format on how to store (and share) process definitions.

jBPM6 allows you to execute processes defined using the BPMN 2.0 XML format. That means that you can use all the different jBPM6 tooling to model, execute, manage and monitor your business processes using the BPMN 2.0 format for specifying your executable business processes. Actually, the full BPMN 2.0 specification also includes details on how to represent things like choreographies and collaboration. The jBPM project however focuses on that part of the specification that can be used to specify executable processes.

Executable processes in BPMN consist of a different types of nodes being connected to each other using sequence flows. The BPMN 2.0 specification defines three main types of nodes:

jBPM6 does not implement all elements and attributes as defined in the BPMN 2.0 specification. We do however support a significant subset, including the most common node types that can be used inside executable processes. This includes (almost) all elements and attributes as defined in the "Common Executable" subclass of the BPMN 2.0 specification, extended with some additional elements and attributes we believe are valuable in that context as well. The full set of elements and attributes that are supported can be found below, but it includes elements like:

For example, consider the following "Hello World" BPMN 2.0 process, which does nothing more that writing out a "Hello World" statement when the process is started.

An executable version of this process expressed using BPMN 2.0 XML would look something like this:

<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.example.org/MinimalExample"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
             xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="com.sample.HelloWorld" name="Hello World" >

    <!-- nodes -->
    <startEvent id="_1" name="StartProcess" />
    <scriptTask id="_2" name="Hello" >
      <script>System.out.println("Hello World");</script>
    </scriptTask>
    <endEvent id="_3" name="EndProcess" >
        <terminateEventDefinition/>
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="Minimal" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="15" y="91" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="95" y="88" width="83" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="258" y="86" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_2" >
        <di:waypoint x="39" y="115" />
        <di:waypoint x="75" y="46" />
        <di:waypoint x="136" y="112" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_3" >
        <di:waypoint x="136" y="112" />
        <di:waypoint x="240" y="240" />
        <di:waypoint x="282" y="110" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>

To create your own process using BPMN 2.0 format, you can

The following code fragment shows you how to load a BPMN2 process into your knowledge base ...


private static KnowledgeBase createKnowledgeBase() throws Exception {
    KieHelper kieHelper = new KieHelper();
    KieBase kieBase = kieHelper
    .addResource(ResourceFactory.newClassPathResource("sample.bpmn2"))
    .build();

    return kieBase;
}

... and how to execute this process ...

KieBase kbase = createKnowledgeBase();
KieSession ksession = kbase.newKieSession();
ksession.startProcess("com.sample.HelloWorld");

For more detail, check out the chapter on the API and the basics.


A business process is a graph that describes the order in which a series of steps need to be executed, using a flow chart. A process consists of a collection of nodes that are linked to each other using connections. Each of the nodes represents one step in the overall process while the connections specify how to transition from one node to the other. A large selection of predefined node types have been defined. This chapter describes how to define such processes and use them in your application.

Processes can be created by using one of the following three methods:

The graphical BPMN2 editor is an editor that allows you to create a process by dragging and dropping different nodes on a canvas and editing the properties of these nodes. The graphical BPMN2 modeler is an Eclipse plugin hosted on eclipse.org that provides number of contributors where one of them is jBPM project. Once you have set up a jBPM project (see the installer for creating a working Eclipse environment where you can start), you can start adding processes. When in a project, launch the "New" wizard (use Ctrl+N) or right-click the directory you would like to put your process in and select "New", then "File". Give the file a name and the extension bpmn (e.g. MyProcess.bpmn). This will open up the process editor (you can safely ignore the warning that the file could not be read, this is just because the file is still empty).

First, ensure that you can see the Properties View down the bottom of the Eclipse window, as it will be necessary to fill in the different properties of the elements in your process. If you cannot see the properties view, open it using the menu "Window", then "Show View" and "Other...", and under the "General" folder select the Properties View.


The process editor consists of a palette, a canvas and an outline view. To add new elements to the canvas, select the element you would like to create in the palette and then add them to the canvas by clicking on the preferred location. For example, click on the "End Event" icon in the palette of the GUI. Clicking on an element in your process allows you to set the properties of that element. You can connect the nodes (as long as it is permitted by the different types of nodes) by using "Sequence Flow" from the palette.

You can keep adding nodes and connections to your process until it represents the business logic that you want to specify.

It is also possible to specify processes using the underlying BPMN 2.0 XML directly. The syntax of these XML processes is defined using the BPMN 2.0 XML Schema Definition. For example, the following XML fragment shows a simple process that contains a sequence of a Start Event, a Script Task that prints "Hello World" to the console, and an End Event.

<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.jboss.org/drools"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"Rule Task
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:g="http://www.jboss.org/drools/flow/gpd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="com.sample.hello" name="Hello Process" >

    <!-- nodes -->
    <startEvent id="_1" name="Start" />
    <scriptTask id="_2" name="Hello" >
      <script>System.out.println("Hello World");</script>
    </scriptTask>
    <endEvent id="_3" name="End" >
        <terminateEventDefinition/>
    </endEvent>

    <!-- connections -->
    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
    <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="com.sample.hello" >
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="16" y="16" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="96" y="16" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="208" y="16" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_1-_2" >
        <di:waypoint x="40" y="40" />
        <di:waypoint x="136" y="40" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_3" >
        <di:waypoint x="136" y="40" />
        <di:waypoint x="232" y="40" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>

The process XML file consists of two parts, the top part (the "process" element) contains the definition of the different nodes and their properties, the lower part (the "BPMNDiagram" element) contains all graphical information, like the location of the nodes. The process XML consist of exactly one <process> element. This element contains parameters related to the process (its type, name, id and package name), and consists of three subsections: a header section (where process-level information like variables, globals, imports and lanes can be defined), a nodes section that defines each of the nodes in the process, and a connections section that contains the connections between all the nodes in the process. In the nodes section, there is a specific element for each node, defining the various parameters and, possibly, sub-elements for that node type.



Represents a script that should be executed in this process. A Script Task should have one incoming connection and one outgoing connection. The associated action specifies what should be executed, the dialect used for coding the action (i.e., Java, JavaScript or MVEL), and the actual action code. This code can access any variables and globals. There is also a predefined variable kcontext that references the ProcessContext object (which can, for example, be used to access the current ProcessInstance or NodeInstance, and to get and set variables, or get access to the ksession using kcontext.getKieRuntime()). When a Script Task is reached in the process, it will execute the action and then continue with the next node. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Action: The action script associated with this action node.

Note that you can write any valid Java code inside a script node. This basically allows you to do anything inside such a script node. There are some caveats however:

  • When trying to create a higher-level business process, that should also be understood by business users, it is probably wise to avoid low-level implementation details inside the process, including inside these script tasks. A Script Task could still be used to quickly manipulate variables etc. but other concepts like a Service Task could be used to model more complex behaviour in a higher-level manner.
  • Scripts should be immediate. They are using the engine thread to execute the script. Scripts that could take some time to execute should probably be modeled as an asynchronous Service Task.
  • You should try to avoid contacting external services through a script node. Not only does this usually violate the first two caveats, it is also interacting with external services without the knowledge of the engine, which can be problematic, especially when using persistence and transactions. In general, it is probably wiser to model communication with an external service using a service task.
  • Scripts should not throw exceptions. Runtime exceptions should be caught and for example managed inside the script or transformed into signals or errors that can then be handled inside the process.


Represents an (abstract) unit of work that should be executed in this process. All work that is executed outside the process engine should be represented (in a declarative way) using a Service Task. Different types of services are predefined, e.g., sending an email, logging a message, etc. Users can define domain-specific services or work items, using a unique name and by defining the parameters (input) and results (output) that are associated with this type of work. Check the chapter on domain-specific processes for a detailed explanation and illustrative examples of how to define and use work items in your processes. When a Service Task is reached in the process, the associated work is executed. A Service Task should have one incoming connection and one outgoing connection.

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Parameter mapping: Allows copying the value of process variables to parameters of the work item. Upon creation of the work item, the values will be copied.

  • Result mapping: Allows copying the value of result parameters of the work item to a process variable. Each type of work can define result parameters that will (potentially) be returned after the work item has been completed. A result mapping can be used to copy the value of the given result parameter to the given variable in this process. For example, the "FileFinder" work item returns a list of files that match the given search criteria within the result parameter Files. This list of files can then be bound to a process variable for use within the process. Upon completion of the work item, the values will be copied.

  • On-entry and on-exit actions: Actions that are executed upon entry or exit of this node, respectively.

  • Additional parameters: Each type of work item can define additional parameters that are relevant for that type of work. For example, the "Email" work item defines additional parameters such as From, To, Subject and Body. The user can either provide values for these parameters directly, or define a parameter mapping that will copy the value of the given variable in this process to the given parameter; if both are specified, the mapping will have precedence. Parameters of type String can use #{expression} to embed a value in the string. The value will be retrieved when creating the work item, and the substitution expression will be replaced by the result of calling toString() on the variable. The expression could simply be the name of a variable (in which case it resolves to the value of the variable), but more advanced MVEL expressions are possible as well, e.g., #{person.name.firstname}.


Processes can also involve tasks that need to be executed by human actors. A User Task represents an atomic task to be executed by a human actor. It should have one incoming connection and one outgoing connection. User Tasks can be used in combination with Swimlanes to assign multiple human tasks to similar actors. Refer to the chapter on human tasks for more details. A User Task is actually nothing more than a specific type of service node (of type "Human Task"). A User Task contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • TaskName: The name of the human task.

  • Priority: An integer indicating the priority of the human task.

  • Comment: A comment associated with the human task.

  • ActorId: The actor id that is responsible for executing the human task. A list of actor id's can be specified using a comma (',') as separator.

  • GroupId: The group id that is responsible for executing the human task. A list of group id's can be specified using a comma (',') as separator.

  • Skippable: Specifies whether the human task can be skipped, i.e., whether the actor may decide not to execute the task.

  • Content: The data associated with this task.

  • Swimlane: The swimlane this human task node is part of. Swimlanes make it easy to assign multiple human tasks to the same actor. See the human tasks chapter for more detail on how to use swimlanes.

  • On entry and on exit actions: Action scripts that are executed upon entry and exit of this node, respectively.

  • Parameter mapping: Allows copying the value of process variables to parameters of the human task. Upon creation of the human tasks, the values will be copied.

  • Result mapping: Allows copying the value of result parameters of the human task to a process variable. Upon completion of the human task, the values will be copied. A human task has a result variable "Result" that contains the data returned by the human actor. The variable "ActorId" contains the id of the actor that actually executed the task.

A user task should define the type of task that needs to be executed (using properties like TaskName, Comment, etc.) and who needs to perform it (using either actorId or groupId). Note that if there is data related to this specific process instance that the end user needs when performing the task, this data should be passed as the content of the task. The task for example does not have access to process variables. Check out the chapter on human tasks to get more detail on how to pass data between human tasks and the process instance.


Represents the invocation of another process from within this process. A sub-process node should have one incoming connection and one outgoing connection. When a Reusable Sub-Process node is reached in the process, the engine will start the process with the given id. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • ProcessId: The id of the process that should be executed.

  • Wait for completion (by default true): If this property is true, this sub-process node will only continue if the child process that was started has terminated its execution (completed or aborted); otherwise it will continue immediately after starting the subprocess (so it will not wait for its completion).

  • Independent (by default true): If this property is true, the child process is started as an independent process, which means that the child process will not be terminated if this parent process is completed (or this sub-process node is canceled for some other reason); otherwise the active sub-process will be canceled on termination of the parent process (or cancellation of the sub-process node). Note that you can only set independent to "false" only when "Wait for completion" is set to true.

  • On-entry and on-exit actions: Actions that are executed upon entry or exit of this node, respectively.

  • Parameter in/out mapping: A sub-process node can also define in- and out-mappings for variables. The variables given in the "in" mapping will be used as parameters (with the associated parameter name) when starting the process. The variables of the child process that are defined for the "out" mappings will be copied to the variables of this process when the child process has been completed. Note that you can use "out" mappings only when "Wait for completion" is set to true.


A Multiple Instance sub-process is a special kind of sub-process that allows you to execute the contained process segment multiple times, once for each element in a collection. A multiple instance sub-process should have one incoming connection and one outgoing connection. It waits until the embedded process fragment is completed for each of the elements in the given collection before continuing. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • CollectionExpression: The name of a variable that represents the collection of elements that should be iterated over. The collection variable should be an array or of type java.util.Collection. If the collection expression evaluates to null or an empty collection, the multiple instances sub-process will be completed immediately and follow its outgoing connection.

  • VariableName: The name of the variable to contain the current element from the collection. This gives nodes within the composite node access to the selected element.

  • CollectionOutput: The name of a variable that represents collection of elements that will gather all output of the multi instance sub process

  • OutputVariableName: The name of the variable to contain the currentl output from the multi instance activitiy

  • CompletionCondition: MVEL expression that will be evaluated on each instance completion to check if given multi instance activity can already be completed. In case it evaluates to true all other remaining instances within multi instance activity will be canceled.


Represents a timer that can trigger one or multiple times after a given period of time. A Timer Event should have one incoming connection and one outgoing connection. The timer delay specifies how long the timer should wait before triggering the first time. When a Timer Event is reached in the process, it will start the associated timer. The timer is canceled if the timer node is canceled (e.g., by completing or aborting the enclosing process instance). Consult the section ???” for more information. The Timer Event contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Timer delay: The delay that the node should wait before triggering the first time. The expression should be of the form [#d][#h][#m][#s][#[ms]]. This allows you to specify the number of days, hours, minutes, seconds and milliseconds (which is the default if you don't specify anything). For example, the expression "1h" will wait one hour before triggering the timer. The expression could also use #{expr} to dynamically derive the delay based on some process variable. Expr in this case could be a process variable, or a more complex expression based on a process variable (e.g. myVariable.getValue()). It does support CRON like expression as well.

  • Timer period: The period between two subsequent triggers. If the period is 0, the timer should only be triggered once. The expression should be of the form [#d][#h][#m][#s][#[ms]]. You can specify the number of days, hours, minutes, seconds and milliseconds (which is the default if you don't specify anything). For example, the expression "1h" will wait one hour before triggering the timer again. The expression could also use #{expr} to dynamically derive the period based on some process variable. Expr in this case could be a process variable, or a more complex expression based on a process variable (e.g. myVariable.getValue()).

Timer events could also be specified as boundary events on sub-processes and tasks that are not automatic tasks like script task that have no wait state as timer will not have a change to fire before task completion.


A Signal Event can be used to respond to internal or external events during the execution of the process. A Signal Event should have one incoming connections and one outgoing connection. It specifies the type of event that is expected. Whenever that type of event is detected, the node connected to this event node will be triggered. It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • EventType: The type of event that is expected.

  • VariableName: The name of the variable that will contain the data associated with this event (if any) when this event occurs.

A process instance can be signaled that a specific event occurred using

ksession.signalEvent(eventType, data, processInstanceId)

This will trigger all (active) signal event nodes in the given process instance that are waiting for that event type. Data related to the event can be passed using the data parameter. If the event node specifies a variable name, this data will be copied to that variable when the event occurs.

It is also possible to use event nodes inside sub-processes. These event nodes will however only be active when the sub-process is active.

You can also generate a signal from inside a process instance. A script (in a script task or using on entry or on exit actions) can use


kcontext.getKieRuntime().signalEvent(eventType, data, kcontext.getProcessInstance().getId());
        

A throwing signal event could also be used to model the signaling of an event.


Allows you to create branches in your process. A Diverging Gateway should have one incoming connection and two or more outgoing connections. There are three types of gateway nodes currently supported:

  • AND or parallel means that the control flow will continue in all outgoing connections simultaneously.

  • XOR or exclusive means that exactly one of the outgoing connections will be chosen. The decision is made by evaluating the constraints that are linked to each of the outgoing connections. The constraint with the lowest priority number that evaluates to true is selected. Constraints can be specified using different dialects. Note that you should always make sure that at least one of the outgoing connections will evaluate to true at runtime (the engine will throw an exception at runtime if it cannot find at least one outgoing connection).

  • OR or inclusive means that all outgoing connections whose condition evaluates to true are selected. Conditions are similar to the exclusive gateway, except that no priorities are taken into account. Note that you should make sure that at least one of the outgoing connections will evaluate to true at runtime because the engine will throw an exception at runtime if it cannot determine an outgoing connection.

It contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • Type: The type of the split node, i.e., AND, XOR or OR (see above).

  • Constraints: The constraints linked to each of the outgoing connections (in case of an exclusive or inclusive gateway).

While the flow chart focuses on specifying the control flow of the process, it is usually also necessary to look at the process from a data perspective. Throughout the execution of a process, data can be retrieved, stored, passed on and used.

For storing runtime data, during the execution of the process, process variables can be used. A variable is defined by a name and a data type. This could be a basic data type, such as boolean, int, or String, or any kind of Object subclass (it must implement Serializable interface). Variables can be defined inside a variable scope. The top-level scope is the variable scope of the process itself. Subscopes can be defined using a Sub-Process. Variables that are defined in a subscope are only accessible for nodes within that scope.

Whenever a variable is accessed, the process will search for the appropriate variable scope that defines the variable. Nesting of variable scopes is allowed. A node will always search for a variable in its parent container. If the variable cannot be found, it will look in that one's parent container, and so on, until the process instance itself is reached. If the variable cannot be found, a read access yields null, and a write access produces an error message, with the process continuing its execution.

Variables can be used in various ways:

Finally, processes (and rules) all have access to globals, i.e. globally defined variables and data in the Knowledge Session. Globals are directly accessible in actions just like variables. Globals need to be defined as part of the process before they can be used. You can for example define globals by clicking the globals button when specifying an action script in the Eclipse action property editor. You can also set the value of a global from the outside using ksession.setGlobal(name, value) or from inside process scripts using kcontext.getKieRuntime().setGlobal(name,value);.

Action scripts can be used in different ways:

Actions have access to globals and the variables that are defined for the process and the predefined variable kcontext. This variable is of type ProcessContext and can be used for several tasks:

  • Getting the current node instance (if applicable). The node instance could be queried for data, such as its name and type. You can also cancel the current node instance.

    NodeInstance node = kcontext.getNodeInstance();
    String name = node.getNodeName();
  • Getting the current process instance. A process instance can be queried for data (name, id, processId, etc.), aborted or signaled an internal event.

    ProcessInstance proc = kcontext.getProcessInstance();
    proc.signalEvent( type, eventObject );
  • Getting or setting the value of variables.

  • Accessing the Knowledge Runtime allows you do things like starting a process, signaling (external) events, inserting data, etc.

jBPM supports multiple dialects, like Java, JavaScript and MVEL. Java actions should be valid Java code, same for JavaScript. MVEL actions can use the business scripting language MVEL to express the action. MVEL accepts any valid Java code but additionally provides support for nested accesses of parameters (e.g., person.name instead of person.getName()), and many other scripting improvements. Thus, MVEL expressions are more convenient for the business user. For example, an action that prints out the name of the person in the "requester" variable of the process would look like this:

// Java dialect
System.out.println( person.getName() );

// JavaScript dialect
print(person.name + '\n);

//  MVEL dialect
System.out.println( person.name );
    

Constraints can be used in various locations in your processes, for example in a diverging gateway. jBPM supports two types of constraints:

Rule constraints do not have direct access to variables defined inside the process. It is however possible to refer to the current process instance inside a rule constraint, by adding the process instance to the Working Memory and matching for the process instance in your rule constraint. We have added special logic to make sure that a variable processInstance of type WorkflowProcessInstance will only match to the current process instance and not to other process instances in the Working Memory. Note that you are however responsible yourself to insert the process instance into the session and, possibly, to update it, for example, using Java code or an on-entry or on-exit or explicit action in your process. The following example of a rule constraint will search for a person with the same name as the value stored in the variable "name" of the process:

processInstance : WorkflowProcessInstance()
Person( name == ( processInstance.getVariable("name") ) )
# add more constraints here ...

Timers wait for a predefined amount of time, before triggering, once or repeatedly. They can be used to trigger certain logic after a certain period, or to repeat some action at regular intervals.

since version 6 timers can be configured with valid ISO8601 date format that supports both one shot timers and repeatable timers. Timers can be defined as date and time representation, time duration or repeating intervals

  • Date - 2013-12-24T20:00:00.000+02:00 - fires exactly at Christmas Eve at 8PM
  • Duration - PT1S - fires once after 1 second
  • Repeatable intervals - R/PT1S - fires every second, no limit, alternatively R5/PT1S will fire 5 times every second

The timer service is responsible for making sure that timers get triggered at the appropriate times. Timers can also be canceled, meaning that the timer will no longer be triggered.

Timers can be used in two ways inside a process:

While it is recommended to define processes using the graphical editor or the underlying XML (to shield yourself from internal APIs), it is also possible to define a process using the Process API directly. The most important process model elements are defined in the packages org.jbpm.workflow.core and org.jbpm.workflow.core.node. A "fluent API" is provided that allows you to easily construct processes in a readable manner using factories. At the end, you can validate the process that you were constructing manually.

This is a simple example of a basic process with a script task only:


RuleFlowProcessFactory factory =
    RuleFlowProcessFactory.createProcess("org.jbpm.HelloWorld");
factory
    // Header
    .name("HelloWorldProcess")
    .version("1.0")
    .packageName("org.jbpm")
    // Nodes
    .startNode(1).name("Start").done()
    .actionNode(2).name("Action")
        .action("java", "System.out.println(\"Hello World\");").done()
    .endNode(3).name("End").done()
    // Connections
    .connection(1, 2)
    .connection(2, 3);
RuleFlowProcess process = factory.validate().getProcess();

KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
Resource resource = ks.getResources().newByteArrayResource(
    XmlBPMNProcessDumper.INSTANCE.dump(process).getBytes());
resource.setSourcePath("helloworld.bpmn2");
kfs.write(resource);
ReleaseId releaseId = ks.newReleaseId("org.jbpm", "helloworld", "1.0");
kfs.generateAndWritePomXML(releaseId);
ks.newKieBuilder(kfs).buildAll();
ks.newKieContainer(releaseId).newKieSession().startProcess("org.jbpm.HelloWorld");

You can see that we start by calling the static createProcess() method from the RuleFlowProcessFactory class. This method creates a new process with the given id and returns the RuleFlowProcessFactory that can be used to create the process. A typical process consists of three parts. The header part comprises global elements like the name of the process, imports, variables, etc. The nodes section contains all the different nodes that are part of the process. The connections section finally links these nodes to each other to create a flow chart.

In this example, the header contains the name and the version of the process and the package name. After that, you can start adding nodes to the current process. If you have auto-completion you can see that you have different methods to create each of the supported node types at your disposal.

When you start adding nodes to the process, in this example by calling the startNode(), actionNode() and endNode() methods, you can see that these methods return a specific NodeFactory, that allows you to set the properties of that node. Once you have finished configuring that specific node, the done() method returns you to the current RuleFlowProcessFactory so you can add more nodes, if necessary.

When you are finished adding nodes, you must connect them by creating connections between them. This can be done by calling the method connection, which will link previously created nodes.

Finally, you can validate the generated process by calling the validate() method and retrieve the created RuleFlowProcess object.

Even though business processes aren't code (we even recommend you to make them as high-level as possible and to avoid adding implementation details), they also have a life cycle like other development artefacts. And since business processes can be updated dynamically, testing them (so that you don't break any use cases when doing a modification) is really important as well.

When unit testing your process, you test whether the process behaves as expected in specific use cases, for example test the output based on the existing input. To simplify unit testing, jBPM includes a helper class called JbpmJUnitBaseTestCase (in the jbpm-test module) that you can use to greatly simplify your JUnit testing, by offering:

For example, consider the following "hello world" process containing a start event, a script task and an end event. The following JUnit test will create a new session, start the process and then verify whether the process instance completed successfully and whether these three nodes have been executed.


public class ProcessPersistenceTest extends JbpmJUnitBaseTestCase {

    public ProcessPersistenceTest() {
        // setup data source, enable persistence
        super(true, true);
    }

    @Test
    public void testProcess() {
        // create runtime manager with single process - hello.bpmn
        createRuntimeManager("hello.bpmn");
 
        // take RuntimeManager to work with process engine
        RuntimeEngine runtimeEngine = getRuntimeEngine();

        // get access to KieSession instance
        KieSession ksession = runtimeEngine.getKieSession();

        // start process
        ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello");

        // check whether the process instance has completed successfully
        assertProcessInstanceCompleted(processInstance.getId(), ksession);

        // check what nodes have been triggered
        assertNodeTriggered(processInstance.getId(), "StartProcess", "Hello", "EndProcess");
    }
}

JbpmJUnitBaseTestCase acts as base test case class that shall be used for jBPM related tests. It provides four usage areas:

  • JUnit life cycle methods

    • setUp: executed @Before and configures data source and EntityManagerFactory, cleans up Singleton's session id

    • tearDown: executed @After and clears out history, closes EntityManagerFactory and data source, disposes RuntimeEngines and RuntimeManager

  • Knowledge Base and KnowledgeSession management methods

    • createRuntimeManager creates RuntimeManager for given set of assets and selected strategy

    • disposeRuntimeManager disposes RuntimeManager currently active in the scope of test

    • getRuntimeEngine creates new RuntimeEngine for given context

  • Assertions

    • assertProcessInstanceCompleted

    • assertProcessInstanceAborted

    • assertProcessInstanceActive

    • assertNodeActive

    • assertNodeTriggered

    • assertProcessVarExists

    • assertNodeExists

    • assertVersionEquals

    • assertProcessNameEquals

  • Helper methods

    • getDs - returns currently configured data source

    • getEmf - returns currently configured EntityManagerFactory

    • getTestWorkItemHandler - returns test work item handler that might be registered in addition to what is registered by default

    • clearHistory - clears history log

    • setupPoolingDataSource - sets up data source

JbpmJUnitBaseTestCase supports all three predefined RuntimeManager strategies as part of the unit testing. It's enough to specify which strategy shall be used whenever creating runtime manager as part of single test:

public class ProcessHumanTaskTest extends JbpmJUnitBaseTestCase {
    
    private static final Logger logger = LoggerFactory.getLogger(ProcessHumanTaskTest.class);

    public ProcessHumanTaskTest() {
        super(true, false);
    }
    
    @Test
    public void testProcessProcessInstanceStrategy() {
        RuntimeManager manager = createRuntimeManager(Strategy.PROCESS_INSTANCE, "manager", "humantask.bpmn");
        RuntimeEngine runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get());
        KieSession ksession = runtimeEngine.getKieSession();
        TaskService taskService = runtimeEngine.getTaskService();
        
        int ksessionID = ksession.getId();
        ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello");

        assertProcessInstanceActive(processInstance.getId(), ksession);
        assertNodeTriggered(processInstance.getId(), "Start", "Task 1");
        
        manager.disposeRuntimeEngine(runtimeEngine);
        runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
        
        ksession = runtimeEngine.getKieSession();
        taskService = runtimeEngine.getTaskService();
        
        assertEquals(ksessionID, ksession.getId());
        
        // let john execute Task 1
        List<TaskSummary> list = taskService.getTasksAssignedAsPotentialOwner("john", "en-UK");
        TaskSummary task = list.get(0);
        logger.info("John is executing task {}", task.getName());
        taskService.start(task.getId(), "john");
        taskService.complete(task.getId(), "john", null);

        assertNodeTriggered(processInstance.getId(), "Task 2");
        
        // let mary execute Task 2
        list = taskService.getTasksAssignedAsPotentialOwner("mary", "en-UK");
        task = list.get(0);
        logger.info("Mary is executing task {}", task.getName());
        taskService.start(task.getId(), "mary");
        taskService.complete(task.getId(), "mary", null);

        assertNodeTriggered(processInstance.getId(), "End");
        assertProcessInstanceCompleted(processInstance.getId(), ksession);
    }
}

Above is more complete example that uses PerProcessInstance runtime manager strategy and uses task service to deal with user tasks.

Real-life business processes typically include the invocation of external services (like for example a human task service, an email server or your own domain-specific services). One of the advantages of our domain-specific process approach is that you can specify yourself how to actually execute your own domain-specific nodes, by registering a handler. And this handler can be different depending on your context, allowing you to use testing handlers for unit testing your process. When you are unit testing your business process, you can register test handlers that then verify whether specific services are requested correctly, and provide test responses for those services. For example, imagine you have an email node or a human task as part of your process. When unit testing, you don't want to send out an actual email but rather test whether the email that is requested contains the correct information (for example the right to email, a personalized body, etc.).

A TestWorkItemHandler is provided by default that can be registered to collect all work items (a work item represents one unit of work, like for example sending one specific email or invoking one specific service and contains all the data related to that task) for a given type. This test handler can then be queried during unit testing to check whether specific work was actually requested during the execution of the process and that the data associated with the work was correct.

The following example describes how a process that sends out an email could be tested. This test case in particular will test whether an exception is raised when the email could not be sent (which is simulated by notifying the engine that the sending the email could not be completed). The test case uses a test handler that simply registers when an email was requested (and allows you to test the data related to the email like from, to, etc.). Once the engine has been notified the email could not be sent (using abortWorkItem(..)), the unit test verifies that the process handles this case successfully by logging this and generating an error, which aborts the process instance in this case.


public void testProcess2() {

    // create runtime manager with single process - hello.bpmn
    createRuntimeManager("sample-process.bpmn");
    // take RuntimeManager to work with process engine
    RuntimeEngine runtimeEngine = getRuntimeEngine();

    // get access to KieSession instance
    KieSession ksession = runtimeEngine.getKieSession();

    // register a test handler for "Email"
    TestWorkItemHandler testHandler = getTestWorkItemHandler();

    ksession.getWorkItemManager().registerWorkItemHandler("Email", testHandler);

    // start the process
    ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello2");

    assertProcessInstanceActive(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "StartProcess", "Email");

    // check whether the email has been requested
    WorkItem workItem = testHandler.getWorkItem();
    assertNotNull(workItem);
    assertEquals("Email", workItem.getName());
    assertEquals("me@mail.com", workItem.getParameter("From"));
    assertEquals("you@mail.com", workItem.getParameter("To"));

    // notify the engine the email has been sent
    ksession.getWorkItemManager().abortWorkItem(workItem.getId());
    assertProcessInstanceAborted(processInstance.getId(), ksession);
    assertNodeTriggered(processInstance.getId(), "Gateway", "Failed", "Error");

}

You can configure whether you want to execute the JUnit tests using persistence or not. By default, the JUnit tests will use persistence, meaning that the state of all process instances will be stored in a (in-memory H2) database (which is started by the JUnit test during setup) and a history log will be used to check assertions related to execution history. When persistence is not used, process instances will only live in memory and an in-memory logger is used for history assertions.

Persistence (and setup of data source) is controlled by the super constructor and allows following

public class ProcessHumanTaskTest extends JbpmJUnitBaseTestCase {
    
    private static final Logger logger = LoggerFactory.getLogger(ProcessHumanTaskTest.class);

    public ProcessHumanTaskTest() {
        // configure this tests to not use persistence for process engine but still use it for human tasks
        super(true, false);
    }
}

jBPM supports the use of human tasks inside processes using a special User Task node defined by the BPMN2 Specification(as shown in the figure above). A User Task node represents an atomic task that needs to be executed by a human actor.

[Although jBPM has a special user task node for including human tasks inside a process, human tasks are considered the same as any other kind of external service that needs to be invoked and are therefore simply implemented as a domain-specific service. See the chapter on domain-specific processes to learn more about this.]

A User Task node contains the following core properties:

You can edit these variables in the properties view (see below) when selecting the User Task node.

A User Task node also contains the following extra properties:

Human tasks typically present some data related to the task that needs to be performed to the actor that is executing the task and usually also request the actor to provide some result data related to the execution of the task. Task forms are typically used to present this data to the actor and request results.

The data that will be used by the Task needs to be specified when we define the User Task in our Process. In order to do that we need to define which data will be copied from the process context to the task context. Notice that the data is copied, so it can be modified inside the Task context but it will not affect the process variables unless we decide to copy back the value from the task to the process context.

Most of the times Forms are used to display data to the end user. Allowing them to generate/create new data that will be propagated to the process context to be used by future activities. In order to decide how the information flow from the process to a particular task and from the task to the process we need to define which pieces of information will be automatically copied by the process engine. The following sections shows how to do these mappings by configuring the DataInputSet, DataOutputSet and the Assignments properties of a User Task.

Let's start defining the Task DataInputSet:

Both GroupId and Comment are automatically generated, so you don't need to worry about that. In this case the only user defined Data Input is called: in_name. This means that the task will be receiving information from the process context and internally this variable will be called in_name. The type is also specified here.

In the Data Outputs represent the data that will be generated by the tasks. In this case we have two variables of type String called: out_name and out_mail and two Integer variables called: out_age and out_score are defined. This means that inside the task context we will need to set the value to these variables.

Finally all the connections with the process context needs to be done in the Data Assignments. The main idea here is to define how Data Inputs and Data Outputs will be associated with process variables.

As shown in the previous screenshot, the assignments between the process variables (in this case (name, age, mail and hr_score)) and the Data Inputs and Outputs are done in the Data Assignments screen. Notice that the example uses a convention that makes it easy to know which is an internal Task variables (Data Input/Output) using the "in_" and "out_" prefix to the variable names. Using this convention you can quickly understand the Assignments screen. The first row maps the process variable called name to the data input called in_name. The second row maps the data output called out_mail to the process variable called mail, and so on.

These mappings at runtime will automatically copy the variables content from one context (process and task) to the other automatically for us.

From the perspective of a process, when a user task node is encountered during the execution, a human task is created. The process will then only leave the user task node when the associated human task has been completed or aborted.

The human task itself usually has a complete life cycle itself as well. For details beyond what is described below, please check out the WS-HumanTask specification. The following diagram is from the WS-HumanTask specification and describes the human task life cycle.

A newly created task starts in the "Created" stage. Usually, it will then automatically become "Ready", after which the task will show up on the task list of all the actors that are allowed to execute the task. The task will stay "Ready" until one of these actors claims the task, indicating that he or she will be executing it.

When a user then eventually claims the task, the status will change to "Reserved". Note that a task that only has one potential (specific) actor will automatically be assigned to that actor upon creation of the task. When the user who has claimed the task starts executing it, the task status will change from "Reserved" to "InProgress".

Lastly, once the user has performed and completed the task, the task status will change to "Completed". In this step, the user can optionally specify the result data related to the task. If the task could not be completed, the user could also indicate this by using a fault response, possibly including fault data, in which case the status would change to "Failed".

While the life cycle explained above is the normal life cycle, the specification also describes a number of other life cycle methods, including:

Only users associated with a specific task are allowed to modify or retrieve information about the task. This allows users to create a jBPM workflow with multiple tasks and yet still be assured of both the confidentiality and integrity of the task status and information associated with a task.

Some task operations will end up throwing a org.jbpm.services.task.exception.PermissionDeniedException when used with information about an unauthorized user. For example, when a user is trying to directly modify the task (for example, by trying to claim or complete the task), the PermissionDeniedException will be thrown if that user does not have the correct role for that operation. Furthermore, a user will not be able to view or retrieve tasks that the user is not involved with, especially if this is via the jBPM Console or KIE Workbench applications.

User 'Administrator' and group 'Administrators' are automatically added to each Human Task.

The permisions matrix below summarizes the actions that specific user roles are allowed to do. On the left side, possible operations are listed while user roles are listed across the top of the matrix.

The cells of the permissions matrix contain one of three possible characters, each of which indicate the user role permissions for that operation:

Furthermore, the following words or abbreviations in the table header refer to the following roles:


User roles are assigned to users by the definition of the task in the jBPM (BPMN2) process definition.

Permissions Matrices.  The following matrix describes the authorizations for all operations which modify a task:


The matrix below describes the authorizations used when retrieving task information. In short, it says that all users which have any role with regards to the specific task, are allowed to see the task. This applies to all operations that are used to retrieve any type of information about the task.


As far as the jBPM engine is concerned, human tasks are similar to any other external service that needs to be invoked and are implemented as a domain-specific service. (For more on domain-specific services, see the chapter on them here.) Because a human task is an example of such a domain-specific service, the process itself only contains a high-level, abstract description of the human task to be executed and a work item handler that is responsible for binding this (abstract) task to a specific implementation.

Users can plug in any human task service implementation, such as the one that's provided by jBPM, or they may register their own implementation. In the next paragraphs, we will describe the human task service implementation provided by jBPM.

The jBPM project provides a default implementation of a human task service based on the WS-HumanTask specification. If you do not need to integrate jBPM with another existing implementation of a human task service, you can use this service. The jBPM implementation manages the life cycle of the tasks (creation, claiming, completion, etc.) and stores the state of all the tasks, task lists, and other associated information. It also supports features like internationalization, calendar integration, different types of assignments, delegation, escalation and deadlines. The code for the implementation itself can be found in the jbpm-human-task module.

The jBPM task service implementation is based on the WS-HumanTask (WS-HT) specification. This specification defines (in detail) the model of the tasks, the life cycle, and many other features. It is very comprehensive and the first version can be found here.

The human task service exposes a Java API for managing the life cycle of tasks. This allows clients to integrate (at a low level) with the human task service. Note that end users should probably not interact with this low-level API directly, but use one of the more user-friendly task clients (see below) instead. These clients offer a graphical user interface to request task lists, claim and complete tasks, and manage tasks in general. The task clients listed below use the Java API to internally interact with the human task service. Of course, the low-level API is also available so that developers can use it in their code to interact with the human task service directly.

A task service (interface org.kie.api.task.TaskService) offers the following methods (among others) for managing the life cycle of human tasks:


              ...
              
              void start( long taskId, String userId );

              void stop( long taskId, String userId );

              void release( long taskId, String userId );

              void suspend( long taskId, String userId );

              void resume( long taskId, String userId );

              void skip( long taskId, String userId );

              void delegate(long taskId, String userId, String targetUserId);

              void complete( long taskId, String userId, Map<String, Object> results );
              
              ...
              
       

If you take a look at the method signatures you will notice that almost all of these methods take the following arguments:

There is also an internal interface that you should check for more methods to interact with the Task Service, this interface is internal until it gets tested. Future version of the External (public) interface can include some of the methods proposed in the InternalTaskService interface. If you want to make use of the methods provided by this interface you need to manually cast to InternalTaskService. One method that can be useful from this interface is getTaskContent():


               Map<String, Object> getTaskContent( long taskId );
       

This method saves you from doing all the boiler plate of getting the ContentMarshallerContext to unmarshall the serialized version of the task content. If you only want to use the stable/public API's you can just copy what this method does:


              Task taskById = taskQueryService.getTaskInstanceById(taskId);
              Content contentById = taskContentService.getContentById(taskById.getTaskData().getDocumentContentId());
              ContentMarshallerContext context = getMarshallerContext(taskById);
              Object unmarshalledObject = ContentMarshallerHelper.unmarshall(contentById.getContent(), context.getEnvironment(), context.getClassloader());
              if (!(unmarshalledObject instanceof Map)) {
                  throw new IllegalStateException(" The Task Content Needs to be a Map in order to use this method and it was: "+unmarshalledObject.getClass());
      
              }
              Map<String, Object> content = (Map<String, Object>) unmarshalledObject;
              return content;
       

Because the content of the Task can be any Object, the previous method assume that you are storing a Map of objects to work. If you are storing other than a Map you should do the correspondent checks.

Task service supports task listeners to be invoked upon various life cycle events happening on given task instance. In majority of cases task event listeners are used to intercept certain operation to perform additional logic - like storing task information in separate tables for business activity monitoring needs.

Task event listeners are pluggable and users can provide their own implementation of org.kie.api.task.TaskLifeCycleEventListener interface. There are beforeTask* and afterTask* methods that are invoked upon given event occured on a task instance.

TaskEvent (org.kie.api.task.TaskEvent) is the only argument available to the listener that provides access to:

In many cases implementors of task event listener need to have access to task variables (either input or output or both) to perform required operations. It can be done as described above (using various services and content marshaller helper) though that in many cases leads to code duplication in multiple listeners thus an extended support was added in 6.5 to simply use TaskContext to obtain that information.

loadTaskVariables(Task task);

Method loadTaskVariables can be used to populate both input and output variables of a given task by simple and single method call. That method is "no op" in case task variables are already set on a task.

To improve performance task variables are automatically set when they are available - usually given by caller on task service:

Other than that loadTaskVariables should be used to populate task variables.

In order to get access to the Task Service API it is recommended to let the Runtime Manager to make sure that everything is setup correctly. Look at the Runtime Manager section for more information. From the API perspective you should be doing something like this:


              ...
              RuntimeEngine engine = runtimeManager.getRuntimeEngine(EmptyContext.get());
              KieSession kieSession = engine.getKieSession();
              // Start a process
              kieSession.startProcess("CustomersRelationship.customers", params);
              // Do Task Operations
              TaskService taskService = engine.getTaskService();
              List<TaskSummary> tasksAssignedAsPotentialOwner = taskService.getTasksAssignedAsPotentialOwner("mary", "en-UK");
              
              // Claim Task
              taskService.claim(taskSummary.getId(), "mary");
              // Start Task
              taskService.start(taskSummary.getId(), "mary");

              ...
       

If you use this approach, there is no need to register the Task Service with the Process Engine. The Runtime Manager will do that for you automatically. If you don't use the Runtime Manager, you will be responsible for setting the LocalHTWorkItemHandler in the session in order to get the Task Service notifying the Process Engine when a task is completed, or the Process Engine notifying that a task has been created.

In jBPM 6.x the Task Service runs locally to the Process and Rule Engine and for that reason multiple light clients can be created for different Process and Rule Engine's instances. All the clients will be sharing the same database (backend storage for the tasks).

jBPM allows the persistent storage of certain information. This chapter describes these different types of persistence, and how to configure them. An example of the information stored is the process runtime state. Storing the process runtime state is necessary in order to be able to continue execution of a process instance at any point, if something goes wrong. Also, the process definitions themselves, and the history information (logs of current and previous process states already) can also be persisted.

Whenever a process is started, a process instance is created, which represents the execution of the process in that specific context. For example, when executing a process that specifies how to process a sales order, one process instance is created for each sales request. The process instance represents the current execution state in that specific context, and contains all the information related to that process instance. Note that it only contains the (minimal) runtime state that is needed to continue the execution of that process instance at some later time, but it does not include information about the history of that process instance if that information is no longer needed in the process instance.

The runtime state of an executing process can be made persistent, for example, in a database. This allows to restore the state of execution of all running processes in case of unexpected failure, or to temporarily remove running instances from memory and restore them at some later time. jBPM allows you to plug in different persistence strategies. By default, if you do not configure the process engine otherwise, process instances are not made persistent.

If you configure the engine to use persistence, it will automatically store the runtime state into the database. You do not have to trigger persistence yourself, the engine will take care of this when persistence is enabled. Whenever you invoke the engine, it will make sure that any changes are stored at the end of that invocation, at so-called safe points. Whenever something goes wrong and you restore the engine from the database, you also should not reload the process instances and trigger them manually to resume execution, as process instances will automatically resume execution if they are triggered, like for example by a timer expiring, the completion of a task that was requested by that process instance, or a signal being sent to the process instance. The engine will automatically reload process instances on demand.

The runtime persistence data should in general be considered internal, meaning that you probably should not try to access these database tables directly and especially not try to modify these directly (as changing the runtime state of process instances without the engine knowing might have unexpected side-effects). In most cases where information about the current execution state of process instances is required, the use of a history log is mostly recommended (see below). In some cases, it might still be useful to for example query the internal database tables directly, but you should only do this if you know what you are doing.

jBPM uses a binary persistence mechanism, otherwise known as marshalling, which converts the state of the process instance into a binary dataset. When you use persistence with jBPM, this mechanism is used to save or retrieve the process instance state from the database. The same mechanism is also applied to the session state and any work item states.

When the process instance state is persisted, two things happen:

Apart from the process instance state, the session itself can also store some state, such as the state of timer jobs, or the session data that any business rules would be evaluated over. This session state is stored separately as a binary blob, along with the id of the session and some metadata. You can always restore session state by reloading the session with the given id. The session id can be retrieved using ksession.getId().

Note that the process instance binary datasets are usually relatively small, as they only contain the minimal execution state of the process instance. For a simple process instance, this usually contains one or a few node instances, i.e., any node that is currently executing, and any existing variable values.

As a result of jBPM using marshalling, the data model is both simple and small:

Figure 8.1. jBPM data model

jBPM data model

The sessioninfo entity contains the state of the (knowledge) session in which the jBPM process instance is running.


The processinstanceinfo entity contains the state of the jBPM process instance.


The eventtypes entity contains information about events that a process instance will undergo or has undergone.


The workiteminfo entity contains the state of a work item.


The CorrelationKeyInfo entity contains information about correlation keys assigned to given process instance - loose relationship as this table is considered optional used only when correlation capabilities are required.


The CorrelationPropertyInfo entity contains information about correlation properties for given correlation key that is assigned to given process instance.


The ContextMappingInfo entity contains information about contextual information mapped to ksession. This is an internal part of RuntimeManager and can be considered optional when RuntimeManager is not used.


In many cases it will be useful (if not necessary) to store information about the execution of process instances, so that this information can be used afterwards. For example, sometimes we want to verify which actions have been executed for a particular process instance, or in general, we want to be able to monitor and analyze the efficiency of a particular process.

However, storing history information in the runtime database can result in the database rapidly increasing in size, not to mention the fact that monitoring and analysis queries might influence the performance of your runtime engine. This is why process execution history information can be stored separately.

This history log of execution information is created based on events that the process engine generates during execution. This is possible because the jBPM runtime engine provides a generic mechanism to listen to events. The necessary information can easily be extracted from these events and then persisted to a database. Filters can also be used to limit the scope of the logged information.

The jbpm-audit module contains an event listener that stores process-related information in a database using JPA. The data model itself contains three entities, one for process instance information, one for node instance information, and one for (process) variable instance information.

The ProcessInstanceLog table contains the basic log information about a process instance.


The NodeInstanceLog table contains more information about which nodes were actually executed inside each process instance. Whenever a node instance is entered from one of its incoming connections or is exited through one of its outgoing connections, that information is stored in this table.


The VariableInstanceLog table contains information about changes in variable instances. The default is to only generate log entries when (after) a variable changes. It's also possible to log entries before the variable (value) changes.


The AuditTaskImpl table contains information about tasks that can be used for queries.


The BAMTaskSummary table that collects information about tasks that is used by BAM engine to build charts and dashboards.


The TaskVariableImpl table contains information about task variable instances.


The TaskEvent table contains information about changes in task instances. Operations such as claim, start, stop etc are stored here to provide time line view of events that happened to given task.


To log process history information in a database like this, you need to register the logger on your session like this:


EntityManagerFactory emf = ...;
StatefulKnowledgeSession ksession = ...;
AbstractAuditLogger auditLogger = AuditLoggerFactory.newJPAInstance(emf);
ksession.addProcessEventListener(auditLogger);

// invoke methods one your session here

      

To specify the database where the information should be stored, modify the file persistence.xml file to include the audit log classes as well (ProcessInstanceLog, NodeInstanceLog and VariableInstanceLog), as shown below.

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


<persistence
  version="2.0"
  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
  http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
  xmlns="http://java.sun.com/xml/ns/persistence"
  xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>

  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/jbpm-ds</jta-data-source>
    <mapping-file>META-INF/JBPMorm.xml</mapping-file>
    <class>org.drools.persistence.info.SessionInfo</class>
    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
    <class>org.drools.persistence.info.WorkItemInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>
    <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class>

    <class>org.jbpm.process.audit.ProcessInstanceLog</class>
    <class>org.jbpm.process.audit.NodeInstanceLog</class>
    <class>org.jbpm.process.audit.VariableInstanceLog</class>

    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.max_fetch_depth" value="3"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.transaction.jta.platform"
      value="org.hibernate.service.jta.platform.internal.BitronixJtaPlatform"/>
    </properties>
  </persistence-unit>
</persistence>
    

All this information can easily be queried and used in a lot of different use cases, ranging from creating a history log for one specific process instance to analyzing the performance of all instances of a specific process.

This audit log should only be considered a default implementation. We don't know what information you need to store for analysis afterwards, and for performance reasons it is recommended to only store the relevant data. Depending on your use cases, you might define your own data model for storing the information you need, and use the process event listeners to extract that information.

Process and task variables are stored in autdit tables by default although there are stored in simplest possible way - by creating string representation of the variable - variable.toString(). In many cases this is enough as even for custom classes used as variables users can implement custom toString() method that produces expected "view" of the variable.

Though this might not cover all needs, especially when there is a need for efficient queries by variables (both task and process). Let's take as an example a Person object that has following structure:

public class Person implements Serializable{

    private static final long serialVersionUID = -5172443495317321032L;
    private String name;
    private int age;   
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }        
}

while at first look this seems to be sufficient as the toString() methods provide human readable format it does not make it easy to be searched by. As searching through strings like "Person [name="john", age="34"] to find people with age 34 would make data base query very inefficient.

To solve the problem variable audit has been based on VariableIndexers that are responsible for extracting relevant parts of the variable that will be stored in audit log.


/**
 * Variable indexer that allows to transform variable instance into other representation (usually string)
 * to be able to use it for queries.
 *
 * @param <V> type of the object that will represent indexed variable
 */
public interface VariableIndexer<V> {

    /**
     * Tests if given variable shall be indexed by this indexer
     * 
     * NOTE: only one indexer can be used for given variable
     * 
     * @param variable variable to be indexed
     * @return true if variable should be indexed with this indexer
     */
    boolean accept(Object variable);
    
    /**
     * Performs index/transform operation of the variable. Result of this operation can be
     * either single value or list of values to support complex type separation.
     * For example when variable is of type Person that has name, address phone indexer could 
     * build three entries out of it to represent individual fields:
     * person = person.name
     * address = person.address.street
     * phone = person.phone
     * that will allow more advanced queries to be used to find relevant entries.
     * @param name name of the variable
     * @param variable actual variable value 
     * @return
     */
    List<V> index(String name, Object variable);
}

By default (indexer that takes the toString()) will prodce single audit entry for single variable, so it's one to one relationship. But that's not the only option. Indexers (as can be seen in the interface) returns list of objects that are the outcome of single variable indexation. To make our person queries more efficient we could build custom indexer that would take Person instance and index it into separate audit entries one representing name and the other representing age.


public class PersonTaskVariablesIndexer implements TaskVariableIndexer {

    @Override
    public boolean accept(Object variable) {
        if (variable instanceof Person) {
            return true;
        }
        return false;
    }

    @Override
    public List<TaskVariable> index(String name, Object variable) {
        
        Person person = (Person) variable;
        List<TaskVariable> indexed = new ArrayList<TaskVariable>();
        
        TaskVariableImpl personNameVar = new TaskVariableImpl();
        personNameVar.setName("person.name");
        personNameVar.setValue(person.getName());
        
        indexed.add(personNameVar);
        
        TaskVariableImpl personAgeVar = new TaskVariableImpl();
        personAgeVar.setName("person.age");
        personAgeVar.setValue(person.getAge()+"");
        
        indexed.add(personAgeVar);
        
        return indexed;
    }

}

That indexer will then be used to index Person class only and rest of variables will be indexed with default (toString()) indexer. Now when we want to find process instances or tasks that have person with age 34 we simple refer to it as

there is not even need to use like based queries so data base can optimize the query and make it efficient even with big set of data.

Building and registering custom indexers

Indexers are supported for both process and task variables. though they are supported by different interfaces as they do produce different type of objects representing audit view of the variable. Following are the interfaces to be implemented to build custom indexers:

Implementation is rather simple, just two methods to be implemented

Once the implementation is done, it should be packaged as jar file and following file needs to be included:

Indexers are discovered by ServiceLoader mechanism and thus the META-INF/services files need. All found indexers will be examined whenever process or task variable is about to be indexed. Only the default (toString() based) indexer is not discovered but added explicitly as last indexer to allow custom ones to take the precedence over it.

The jBPM engine supports JTA transactions. It also supports local transactions only when using Spring. It does not support pure local transactions at the moment. For more information about using Spring to set up persistence, please see the Spring chapter in the Drools integration guide.

Whenever you do not provide transaction boundaries inside your application, the engine will automatically execute each method invocation on the engine in a separate transaction. If this behavior is acceptable, you don't need to do anything else. You can, however, also specify the transaction boundaries yourself. This allows you, for example, to combine multiple commands into one transaction.

You need to register a transaction manager at the environment before using user-defined transactions. The following sample code uses the Bitronix transaction manager. Next, we use the Java Transaction API (JTA) to specify transaction boundaries, as shown below:


// create the entity manager factory
EntityManagerFactory emf = EntityManagerFactoryManager.get().getOrCreate("org.jbpm.persistence.jpa");
TransactionManager tm = TransactionManagerServices.getTransactionManager();

// setup the runtime environment
RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.addAsset(ResourceFactory.newClassPathResource("MyProcessDefinition.bpmn2"), ResourceType.BPMN2)
    .addEnvironmentEntry(EnvironmentName.TRANSACTION_MANAGER, tm)
    .get();

// get the kie session
RuntimeManager manager = RuntimeManagerFactory.Factory.get().newPerRequestRuntimeManager(environment);
RuntimeEngine runtime = manager.getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtime.getKieSession();

// start the transaction
UserTransaction ut = InitialContext.doLookup("java:comp/UserTransaction");
ut.begin();

// perform multiple commands inside one transaction
ksession.insert( new Person( "John Doe" ) );
ksession.startProcess("MyProcess");

// commit the transaction
ut.commit();
    

Note that, if you use Bitronix as the transaction manager, you should also add a simple jndi.properties file in you root classpath to register the Bitronix transaction manager in JNDI. If you are using the jbpm-test module, this is already included by default. If not, create a file named jndi.properties with the following content:


java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory
    

If you would like to use a different JTA transaction manager, you can change the persistence.xml file to use your own transaction manager. For example, when running inside JBoss Application Server v5.x or v7.x, you can use the JBoss transaction manager. You need to change the transaction manager property in persistence.xml to:


<property name="hibernate.transaction.jta.platform" value="org.hibernate.transaction.JBossTransactionManagerLookup" />
    

Special consideration need to be taken when embedding jBPM inside an application that executes in Container Managed Transaction (CMT) mode, for instance EJB beans. This especially applies to application servers that does not allow accessing UserTransaction instance from JNDI when being part of container managed transaction, e.g. WebSphere Application Server. Since default implementation of transaction manager in jBPM is based on UserTransaction to get transaction status which is used to decide if transaction should be started or not, in environments that prevent accessing UserTrancation it won't do its job. To secure proper execution in CMT environments a dedicated transaction manager implementation is provided:

org.jbpm.persistence.jta.ContainerManagedTransactionManager

This transaction manager expects that transaction is active and thus will always return ACTIVE when invoking getStatus method. Operations like begin, commit, rollback are no-op methods as transaction manager runs under managed transaction and can't affect it.

To configure this transaction manager following must be done:

With following configuration jBPM should run properly in CMT environment.

By default, the engine does not save runtime data persistently. This means you can use the engine completely without persistence (so not even requiring an in memory database) if necessary, for example for performance reasons, or when you would like to manage persistence yourself. It is, however, possible to configure the engine to do use persistence by configuring it to do so. This usually requires adding the necessary dependencies, configuring a datasource and creating the engine with persistence configured.

You need to make sure the necessary dependencies are available in the classpath of your application if you want to user persistence. By default, persistence is based on the Java Persistence API (JPA) and can thus work with several persistence mechanisms. We are using Hibernate by default.

If you're using the Eclipse IDE and the jBPM Eclipse plugin, you should make sure the necessary JARs are added to your jBPM runtime directory. You don't really need to do anything (as the necessary dependencies should already be there) if you are using the jBPM runtime that is configured by default when using the jBPM installer, or if you downloaded and unzipped the jBPM runtime artifact (from the downloads) and pointed the jBPM plugin to that directory.

If you would like to manually add the necessary dependencies to your project, first of all, you need the JAR file jbpm-persistence-jpa.jar, as that contains code for saving the runtime state whenever necessary. Next, you also need various other dependencies, depending on the persistence solution and database you are using. For the default combination with Hibernate as the JPA persistence provider and using an H2 in-memory database and Bitronix for JTA-based transaction management, the following list of additional dependencies is needed:

You can use the JPAKnowledgeService to create your knowledge session. This is slightly more complex, but gives you full access to the underlying configurations. You can create a new knowledge session using JPAKnowledgeService based on a knowledge base, a knowledge session configuration (if necessary) and an environment. The environment needs to contain a reference to your Entity Manager Factory. For example:


// create the entity manager factory and register it in the environment
EntityManagerFactory emf =
    Persistence.createEntityManagerFactory( "org.jbpm.persistence.jpa" );
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, emf );

// create a new knowledge session that uses JPA to store the runtime state
StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env );
int sessionId = ksession.getId();

// invoke methods on your method here
ksession.startProcess( "MyProcess" );
ksession.dispose();

You can also use the JPAKnowledgeService to recreate a session based on a specific session id:


// recreate the session from database using the sessionId
ksession = JPAKnowledgeService.loadStatefulKnowledgeSession(sessionId, kbase, null, env );

Note that we only save the minimal state that is needed to continue execution of the process instance at some later point. This means, for example, that it does not contain information about already executed nodes if that information is no longer relevant, or that process instances that have been completed or aborted are removed from the database. If you want to search for history-related information, you should use the history log, as explained later.

You need to add a persistence configuration to your classpath to configure JPA to use Hibernate and the H2 database (or your own preference), called persistence.xml in the META-INF directory, as shown below. For more details on how to change this for your own configuration, we refer to the JPA and Hibernate documentation for more information.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
      version="2.0"
      xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd
      http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"
      xmlns="http://java.sun.com/xml/ns/persistence"
      xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance>

  <persistence-unit name="org.jbpm.persistence.jpa" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>jdbc/jbpm-ds</jta-data-source>
    <mapping-file>META-INF/JBPMorm.xml</mapping-file>
    <class>org.drools.persistence.info.SessionInfo</class>
    <class>org.jbpm.persistence.processinstance.ProcessInstanceInfo</class>
    <class>org.drools.persistence.info.WorkItemInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationKeyInfo</class>
    <class>org.jbpm.persistence.correlation.CorrelationPropertyInfo</class>
    <class>org.jbpm.runtime.manager.impl.jpa.ContextMappingInfo</class>

    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.max_fetch_depth" value="3"/>
      <property name="hibernate.hbm2ddl.auto" value="update"/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.transaction.jta.platform"
                value="org.hibernate.service.jta.platform.internal.BitronixJtaPlatform"/>
    </properties>
  </persistence-unit>
</persistence>
    

This configuration file refers to a data source called "jdbc/jbpm-ds". If you run your application in an application server (like for example JBoss AS), these containers typically allow you to easily set up data sources using some configuration (like for example dropping a datasource configuration file in the deploy directory). Please refer to your application server documentation to know how to do this.

For example, if you're deploying to JBoss Application Server v5.x, you can create a datasource by dropping a configuration file in the deploy directory, for example:

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>jdbc/jbpm-ds</jndi-name>
    <connection-url>jdbc:h2:tcp://localhost/~/test</connection-url>
    <driver-class>org.h2.jdbcx.JdbcDataSource</driver-class>
    <user-name>sa</user-name>
    <password></password>
  </local-tx-datasource>
</datasources>

If you are however executing in a simple Java environment, you can use the JBPMHelper class to do this for you (see below for tests only) or the following code fragment could be used to set up a data source (where we are using the H2 in-memory database in combination with Bitronix in this case).


PoolingDataSource ds = new PoolingDataSource();
ds.setUniqueName("jdbc/jbpm-ds");
ds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
ds.setMaxPoolSize(3);
ds.setAllowLocalTransactions(true);
ds.getDriverProperties().put("user", "sa");
ds.getDriverProperties().put("password", "sasa");
ds.getDriverProperties().put("URL", "jdbc:h2:mem:jbpm-db");
ds.getDriverProperties().put("driverClassName", "org.h2.Driver");
ds.init();

You need to configure the jBPM engine to use persistence, usually simply by using the appropriate constructor when creating your session. There are various ways to create a session (as we have tried to make this as easy as possible for you and have several utility classes for you, depending for example if you are trying to write a process JUnit test).

The easiest way to do this is to use the jbpm-test module that allows you to easily create and test your processes. The JBPMHelper class has a method to create a session, and uses a configuration file to configure this session, like whether you want to use persistence, the datasource to use, etc. The helper class will then do all the setup and configuration for you.

To configure persistence, create a jBPM.properties file and configure the following properties (note that the example below are the default properties, using an H2 in-memory database with persistence enabled, if you are fine with all of these properties, you don't need to add new properties file, as it will then use these properties by default):


# for creating a datasource
persistence.datasource.name=jdbc/jbpm-ds
persistence.datasource.user=sa
persistence.datasource.password=
persistence.datasource.url=jdbc:h2:tcp://localhost/~/jbpm-db
persistence.datasource.driverClassName=org.h2.Driver

# for configuring persistence of the session
persistence.enabled=true
persistence.persistenceunit.name=org.jbpm.persistence.jpa
persistence.persistenceunit.dialect=org.hibernate.dialect.H2Dialect

# for configuring the human task service
taskservice.enabled=true
taskservice.datasource.name=org.jbpm.task
taskservice.usergroupcallback=org.jbpm.services.task.identity.JBossUserGroupCallbackImpl
taskservice.usergroupmapping=classpath:/usergroups.properties
      

If you want to use persistence, you must make sure that the datasource (that you specified in the jBPM.properties file) is initialized correctly. This means that the database itself must be up and running, and the datasource should be registered using the correct name. If you would like to use an H2 in-memory database (which is usually very easy to do some testing), you can use the JBPMHelper class to start up this database, using:


JBPMHelper.startH2Server();
      

To register the datasource (this is something you always need to do, even if you're not using H2 as your database, check below for more options on how to configure your datasource), use:


JBPMHelper.setupDataSource();
      

Next, you can use the JBPMHelper class to create your session (after creating your knowledge base, which is identical to the case when you are not using persistence):


StatefulKnowledgeSession ksession = JBPMHelper.newStatefulKnowledgeSession(kbase);
      

Once you have done that, you can just call methods on this ksession (like startProcess) and the engine will persist all runtime state in the created datasource.

You can also use the JBPMHelper class to recreate your session (by restoring its state from the database, by passing in the session id (that you can retrieve using ksession.getId())):


StatefulKnowledgeSession ksession = JBPMHelper.loadStatefulKnowledgeSession(kbase, sessionId);
      

How to use the web-based Workbench

Table of Contents

9. Workbench (General)
9.1. Installation
9.1.1. War installation
9.1.2. Workbench data
9.1.3. System properties
9.1.4. Trouble shooting
9.2. Quick Start
9.2.1. Add repository
9.2.2. Add project
9.2.3. Define Data Model
9.2.4. Define Rule
9.2.5. Build and Deploy
9.3. Administration
9.3.1. Administration overview
9.3.2. Organizational unit
9.3.3. Repositories
9.4. Configuration
9.4.1. Basic user management
9.4.2. Roles
9.4.3. Restricting access to repositories
9.4.4. Command line config tool
9.5. Introduction
9.5.1. Log in and log out
9.5.2. Home screen
9.5.3. Workbench concepts
9.5.4. Initial layout
9.6. Changing the layout
9.6.1. Resizing
9.6.2. Repositioning
9.7. Authoring (General)
9.7.1. Artifact Repository
9.7.2. Asset Editor
9.7.3. Tags Editor
9.7.4. Project Explorer
9.7.5. Project Editor
9.7.6. Validation
9.7.7. Data Modeller
9.7.8. Data Sets
9.8. User and group management
9.8.1. Introduction
9.8.2. Security management providers
9.8.3. Installation and setup
9.8.4. Usage
9.9. Embedding Workbench In Your Application
9.10. Asset Management
9.10.1. Asset Management Overview
9.10.2. Managed vs Unmanaged Repositories
9.10.3. Asset Management Processes
9.10.4. Usage Flow
9.10.5. Repository Structure
9.10.6. Managed Repositories Operations
9.11. Execution Server Management UI
9.11.1. Server Templates
9.11.2. Container
9.11.3. Remote Server
10. Workbench Integration
10.1. REST
10.1.1. Job calls
10.1.2. Repository calls
10.1.3. Organizational unit calls
10.1.4. Maven calls
10.1.5. REST summary
10.2. Keycloak SSO integration
10.2.1. Scenario
10.2.2. Install and setup a Keycloak server
10.2.3. Create and setup the demo realm
10.2.4. Install and setup jBPM Workbench
10.2.5. Securing workbench remote services via Keycloak
10.2.6. Securing workbench's file system services via Keycloak
10.2.7. Execution server
10.2.8. Consuming remote services
11. Workbench High Availability
11.1.
11.1.1. VFS clustering
11.1.2. jBPM clustering
12. Designer
12.1. Designer UI Explained
12.2. Getting started with Modelling
12.3. Designer Toolbar
13. Forms
13.1. Configure process and human tasks
13.2. Generate forms from task definitions
13.3. Edit forms
13.3.1. Form generated description
13.3.2. Customizing form
13.3.3. Field types
13.4. Document attachments
13.4.1. Process and forms configuration
13.4.2. Marshalling strategy and deployment configuration
13.5. Using forms on client applications
13.5.1. What does the API provides?
13.5.2. Sample usage
14. Runtime Management
14.1. Deployments
14.1.1. Deployment descriptors
14.2. Process Deployments
14.3. Jobs
15. Process and Task Management
15.1. Process Management
15.1.1. Process Definitions
15.1.2. Process Instances
15.2. Tasks
15.2.1. Task List
15.2.2. New Task (Ad-Hoc Task)
16. Business Activity Monitoring
16.1. Overview
16.2. Business Dashboards
16.3. Process Dashboard
16.3.1. Task Dashboard
17. Remote API
17.1. Remote Java API
17.1.1. Remote REST Java API Client Configuration
17.1.2. Remote JMS Java API Client Configuration
17.1.3. Remote CommandWebService Java API Client Configuration
17.1.4. Supported methods
17.2. REST
17.2.1. REST permissions
17.2.2. Runtime calls
17.2.3. Task calls
17.2.4. Deployment Calls
17.2.5. Deployment call details
17.2.6. Execute calls
17.2.7. REST summary
17.3. REST Query API
17.3.1. Query URL layout
17.3.2. Query Parameters
17.3.3. Parameter Table
17.3.4. Parameter examples
17.3.5. Query Output Format
17.4. JMS
17.4.1. JMS Queue setup
17.4.2. Using the remote Java API
17.4.3. Example JMS usage
17.5. Additional Information
17.5.1. REST Serialization: JAXB or JSON
17.5.2. Sending and receiving user class instances
17.5.3. Including the deployment id
17.5.4. REST Pagination
17.5.5. REST Map query parameters
17.5.6. REST Number query parameters
17.5.7. Runtime strategies

Here's a list of all system properties:

To change one of these system properties in a WildFly or JBoss EAP cluster:

These steps help you get started with minimum of effort.

They should not be a substitute for reading the documentation in full.

Provides capabilities to manage the system repository from command line. System repository contains the data about general workbench settings: how editors behave, organizational groups, security and other settings that are not editable by the user. System repository exists in the .niogit folder, next to all the repositories that have been created or cloned into the workbench.

Projects often need external artifacts in their classpath in order to build, for example a domain model JARs. The artifact repository holds those artifacts.

The Artifact Repository is a full blown Maven repository. It follows the semantics of a Maven remote repository: all snapshots are timestamped. But it is often stored on the local hard drive.

By default the artifact repository is stored under $WORKING_DIRECTORY/repositories/kie, but it can be overridden with the system property -Dorg.guvnor.m2repo.dir. There is only 1 Maven repository per installation.

The Artifact Repository screen shows a list of the artifacts in the Maven repository:

To add a new artifact to that Maven repository, either:

  • Use the upload button and select a JAR. If the JAR contains a POM file under META-INF/maven (which every JAR build by Maven has), no further information is needed. Otherwise, a groupId, artifactId and version need be given too.

  • Using Maven, mvn deploy to that Maven repository. Refresh the list to make it show up.

Note

This remote Maven repository is relatively simple. It does not support proxying, mirroring, ... like Nexus or Archiva.

The Asset Editor is the principle component of the workbench User-Interface. It consists of two main views Editor and Overview.

The Project Explorer provides the ability to browse different Organizational Units, Repositories, Projects and their files.

The Project Editor screen can be accessed from Project Explorer. Project Editor shows the settings for the currently active project.

Unlike most of the workbench editors, project editor edits more than one file. Showing everything that is needed for configuring the KIE project in one place.


By default, a data model is always constrained to the context of a project. For the purpose of this tutorial, we will assume that a correctly configured project already exists and the authoring perspective is open.

To start the creation of a data model inside a project, take the following steps:

This will start up the Data Modeller tool, which has the following general aspect:


The "Editor" tab is divided into the following sections:

The "Source" tab shows an editor that allows the visualization and modification of the generated java code.

The "Overview" tab shows the standard metadata and version information as the other workbench editors.

Once the data object has been created, it now has to be completed by adding user-defined properties to its definition. This can be achieved by pressing the "add field" button. The "New Field" dialog will be opened and the new field can be created by pressing the "Create" button. The "Create and continue" button will also add the new field to the Data Object, but won't close the dialog. In this way multiple fields can be created avoiding the popup opening multiple times. The following fields can (or must) be filled out:

When finished introducing the initial information for a new field, clicking the 'Create' button will add the newly created field to the end of the data object's fields table below:


The new field will also automatically be selected in the data object's field list, and its properties will be shown in the Field general properties editor. Additionally the field properties will be loaded in the different tool windows, in this way the field will be ready for edition in whatever selected tool window.

At any time, any field (without restrictions) can be deleted from a data object definition by clicking on the corresponding 'x' icon in the data object's fields table.

As stated before, both Data Objects as well as Fields require some of their initial properties to be set upon creation. Additionally there are three domains of properties that can be configured for a given Data Object. A domain is basically a set of properties related to a given business area. Current available domains are, "Drools & jJBPM", "Persistence" and the "Advanced" domain. To work on a given domain the user should select the corresponding "Tool window" (see below) on the right side toolbar. Every tool window usually provides two editors, the "Data Object" level editor and the "Field" level editor, that will be shown depending on the last selected item, the Data Object or the Field.

The Drools & jBPM domain editors manages the set of Data Object or Field properties related to drools applications.

The Drools & jBPM object editor manages the object level drools properties


  • TypeSafe: this property allows to enable/disable the type safe behaviour for current type. By default all type declarations are compiled with type safety enabled. (See Drools for more information on this matter).

  • ClassReactive: this property allows to mark this type to be treated as "Class Reactive" by the Drools engine. (See Drools for more information on this matter).

  • PropertyReactive: this property allows to mark this type to be treated as "Property Reactive" by the Drools engine. (See Drools for more information on this matter).

  • Role: this property allows to configure how the Drools engine should handle instances of this type: either as regular facts or as events. By default all types are handled as a regular fact, so for the time being the only value that can be set is "Event" to declare that this type should be handled as an event. (See Drools Fusion for more information on this matter).

  • Timestamp: this property allows to configure the "timestamp" for an event, by selecting one of his attributes. If set the engine will use the timestamp from the given attribute instead of reading it from the Session Clock. If not, the engine will automatically assign a timestamp to the event. (See Drools Fusion for more information on this matter).

  • Duration: this property allows to configure the "duration" for an event, by selecting one of his attributes. If set the engine will use the duration from the given attribute instead of using the default event duration = 0. (See Drools Fusion for more information on this matter).

  • Expires: this property allows to configure the "time offset" for an event expiration. If set, this value must be a temporal interval in the form: [#d][#h][#m][#s][#[ms]] Where [ ] means an optional parameter and # means a numeric value. e.g.: 1d2h, means one day and two hours. (See Drools Fusion for more information on this matter).

  • Remotable: If checked this property makes the Data Object available to be used with jBPM remote services as REST, JMS and WS. (See jBPM for more information on this matter).

The Persistence domain editors manages the set of Data Object or Field properties related to persistence.

The persistence domain field editor manages the field level persistence properties and is divided in three sections.


A persistable Data Object should have one and only one field defined as the Data Object identifier. The identifier is typically a unique number that distinguishes a given Data Object instance from all other instances of the same class.

When the field's type is a Data Object type, or a list of a Data Object type a relationship type should be set in order to let the persistence provider to manage the relation. Fortunately this relation type is automatically set when such kind of fields are added to an already marked as persistable Data Object. The relationship type is set by the following popup.


  • Relationship type: sets the type of relation from one of the following options:

    One to one: typically used for 1:1 relations where "A is related to one instance of B", and B exists only when A exists. e.g. PurchaseOrder -> PurchaseOrderHeader (a PurchaseOrderHeader exists only if the PurchaseOrder exists)

    One to many: typically used for 1:N relations where "A is related to N instances of B", and the related instances of B exists only when A exists. e.g. PurchaseOrder -> PurchaseOrderLine (a PurchaseOrderLine exists only if the PurchaseOrder exists)

    Many to one: typically used for 1:1 relations where "A is related to one instance of B", and B can exist even without A. e.g. PurchaseOrder -> Client (a Client can exist in the database even without an associated PurchaseOrder)

    Many to many: typically used for N:N relations where "A can be related to N instances of B, and B can be related to M instances of A at the same time", and both B an A instances can exits in the database independently of the related instances. e.g. Course -> Student. (Course can be related to N Students, and a given Student can attend to M courses)

    When a field of type "Data Object" is added to a given persistable Data Object, the "Many to One" relationship type is generated by default.

    And when a field of type "list of Data Object" is added to a given persistable Data Object , the "One to Many" relationship is generated by default.

  • Cascade mode: Defines the set of cascadable operations that are propagated to the associated entity. The value cascade=ALL is equivalent to cascade={PERSIST, MERGE, REMOVE, REFRESH}. e.g. when A -> B, and cascade "PERSIST or ALL" is set, if A is saved, then B will be also saved.

    The by default cascade mode created by the data modeller is "ALL" and it's strongly recommended to use this mode when Data Objects are being used by jBPM processes and forms.

  • Fetch mode: Defines how related data will be fetched from database at reading time.

    EAGER: related data will be read at the same time. e.g. If A -> B, when A is read from database B will be read at the same time.

    LAZY: reading of related data will be delayed usually to the moment they are required. e.g. If PurchaseOrder -> PurchaseOrderLine the lines reading will be postponed until a method "getLines()" is invoked on a PurchaseOrder instance.

    The default fetch mode created by the data modeller is "EAGER" and it's strongly recommended to use this mode when Data Objects are being used by jBPM processes and forms.

  • Optional: establishes if the right side member of a relationship can be null.

  • Mapped by: used for reverse relations.

The advanced domain enables the configuration of whatever parameter set by the other domains as well as the adding of arbitrary parameters. As it will be shown in the code generation section every "Data Object / Field" parameter is represented by a java annotation. The advanced mode enables the configuration of this annotations.

The advanced domain editor has the same shape for both Data Object and Field.


The following operations are available

The data model in itself is merely a visual tool that allows the user to define high-level data structures, for them to interact with the Drools Engine on the one hand, and the jBPM platform on the other. In order for this to become possible, these high-level visual structures have to be transformed into low-level artifacts that can effectively be consumed by these platforms. These artifacts are Java POJOs (Plain Old Java Objects), and they are generated every time the data model is saved, by pressing the "Save" button in the top Data Modeller Menu. Additionally when the user round trip between the "Editor" and "Source" tab, the code is auto generated to maintain the consistency with the Editor view and vice versa.


The resulting code is generated according to the following transformation rules:

  • The data object's identifier property will become the Java class's name. It therefore needs to be a valid Java identifier.

  • The data object's package property becomes the Java class's package declaration.

  • The data object's superclass property (if present) becomes the Java class's extension declaration.

  • The data object's label and description properties will translate into the Java annotations "@org.kie.api.definition.type.Label" and "@org.kie.api.definition.type.Description", respectively. These annotations are merely a way of preserving the associated information, and as yet are not processed any further.

  • The data object's role property (if present) will be translated into the "@org.kie.api.definition.type.Role" Java annotation, that IS interpreted by the application platform, in the sense that it marks this Java class as a Drools Event Fact-Type.

  • The data object's type safe property (if present) will be translated into the "@org.kie.api.definition.type.TypeSafe Java annotation. (see Drools)

  • The data object's class reactive property (if present) will be translated into the "@org.kie.api.definition.type.ClassReactive Java annotation. (see Drools)

  • The data object's property reactive property (if present) will be translated into the "@org.kie.api.definition.type.PropertyReactive Java annotation. (see Drools)

  • The data object's timestamp property (if present) will be translated into the "@org.kie.api.definition.type.Timestamp Java annotation. (see Drools)

  • The data object's duration property (if present) will be translated into the "@org.kie.api.definition.type.Duration Java annotation. (see Drools)

  • The data object's expires property (if present) will be translated into the "@org.kie.api.definition.type.Expires Java annotation. (see Drools)

  • The data object's remotable property (if present) will be translated into the "@org.kie.api.remote.Remotable Java annotation. (see jBPM)

A standard Java default (or no parameter) constructor is generated, as well as a full parameter constructor, i.e. a constructor that accepts as parameters a value for each of the data object's user-defined fields.

The data object's user-defined fields are translated into Java class fields, each one of them with its own getter and setter method, according to the following transformation rules:

  • The data object field's identifier will become the Java field identifier. It therefore needs to be a valid Java identifier.

  • The data object field's type is directly translated into the Java class's field type. In case the field was declared to be multiple (i.e. 'List'), then the generated field is of the "java.util.List" type.

  • The equals property: when it is set for a specific field, then this class property will be annotated with the "@org.kie.api.definition.type.Key" annotation, which is interpreted by the Drools Engine, and it will 'participate' in the generated equals() method, which overwrites the equals() method of the Object class. The latter implies that if the field is a 'primitive' type, the equals method will simply compare its value with the value of the corresponding field in another instance of the class. If the field is a sub-entity or a collection type, then the equals method will make a method-call to the equals method of the corresponding data object's Java class, or of the java.util.List standard Java class, respectively.

    If the equals property is checked for ANY of the data object's user defined fields, then this also implies that in addition to the default generated constructors another constructor is generated, accepting as parameters all of the fields that were marked with Equals. Furthermore, generation of the equals() method also implies that also the Object class's hashCode() method is overwritten, in such a manner that it will call the hashCode() methods of the corresponding Java class types (be it 'primitive' or user-defined types) for all the fields that were marked with Equals in the Data Model.

  • The position property: this field property is automatically set for all user-defined fields, starting from 0, and incrementing by 1 for each subsequent new field. However the user can freely change the position among the fields. At code generation time this property is translated into the "@org.kie.api.definition.type.Position" annotation, which can be interpreted by the Drools Engine. Also, the established property order determines the order of the constructor parameters in the generated Java class.

As an example, the generated Java class code for the Purchase Order data object, corresponding to its definition as shown in the following figure purchase_example.jpg is visualized in the figure at the bottom of this chapter. Note that the two of the data object's fields, namely 'header' and 'lines' were marked with Equals, and have been assigned with the positions 2 and 1, respectively).




    package org.jbpm.examples.purchases;

    /**
    * This class was automatically generated by the data modeler tool.
    */
    @org.kie.api.definition.type.Label("Purchase Order")
    @org.kie.api.definition.type.TypeSafe(true)
    @org.kie.api.definition.type.Role(org.kie.api.definition.type.Role.Type.EVENT)
    @org.kie.api.definition.type.Expires("2d")
    @org.kie.api.remote.Remotable
    public class PurchaseOrder implements java.io.Serializable
    {

    static final long serialVersionUID = 1L;

    @org.kie.api.definition.type.Label("Total")
    @org.kie.api.definition.type.Position(3)
    private java.lang.Double total;

    @org.kie.api.definition.type.Label("Description")
    @org.kie.api.definition.type.Position(0)
    private java.lang.String description;

    @org.kie.api.definition.type.Label("Lines")
    @org.kie.api.definition.type.Position(2)
    @org.kie.api.definition.type.Key
    private java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> lines;

    @org.kie.api.definition.type.Label("Header")
    @org.kie.api.definition.type.Position(1)
    @org.kie.api.definition.type.Key
    private org.jbpm.examples.purchases.PurchaseOrderHeader header;

    @org.kie.api.definition.type.Position(4)
    private java.lang.Boolean requiresCFOApproval;

    public PurchaseOrder()
    {
    }

    public java.lang.Double getTotal()
    {
    return this.total;
    }

    public void setTotal(java.lang.Double total)
    {
    this.total = total;
    }

    public java.lang.String getDescription()
    {
    return this.description;
    }

    public void setDescription(java.lang.String description)
    {
    this.description = description;
    }

    public java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> getLines()
    {
    return this.lines;
    }

    public void setLines(java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> lines)
    {
    this.lines = lines;
    }

    public org.jbpm.examples.purchases.PurchaseOrderHeader getHeader()
    {
    return this.header;
    }

    public void setHeader(org.jbpm.examples.purchases.PurchaseOrderHeader header)
    {
    this.header = header;
    }

    public java.lang.Boolean getRequiresCFOApproval()
    {
    return this.requiresCFOApproval;
    }

    public void setRequiresCFOApproval(java.lang.Boolean requiresCFOApproval)
    {
    this.requiresCFOApproval = requiresCFOApproval;
    }

    public PurchaseOrder(java.lang.Double total, java.lang.String description,
    java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> lines,
    org.jbpm.examples.purchases.PurchaseOrderHeader header,
    java.lang.Boolean requiresCFOApproval)
    {
    this.total = total;
    this.description = description;
    this.lines = lines;
    this.header = header;
    this.requiresCFOApproval = requiresCFOApproval;
    }

    public PurchaseOrder(java.lang.String description,
    org.jbpm.examples.purchases.PurchaseOrderHeader header,
    java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> lines,
    java.lang.Double total, java.lang.Boolean requiresCFOApproval)
    {
    this.description = description;
    this.header = header;
    this.lines = lines;
    this.total = total;
    this.requiresCFOApproval = requiresCFOApproval;
    }

    public PurchaseOrder(
    java.util.List<org.jbpm.examples.purchases.PurchaseOrderLine> lines,
    org.jbpm.examples.purchases.PurchaseOrderHeader header)
    {
    this.lines = lines;
    this.header = header;
    }

    @Override
    public boolean equals(Object o)
    {
    if (this == o)
    return true;
    if (o == null || getClass() != o.getClass())
    return false;
    org.jbpm.examples.purchases.PurchaseOrder that = (org.jbpm.examples.purchases.PurchaseOrder) o;
    if (lines != null ? !lines.equals(that.lines) : that.lines != null)
    return false;
    if (header != null ? !header.equals(that.header) : that.header != null)
    return false;
    return true;
    }

    @Override
    public int hashCode()
    {
    int result = 17;
    result = 31 * result + (lines != null ? lines.hashCode() : 0);
    result = 31 * result + (header != null ? header.hashCode() : 0);
    return result;
    }

    }

  

Using an external model means the ability to use a set for already defined POJOs in current project context. In order to make those POJOs available a dependency to the given JAR should be added. Once the dependency has been added the external POJOs can be referenced from current project data model.

There are two ways to add a dependency to an external JAR file:

Current version implements roundtrip and code preservation between Data modeller and Java source code. No matter where the Java code was generated (e.g. Eclipse, Data modeller), the data modeller will only create/delete/update the necessary code elements to maintain the model updated, i.e, fields, getter/setters, constructors, equals method and hashCode method. Also whatever Type or Field annotation not managed by the Data Modeler will be preserved when the Java sources are updated by the Data modeller.

Aside from code preservation, like in the other workbench editors, concurrent modification scenarios are still possible. Common scenarios are when two different users are updating the model for the same project, e.g. using the data modeller or executing a 'git push command' that modifies project sources.

From an application context's perspective, we can basically identify two different main scenarios:

A data set is basically a set of columns populated with some rows, a matrix of data composed of timestamps, texts and numbers. A data set can be stored in different systems: a database, an excel file, in memory or in a lot of other different systems. On the other hand, a data set definition tells the workbench modules how such data can be accessed, read and parsed.

Notice, it's very important to make crystal clear the difference between a data set and its definition since the workbench does not take care of storing any data, it just provides a standard way to define access to those data sets regardless where the data is stored.

Let's take for instance the data stored in a remote database. A valid data set could be, for example, an entire database table or the result of an SQL query. In both cases, the database will return a bunch of columns and rows. Now, imagine we want to get access to such data to feed some charts in a new workbench perspective. First thing is to create and register a data set definition in order to indicate the following:

This chapter introduces the available workbench tools for registering and handling data set definitions and how these definitions can be consumed in other workbench modules like, for instance, the Perspective Editor.

Clicking on the New Data Set button opens a new screen from which the user is able to create a new data set definition in three steps:

After clicking on the Test button (see previous step), the system executes a data set lookup test call in order to check if the remote system is up and the data is available. If everything goes ok the user will see the following screen:


This screen shows a live data preview along with the columns the user wants to be part of the resulting data set. The user can also navigate through the data and apply some changes to the data set structure. Once finished, we can click on the Save button in order to register the new data set definition.

We can also change the configuration settings at any time just by going back to the configuration tab. We can repeat the Configuration>Test>Preview cycle as may times as needed until we consider it's ready to be saved.

Columns

In the Columns tab area the user can select what columns are part of the resulting data set definition.


  • (1) To add or remove columns. Select only those columns you want to be part of the resulting data set

  • (2) Use the drop down image selector to change the column type

A data set may only contain columns of any of the following 4 types:

  • Label - For text values supporting group operations (similar to the SQL "group by" operator) which means you can perform data lookup calls and get one row per distinct value.

  • Text - For text values NOT supporting group operations. Typically for modeling large text columns such as abstracts, descriptions and the like.

  • Number - For numeric values. It does support aggregation functions on data lookup calls: sum, min, max, average, count, disctinct.

  • Date - For date or timestamp values. It does support time based group operations by different time intervals: minute, hour, day, month, year, ...

No matter which remote system you want to retrieve data from, the resulting data set will always return a set of columns of one of the four types above. There exists, by default, a mapping between the remote system column types and the data set types. The user is able to modify the type for some columns, depending on the data provider and the column type of the remote system. The system supports the following changes to column types:

  • Label <> Text - Useful when we want to enable/disable the categorization (grouping) for the target column. For instance, imagine a database table called "document" containing a large text column called "abstract". As we do not want the system to treat such column as a "label" we might change its column type to "text". Doing so, we are optimizing the way the system handles the data set and

  • Number <> Label - Useful when we want to treat numeric columns as labels. This can be used for instance to indicate that a given numeric column is not a numeric value that can be used in aggregation functions. Despite its values are stored as numbers we want to handle the column as a "label". One example of such columns are: an item's code, an appraisal id., ...

Note

BEAN data sets do not support changing column types as it's up to the developer to decide which are the concrete types for each column.

Filter

A data set definition may define a filter. The goal of the filter is to leave out rows the user does not consider necessary. The filter feature works on any data provider type and it lets the user to apply filter operations on any of the data set columns available.


While adding or removing filter conditions and operations, the preview table on central area is updated with live data that reflects the current filter status.

There exists two strategies for filtering data sets and it's also important to note that choosing between the two have important implications. Imagine a dashboard with some charts feeding from a expense reports data set where such data set is built on top of an SQL table. Imagine also we only want to retrieve the expense reports from the "London" office. You may define a data set containing the filter "office=London" and then having several charts feeding from such data set. This is the recommended approach. Another option is to define a data set with no initial filter and then let the individual charts to specify their own filter. It's up to the user to decide on the best approach.

Depending on the case it might be better to define the filter at a data set level for reusing across other modules. The decision may also have impact on the performance since a filtered cached data set will have far better performance than a lot of individual non-cached data set lookup requests. (See the next section for more information about caching data sets).

Note

Notice, for SQL data sets, the user can use both the filter feature introduced or, alternatively, just add custom filter criteria to the SQL sentence. Although, the first approach is more appropriated for non technical users since they might not have the required SQL language skills.

The system provides caching mechanisms out-of-the-box for holding data sets and performing data operations using in-memory strategies. The use of these features brings a lot of advantages, like reducing the network traffic, remote system payload, processing times etc. On the other hand, it's up to the user to fine tune properly the caching settings to avoid hitting performance issues.

Two cache levels are supported:

The following diagram shows how caching is involved in any data set operation:


Any data look up call produces a resulting data set, so the use of the caching techniques determines where the data lookup calls are executed and where the resulting data set is located.

Client cache

If ON then the data set involved in a look up operation is pushed into the web browser so that all the components that feed from this data set do not need to perform any requests to the backend since data set operations are resolved at a client side:

  • The data set is stored in the web browser's memory

  • The client components feed from the data set stored in the browser

  • Data set operations (grouping, aggregations, filters and sort) are processed within the web browser, by means of a Javascript data set operation engine.

If you know beforehand that your data set will remain small, you can enable the client cache. It will reduce the number of backend requests, including the requests to the storage system. On the other hand, if you consider that your data set will be quite big, disable the client cache so as to not hitting with browser issues such as slow performance or intermittent hangs.

Backend cache

Its goal is to provide a caching mechanism for data sets on backend side.

This feature allows to reduce the number of requests to the remote storage system , by holding the data set in memory and performing group, filter and sort operations using the in-memory engine.

It's useful for data sets that do not change very often and their size can be considered acceptable to be held and processed in memory. It can be also helpful on low latency connectivity issues with the remote storage. On the other hand, if your data set is going to be updated frequently, it's better to disable the backend cache and perform the requests to the remote storage on each look up request, so the storage system is in charge of resolving the data set lookup request.

Note

BEAN and CSV data providers relies by default on the backend cache, as in both cases the data set must be always loaded into memory in order to resolve any data lookup operation using the in-memory engine. This is the reason why the backend settings are not visible in the Advanced settings tab.

The refresh feature allows for the invalidation of any cached data when certain conditions are meet.


  • (1) To enable or disable the refresh feature.

  • (2) To specify the refresh interval.

  • (3) To enable or disable data set invalidation when the data is outdated.

The data set refresh policy is tightly related to data set caching, detailed in previous section. This invalidation mechanism determines the cache life-cycle.

Depending on the nature of the data there exist three main use cases:

  • Source data changes predictable - Imagine a database being updated every night. In that case, the suggested configuration is to use a "refresh interval = 1 day" and disable "refresh on stale data". That way, the system will always invalidate the cached data set every day. This is the right configuration when we know in advance that the data is going to change.

  • Source data changes unpredictable - On the other hand, if we do not know whether the database is updated every day, the suggested configuration is to use a "refresh interval = 1 day" and enable "refresh on stale data". If so the system, before invalidating any data, will check for modifications. On data modifications, the system will invalidate the current stale data set so that the cache is populated with fresh data on the next data set lookup call.

  • Real time scenarios - In real time scenarios caching makes no sense as data is going to be updated constantly. In this kind of scenarios the data sent to the client has to be constantly updated, so rather than enabling the refresh settings (remember this settings affect the caching, and caching is not enabled) it's up to the clients consuming the data set to decide when to refresh. When the client is a dashboard then it's just a matter of modifying the refresh settings in the Displayer Editor configuration screen and set a proper refresh period, "refresh interval = 1 second" for example.

A security environment is usually provided by the use of a realm. Realms are used to restrict the access for the different application's resources. So realms contains information about the users, groups, roles, permissions and and any other related information.

In most of the typical scenarios the application's security is delegated to the container's security mechanism, which consumes a given realm at same time. It's important to consider that there exist several realm implementations, for example Wildfly provides a realm based on the application-users.properties/application-roles.properties files, Tomcat provides a realm based on the tomcat-users.xml file, etc. So keep in mind that there is no single security realm to rely on, it can be different in each installation.

The jBPM and Drools workbenches are not an exception, they're build on top Uberfire framework (aka UF), which delegates the authorization and authentication to the underlying container's security environment as well, so the consumed realm is given by the concrete deployment configuration.

Each security realm can provide support different operations. For example consider the use of a Wildfly's realm based on properties files, The contents for the applications-users.properties is like:

admin=207b6e0cc556d7084b5e2db7d822555c
salaboy=d4af256e7007fea2e581d539e05edd1b
maciej=3c8609f5e0c908a8c361ca633ed23844
kris=0bfd0f47d4817f2557c91cbab38bb92d
katy=fd37b5d0b82ce027bfad677a54fbccee
john=afda4373c6021f3f5841cd6c0a027244
jack=984ba30e11dda7b9ed86ba7b73d01481
director=6b7f87a92b62bedd0a5a94c98bd83e21
user=c5568adea472163dfc00c19c6348a665
guest=b5d048a237bfd2874b6928e1f37ee15e
kiewb=78541b7b451d8012223f29ba5141bcc2
kieserver=16c6511893651c9b4b57e0c027a96075

Note that it's based on key-value pairs where the key is the username, and the value is the hashed value for the user's password. So a user is just defined by the key, by its username, it does not have a name nor address or any other meta information.

On the other hand, consider the use of a realm provided by a Keycloak server. The information for a user is composed by more user meta-data, such as surname, address, etc, as in the following image:


So the different services and client side components from the users and group management API are based on capabilities.Capabilities are used to expose or restrict the available functionality provided by the different services and client side components. Examples of capabilities are:

  • Create a user

  • Update a user

  • Delete a user

  • Update user's attributes

  • Create a group

  • Update a group

  • Assign groups to a user

  • Assign roles to a user

Each security management provider must specify a set of capabilities supported. From the previous examples you can note that the Wildfly security management provider does not support the capability for the management of the attributes for a user - the user is only composed by the user name. On the other hand the Keycloak provider does support this capability.

The different views and user interface components rely on the capabilities supported by each provider, so if a capability is not supported by the provider in use, the UI does not provide the views for the management of that capability. As an example, consider that a concrete provider does not support deleting users - the delete user button on the user interface will be not available.

Please take a look at the concrete service provider documentation to check all the supported capabilities for each one, the default ones can be found here.

Before considering the installation and setup steps please note the following Drools and jBPM distributions come with built-in, pre-installed security management providers by default:

Please read each provider's documentation in order to apply the concrete settings for the target deployment environment.

On the other hand, if using a custom security management provider or need to include it on an existing application, consider the following installation options:

  • Enable the security management feature on an existing WAR distribution

  • Setup and installation in an existing or new project

NOTE: If no security management provider is installed in the application, there will be no available user interface for managing the security realm. Once a security management provider is installed and setup, the user and group management user interfaces are automatically enabled and accessible from the main menu.

If you're building an Uberfire based web application and you want to include the user and group management feature, please read this instructions.

The user and group management feature is presented using two different perspectives that are available from the main Home menu (considering that the feature is enabled) as:


Read the following sections for using both user and group management perspectives.

The user management interface is available from the User management menu entry in the Home menu.

The interface is presented using two main panels: the users explorer on the west panel and the user editor on the center one:


The users explorer, on west panel, lists by default all the users present on the application's security realm:


In addition to listing all users, the users explorer allows:

The user editor, on the center panel, is used to create, view, update or delete users. Once creating a new user o clicking an existing user on the users explorer, the user editor screen is opened.

To view an existing user, click on an existing user in the Users Explorer to open the User Editor screen. For example, viewing the admin user when using the Wildfly security management provider results in this screen:


Same admin user view operation but when using the Keycloak security management provider, instead of the Wildfly's one, results in this screen:


Note that the user editor, when using the Keycloak sec. management provider, includes the user attributes management section, but it's not present when using the Wildfly's one. So remember that the information and actions available on the user interface depends on each provider's capabilities (as explained in previous sections).

Viewing a user in the user editor provides the following information (if provider supports it):

  • The user name

  • The user's attributes

  • The assigned groups

  • The assigned roles

In order to update or delete an existing user, click on the Edit button present near to the username in the user editor screen:


Once the user editor presented in edit mode, different operations can be done (if the security management provider in use supports it):

There are 4 main processes which represent the stages of the Asset Management feature: Configure Repository, Promote Changes, Build and Release.

The Execution Server Management UI allows users create and modify Server Templates and Containers, it also allows users manage Remote Servers. This screen is available via Deploy -> Rule Deployments menu.


Note

The management UI is only available for KIE Managed Servers.

A Container is a KIE Container configuration of the Server Template. Click the Add Container button to create a new container for the current Server Template.

The search area can help users find an specific KJARs that they are looking for.


For Server Templates that have Process capabilities enabled, the Wizard has a 2nd optional step where users can configure some process related behaviors.


Once created the new Container will be displayed on the containers list just above the list of remote servers. Just after created a container is by default Stopped which is the only state that allows users to remove it.


A Container has the following tabs available for management and/or configuration:

  • Status

  • Version Configuration

  • Process Configuration

Status tab lists all the Remote Servers that are running the active Container. Each Remote Server is rendered as a Card, which displays to users status and endpoint.

Note

Only started Containers are deployed to remote servers.


Version Configuration tab allow users change the current version of the Container. User's can upgrade manually to a specific version using the "Upgrade" button, or enable/disable the Scanner. It's also possible to execute a ScanNow operation, that will scan for new versions only once.


Process Configuration is the same form that is displayed during New Container Wizard for Template Servers that have Process Capability. If Template Server doesn't have such capability, the action buttons will be disabled.


REST API calls to Knowledge Store allow you to manage the Knowledge Store content and manipulate the static data in the repositories of the Knowledge Store. The calls are asynchronous, that is, they continue their execution after the call was performed as a job. The job ID is returned by every calls to allow after the REST API call was performed to request the job status and verify whether the job finished successfully. Parameters of these calls are provided in the form of JSON entities.

When using Java code to interface with the REST API, the classes used in POST operations or otherwise returned by various operations can be found in the (org.kie.workbench.services:)kie-wb-common-services JAR. All of the classes mentioned below can be found in the org.kie.workbench.common.services.shared.rest package in that JAR.

Every Knowledge Store REST call returns its job ID after it was sent. This is necessary as the calls are asynchronous and you need to be able to reference the job to check its status as it goes through its lifecycle. During its lifecycle, a job can have the following statuses:

The following job calls are provided:

Repository calls are calls to the Knowledge Store that allow you to manage its Git repositories and their projects.

The following repositories calls are provided:

[GET] /repositories

Gets information about the repositories in the Knowledge Store

Returns a Collection<Map<String, String>> or Collection<RepositoryRequest> instance, depending on the JSON serialization library being used. The keys used in the Map<String, String> instance match the fields in the RepositoryRequest class


[GET] /repositories/{repositoryName}

Gets information about a repository

Returns a Map<String, String> or RepositoryRequest instance, depending on the JSON serialization library being used. The keys used in the Map<String, String> instance match the fields in the RepositoryRequest class


[POST] /repositories

Creates a new empty repository or a new repository cloned from an existing (git) repository

Consumes a RepositoryRequest instance

Returns a CreateOrCloneRepositoryRequest instance


[DELETE] /repositories/{repositoryName}

Removes the repository from the Knowledge Store

Returns a RemoveRepositoryRequest instance

[POST] /repositories/{repositoryName}/projects/

Creates a project in the repository

Consumes an Entity instance

Returns a CreateProjectRequest instance


[DELETE] /repositories/{repositoryName}/projects/

Deletes the project in the repository

Returns a DeleteProjectRequest instance

[GET] /repositories/{repositoryName}/projects/

Gets information about the projects

Returns a Collection<Map<String, String>> or Collection<ProjectResponse> instance, depending on the JSON serialization library being used. The keys used in the Map<String, String> instance match the fields in the ProjectResponse class


Organizational unit calls are calls to the Knowledge Store that allow you to manage its organizational units, so as to organize the connected Git repositories.

The following organizationalUnits calls are provided:

[POST] /organizationalunits

Creates an organizational unit in the Knowledge Store

Consumes an OrganizationalUnit instance

Returns a CreateOrganizationalUnitRequest instance


[GET] /organizationalunits/{orgUnitName}

Creates an organizational unit

Consumes an OrganizationalUnit instance

Returns a CreateOrganizationalUnitRequest instance


[POST] /organizationalunits/{orgUnitName}

Creates an organizational unit in the Knowledge Store

Consumes an UpdateOrganizationalUnit instance

Returns a UpdateOrganizationalUnitRequest instance


[DELETE] /organizationalunits/{organizationalUnitName}

Deletes a organizational unit

Returns a RemoveOrganizationalUnitRequest instance

[POST] /organizationalunits/{organizationalUnitName}/repositories/{repositoryName}

Adds the repository to the organizational unit

Returns a AddRepositoryToOrganizationalUnitRequest instance

[DELETE] /organizationalunits/{organizationalUnitName}/repositories/{repositoryName}

Removes the repository from the organizational unit

Returns a RemoveRepositoryFromOrganizationalUnitRequest instance

The URL templates in the table below are relative the following URL:


Single Sign On (SSO) and related token exchange mechanisms are becoming the most common scenario for the authentication and authorization in different environments on the web, specially when moving into the cloud.

This section talks about the integration of Keycloak with jBPM or Drools applications in order to use all the features provided on Keycloak. Keycloak is an integrated SSO and IDM for browser applications and RESTful web services. Lean more about it in the Keycloak's home page.

The result of the integration with Keycloak has lots of advantages such as:

  • Provide an integrated SSO and IDM environment for different clients, including jBPM and Drools workbenches

  • Social logins - use your Facebook, Google, Linkedin, etc accounts

  • User session management

  • And much more...

Next sections cover the following integration points with Keycloak:

  • Workbench authentication through a Keycloak server

    It basically consists of securing both web client and remote service clients through the Keycloak SSO. So either web interface or remote service consumers ( whether a user or a service ) will authenticate into trough KC.

  • Execution server authentication through a Keycloak server

    Consists of securing the remote services provided by the execution server (as it does not provides web interface). Any remote service consumer ( whether a user or a service ) will authenticate trough KC.

  • Consuming remote services

    This section describes how a third party clients can consume the remote service endpoints provided by both Workbench and Execution Server, such as the REST API or remote file system services.

Keycloak provides an extensive documentation and several articles about the installation on different environments. This section describes the minimal setup for being able to build the integrated environment for the example. Please refer to the Keycloak documentation if you need more information.

Here are the steps for a minimal Keycloak installation and setup:

  • Download latest version of Keycloak from the Downloads section. This example is based on Keycloak 1.9.0.Final

  • Unzip the downloaded distribution of Keycloak into a folder, let's refer it as

    $KC_HOME
  • Run the KC server - This example is based on running both Keycloak and jBPM on same host. In order to avoid port conflicts you can use a port offset for the Keycloak's server as:

    $KC_HOME/bin/standalone.sh -Djboss.socket.binding.port-offset=100

  • Create a Keycloak's administration user - Execute the following command to create an admin user for this example:

    $KC_HOME/bin/add-user.sh -r master -u 'admin' -p 'admin'

The Keycloak administration console will be available at http://localhost:8180/auth/admin (use the admin/admin for login credentials).

Security realms are used to restrict the access for the different application's resources.

Once the Keycloak server is running next step is about creating a realm. This realm will provide the different users, roles, sessions, etc for the jBPM application/s.

Keycloak provides several examples for the realm creation and management, from the official examples to different articles with more examples.

Follow these steps in order to create the demo realm used later in this document:

  • Go to the Keycloak administration console and click on Add realm button. Give it the name demo.

  • Go to the Clients section (from the main admin console menu) and create a new client for the demo realm:

    • Client ID: kie

    • Client protocol: openid-connect

    • Acces type: confidential

    • Root URL: http://localhost:8080

    • Base URL: /kie-wb-6.4.0.Final

    • Redirect URIs: /kie-wb-6.4.0.Final/*

The resulting kie client settings screen:


Note: As you can see in the above settings it's being considered the value kie-wb-6.4.0.Final for the application's context path. If your jbpm application will be deployed on a different context path, host or port, just use your concrete settings here.

Last step for being able to use the demo realm from the jBPM workbench is create the application's user and roles:

At this point a Keycloak server is running on the host, setup with a minimal configuration set. Let's move to the jBPM workbench setup.

For this tutorial let's use a Wildfly as the application server for the jBPM workbench, as the jBPM installer does by default.

Let's assume, after running the jBPM installer, the $JBPM_HOME as the root path for the Wildfly server where the application has been deployed.

In order to use the Keycloak's authentication and authorization modules from the jBPM application, the Keycloak adapter for Wildfly must be installed on our server at $JBPM_HOME. Keycloak provides multiple adapters for different containers out of the box, if you are using another container or need to use another adapter, please take a look at the adapters configuration from Keycloak docs. Here are the steps to install and setup the adapter for Wildfly 8.2.x:

  • Download the adapter from here

  • Execute the following commands on your shell:

    cd $JBPM_HOME/unzip keycloak-wf8-adapter-dist.zip // Install the KC client adapter
    
    cd $JBPM_HOME/bin
    ./standalone.sh -c standalone-full.xml // Setup the KC client adapter.
    
    // ** Once server is up, open a new command line terminal and run:
    cd $JBPM_HOME/bin
    ./jboss-cli.sh -c --file=adapter-install.cli

Once installed the KC adapter into Wildfly, next step is to configure the adapter in order to specify different settings such as the location for the authentication server, the realm to use and so on.

Keycloak provides two ways of configuring the adapter:

In this example let's use the second option, use the Keycloak subsystem, so our WAR is free from this kind of settings. If you want to use the per WAR approach, please take a look here.

Edit the configuration file $JBPM_HOME/standalone/configuration/standalone-full.xml and locate the subsystem configuration section. Add the following content:

<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
  <secure-deployment name="kie-wb-6.4.0-Final.war">
    <realm>demo</realm>
    <realm-public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCA...</realm-public-key>
    <auth-server-url>http://localhost:8180/auth</auth-server-url>
    <ssl-required>external</ssl-required>
    <resource>kie</resource>
    <enable-basic-auth>true</enable-basic-auth>
    <credential name="secret">925f9190-a7c1-4cfd-8a3c-004f9c73dae6</credential>
    <principal-attribute>preferred_username</principal-attribute>
  </secure-deployment>
</subsystem>

If you have imported the example json files from this document in step 2, you can just use same configuration as above by using your concrete deployment name . Otherwise please use your values for these configurations:

  • Name for the secure deployment - Use your concrete application's WAR file name

  • Realm - Is the realm that the applications will use, in our example, the demo realm created the previous step.

  • Realm Public Key - Provide here the public key for the demo realm. It's not mandatory, if it's not specified, it will be retrieved from the server. Otherwise, you can find it in the Keycloak admin console -> Realm settings ( for demo realm ) -> Keys

  • Authentication server URL - The URL for the Keycloak's authentication server

  • Resource - The name for the client created on step 2. In our example, use the value kie.

  • Enable basic auth - For this example let's enable Basic authentication mechanism as well, so clients can use both Token (Baerer) and Basic approaches to perform the requests.

  • Credential - Use the password value for the kie client. You can find it in the Keycloak admin console -> Clients -> kie -> Credentials tab -> Copy the value for the secret.

For this example you have to take care about using your concrete values for secure-deployment name, realm-public-key and credential password. You can find detailed information about the KC adapter configurations here.

Both jBPM and Drools workbenches provides different remote service endpoints that can be consumed by third party clients using the remote API.

In order to authenticate those services thorough Keycloak the BasicAuthSecurityFilter must be disabled, apply those modifications for the the WEB-INF/web.xml file (app deployment descriptor) from jBPM's WAR file:

  • Remove the following filter from the deployment descriptor:

    <filter>  
      <filter-name>HTTP Basic Auth Filter</filter-name>
      <filter-class>org.uberfire.ext.security.server.BasicAuthSecurityFilter</filter-class>
      <init-param>
        <param-name>realmName</param-name>
        <param-value>KIE Workbench Realm</param-value>
      </init-param>
    </filter>
    
    <filter-mapping>
      <filter-name>HTTP Basic Auth Filter</filter-name>
      <url-pattern>/rest/*</url-pattern>
      <url-pattern>/maven2/*</url-pattern>
      <url-pattern>/ws/*</url-pattern>
    </filter-mapping>

  • Constraint the remote services URL patterns as:

    <security-constraint>
      <web-resource-collection>
        <web-resource-name>remote-services</web-resource-name>
        <url-pattern>/rest/*</url-pattern>
        <url-pattern>/maven2/*</url-pattern>
        <url-pattern>/ws/*</url-pattern>
      </web-resource-collection>
      <auth-constraint>
        <role-name>rest-all</role-name>
      </auth-constraint>
    </security-constraint>

Important note: The user that consumes the remote services must be member of role rest-all. As on described previous steps, the admin user in this example it's already a member of the rest-all role.

In order to consume other remote services such as the file system ones (e.g. remote GIT), a specific Keycloak login module must be used for the application's security domain in the $JBPM_HOME/standalone/configuration/standalone-full.xml file. By default the workbench uses the other security domain, so the resulting configuration on the $JBPM_HOME/standalone/configuration/standalone-full.xml should be such as:


      <security-domain name="other" cache-type="default">
        <authentication>
          <login-module code="org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule" flag="required">
            <!-- Parameter value can be a file system absolute path or a classpath (e.g. "classpath:/some-path/kie-git.json")-->
            <module-option name="keycloak-config-file" value="$JBPM_HOME/kie-git.json"/>
          </login-module>
        </authentication>
      </security-domain>
    

Note that:

At this point, remote services that use JAAS for the authentication process, such as the file system ones ( e.g. GIT ), are secured by Keycloak using the client specified in the above json configuration file. So let's create this client on Keycloak and generate the required JSON file:

At this point, the internal Git repositories can be cloned by all users authenticated via the Keycloak server. Command example:

git clone ssh://admin@localhost:8001/system

The KIE Execution Server provides a REST API than can be consumed for any third party clients,. This this section is about how to integration the KIE Execution Server with the Keycloak SSO in order to delegate the third party clients identity management to the SSO server.

Consider the above environment running, so consider having:

  • A Keycloak server running and listening on http://localhost:8180/auth

  • A realm named demo with a client named kie for the jBPM Workbench

  • A jBPM Workbench running at http://localhost:8080/kie-wb-6.4.0-Final

Follow these steps in order to add an execution server into this environment:

  • Create the client for the execution server on Keycloak

  • Install setup and the Execution server ( with the KC client adapter )

As per each execution server is going to be deployed, you have to create a new client on the demo realm in Keycloak.:

  • Go to the KC admin console -> Clients -> New client

  • Name: kie-execution-server

  • Root URL: http://localhost:8280/

  • Client protocol: openid-connect

  • Access type: confidential ( or public if you want so, but not recommended for production environments)

  • Valid redirect URIs: /kie-server-6.4.0.Final/*

  • Base URL: /kie-server-6.4.0.Final

In this example the admin user already created on previous steps is the one used for the client requests. So ensure that the admin user is member of the role kie-server in order to use the execution server's remote services. If the role does not exist, create it.

Note: This example considers that the execution server will be configured to run using a port offset of 200, so the HTTP port will be available at localhost:8280.

At this point, a client named kie-execution-server is ready on the KC server to use from the execution server.

Let's install, setup and deploy the execution server:

Consider your concrete environment settings if different from this example:

  • Secure deployment name -> use the name of the execution server war file being deployed

  • Public key -> Use the demo realm public key or leave it blank, the server will provide one if so

  • Resource -> This time, instead of the kie client used in the WB configuration, use the kie-execution-server client

  • Enable basic auth -> Up to you. You can enable Basic auth for third party service consumers

  • Credential -> Use the secret key for the kie-execution-server client. You can find it in the Credentialstab of the KC admin console

In order to use the different remote services provided by the Workbench or by an Execution Server, your client must be authenticated on the KC server and have a valid token to perform the requests.

Remember that in order to use the remote services, the authenticated user must have assigned:

Please ensure necessary roles are created and assigned to the users that will consume the remote services on the Keycloak admin console.

You have two options to consume the different remove service endpoints:

First step is to create a new client on Keycloak that allows the third party remote service clients to obtain a token. It can be done as:

Once a public client for our remote clients has been created, you can now obtain the token by performing an HTTP request to the KC server's tokens endpoint. Here is an example for command line:

RESULT=`curl --data "grant_type=password&client_id=kie-remote&username=admin&passwordpassword=<the_client_secret>" http://localhost:8180/auth/realms/demo/protocol/openid-connect/token`

TOKEN=`echo $RESULT | sed 's/.*access_token":"//g' | sed 's/".*//g'`

At this point, if you echo the $TOKEN it will output the token string obtained from the KC server, that can be now used to authorize further calls to the remote endpoints. For exmple, if you want to check the internal jBPM repositories:

curl -H "Authorization: bearer $TOKEN" http://localhost:8080/kie-wb-6.4.0.Final/rest/repositories

The VFS repositories (usually git repositories) stores all the assets (such as rules, decision tables, process definitions, forms, etc). If that VFS resides on each local server, then it must be kept in sync between all servers of a cluster.

Use Apache Zookeeper and Apache Helix to accomplish this. Zookeeper glues all the parts together. Helix is the cluster management component that registers all cluster details (nodes, resources and the cluster itself). Uberfire (on top of which Workbench is build) uses those 2 components to provide VFS clustering.

To create a VFS cluster:

  1. Download Apache Zookeeper and Apache Helix.

  2. Install both:

    1. Unzip Zookeeper into a directory ($ZOOKEEPER_HOME).

    2. In $ZOOKEEPER_HOME, copy zoo_sample.conf to zoo.conf

    3. Edit zoo.conf. Adjust the settings if needed. Usually only these 2 properties are relevant:

      # the directory where the snapshot is stored.
      dataDir=/tmp/zookeeper
      # the port at which the clients will connect
      clientPort=2181
    4. Unzip Helix into a directory ($HELIX_HOME).

  3. Configure the cluster in Zookeeper:

    1. Go to its bin directory:

      $ cd $ZOOKEEPER_HOME/bin
    2. Start the Zookeeper server:

      $ sudo ./zkServer.sh start

      If the server fails to start, verify that the dataDir (as specified in zoo.conf) is accessible.

    3. To review Zookeeper's activities, open zookeeper.out:

      $ cat $ZOOKEEPER_HOME/bin/zookeeper.out
  4. Configure the cluster in Helix:

    1. Go to its bin directory:

      $ cd $HELIX_HOME/bin
    2. Create the cluster:

      $ ./helix-admin.sh --zkSvr localhost:2181 --addCluster kie-cluster

      The zkSvr value must match the used Zookeeper server. The cluster name (kie-cluster) can be changed as needed.

    3. Add nodes to the cluster:

      # Node 1
      $ ./helix-admin.sh --zkSvr localhost:2181 --addNode kie-cluster nodeOne:12345
      # Node 2
      $ ./helix-admin.sh --zkSvr localhost:2181 --addNode kie-cluster nodeTwo:12346
      ...

      Usually the number of nodes a in cluster equal the number of application servers in the cluster. The node names (nodeOne:12345 , ...) can be changed as needed.

      Note

      nodeOne:12345 is the unique identifier of the node, which will be referenced later on when configuring application servers. It is not a host and port number, but instead it is used to uniquely identify the logical node.

    4. Add resources to the cluster:

      $ ./helix-admin.sh --zkSvr localhost:2181 --addResource kie-cluster vfs-repo 1 LeaderStandby AUTO_REBALANCE

      The resource name (vfs-repo) can be changed as needed.

    5. Rebalance the cluster to initialize it:

      $ ./helix-admin.sh --zkSvr localhost:2181 --rebalance kie-cluster vfs-repo 2
    6. Start the Helix controller to manage the cluster:

      $  ./run-helix-controller.sh --zkSvr localhost:2181 --cluster kie-cluster 2>&1 > /tmp/controller.log &
  5. Configure the security domain correctly on the application server. For example on WildFly and JBoss EAP:

    1. Edit the file $JBOSS_HOME/domain/configuration/domain.xml.

      For simplicity sake, presume we use the default domain configuration which uses the profile full that defines two server nodes as part of main-server-group.

    2. Locate the profile full and add a new security domain by copying the other security domain already defined there by default:

      <security-domain name="kie-ide" cache-type="default">
          <authentication>
               <login-module code="Remoting" flag="optional">
                   <module-option name="password-stacking" value="useFirstPass"/>
               </login-module>
               <login-module code="RealmDirect" flag="required">
                   <module-option name="password-stacking" value="useFirstPass"/>
               </login-module>
          </authentication>
      </security-domain>

      Important

      The security-domain name is a magic value.

  6. Configure the system properties for the cluster on the application server. For example on WildFly and JBoss EAP:

    1. Edit the file $JBOSS_HOME/domain/configuration/host.xml.

    2. Locate the XML elements server that belong to the main-server-group and add the necessary system property.

      For example for nodeOne:

      <system-properties>
        <property name="jboss.node.name" value="nodeOne" boot-time="false"/>
        <property name="org.uberfire.nio.git.dir" value="/tmp/kie/nodeone" boot-time="false"/>
        <property name="org.uberfire.metadata.index.dir" value="/tmp/kie/nodeone" boot-time="false"/>
        <property name="org.uberfire.cluster.id" value="kie-cluster" boot-time="false"/>
        <property name="org.uberfire.cluster.zk" value="localhost:2181" boot-time="false"/>
        <property name="org.uberfire.cluster.local.id" value="nodeOne_12345" boot-time="false"/>
        <property name="org.uberfire.cluster.vfs.lock" value="vfs-repo" boot-time="false"/>
        <!-- If you're running both nodes on the same machine: -->
        <property name="org.uberfire.nio.git.daemon.port" value="9418" boot-time="false"/>
      </system-properties>

      And for nodeTwo:

      <system-properties>
        <property name="jboss.node.name" value="nodeTwo" boot-time="false"/>
        <property name="org.uberfire.nio.git.dir" value="/tmp/kie/nodetwo" boot-time="false"/>
        <property name="org.uberfire.metadata.index.dir" value="/tmp/kie/nodetwo" boot-time="false"/>
        <property name="org.uberfire.cluster.id" value="kie-cluster" boot-time="false"/>
        <property name="org.uberfire.cluster.zk" value="localhost:2181" boot-time="false"/>
        <property name="org.uberfire.cluster.local.id" value="nodeTwo_12346" boot-time="false"/>
        <property name="org.uberfire.cluster.vfs.lock" value="vfs-repo" boot-time="false"/>
        <!-- If you're running both nodes on the same machine: -->
        <property name="org.uberfire.nio.git.daemon.port" value="9419" boot-time="false"/>
      </system-properties>

      Make sure the cluster, node and resource names match those configured in Helix.

Designer is a graphical web-based BPMN2 editor. It allows users to model and simulate executable BPMN2 processes. The main goal of Designe is to provide intuitive means to both technical and non-technical users to quickly create their executable business processes. This chapter intends to describe all feature Designer offers currently.


Designer targets the following business process modelling scenarios:

  • View and/or edit existing BPMN2 processes: Designer allows you to open existing BPMN2 processes (for example created using the BPMN2 Eclipse editor or any other tooling that exports BPMN2 XML).

  • Create fully executable BPMN2 processes: A user can create a new BPMN2 process in the Designer and use the editing capabilities (drag and drop and filling in properties in the properties panel) to fill in the details. This for example allows business users to create complete business processes all inside a a browser. The integration with Drools Guvnor allows for your business processes as wells as other business assets such as business rules, process forms/images, etc. to be stored and versioned inside a content repository.

  • View and/or edit Human Task forms during process modelling (using the in-line form editor or the Form Modeller).

  • Simulate your business process models. Busines Process Simulation is based on the BPSIM 1.0 specification.

Designer supports all BPMN2 elements that are also supported by jBPM as well as all jBPM-specific BPMN2 extension elements and attributes.

Designer UI is composed of a number of sections as shown below:


  • (1) Modelling Canvas - this is your process drawing board. After dropping different shapes onto the canvas, you can move them around, connect them, etc. Clicking on a shape on the canvas allows you to set its properties in the expandable Properties Window (3) (as well as create connecting shapes and morph the shape into other shapes).

  • (2) Toolbar - the toolbar contains a vast number of functions offered by Designer (described later). These includes operations that can be performed on shapes present on the Canvas. Individual operations are disabled or enabled depending on what is selected. For example, if no shapes are selected, the Cut/Paste/Delete operations are disabled, and become enabled once you select a shape. Hovering over the icons in the Toolbar displays the description text of the operation.

  • (3) Properties Panel - this expandable section on the right side of Designer allows you to set both process and shape properties. It is divided in four sections, namely "Core properties", and "Extra Properties, "Graphical Settings", and "Simulation Properties" are is expandable. When clicking on a shape in the Canvas, this panel is reloaded to show properties specific to the shape type. If you click on the canvas itself (not on a shape) the section shows your general process properties.

  • (4) Object Repository Panel - the expandable section on the left side of Designer shows the jBPM BPMN2 (default) shape repository tree. It includes all shapes of the jBPM BPMN2 stencil set which can be used to assemble your processes. If you expand each section sub-group you can see the BPMN2 elements that can be placed onto the Designer Canvas (1) by dragging and dropping the shape onto it.

  • (5) View Tabs - currently Designer offers functionality tabs for Process Modelling and Simulation. Process Modelling is the default tab. When users run process simulation, its results are presented in the Simulation tab.

  • (6) Info Tabls - On the bottom Designer shows two different Info tabs. The Business Process tab includes the process modeling while the Metadata tab displays the process metadata such as created by and last modified information.

The Object Repository panel provide means for users to select and drag/drop BPMN2 shapes onto the modelling canvas. Shapes are divided into sections as shown below:


Once a shape is dropped onto the canvas users have a much faster way of continuing modelling without having to go back to the Object Repository panel. This is realized through the shape morphing menu which is presented when a shape on the drawing canvas is clicked on. This menu allows users to either select a connecting shape (next shape) or morph the selected node into another node type. In addition this menu includes means to store the shape name as a dictionary item (explained later), view the specific BPMN2 code of the selected shape, as well as create/edit the task form (in the case of user tasks only).


When connecting shapes Designer applies connection rules that follow the BPMN2 specification. The connection shapes presented in the morphing menu only show shapes that are allowed to be connections. Similarly same rules are applied when dropping a shape from the Object Library from the canvas and trying to connect an existing shape to it. Additional connection rules for boundary events are also available (explained later) and applied when for example moving an intermediate event node onto the edge of a task node.

Users can give names to every shape on the drawing canvas. This is done by double-clicking onto the shape as shown below.


The name of a shape can be pulled from the Process Dictionary. If terms are set up in the dictionary, auto-complete can be used for the node names:


Designer also shows three buttons on top of a clicked shape as shown below.


These include:

  • (1) Add To Dictionary - this option allows users to add the name of the task to the Process Dictionary (explained in more details later)

  • (2) Edit Task Form - allows users to create/edit the Task Form. This option is only available for User Tasks

  • (3) View shape sources - shows the BPMN2 for this particular shape only.

The section should get you started with creating simple business process models by dragging/dropping BPMN2 shapes onto the drawing canvas. Next sections will dive deeper into many other aspects of Designer.

The Designer toolbar contains many different functions which can be used during process modelling.


We will now go through each of the buttons in the Designer Toolbar and give a brief overview of what it does.

(1) Save - allows users to save, copy, rename and delete the business process model. In addition users can turn on auto-save which will automatically save the business process within a defined time interval.


(2) Cut - enabled when a portion of the model is selected.

(3) Copy - enabled when a portion of the model is selected.

(4) Paste - paste the copied portion of the model onto the drawing board.

(5) Delete - enabled when there is a portion of the model is selected and removes it.

(6, 7) Undo/Redo - undo the last performed operation on the drawing canvas.

(8) Local History - local history allows continuous storage of your business process onto your browsers internal storage. Stored version of the business process can persist internet outages or browser crashes so your work will not be lost. This feature is disabled by default and must be enabled by users. Once local history has been enabled users are able to view all previously stored snapshots of their business model, clear local history, configure the snapshot interval, or disable local history. Note that local history will only take a snapshot of your business process on the set storing interval if there were some changes done in the model. If at the end of the snapshot interval Designer detects that there were no changes since the last local history save, no new snapshot will be created.


The Local History results screen allows users to select a stored snapshot of the model and view its process image, and restore it back onto their drawing board.


(9) Object positioning - allows users to position one or more nodes in the business. Note that at last one shape must be selected first, otherwise these options are disable. Contains options "Bring to Front", "Bring to back", "Bring forward", and "Bring Backward"

(10) Alignment: enabled when a portion of the model is selected. Includes options "Align Bottom", "Align Middle", "Align Top", "Align Left", "Align Center", "Align Right", and "Align Same Size".

(11, 12) Group and Ungroup - allows grouping and ungrouping of selected shapes on the drawing board.

(13, 14) Locking and Unlocking - allows parts of the business model to be locked and unlocked. Locked parts of the model cannot be edited (visual display and properties are both locked). Locked nodes are displayed in a light blue color. This feature fosters collaboration of process modelling by allowing users to set parts of their model as "completed" and preventing any further changes to that portion. Other parts of the model can continue to be edited.


(15, 16) Add/Remove Docker - this allows users to add or remove Dockers, or edge points, to sequence flows in the model. Enables when a sequence flow (connector) is selected. It allows users to create very customized connection points from one shape to another. Users can add and remove as many dockers as they would like on a single sequence flow.


(17) Color Themes - Colors are a big part or process modelling as they help with expressing intent as well as help allowing visually impaired users to better view the model. Designer provides two default color themes out of the box named "jBPM" and "High Contrast". The jBPM theme is the default theme used for all new business processes created. Users can switch color themes and the changes will be applied to all nodes that are currently on the model, as well as any new shapes added. Users have the ability to add new custom color themes by adding their own definitions in the Designer themes.json file. Color theme selection is persisted over browser close or possible crash/internet loss.



(18) Process and Task forms - here users have the ability to generate/edit process and task forms. When no user task is selected the default enabled options are "Edit Process Form" and "Generate all Forms". Generate all forms will apply the current model information such as process variables, data objects, and the user tasks data input/output parameters and associations to generate default executable input forms. Upon editing a process and task form, users have the choice between two form editors, the jBPM Form Modeler, and the Designer in-line meta editor. The Designer meta editor is targeted more to technical users as it is text based with the ability for live preview. When the user selects an user task in the model, the "Edit Task Form" and "Generate Task Form" options are enabled which allow users to edit the particular task form, or choose to apply the same generation logic to create a task form for the selected task only. Users have the ability to extend the default form generation templates in designer to create fully customized templates. Node that in the case of the Designer meta editor for forms, generating forms will overwrite existing forms for the process and user tasks. In the case of Form Modeler form generation, a merging algorithm is applied when generating.


When selecting a task, users have the ability to edit the selected tasks form via the form button shown above the user task node.


When editing forms, users are asked to choose between the Form Modeler and the Designer in-line meta editor. If the user selects Form Modeler the form is shown in a new asset tab separately from Designer. Designer meta editor is in-line and part of the Designer application.


The Designer in-line meta form editor is a powerful text-based editor with a live preview feature as well as auto-completion on process variables and user task data inputs/outputs.


(19) Process Information Sharing - this section includes many functions that help with sharing information of your model. These include:


(20) Extra tooling - this section allows users to import their existing BPMN2 processes into designer as well as be able to migrate their old jPDL based processes to BPMN2. For BPMN2 or JSON imports users can choose to add the import ontop of the existing model on the drawing board or choose to replace the current one with the import.




(21) Visual Validation - Designer includes over 100 validation checks and this list is growing. It allows users to view validation issues in real-time as they are modelling their business process. Users can enable visual validation, disable it, as well as view all validation issues at once. If Visual Validation is turned on, Designer with set the shape border of shapes that do not pass validation to red color. Users can then click on that particular shape to view the validation issues for that particular shape only. Alternatively "View All Issues" present a combined list of all validation errors currently found. Note that you do not have to periodically save your business process in order for validation to update. It will do so on its own short intervals during modelling. Users can extend the list of validation issues to include their own types of validation on certain elements of their business model.





(22) Process Simulation - Business Process Simulation deals with statistical analysis of process models over time. It's main goals include

Designer includes a powerful simulation engine which is based on jBPM and Drools and a graphical user interface to view and interpret simulation results. In addition users are able to view all process paths included in their current model on the drawing board. Designer Process Simulation is based on the BPSim 1.0 specification. Details of Process Simulation capabilities in Designer are can be found in its Simulation documentation chapter. Here we just give a brief overview of all features it contains.


When selecting Process Paths, the simulation engine find all possible paths in the business model. Users can choose certain found paths and choose to display them. The chosen path is marked with given colors as shown below.


When selecting "Run Simulation", users have to enter in simulation runtime properties. These include the number of instances of this business process to simulate and the interval time and units. This interval is the time in-between consecutive simulation.


Each shape on the drawing board includes Simulation properties (properties panel) where users can set numerous simulation properties for that particular shape. More info on each of these properties can be found in the Simulation chapter of the documentation. Designer pre-sets some defaults for new processes, which allows business processes to be simulated by default without any modifications of these properties. Note however that the results of the default settings may not be optimal or targeted for the users particular needs.


Once the simulation runtime has completed, users are shown the simulation results in the "Simulation Results" tab of Designer. The results default to the process results. Users can switch to results for each particular shape in their business process to see more specific detauls. In addition, the results contain process paths simulation results for each path in the business process.


Designer simulation presents the users with many different chart types. These include:

The below image shows a number of possible chart types users can view after process simulation has completed.


In addition to the chart results, Designer simulation also offers a full timeline display that includes all details of what happened during simulation. This timeline allows users to navigate through each event that happened during process simulation and select a particular node to display results at that particular point in time.


The simulation timeline can be switched to the Model view. This view displays the process model with the currently selected node in the timeline highlighted. The highlighted node displays the simulation results at that particular point in time of the simulation.


Path execution results shows a chart displaying the chosen path as well as path instance execution details.


(23) Service Repository - Allows users to connect to a service repository via its URL and see the list of available services it provides. Each of the listed services can then be installed into the users project by clicking on the "wrench" icon next to each listed service. Installing a service does the following things:

Users will be notified when the service is successfully installed. After the install users have to re-open the business process to be able to start using the installed services.


(24) Full screen Modev - allows users to place the drawing board of Designer into full-screen mode. This can help with better visualizing larger business processes without having to scroll. Note that this feature is possible only if your browser has full screen mode capabilities. If it does not designer will show a message stating this to the user.


(25) Process Dictionary - Designer Dictionary Editor allows users to create their own dictionary entries or harvest from process documentation or business requirement documents. Process Dictionary entries can be used as auto-completion for shape names. This will be expanded in the future versions to allow mapping of node patters to specific dictionary entries as well. Users can add entries to the dictionary in the Dictioanry Editor or from the selected shapes directly.



(26, 27, 28, 29) Zooming - zooming allows users to zoom in/out of the model, zoom in/out back to the original setting as well as zoom the process model on the drawing board to fit the currently dimensions of the drawing board.

This chapter intends to describe in a simple ways all the steps required to create a process with human tasks, generate and modify the forms for these tasks and execute them. It will provide initial guidance to perform all initial steps, but it will not provide a full description of all available features.

Given that forms are going to be used in tasks, it's possible to generate forms automatically from process variables and task definitions. These forms can be later be modified by using the form editor. In runtime, forms will receive data from process variables, display it to the user and capture his input, and then finally updating process variables again with the new values.

The following example will show all the steps to follow to create a form for the 'Create order' task in the process below.


This form must look like the following in execution:


Once the forms have been generated, you can start editing them. There are several artifacts that are generated in the previous process, but also can be created manually.

We can change the way the form is displayed to the user in the task list. Next, we will show different levels of customization that will allow change it

Each field can be configured to enhance performance in the form. There are a group of common properties, that we call ‘Generic field properties’ and a group of specific properties that depends on the field type.

Let's explain the specific properties of each field type:

  • Short Text (java.lang.String)

  • Long Text (java.lang.String)

    • Compatible field type: Long text, E-mail, Rich text

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Height: The number or rows to show at text area.

      • Formula. to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section .

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section

      • Pattern. Allow introduce an expression to specify the validation of the field. In case that the field value introduced hasn’t match the expression, and error is thrown and the error message has to be shown.

      • Default Value formula. Expression to set the field default value.

  • Float (java.lang.Float)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section .

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section

      • Pattern. Allow introduce an expression to specify how the Float value has to be displayed. The pattern allowed is show in section pattern in http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

      • Default Value formula. Expression to set the field default value.

  • Decimal (java.lang.Double)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Pattern. Allow introduce an expression to specify how the Double value has to be displayed. The pattern allowed is show in section pattern in http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

      • Default Value formula. Expression to set the field default value.

  • BigDecimal (java.math.BigDecimal)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Pattern. Allow introduce an expression to specify how the BigDecimal value has to be displayed. The pattern allowed is show in section pattern in http://docs.oracle.com/javase/6/docs/api/java/text/DecimalFormat.html

      • Default Value formula. Expression to set the field default value.

  • Big integer (java.math.BigInteger)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • Short (java.lang.Short)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • Integer (java.lang.Integer)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • Long Integer (java.lang.Long)

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. Used to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section.

      • Range value. A range formula allows you to let you specify the values that the user can select from an specific field. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • E-mail (java.lang.String)

    • Compatible field type: Short text, Long text, Rich text

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Default Value formula. Expression to set the field default value.

  • Checkbox (java.lang.Boolean)

    • Specific properties

      • Required: Indicates if it’s mandatory to fill this field.

      • Default Value formula. Expression to set the field default value.

  • Rich text: (java.lang.String)

    • Compatible field type: Short text, Long text, E-mail

    • Specific properties

      • Size: input text length.

      • MaxLength: Maximum number of characters allowed.

      • Required: Indicates if it’s mandatory to fill this field.

      • Height: The number or rows to show at text area.

      • Default Value formula. Expression to set the field default value.

  • Timestamp (java.util.Date)

    • Compatible field type: Short date

    • Specific properties

      • Size: input text length.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • Short date (java.util.Date)

    • Compatible field type: Timestamp

    • Specific properties

      • Size: input text length.

      • Required: Indicates if it’s mandatory to fill this field.

      • Formula. to enter expressions that will be evaluated to set the field value. These expressions are described in Formula & expression section .

      • Default Value formula. Expression to set the field default value.

  • Document (org.jbpm.document.Document)

    • Specific properties

      • Required: Indicates if it’s mandatory to fill this field.

  • Simple subform (Object)

    • For more details see sectionSimple Object (Subform field Type).

      Specific properties

      • Default form. Show the list of available forms to select what one will be displayed to show the object.

  • Multiple subform (Multiple Object)

    • For more details see sectionArrays of objects.( Multiple subform field Type).

      Specific properties

      • Default form. Show the list of available forms to select what one will be displayed to show the object when no other form is configured with an specific purpose.

      • Preview form. If a form is specified, it will be used to show the item details

      • Table form. If a form is specified, it will be used to show the table columns when the item list is showed

      • New item text. Text to show at New Item button

      • Add item text. Text to show at Add Item button

      • Cancel text. Text to show at Cancel button

      • Allow remove Items. If this check is selected, the form allow remove items in table view.

      • Allow edit items. If this check is selected, the form allow edit items in table view.

      • Allow preview items. If this check is selected, the form allow preview items in table view.

      • Hide creation button. Check to not show the creation button

      • Expanded. If is checked, when a new item is being added, the field display the table with the existing items and the creation form at same time

      • Allow data enter in table mode. Allow modify data in table view directly.

There are two types of complex fields: fields representing an object, and fields representing an object array.

Once the field is added to the form, either automatically or manually, it must be configured so that the form had to know how to display the objects that will contain in execution time.

Next we describe how can be the configuration process:

Once the form to represent the object, the parent form has to be configured to use them in the parent Subform or Multiple subform.

Below we will describe how the setup would be:

Form Modeler provides a Formula Engine that you can use to automatically calculate field values. That Formula engine supports Java and XPATH expressions to access the form fields values. Let’s see some examples.

There are three types of field types that you can use to model your form:

Is possible to extend the platform to add Custom Field Types that make a specific field (of any type) on the form to look and behave totally different than the standard platform fields. On this section we will take a look on how to create them and how to configure them.

Basically a Custom Field Type is a Java class that implements the org.jbpm.formModeler.core.fieldTypes.CustomFieldType interface and is packaged inside inside a JAR file that is placed on the Application Server classpath or inside the application WAR.

Lets take a look atorg.jbpm.formModeler.core.fieldTypes.CustomFieldType:


package org.jbpm.formModeler.core.fieldTypes;

import java.util.Locale;
import java.util.Map;

/**
* Definition interface for custom fields
*/
public interface CustomFieldType {
    /**
    * This method returns a text definition for the custom type. This text will be shown
    * on the UI to identify the CustomFieldType
    * @param locale The current user locale
    * @return A String that describes the field type on the specified locale.
    */
    public String getDescription(Locale locale);

    /**
    * This method returns a string that contains the HTML code that will be used to show
    * the field value on screen
    * @param value The current field value
    * @param fieldName The field name
    * @param namespace The unique id for the rendered form, it should be used to generate
    * identifiers inside the HTML code.
    * @param required Determines if the field is required or not
    * @param readonly Determines if the field must be shown on read only mode
    * @param params A list of configuration params that can be set on the field
    * configuration screen
    * @return The HTML that will be used to show the field value
    */
    public String getShowHTML(Object value, String fieldName, String namespace,
              boolean required, boolean readonly, String... params);

    /**
    * This method returns a String that contains the HTML code that will show the input
    * view of the field. That will be used to set the field value.
    * @param value The current field value
    * @param fieldName The field name
    * @param namespace The unique id for the rendered form, it should be used to
    * generate identifiers inside the HTML code.
    * @param required Determines if the field is required or not
    * @param readonly Determines if the field must be shown on read only mode
    * @param params A list of configuration params that can be set on the field
    * configuration screen
    * @return The HTML code that will be used to show the input view of the field.
    */
    public String getInputHTML(Object value, String fieldName, String namespace,
              boolean required, boolean readonly, String... params);

    /**
    * This method is used to obtain the field value from the submitted values.
    * @param requestParameters A Map containing the request parameters for the
    * submitted form
    * @param requestFiles A Map containing the java.io.Files uploaded on the request
    * @param fieldName The field name
    * @param namespace The unique id for the rendered form, it should be used to generate
    * identifiers inside the HTML code.
    * @param previousValue The previous value of the current field
    * @param required Determines if the field is required or not
    * @param readonly Determines if the field must be shown on read only mode
    * @param params A list of configuration params that can be set on the field
    * configuration screen
    * @return The value of the field based on the submitted form values.
    */
    public Object getValue(Map requestParameters, Map requestFiles, String fieldName,
              String namespace, Object previousValue, boolean required, boolean readonly,
              String... params);
}
  

As you can see this Interface defines the methods that determines how the field has to be shown on the screen for when the form is shown on insert(getInputHTML(...)) or readonly (getShowHTML(...)) mode. It also provides the method (getValue(...)) that reads the needed parameters from the request and to obtain the correct field value. Te returned value type must match with the type of the field added on the form.

Now let's see how to use and configure and use a Custom Field type. Following the example on the previous chapter, we have created a File Input type and we have it already installed on our application. So now we are going to create a new form and add a Short Text property and turn it into a File Input and edit the field properties changing the Field Type from Short text toCustom field.


After changing the field type a new set of properties will appear:



So opening the Custom field select box we'll be able to select the File Input from the available custom types:


After selecting the File Input type on the list and saving the field properties the form will look like:


If we build a simple process and configure a Short text to be shown as the sampleFile Input, if we build the project on runtime the field will behave uploading the chosen files to the server and allowing the user to download it like this:



If we take a look at what's the process variable value, we'll see that is storing a String with the file path stored in server.


On this section we are going to describe step by step how to attach documents to your process variables from your forms and how you can configure to store the uploaded documents anywhere (File System, Data Base, Alfresco...) using the Pluggable Variable Persistence.

In order to store the document using the Pluggable Variable Persistence you'll have to define your Marshalling Strategy to manage the uploaded Documents. To start create a Maven project with your favourite IDE and add the following dependencies:


<dependency>
  <groupId>org.kie</groupId>
  <artifactId>kie-api</artifactId>
  <version>{version}</version>
</dependency>
<dependency>
  <groupId>org.jbpm</groupId>
  <artifactId>jbpm-document</artifactId>
  <version>{version}</version>
</dependency>
    

Once you did that is time to create your Document Marshalling Strategy, to do so you just have to create a class that extends:


package org.jbpm.document.marshalling;

public abstract class AbstractDocumentMarshallingStrategy implements ObjectMarshallingStrategy {

  public abstract Document buildDocument( String name, long size, Date lastModified, Map<String, String> params );

  public void write( ObjectOutputStream os, Object object )
        throws IOException;

  public Object read( ObjectInputStream os )
        throws IOException, ClassNotFoundException;

  public byte[] marshal( Context context, ObjectOutputStream os, Object object )
        throws IOException;

  public Object unmarshal( Context context, ObjectInputStream is, byte[] object,
        ClassLoader classloader ) throws IOException, ClassNotFoundException;

  public Context createContext();
}
    

The methods to implement are:

You can see how the default DocumentMarshallingStrategy is implemented looking at this link.

After creating your Document Marshalling Strategy and add it to your server classpath the only thing remaining is to configure your project deployment descriptor add it to the marshalling strategies list. To do it you just have to open the Kie-Workbench on your browser, open your project on the Authoring view and edit the kie-deployment-descriptor.xml file located on <yourproject>/src/main/resources/META-INF and add your Document Marshalling Strategy to the <marshalling-strategies> list like this:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<deployment-descriptor
    xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <persistence-unit>org.jbpm.domain</persistence-unit>
  <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit>
  <audit-mode>JPA</audit-mode>
  <persistence-mode>JPA</persistence-mode>
  <runtime-strategy>SINGLETON</runtime-strategy>
  <marshalling-strategies>
    <marshalling-strategy>
      <resolver>reflection</resolver>
      <identifier>
        org.jbpm.document.marshalling.DocumentMarshallingStrategy
      </identifier>
    </marshalling-strategy>
  </marshalling-strategies>
  <event-listeners/>
  <task-event-listeners/>
  <globals/>
  <work-item-handlers/>
  <environment-entries/>
  <configurations/>
  <required-roles/>
</deployment-descriptor>
    

Since this is done you're able to build your project and upload your documents on your process.



Note

On this example we are configuring the default DocumentMarshallingStrategy, please use it for test and demo purposes.

This chapter intends to describe how you can embed process forms and interact with them on another webapp including the new Javascript API provided by the platform.

You can find the library inside the kie-wb-*.war on the js file located on js/jbpm-forms-rest-integration.js.

This JavaScript API tries to be a simple mechanism to use forms on remote applications allowing to load the forms from different KIE Workbench instances, submit them, launch processes/tasks and execute callback functions when the actions are done.

The basic methods are:

showStartProcessForm(hostUrl, deploymentId, processId, divId, onsuccessCallback, onerrorCallback): Makes a call to the REST endpoint to obtain the form URL and if it gets a valid response will embed the process start form on the given div. The parameteres needed are:
startProcess(divId, onsuccessCallback, onerrorCallback): Submits the form loaded on the given div and starts the process. The parameteres needed are:
showTaskForm(hostUrl, taskId, divId, onsuccessCallback, onerrorCallback): Makes a call to the REST endpoint to obtain the form URL and if it gets a valid response will embed the task form on the given div. The parameteres needed are:
claimTask(divId, onsuccessCallback, onerrorCallback): Claims the task whose form is being rendered
startTask(divId, onsuccessCallback, onerrorCallback): Starts the task whose form is being rendered
releaseTask(divId, onsuccessCallback, onerrorCallback): Releases the task whose form is being rendered
saveTask(divId, onsuccessCallback, onerrorCallback): Submits the form and saves the state of the task whose form is being rendered
completeTask(divId, onsuccessCallback, onerrorCallback): Submits the form and completes task whose form is being rendered
clearContainer(divId): Cleans the div content and the related data stored on the component.

Now let's see an example how you can use the library to load the HR process form and start a new process instance. We are going to define a HTML page that will contain very simple components:

First we are look at the HTML code:


<head>
  <script src="js/jbpm-forms-rest-integration.js"></script>
  <script>
      var formsAPI = new jBPMFormsAPI();
  </script>
</head>
<body>
  <input type="button" id="showformButton"
      value="Show Process Form" onclick="showProcessForm()">
  <p/>
  <div id="myform" style="border: solid black 1px; width: 500px; height: 200px;">
  </div>
  <p/>
  <input type="button" id="startprocessButton"
      style="display: none;" value="Start Process" onclick="startProcess()">
</body>
    

Notice that in first place we have added the js library and created an instance of the jBPMFormsAPI object that will manage the form rendering.

Now let's see how the showProcessForm() function looks like:


function showProcessForm() {
  var onsuccessCallback = function(response) {
    document.getElementById("showformButton").style.display = "none";
    document.getElementById("startprocessButton").style.display = "block";
  }

  var onerrorCallback = function(errorMessage) {
    alert("Unable to load the form, something wrong happened: " + errorMessage);
    formsAPI.clearContainer("myform");
  }
  formsAPI.showStartProcessForm("http://localhost:8080/kie-wb/", "org.jbpm:HR:1.0", "hiring", "myform", onsuccessCallback, onerrorCallback);
}
    

As you can see, first we are defining the callback functions:

Once we defined the callback function we proceed to call the formsAPI.showStartProcessForm(...) that is going make the REST call and embedd the form inside the specified div. Notice that we are providing a bunch of information in order to load the form, the URL where the KIE-Workbench is running (in this example "http://localhost:8080/kie-wb/"), the deployment where the process is located ("org.jbpm:HR:1.0"), the process id ("hiring"), the DIV id that is going to contain the form ("myform") and the callback functions (onsuccessCallback and onerrorCallback).

Now let's take a look at the startProcess() that is the one that is going to submit the form and start the process:


function startProcess() {
  var onsuccessCallback = function(response) {
    document.getElementById("showformButton").style.display = "block";
    document.getElementById("startprocessButton").style.display = "none";
    formsAPI.clearContainer("myform");
    alert(response);
  }

  var onerrorCallback = function(response) {
    document.getElementById("showformButton").style.display = "block";
    document.getElementById("startprocessButton").style.display = "none";
    formsAPI.clearContainer("myform");
    alert("Unable to start the process, something wrong happened: " + response);
  }
  formsAPI.startProcess("myform", onsuccessCallback, onerrorCallback);
}
    

As showProcessForm(), first we are defining the callback functions. Both are doing basically the same:

Once that is done we just do the call to the formsAPI.startProcess(...) that will send a message to the component that renders the form inside the "myform" DIV and will exectue the callback functions when the action is done. Notice that we don't need the provide any other information than the DIV that contains the form and optionally the callback functions.

With a simple code like this you'll be able to run process/task forms that are located on different Kie-Workbench instances from any other application.




In version 5.x processes were stored in so called packages produced by Guvnor and next downloaded by jbpm console for execution using KnowledgeAgent. Alternatively one could drop their process files (bpmn2 files) into a predefined directory that was scanned on the jbpm console start. That was it. That enforces users to always use Guvnor when dynamic deployment was needed. Although there is nothing wrong with it, actually that was recommended approach but not everytime it was desired.

Version 6, on the other hand moves away from proprietary packages in favor of, well known and mature, Apache Maven based packaging - known as knowledge archives - kjar. Processes, rules etc (aka business assets) are now part of a simple jar file built and managed by Maven. Along the business assets, java classes and other file types are stored in the jar file too. Moreover, as any other maven artifact, kjar can have defined dependencies on other artifacts including other kjars. What makes the kjar special when compared with regular jars is a single descriptor file kept inside META-INF directory of the kjar - kmodule.xml. That descriptor allows to define:

By default, this descriptor is empty (just kmodule root element) and is considered as marker file. Whenever a runtime component (such as jbpm console) is about to process kjar it looks up kmodule.xml to build its runtime representation. In addition to kmodule.xml a deployment descriptor (that provides fine graind control over deployment) is available (since 6.1).

While kmodule is mainly targeting on knowledge base and knowledge session basic configuration, deployment descriptors are considered more technical configuration. Following are the items available for configuration via deployment descriptors:

Deployment descriptor is an xml file that is placed inside META-INF folder of the kjar, although it is an optional file and deployments will succeed even when such descriptor is missing.


<deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <persistence-unit>org.jbpm.domain</persistence-unit>
    <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit>
    <audit-mode>JPA</audit-mode>
    <persistence-mode>JPA</persistence-mode>
    <runtime-strategy>PER_PROCESS_INSTANCE</runtime-strategy>
    <marshalling-strategies/>
    <event-listeners/>
    <task-event-listeners/>
    <globals/>
    <work-item-handlers/>
    <environment-entries/>
    <configurations/>
    <required-roles/>
    <remoteable-classes/>
    <limit-serialization-classes/>
</deployment-descriptor>

It provides more configuration options then the standard deployment has. Deployment descriptors are used in hierarchical way meaning they can be placed on various levels of the system and merged on runtime. jBPM supports following levels of deployment descriptors:

Deployment descriptors on different levels are merged on deployment time where the master is considered descriptor lower in the hierarchy and slave one that is higher in hierarchy. To give an example, when a kjar is deployed and it contains deployment descriptor kjar's deployment descriptor is considered slave and server level descriptor is considered master. With default merge mode it will override all master entries with slave ones as long as they are not empty and combine all collections.

Since kjar can have dependencies to other kjars, and in turn that dependencies might have deployment descriptors as well, they will be placed in deployment descriptors hierarchy lower than the actual kjar that is being deployed. With that said, this is how it will look like from hierarchy point of view, starting with master (server level):

That in default merging mode will result in deployment descriptor where with non empty values from kjar's deployment descriptors and merged collection from all levels.

So far all merging was done with default mode, which is MERGE_COLLECTIONS but that's not the only mode that is available:

Default deployment descriptor

There is always default deployment descriptor available, even if it was not explicitly configured, when running in jbpm-console (kie-workbench) the default values are as follows:

Default deployment descriptor can be altered by specifying valid URL location to an xml file that will provide fully defined deployment descriptor. By fully defined we mean that all elements should be specified as this deployment descriptor will become server level deployment descriptor.

-Dorg.kie.deployment.desc.location=file:/my/custom/location/deployment-descriptor.xml

Collection configuration items

Deployment descriptor consists of collection based items (event listeners, work item handlers, globals, etc) that usually require definition of an object that should be created on runtime. There are two types of collection based configuration items:

Object model consits of:


Depending on resolver type, creation or look up of the object will be performed. The default (and easiest) is reflection that will use both parameters and identifier (in this case is FQCN) to construct the object. Parameters in this case can be String or another object model for representing other types than String. Following is an example of an object model that will create an instance of org.jbpm.test.CustomStrategy using reflection resolver that will use constructor of that class with two String parameters. Note that String paramaters are created with different ways (using object model - first param, directly by giving String - second param).


Same can be done by using DeploymentDescriptor fluent API:

// create instance of DeploymentDescriptor with default persistence unit name
DeploymentDescriptor descriptor = new DeploymentDescriptorImpl("org.jbpm.domain");

// get builder and modify the descriptor		
descriptor.getBuilder()
.addMarshalingStrategy(new ObjectModel("org.jbpm.testCustomStrategy", 
			new Object[]{
			new ObjectModel("java.lang.String", new Object[]{"param1"}),
			"param2"}));

Reflection based object model resolver is the most verbose in case there are parameters involved but there are few parameters that are available out of the box and do not need to be created, they are simply referenced by name:

So to be able to use one of these it's enough to reference them by name and make sure that proper object type is used within your class:

...
<marshalling-strategy>
  <resolver>reflection</resolver>
  <identifier>org.jbpm.test.CustomStrategy</identifier>
  <parameters>
     <parameter xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema">runtimeManager</parameter>
   </parameters>
</marshalling-strategy>
...

In case reflection based resolver is not enough, more advanced resolver can be used that utilizes power of MVEL language. It's much easier in the configuration as it expects mvel expression as identifier of the object model. It will provide the out of the box parameters (listed above: runtime manager, kie session, etc) into the mvel context while evaluating expression. To define object model with mvel resolver use following xml (that will be equivalent to replection based above):

...
<marshalling-strategy>
  <resolver>mvel</resolver>
  <identifier>new org.jbpm.test.CustomStrategy(runtimeManager)</identifier>
</marshalling-strategy>
...

Last but not least, there is Spring based resolver available as well that allows to simply look up a bean by its identifier from spring application context. This resolver is not used in jbpm console (kie-workbench) as it does not use spring but whenever jBPM is used together with Spring it might become handy when deploying kjars into the runtime. It's very simple definition in xml, again equivalent to the other one assuming org.jbpm.test.CustomStrategy is registered in spring application context under customStrategy id.

...
<marshalling-strategy>
  <resolver>spring</resolver>
  <identifier>customStrategy</identifier>
</marshalling-strategy>
...

Manage deployment descriptor

Deployment descriptor is created as soon as project is created. It does contins the most basic deployment descriptor that is based on the default one. Meaning all settings present in default deployment descriptor will be copied into the one placed in the project. Further changes can be done directly in the xml content (in next versions more user friendly editor will most likely be provided). It is accessible from Administration perspecitve as this is considered technical administration task rather than business related activity.

Restrict access to runtime engine

jbpm console (kie-workbench) provides access restriction to repositories that can be configured with supplementary tool called kie-config-cli. This protects repositories in the authoring perspsective based on roles membership. Deployment descriptors moves this capability to the runtime engine by ensuring that access to processes will be granted only to users that belong to groups defined in the deployment descriptor as required roles. By default when project is created (at the same time deployment descriptor is created as well) required roles are automatically filled in based on repository restrictions. These roles can be still altered by editing deployment descriptor via Administration perspective as presented in Manage deployment descriptor section.

Security is enforced on two levels:

Required roles are defined as simple strings that should match actual roles defined in security realm. Following is a xml snippet that shows definition of required roles in deployment descriptor:

<deployment-descriptor>
...
    <required-roles>
        <required-role>experts</required-role>
    </required-roles>
...
</deployment-descriptor>

In case fine grained control is required defined roles can be prefixed with one of the following to control it on further level:

For example to restrict access to show process from given kjar only to group 'management' but still allow them to be executed by anyone (sort of system processes) one could define it as follows:


<deployment-descriptor>
...
   <required-roles>
      <required-role>view:management</required-role>
   </required-roles>
...
</deployment-descriptor>

Classes used for serialization in the remote services

When processes make use of custom types (or in general non promitive types) and there is a use case to include remote api invocations (REST, SOAP, JMS) such types must be available to the remote services marshalling mechanism that is based on JAXB for XML type. By default all types defined in kjar will be automatically included in JAXB context and therefore will be avialble for remote interaction. Though there might be more classes (like from dependent model) that shall be included there too.

Upon deployment, jBPM will scan classpath of given kjar to automatically register classes that might be needed for remote interaction. This is done based on following rules:

If that is not enough deployment descriptor allows to manually specify classes that shall be added to the JAXB context via remoteable-classes element:

<remoteable-classes>
   ...
   <remotable-class>org.jbpm.test.CustomClass</remotable-class>
   <remotable-class>org.jbpm.test.AnotherCustomClass</remotable-class>
   ...
</remoteable-classes>

With this all classes can be added to the JAXB context to properly marshal and unmarshal data types when interacting with jBPM remotely.

Limiting classes usd for serialization in the remote services

When there are classes in the kjar project or in the dependencies of the kjar project that woudl cause problems when used for serialization, the limit-serialization-classes property can be used to limit which classes are used for serialization

<limit-serialization-classes>true</limit-serialization-classes>

This property limits classes used for serialization to classes which fulfill both of the following "location" and "annotation" criteria:

Classes that:

These classes must also be annotated with one of the following type annotations:

Additionally, classes will be excluded if they are any of the following: interfaces, local classes, member classes or anonymous classes.

This chapter describes the screens related with the creation and management of process definitions and process instances.

Once you have modelled, configured all the technical details and build and deployed your projects containing your business processes you should be able to see all the available process definitions in the Process Definition List. For all the process definitions listed in the Process Definitions List you will be able to inspect the Process Definition details and start as many Process Instances as needed. The following sections describes the features provided by all the screens in charge of the manipulation of process definitions and process instances. You can find these screens under the Process Management Menu, in the jBPM Console NG or in KIE Workbench.

You can find the source code related with the process definition and instances manupilation inside this module: http://github.com/droolsjbpm/jbpm-console-ng/tree/master/jbpm-console-ng-process-runtime Feel free to report issues, send Pull Requests and get in contact with the team via comments in github.

The process definition section is composed by two main screens: the Process Definition Lists and the Process Definition Details.

The process instances section is composed by two main screens: the Process Instance Lists and the Process Instance Details. In this case the Process Instance Details provides several tabs with the runtime information related with the process.

This list works with the concept of view. A view is a set of visualization parameters that modify what items has to be displayed and how the items details has to be shown.

A view embrace

We find here different areas with different purposes:Filtering, general section configuration and specific view parameter setting in the data grid presentation:

This chapter introduces the Task Management screens and the its integration with the Form Modeller component to allow users to work on their assigned tasks. You can find the source code of these screens here: https://github.com/droolsjbpm/jbpm-console-ng/tree/master/jbpm-console-ng-human-tasks . Feel free to report issues, send Pull Requests and get in contact with the team via comments in github. At the end of this section you will find a technical description about how to customize these views.

Every user with access to the platform will have access to its personal task list where tasks assigned to him/her will be displayed. Each user will be able to create its own personal tasks or work on tasks that were create as a result of a business process execution.

You can access to the Task List accessing Tasks main menu:

Pending tasks for each user will be displayed in their task list screen. Notice that you will not be able to see assigned tasks from another user different from the one that is currenlty logged in.

The jBPM Process Dashboard is an specific use case of a dashboard feed from data coming from a relational database via SQL queries. In this case, the database tables consumed are: processinstancelog and bamtasksummary both belonging to the jBPM engine.

Every time the jBPM runtime updates the information stored into such tables the data becomes automatically available to the dashboard indicators. The following picture shows the main screen that users get when navigating to the Process & Task Dashboard.


Note

Notice, those are generic metrics not tied to any specific business process. Nonetheless, it's worth to mention that it would be very easy for customers to modify, extend or adapt this generic dashboard for custom needs. A customer could take the jBPM Process Dashboard as the base template for building a custom dashboard which mixes data coming from the jBPM engine plus data coming from its own business domain.

As you can see there exists two tabs in the top of the screen: Processes and Tasks. As their name indicates, every tab contains only indicators related to either processes or tasks.

To filter through the data users can click on the charts in order to select, for instance, a given process, a given status, etc... Every time a filter is applied, all the indicators are automatically updated and synced according to the criteria set. The next picture shows, for instance, what happens when both the process Sales and the status Active are selected.


Using the built-in filter features is a good way to select the process instances the users want to look into. Additionally, at any time, no matter whether there is any active filter or not, users can also navigate to the actual list of instances the dashboard indicators are showing. The Show Instances link at the top right side on the screen can be used to display those instances. Once clicked, the view is switched to the screen shown in the next picture:


From this view, users can sort the instances just by clicking on any column. They can get a detailed view of a particular instance just by clicking on the desired row as well.


The process instance details panel is shown on the right of the screen just after clicking on a row. Notice this is a read only view, just for monitoring purposes. After identifying a target process instance the next step is to use the jBPM Process Instance Console in case the user needs to manage such process instance.

To sum up, the jBPM Process & Task Dashboard let users:

The workbench contains an execution server (for executing processes and tasks), which also allows you to invoke various process and task related operations through a remote API. As a result, you can setup your process engine "as a service" and integrate this into your applications easily by doing remote requests and/or sending the necessary triggers to the execution server whenever necessary (without the need to embed or manage this as part of your application).

Both a REST and JMS based service are available (which you can use directly), and a Java remote client allows you to invoke these operations using the existing KieSession and TaskService interfaces (you also use for local interaction), making remote integration as easy as if you were interacting with a local process engine.

The Remote Java API provides KieSession, TaskService and AuditService interfaces to the JMS and REST APIs.

The interface implementations provided by the Remote Java API take care of the underlying logic needed to communicate with the JMS or REST APIs. In other words, these implementations will allow you to interact with a remote workbench instance (i.e. KIE workbench or the jBPM Console) via known interfaces such as the KieSession or TaskService interface, without having to deal with the underlying transport and serialization details.

The first step in interacting with the remote runtime is to use a RemoteRuntimeEngineFactory static newRestBuilder(), newJmsBuilder() or newCommandWebServiceClientBuilder() to create a builder instance. Use the new builder instance to configure and to create a RuntimeEngine instance to interact with the server.

Each of the REST, JMS or WebService RemoteClientBuilder instances exposes different methods that allow the configuration of properties like the base URL of the REST API, JMS queue locations or timeout while waiting for responses.

The Remote Java API provides clients, not "instances"

While the KieSession, TaskSerivce and AuditService instances provided by the Remote Java API may "look" and "feel" like local instances of the same interfaces, please make sure to remember that these instances are only wrappers around a REST or jMS client that interacts with a remote REST or JMS API.

 

This means that if a requested operation fails on the server, the Remote Java API client instance on the client side will throw a RuntimeException indicating that the REST call failed. This is different from the behaviour of a "real" (or local) instance of a KieSession, TaskSerivce and AuditService instance because the exception the local instances will throw will relate to how the operation failed. Operations on a Remote Java API client instance that would normally throw other exceptions (such as the TaskService.claim(taskId, userId) operation when called by a user who is not a potential owner), will now throw a RuntimeException instead when the requested operation fails on the server.

 

Also, while local instances require different handling (such as having to dispose of a KieSession), client instances provided by the Remote Java API hold no state and thus do not require any special handling.

 

Finally, the instances returned by the client KieSession and TaskService instances (for example, process instances or task summaries) are not the same (internal) objects as used by the core engines. Instead, these returned instances are simple data transfer objects (DTOs) that implement the same interfaces but are designed to only return the associated data. Modifying or casting these returned instances to an internal implementation class will not succeed.

Each builder has a number of different (required or optional) methods to configure a client RuntimeEngine instance.

The following example illustrates how the Remote Java API can be used with the REST API.

public void startProcessAndHandleTaskViaRestRemoteJavaAPI(URL serverRestUrl, String deploymentId, String user, String password) {
        // the serverRestUrl should contain a URL similar to "http://localhost:8080/jbpm-console/"
        
        // Setup the factory class with the necessarry information to communicate with the REST services
        RuntimeEngine engine = RemoteRuntimeEngineFactory.newRestBuilder()
        .addUrl(serverRestUrl)
        .addTimeout(5)
        .addDeploymentId(deploymentId)
        .addUserName(user)
        .addPassword(password)
        // if you're sending custom class parameters, make sure that
        // the remote client instance knows about them!
        .addExtraJaxbClasses(MyType.class)
        .build();
        
        // Create KieSession and TaskService instances and use them
        KieSession ksession = engine.getKieSession();
        TaskService taskService = engine.getTaskService();
        
        // Each operation on a KieSession, TaskService or AuditLogService (client) instance
        // sends a request for the operation to the server side and waits for the response
        // If something goes wrong on the server side, the client will throw an exception.
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("paramName", new MyType("name", 23));
        ProcessInstance processInstance
        = ksession.startProcess("com.burns.reactor.maintenance.cycle", params);
        long procId = processInstance.getId();
        
        String taskUserId = user;
        taskService = engine.getTaskService();
        List<TaskSummary> tasks = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
        
        long taskId = -1;
        for (TaskSummary task : tasks) {
        if (task.getProcessInstanceId() == procId) {
        taskId = task.getId();
        }
        }
        
        if (taskId == -1) {
        throw new IllegalStateException("Unable to find task for " + user +
        " in process instance " + procId);
        }
        
        taskService.start(taskId, taskUserId);
        
        // resultData can also just be null
        Map<String, Object> resultData = new HashMap<String, Object>();
        taskService.complete(taskId, taskUserId, resultData);
        }

When configuring the remote JMS client, you must choose one of the following ways to configure the JMS connection:

In addition, if you are doing an operation on a task via the remote JMS client (and are not using the disableTaskSecurity() method), then you must also configure SSL. The following methods (described in more detail below) are available for this:

Each builder has a number of different (required or optional) methods to configure a client RuntimeEngine instance.

Remote JMS Runtime Engine Builder methods

addConnectionFactory(ConnectionFactory connectionFactory) when

Add a ConnectionFactory used to create JMS session to send and receive messages

addDeploymentId(String deploymentId) when

Set the deployment id of the deployment

addExtraJaxbClasses(Class…​ extraJaxbClasses ) when

Add extra classes to the client for when user-defined class instances are passed as parameters to client methods

When passing instances of user-defined classes in a Remote Java API call, it’s important to use this method first to add the classes so that the class instances can be serialized correctly.

addHostName(String hostname) when

Set the host name for the server that the client is making a JMS connection with

addJbossServerHostName(String hostname)

Set the host name of the JBoss EAP server that the client is making a JMS connection with

After using this method, no other configuration is needed with regards to the server. However, additional server parameters (host name, port) may be needed when also configuring SSL.

Make sure that the EAP version-appropriate org.jboss.naming:jboss-naming dependency is available on the classpath when doing this

addJmsConnectorPort(int port) when

Set the port used for the JMS connection connection with the server

addKeystoreLocation(String keystorePath) when

Set the location (path) of the keystore

addKeystorePassword(String keystorePassword) when

Set the password for the keystore

addKieSessionQueue(Queue ksessionQueue) when

Pass the javax.jms.Queue instance representing the KIE.SESSION queue used to receive process instance requests from the client

addPassword(String password) always
Set the password of the user connecting to the server
addProcessInstanceId(long process) when

Set the process instance id of the deployment

addRemoteInitialContext(InitialContext remoteInitialContext)

Set the remote InitialContext instance from the remote application server, which is then used to retrieve the ConnectionFactory and Queue instances

After using this method, no other configuration is needed with regards to the server. However, additional server parameters (host name, port) may be needed when also configuring SSL.

addResponseQueue(Queue responseQueue) when

Pass the javax.jms.Queue instance representing the KIE.RESPONSE queue used to send responses to the client

addTaskServiceQueue(Queue taskServiceQueue) when

Pass the javax.jms.Queue instance representing the KIE.TASK queue used to receive task operation requests to the client

addTimeout(int timeoutInSeconds)
Set the timeout for the JMS message. The default is 5 seconds.
addTruststoreLocation(String truststorePath) when

Set the location (path) of the keystore

addTruststoreLocation(String truststorePassword) when

Set the password for the keystore

addUserName(String userName) always

Set the name of the user connecting to the server

This is also the user whose permissions will be used when doing any task operations

clearJaxbClasses()
Clears the list of (user-defined) Classes that the client instance should know about
disableTaskSecurity()
Enables configuration of a client that can send task-related operation requests without having to use SSL. If this option is not used and a task operation is called, the client will throw an exception.
useKeystoreAsTruststore()
Uses the same file (location) and password for the truststore configuration when configuring SSL.

The following example illustrates how to configure a Remote Java API JMS client using a remote InitialContext instance along with SSL. In this case, the same file is being used as both the client’s keystore (the client’s identifying keys and certificates) and as the client’s truststore (the client’s list of trusted certificates from other parties, in this case, the server).

public void startProcessAndHandleTaskViaJmsRemoteJavaAPISsl(String hostNameOrIpAdress, int jmsSslConnectorPort,
        String deploymentId, String user, String password,
        String keyTrustStoreLocation, String keyTrustStorePassword,
        String processId) {
        
        InitialContext remoteInitialContext = getRemoteInitialContext();
        
        RuntimeEngine engine = RemoteRuntimeEngineFactory.newJmsBuilder()
        .addDeploymentId(deploymentId)
        .addRemoteInitialContext(remoteInitialContext)
        .addUserName(user)
        .addPassword(password)
        .addHostName(hostNameOrIpAdress)
        .addJmsConnectorPort(jmsSslConnectorPort)
        .useKeystoreAsTruststore()
        .addKeystoreLocation(keyTrustStoreLocation)
        .addKeystorePassword(keyTrustStorePassword)
        .build();
        
        // create JMS request
        KieSession ksession = engine.getKieSession();
        ProcessInstance processInstance = ksession.startProcess(processId);
        long procInstId = processInstance.getId();
        logger.debug("Started process instance: " + procInstId );
        
        TaskService taskService = engine.getTaskService();
        List<TaskSummary> taskSumList
        = taskService.getTasksAssignedAsPotentialOwner(user, "en-UK");
        TaskSummary taskSum = null;
        for( TaskSummary taskSumElem : taskSumList ) {
        if( taskSumElem.getProcessInstanceId().equals(procInstId) ) {
        taskSum = taskSumElem;
        }
        }
        long taskId = taskSum.getId();
        logger.debug("Found task " + taskId);
        
        // get other info from task if you want to
        Task task = taskService.getTaskById(taskId);
        logger.debug("Retrieved task " + taskId );
        
        taskService.start(taskId, user);
        
        Map<String, Object> resultData = new HashMap<String, Object>();
        // insert results for task in resultData
        taskService.complete(taskId, user, resultData);
        logger.debug("Completed task " + taskId );
        }

Starting with this release, a simple webservice has been added to the remote API.

As mentioned above, the Remote Java API provides client-like instances of the RuntimeEngine, KieSession, TaskService and AuditService interfaces.

This means that while many of the methods in those interfaces are available, some are not. The following tables lists the methods which are available. Methods not listed in the below, will throw an UnsupportedOperationException explaining that the called method is not available.







REST API calls to the execution server allow you to remotely manage processes and tasks and retrieve various dynamic information from the execution server. The majority of the calls are synchronous, which means that the call will only finish once the requested operation has completed on the server. The exceptions to this are the deployment POST calls, which will return the status of the request while the actual operation requested will asynchronously execute.

When using Java code to interface with the REST API, the classes used in POST operations or otherwise returned by various operations can be found in the (org.kie.remote:)kie-services-client JAR.

As of the community 6.2.0.Final release, users now need to be assigned one of the following roles in order to be able to access REST URLs:


Specifically, the roles give the user access to the following URLs:

Table 17.8. REST permission roles

RoleDescription

rest-all

All REST URLs

rest-project

Only the following URLs are available with this role:

rest-deployment

Only the following URLs are available with this role:

rest-process

In short, only URLs that start with one of the following are available with this role:

.../rest/runtime/deployment/{deploymentId}/*
.../rest/history/*

Only the following URLs are available with this role:

rest-process-read-only

In short, all GET URLs that start with one of the following are available with this role:

.../rest/runtime/deployment/{deploymentId}/*
.../rest/history/*

Only the following URLs are available with this role:

rest-task

In short, all URLs that start with the following are available with this role:

.../rest/task/*

Only the following URLs are available with this role:

rest-task-read-only

In short, all GET URLs that start with one of the following are available with this role:

.../rest/task/*

Only the following URLs are available with this role:

rest-query

Only the following URLs are available with this role:

rest-client

Only the following URLs are available with this role:

This URL is used by the Java remote API to communicate with the server. Use of this URL without the Java remote API code is not recommended!


This section lists REST calls that interface process instances.

The deploymentId component of the REST calls below must conform to the following regular expression:

[\w\.-]+(:[\w\.-]+){2,2}(:[\w\.-]*){0,2}

For more information about the composition of the deployment id, see the Deployment Calls section.

[POST] /runtime/{deploymentId}/process/{processDefId}/start

[GET] rest/runtime/{deploymentId}/process/{processDefId}/startform

[GET] /runtime/{deploymentId}/process/instance/{procInstId}

[POST] /runtime/{deploymentId}/process/instance/{procInstId+}/abort

[POST] /runtime/{deploymentId}/process/instance/{procInstId}/signal

[GET] /runtime/{deploymentId}/process/instance/{procInstId}/variable/{varName}

[POST] /runtime/{deploymentId}/signal

[GET] /runtime/{deploymentId}/workitem/{workItemId}

[POST] /runtime/{deploymentId}/workitem/{workItemId}/complete

[POST] /runtime/{deploymentId}/workitem/{workItemId: [0-9-]+}/abort

[POST] /history/clear

[GET] /history/instances

[GET] /history/instance/{procInstId}

[GET] /history/instance/{procInstId}/child

[GET] /history/instance/{procInstId}/node

[GET] /history/instance/{procInstId}/variable

[GET] /history/instance/{procInstId}/node/{nodeId}

[GET] /history/instance/{procInstId}/variable/{varId}

[GET] /history/process/{processDefId}

[GET] /history/variable/{varId}

[GET] /history/variable/{varId}/value/{value}

[GET] /history/variable/{varId}/instances

[GET] /history/variable/{varId}/value/{value}/instances

[GET] /runtime/{deploymentId}/history/variable/{varId}

[GET] /runtime/{deploymentId}/history/variable/{varId}/value/{value}

[GET] /runtime/{deploymentId}/history/variable/{varId}/instances

[GET] /runtime/{deploymentId}/history/variable/{varId}/value/{value}/instances

The following section describes the three different types of task calls: * Task REST operations that mirror the TaskService interface, allowing the user to interact with the remote TaskService instance * The Task query REST operation, that allows users to query for Task instances * Other Task REST operations that retrieve information

Task operation authorizations. Task REST operations use the user information (used to authorize and authenticate the HTTP call) to check whether or not the requested operations can happen. This also applies to REST calls that retrieve information, such as the task query operation. REST calls that request information will only return information about tasks that the user is allowed to see.

With regards to retrieving information, only users associated with a task may retrieve information about the task. However, the authorizations of progress and other modifications of task information are more complex. See the Task Permissions section in the Task Service documentation for more infomration.

Note

Given that many users have expressed the wish for a "super-task-user" that can execute task REST operations on all tasks, regardless of the users associated with the task, there are now plans to implement that feature. However, so far for the 6.x releases, this feature is not available.

All of the task operation calls described in this section use the user (id) used in the REST basic authorization as input for the user parameter in the specific call.

Some of the operations take an optional lanaguage query parameter. If this parameter is not given as a element of the URL itself, the default value of “en-UK” is used.

The taskId component of the REST calls below must conform to the following regex:

[0-9]+

[POST] /task/{taskId}/activate

[POST] /task/{taskId}/claim

[POST] /task/{taskId}/claimnextavailable

[POST] /task/{taskId}/complete - Completes a task - Returns a JaxbGenericResponse with the status of the operation - Parameters: Takes map query parameters, which are the "results" input for the complete operation

[POST] /task/{taskId}/delegate

[POST] /task/{taskId}/exit

[POST] /task/{taskId}/fail

[POST] /task/{taskId}/forward

[POST] /task/{taskId}/nominate

[POST] /task/{taskId}/release

[POST] /task/{taskId}/resume

[POST] /task/{taskId}/skip

[POST] /task/{taskId}/start

[POST] /task/{taskId}/stop - Stops a task - Returns a JaxbGenericResponse with the status of the operation

[POST] /task/{taskId}/suspend

The calls described in this section allow users to manage deployments. Deployments are in fact KieModule JARs which can be deployed or undeployed, either via the UI or via the REST calls described below. Configuration options, such as the runtime strategy, should be specified when deploying the deployment: the configuration of a deployment can not be changed after it has already been deployed.

The above deploymentId regular expression describes an expression that contains the following elements, separated from eachother by a : character:

In a more formal sense, the deploymentId component of the REST calls below must conform to the following regex:

`[\w\.-]+(:[\w\.-]+){2,2}(:[\w\.-]*){0,2}`

This regular expression is explained as follows:


This [\w\.-] element occurs at least 3 times and at most 5 times, separated by a : character each time.


There are 2 operations that can be used to modify the status of a deployment:

These POST deployment calls are both asynchronous, which means that the information returned by the POST request does not reflect the eventual final status of the operation itself.

Important

As noted above, both the /deploy and /undeploy operations are asynchronous REST operations. Successfull requests to these URLs will return the status 202 upon the request completion. RFC 2616 defines the 202 status as meaning the following:

 

RFC 2616: "the request has been accepted for processing, but the processing has not been completed."

 

This means the following:

  1. While the request may have been accepted "successfully", the operation itself (deploying or undeploying the deployment unit) may actually fail.
  2. Furthermore, information about deployments, such as that retrieved by calling the GET operations described below, are snapshots and the information (including the status of the deployment unit) may have changed by the time the user client receives the answer to the GET request.

[GET] /deployment/

[GET] /deployment/processes

[GET] /deployment/ {deploymentId}

[POST] /deployment/{deploymentId}/deploy

[POST] /deployment/{deploymentId}/undeploy

[POST] /deployment/{deploymentId}/activate

[POST] /deployment/{deploymentId}/deactivate

[GET] /deployment/{deploymentId}/processes

While there is a /runtime/{id}/execute and a task/execute method, both will take all types of commands. This is possible because execute takes a JaxbCommandsRequest object, which contains a list of (org.kie.api.command.)Command objects. The JaxbCommandsRequest has fields to store the proper deploymentId and processInstanceId information.

Of course, if you send a request with a command that needs this information (deploymentId, for example) and don’t fill the deploymentId in, the request will fail.

The following /rest/execute call can be used to start a process (with process id ‘evaluation’ in the project with deployment id ‘org.jbpm:Evaluation:1.0’) and two parameters (parameter employee equal to ‘krisv’ and reason equal to ‘Yearly performance evaluation’).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<command-request>
  <deployment-id>org.jbpm:Evaluation:1.0</deployment-id>
  <ver>6.2.0.1</ver>
  <user>krisv</user>
  <start-process processId="evaluation">
    <parameter>
      <item key="reason">
        <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Yearly performance evaluation</value>
      </item>
      <item key="employee">
        <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">krisv</value>
      </item>
    </parameter>
  </start-process>
</command-request>

Note that the request should also contain the following HTTP headers:

  • A Content-Type header with the value of application/xml
  • A Authorization header with basic authentication information, as specificed by RFC2616 (see link).

The response will contain information about the process instance that was just started:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  <command-response>
    <deployment-id>org.jbpm:Evaluation:1.0</deployment-id>
    <ver>6.2.0.1</ver>
    <process-instance index="0">
    <process-id>evaluation</process-id>
    <id>15</id>
    <state>1</state>
    <parentProcessInstanceId>0</parentProcessInstanceId>
    <command-name>StartProcessCommand</command-name>
  </process-instance>
</command-response>

The /execute operation also supports sending user-defined class instances as parameters in the command. This relies on JAXB for serialization and deserialization. To be able to deserialize the custom class on the server side, a "Kie-Deployment-Id" header must also be set to the deployment id of the project.

For example, when starting a process or completing a task, a user typically passes additional parameters (process variable values or the result data for the completed task). These values are then either primitives (Strings, ints, etc.) or user-defined classes that were created using the data modeler in the workbench, added directly to the deployed project or part of a dependency to the deployment (project).

The following request starts a process which contains a custom TestObject class (with two fields) as a parameter.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<command-request>
  <deployment-id>demo:testproject:1.0</deployment-id>
  <ver>6.2.0.1</ver>
  <user>krisv</user>
  <start-process processId="testproject.testprocess">
    <parameter>
      <item key="testobject">
        <value xsi:type="testObject" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <field1>1</field1>
          <field2>2</field2>
        </value>
      </item>
    </parameter>
  </start-process>
</command-request>

Just as in the basic example above, both a Content-Type and Authorization header should be set in the request.

The 3 headers that therefore need to be set in the requst are the following:

  • A Content-Type header with the value of application/xml
  • A Authorization header with basic authentication information, as specificed by RFC2616 (see link).
  • A Kie-Deployment-Id header with the value of the deployment id containing the class definitions of any parameters sent with the command .

The URL templates in the table below are relative to the one of the following URLs:

The REST Query API allows users of the jBPM console and the KIE workbench (as well as products based on these applications) to "richly" query tasks, variables and process instances.

The rich query operations can be reached via the following URLs:

http://server.address:port/{application-id}/rest/query/
  task                       * [GET] rich query operation for task summaries
  runtime
    process                  * [GET] rich query operation for process instances and process variables
    task                     * [GET] rich query operation for task summaries and process variables

Both url’s take a number of different query parameters. See the next section for a description of these.

The following is a summary of the query operations:

Example 17.3.  /query/task usage

The following /query/task operation retrieves the task summaries of all tasks that have a work item id of 3, 4, or 5. If you specify the same parameter multiple times, the query will select tasks that match any of that parameter.

http://server:port/rest/task/query?workItemId=3&workItemId=4&workItemId=5

The next call will retrieve any task summaries for which the task id is 27 and for which the work item id is 11. Specifying different parameters will result in a set of tasks that match both (all) parameters.

`http://server:port/rest/task/query?workItemId=11&taskId=27`

The next call will retrieve any task summaries for which the task id is 27 or the work item id is 11. While these are different parameters, the union parameter is being used here so that the union of the two queries (the work item id query and the task id query) is returned.

http://server:port/rest/task/query?workItemId=11&taskId=27&union=true`

The next call will retrieve any task summaries for which the status is Created and the potential owner of the task is Bob. Note that the letter case for the status parameter value is case-'in’sensitve.

http://server:port/rest/task/query?status=creAted&potentialOwner=Bob`

The next call will return any task summaries for which the status is Created and the potential owner of the task is bob. Note that the potential owner parameter is case-'sensitive'. bob is not the same user id as Bob!

http://server:port/rest/task/query?status=created&potentialOwner=bob`

The next call will return the intersection of the set of task summaries for which the process instance is 201, the potential owner is bob and for which the status is Created or Ready.

http://server:port/rest/task/query?status=created&status=ready&potentialOwner=bob&processInstanceId=201

That means that the task summaries that have the following characteristics would be included:

  • process instance id 201, potential owner bob, status Ready
  • process instance id 201, potential owner bob, status Created

And that following task summaries will not be included:

  • process instance id 183, potential owner bob, status Created
  • process instance id 201, potential owner '`mary`, status Ready
  • process instance id 201, potential owner bob, status `Complete`

In the documentation below,

When you submit a REST call to the query operation, your URL will look something like this:

http://localhost:8080/business-central/rest/query/runtime/process/processId=org.process.frombulator&piid=29

A query containing multiple different query parameters will search for the intersection of the given parameters.

However, many of the query parameters described below can be entered multiple times: when multiple values are given for the same query parameter, the query will then search for any results that match one or more of the values entered.


The "task or process" column describes whether or not a query parameter can be used with the task and/or the process instance query operations.


[1] The date operations take strings with a specific date format as their values: yy-MM-dd_HH:mm:ss.SSS. However, users can also submit only part of the date:

  • Submitting only the date (yy-MM-dd) means that a time of 00:00:00 is used (the beginning of the day).
  • Submitting only the time (HH:mm:ss) means that the current date is used.

For the format used, see the SimpleDateFormat documentation.

[2] The var query parameter is used differently than other parameters. If you want to specify both the variable id and value of a variable (as opposed to just the variable id), then you can do it by using the var query parameter. The syntax is var_<variable-id>=<variable-value>


[3] The varreggex (or shortened version vr) parameter works similarly to the var query parameter. However, the value part of the query parameter can be a regular expression.

[4] By default, only the information from most recent (last) variable instance logs is retrieved. However, users can also retrieve all variable instance logs (that match the given criteria) by using this parameter.

The process instance query returns a JaxbQueryProcessInstanceResult instance.

The task query returns a JaxbQueryTaskResult instance.

Results are structured as follows:

A JaxbQueryProcessInstanceInfo object contains:

  • a process instance object
  • a list of 0 or more variable objects

A JaxbQueryTaskInfo info object contains:

  • the process instance id
  • a list of 0 or more task summary obejcts
  • a list of 0 or more variable objects

The Java Message Service (JMS) is an API that allows Java Enterprise components to communicate with each other asynchronously and reliably.

Operations on the runtime engine and tasks can be done via the JMS API exposed by the jBPM console and KIE workbench. However, it’s not possible to manage deployments or the knowledge base via this JMS API.

Unlike the REST API, it is possible to send a batch of commands to the JMS API that will all be processed in one request after which the responses to the commands will be collected and return in one response message.

The following is a rather long example that shows how to use the JMS API. The numbers ("callouts") along the side of the example refer to notes below that explain particular parts of the example. It’s supplied for those advanced users that do not wish to use the jBPM Remote Java API.

The jBPM Remote Java API, described here, will otherwise take care of all of the logic shown below.

package org.kie.remote.client.documentation.jms;
      
      import static org.kie.services.client.serialization.SerializationConstants.DEPLOYMENT_ID_PROPERTY_NAME;
      import static org.kie.services.client.serialization.SerializationConstants.SERIALIZATION_TYPE_PROPERTY_NAME;
      import static org.kie.services.shared.ServicesVersion.VERSION;
      
      import java.util.Collections;
      import java.util.List;
      import java.util.Set;
      import java.util.UUID;
      
      import javax.jms.Connection;
      import javax.jms.ConnectionFactory;
      import javax.jms.JMSException;
      import javax.jms.Message;
      import javax.jms.MessageConsumer;
      import javax.jms.MessageProducer;
      import javax.jms.Queue;
      import javax.jms.Session;
      import javax.jms.TextMessage;
      import javax.naming.InitialContext;
      import javax.naming.NamingException;
      
      import org.kie.api.command.Command;  1
      import org.kie.api.runtime.process.ProcessInstance;
      import org.kie.api.task.model.TaskSummary;
      import org.kie.remote.client.api.RemoteRuntimeEngineFactory;  2
      import org.kie.remote.client.api.exception.MissingRequiredInfoException;
      import org.kie.remote.client.api.exception.RemoteApiException;
      import org.kie.remote.client.api.exception.RemoteCommunicationException;
      import org.kie.remote.client.jaxb.ClientJaxbSerializationProvider;
      import org.kie.remote.client.jaxb.JaxbCommandsRequest;
      import org.kie.remote.client.jaxb.JaxbCommandsResponse;
      import org.kie.remote.jaxb.gen.AuditCommand; 3
      import org.kie.remote.jaxb.gen.GetTaskAssignedAsPotentialOwnerCommand;
      import org.kie.remote.jaxb.gen.StartProcessCommand;
      import org.kie.remote.jaxb.gen.TaskCommand;
      import org.kie.services.client.serialization.JaxbSerializationProvider;
      import org.kie.services.client.serialization.SerializationException;
      import org.kie.services.client.serialization.SerializationProvider;
      import org.kie.services.client.serialization.jaxb.impl.JaxbCommandResponse;
      import org.kie.services.client.serialization.jaxb.rest.JaxbExceptionResponse;
      import org.slf4j.Logger;
      import org.slf4j.LoggerFactory;
      
      public class SendJmsExample {
      
      protected static final Logger logger = LoggerFactory.getLogger(SendJmsExample.class);
      
      public void sendCommandsViaJms( String user, String password, String connectionUser, String connectionPassword,
      String deploymentId, String processId, String hostName ) {
      
      /**
      * JMS setup
      */
      // Get JNDI context from server
      InitialContext context = RemoteRuntimeEngineFactory.getRemoteJbossInitialContext(hostName, connectionUser, connectionPassword);
      
      // Create JMS connection
      ConnectionFactory connectionFactory;
      try {
      connectionFactory = (ConnectionFactory) context.lookup("jms/RemoteConnectionFactory");
      } catch( NamingException ne ) {
      throw new RuntimeException("Unable to lookup JMS connection factory.", ne);
      }
      
      // Setup queues
      Queue sessionQueue, taskQueue, sendQueue, responseQueue;
      try {
      sendQueue = sessionQueue = (Queue) context.lookup("jms/queue/KIE.SESSION");
      taskQueue = (Queue) context.lookup("jms/queue/KIE.TASK");
      responseQueue = (Queue) context.lookup("jms/queue/KIE.RESPONSE");
      } catch( NamingException ne ) {
      throw new RuntimeException("Unable to lookup send or response queue", ne);
      }
      
      /**
      * Command preparation
      */
      StartProcessCommand startProcCmd = new StartProcessCommand();
      startProcCmd.setProcessId(processId);
      
      /**
      * Send command via JMS and receive response
      */
      SerializationProvider serializationProvider = ClientJaxbSerializationProvider.newInstance();
      ProcessInstance procInst = (ProcessInstance) sendJmsCommand(startProcCmd,
      connectionUser, connectionPassword,
      user, password, deploymentId, null,
      connectionFactory, sendQueue, responseQueue,
      serializationProvider, Collections.EMPTY_SET, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE,
      5 * 1000);
      
      /**
      * Command preparation
      */
      GetTaskAssignedAsPotentialOwnerCommand gtaapoCmd = new GetTaskAssignedAsPotentialOwnerCommand();
      gtaapoCmd.setUserId(user);
      
      // Send command request
      Long processInstanceId = null; // needed if you're doing an operation on a PER_PROCESS_INSTANCE deployment
      
      /**
      * Send command via JMS and receive response
      */
      @SuppressWarnings("unchecked")
      List<TaskSummary> taskSumList = (List<TaskSummary>) sendJmsCommand(gtaapoCmd,
      connectionUser, connectionPassword,
      user, password, deploymentId, processInstanceId,
      connectionFactory, sendQueue, responseQueue,
      serializationProvider, Collections.EMPTY_SET, JaxbSerializationProvider.JMS_SERIALIZATION_TYPE,
      5 * 1000);
      
      long taskId = taskSumList.get(0).getId();
      }
      
      // @formatter:off
      public static Object sendJmsCommand( Command command,
      String connUser, String connPassword,
      String userName, String password, String deploymentId, Long processInstanceId,
      ConnectionFactory factory, Queue sendQueue, Queue responseQueue,
      SerializationProvider serializationProvider, Set<Class<?>> extraJaxbClasses, int serializationType,
      long timeoutInMillisecs ) {
      // @formatter:on
      
      if( deploymentId == null && !(command instanceof TaskCommand || command instanceof AuditCommand) ) {
      throw new MissingRequiredInfoException("A deployment id is required when sending commands involving the KieSession.");
      }
      JaxbCommandsRequest req; 4
      if( command instanceof AuditCommand ) {
      req = new JaxbCommandsRequest(command);
      } else {
      req = new JaxbCommandsRequest(deploymentId, command);
      }
      
      req.setProcessInstanceId(processInstanceId);
      req.setUser(userName);
      req.setVersion(VERSION);
      
      Connection connection = null;
      Session session = null;
      JaxbCommandsResponse cmdResponse = null;
      String corrId = UUID.randomUUID().toString();
      String selector = "JMSCorrelationID = '" + corrId + "'";
      try {
      
      // setup
      MessageProducer producer;
      MessageConsumer consumer;
      try {
      if( password != null ) {
      connection = factory.createConnection(connUser, connPassword);
      } else {
      connection = factory.createConnection();
      }
      session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      
      producer = session.createProducer(sendQueue);
      consumer = session.createConsumer(responseQueue, selector);
      
      connection.start();
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to setup a JMS connection.", jmse);
      }
      
      // Create msg
      TextMessage textMsg; 5
      try {
      
      // serialize request
      String xmlStr = serializationProvider.serialize(req); 6
      textMsg = session.createTextMessage(xmlStr);
      
      // set properties
      // 1. corr id
      textMsg.setJMSCorrelationID(corrId);
      // 2. serialization info
      textMsg.setIntProperty(SERIALIZATION_TYPE_PROPERTY_NAME, serializationType);
      if( extraJaxbClasses != null && !extraJaxbClasses.isEmpty() ) {
      if( deploymentId == null ) {
      throw new MissingRequiredInfoException(
      "Deserialization of parameter classes requires a deployment id, which has not been configured.");
      }
      textMsg.setStringProperty(DEPLOYMENT_ID_PROPERTY_NAME, deploymentId);
      }
      // 3. user/pass for task operations
      boolean isTaskCommand = (command instanceof TaskCommand);
      if( isTaskCommand ) {
      if( userName == null ) {
      throw new RemoteCommunicationException(
      "A user name is required when sending task operation requests via JMS");
      }
      if( password == null ) {
      throw new RemoteCommunicationException(
      "A password is required when sending task operation requests via JMS");
      }
      textMsg.setStringProperty("username", userName);
      textMsg.setStringProperty("password", password);
      }
      // 4. process instance id
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to create and fill a JMS message.", jmse);
      } catch( SerializationException se ) {
      throw new RemoteCommunicationException("Unable to deserialze JMS message.", se.getCause());
      }
      
      // send
      try {
      producer.send(textMsg);
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to send a JMS message.", jmse);
      }
      
      // receive
      Message response;
      try {
      response = consumer.receive(timeoutInMillisecs);
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to receive or retrieve the JMS response.", jmse);
      }
      
      if( response == null ) {
      logger.warn("Response is empty");
      return null;
      }
      // extract response
      assert response != null: "Response is empty.";
      try {
      String xmlStr = ((TextMessage) response).getText();
      cmdResponse = (JaxbCommandsResponse) serializationProvider.deserialize(xmlStr); 7
      } catch( JMSException jmse ) {
      throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
      + " instance from JMS response.", jmse);
      } catch( SerializationException se ) {
      throw new RemoteCommunicationException("Unable to extract " + JaxbCommandsResponse.class.getSimpleName()
      + " instance from JMS response.", se.getCause());
      }
      assert cmdResponse != null: "Jaxb Cmd Response was null!";
      } finally {
      if( connection != null ) {
      try {
      connection.close();
      if( session != null ) {
      session.close();
      }
      } catch( JMSException jmse ) {
      logger.warn("Unable to close connection or session!", jmse);
      }
      }
      }
      
      String version = cmdResponse.getVersion();
      if( version == null ) {
      version = "pre-6.0.3";
      }
      if( !version.equals(VERSION) ) {
      logger.info("Response received from server version [{}] while client is version [{}]! This may cause problems.",
      version, VERSION);
      }
      
      List<JaxbCommandResponse<?>> responses = cmdResponse.getResponses();
      if( responses.size() > 0 ) {
      JaxbCommandResponse<?> response = responses.get(0);
      if( response instanceof JaxbExceptionResponse ) {
      JaxbExceptionResponse exceptionResponse = (JaxbExceptionResponse) response;
      throw new RemoteApiException(exceptionResponse.getMessage());
      } else {
      return response.getResult();
      }
      } else {
      assert responses.size() == 0: "There should only be 1 response, not " + responses.size() + ", returned by a command!";
      return null;
      }
      }
      }

1 2 3

These classes can all be found in the kie-api, kie-services-client and kie-services-jaxb JARs. For this example, the only dependency needed is org.kie.remote:kie-remote-client, which transitively depends on both the org.kie:kie-api and org.kie.remote:kie-remote-jaxb artifacts.

4

The JaxbCommandsRequest instance is the "holder" object in which you can place all of the commands you want to execute in a particular request. By using the JaxbCommandsRequest.getCommands() method, you can retrieve the list of commands in order to add more commands to the request. A deployment id is required for command request messages that deal with business processes. Command request messages that only contain human task-related commands do not require a deployment id.

5

Note that the JMS message sent to the remote JMS API must be constructed as follows:

  • It must be a JMS text message (javax.jms.TextMessage).
  • It must contain a serialized instance of a JaxbCommandsRequest, added to the message as a UTF string
  • It must have a filled JMS Correlation ID property.
  • It must have an int property with the name of "serialization" set to an acceptable value (only 0 at the moment).

6 7

The same serialization mechanism used to serialize the request message will be used to serialize the response message.

Except for the Execute calls, all other REST calls described below can use either JAXB or JSON.

All REST calls, unless otherwise specified, use JAXB serialization.

When using JSON, make sure to add the JSON media type ("application/json") to the ACCEPT header of your REST call.

Sometimes, users may wish to pass instances of their own classes as parameters to commands sent in a REST or Webservice request or JMS message. In order to do this, there are a number of requirements.

Some of the REST calls below return lists of information. The results of these operations can be paginated, which means that the lists can be split up and returned according to the parameters sent by the user.

For example, if the REST call parameters indicate that page 2 with page size 10 should be returned for the results, then results 10 to (and including) 19 will be returned.

The first page is always page 1 (as opposed to page "0").


If both a "long" pagination parameter and its synonym are used, then only the value from the "long" variant is used. For example, if the page is given with a value of 11 and the p parameter is given with a value of 37, then the value of the page parameter, 11 , will be used and the p parameter will be ignored.

For the following operations, pagination is always used. See above for the default values used.


The jBPM Eclipse plugin provides developers (and very technical users) with an environment to edit and test processes, and integrate it deeply with their applications. It provides the following features (on top of the Eclipse IDE):

The jBPM installer is capable of downloading and installing an Eclipse installation, including the Drools and jBPM Eclipse plugin (with a full jBPM runtime preconfigured) and the Eclipse BPMN2 Modeler.

You can however also download and install the jBPM Eclipse Plugin manually. To do so, you need to:

Note that, when doing a manual install, you still need to manually install the Eclipse BPMN 2.0 Modeler plugin as well. Check out the chapter on the Eclipse BPMN 2.0 Modeler on how to do that.

The aim of the new project wizard is to set up an executable sample project to start using processes immediately. This will set up a basic structure, the classpath, sample process and a test case to get you started. To create a new jBPM project, in the "File" menu select "New" and then "Project ..." and under the jBPM category, select "jBPM Project". A dialog as shown below should pop up.


Fill in a name for your project and if necessary change the location where this project should be located (by default Eclipse will generate it inside your Eclipse workspace folder) and click "Next >".

Now you can optionally include a sample process in your project to get started. You can select to either use a simple "Hello World" process, a slightly more advanced process including human tasks and persistence or simply an empty project. You can also select to include a JUnit test class that you can use to test your process. These can serve as a starting point, and will give you something executable almost immediately, which you can then modify to your needs.


Finally, the last page in the wizard allows you select a jBPM runtime, as shown below. You can either use the default runtime (as configured for you workspace, in your workspace preferences), or you can select a specific runtime for this project. For more information about runtimes and how to create them, see the section on jBPM runtimes in this chapter.

You can also select which version of jBPM you want to generate sample code for. By default it will generate an example using the latest jBPM 6.x API, but you could also generate examples using the old jBPM 5.x API. Note that you yourself are responsible for making sure that the code you generate can be understood by the runtime (for example, if you create an example using jBPM6 API but select a jBPM5 runtime, your sample will not compile). Also note that, if you want to execute a jBPM5 example on jBPM6, you will need to have the knowledge-api JAR inside your jBPM6 runtime, as this is responsible for the backwards compatibility of the jBPM5 API in jBPM6.


When you selected the simple 'hello world' example, the result is shown below. Feel free to experiment with the plug-in at this point.


The newly created project contains an example process file (sample.bpmn) in the src/main/resources directory and an example Java file (ProcessTest.java) that can be used to test the process in a jBPM engine. You'll find this in the folder src/main/java, in the com.sample package. All the other JARs that are necessary during execution are also added to the classpath in a custom classpath container called jBPM Library.

You can also convert an existing Java project to a jBPM project by selecting the "Convert to jBPM Project" action. Right-click the project you want to convert and under the "Configure" category (at the bottom) select "Convert to jBPM Project". This will add the jBPM Library to your project's classpath.

A jBPM runtime is a collection of JAR files that represent one specific release of the jBPM project JARs. To create a runtime, download the binary distribution of the version of jBPM you want to use and unzip on your local file system. You must then point the IDE to the release of your choice by selecting the folder where these JARs are located. If you want to create a new runtime based on the latest jBPM project JARs included in the plugin itself, you can also easily do that. You are required to specify a default jBPM runtime for your Eclipse workspace, but each individual project can override the default and select the appropriate runtime for that project specifically.

To define one or more jBPM runtimes using the Eclipse preferences view you open up your Preferences, by selecting the "Preferences" menu item in the menu "Window". A "Preferences" dialog should show all your settings. On the left side of this dialog, under the jBPM category, select "Installed jBPM runtimes". The panel on the right should then show the currently defined jBPM runtimes. For example, if you used the jBPM Installer, it should look like the figure below.

To define a new jBPM runtime, click on the "Add" button. A dialog such as the one shown below should pop up, asking for the name of your runtime and the location on your file system where it can be found.

In general, you have two options:

After clicking the OK button, the runtime should show up in your table of installed jBPM runtimes, as shown below. Click on the checkbox in front of one of the installed runtimes to make it the default jBPM runtime. The default jBPM runtime will be used as the runtime of all your new jBPM projects (in case you didn't select a project-specific runtime).

You can add as many jBPM runtimes as you need. Note that you will need to restart Eclipse if you changed the default runtime and you want to make sure that all the projects that are using the default runtime update their classpath accordingly.

The aim of the new Maven project wizard is to set up an executable sample project to start using processes immediately (but not as normal Java project with all jBPM dependencies added using a jBPM library but by using Maven (and thus a pom.xml) to define your project's properties and dependencies. This wizard will set up a Maven project using a pom.xml, and include a sample process and Java class to execute it. To create a new jBPM Maven project, in the "File" menu select "New" and then "Project ..." and under the jBPM category, select "jBPM Project (Maven)". Give your project a name and click finish. The result should be as shown below.


The pom.xml that is generated for your project contains the following:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sample</groupId>
  <artifactId>jbpm-example</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <name>jBPM :: Sample Maven Project</name>
  <description>A sample jBPM Maven project</description>

  <properties>
    <version.org.jbpm>6.0.0.Final</version.org.jbpm>
  </properties>

  <repositories>
    <repository>
      <id>jboss-public-repository-group</id>
      <name>JBoss Public Repository Group</name>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
      </snapshots>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-test</artifactId>
      <version>${version.org.jbpm}</version>
    </dependency>
  </dependencies>
</project>

In the properties section, you can specify which version of jBPM you would like to use (by default it uses 6.0.0.Final). It adds the JBoss Nexus Maven repository (where all the jBPM JARs and their dependencies are located) to your project and configures the dependencies.

Note

By default, only the jbpm-test JAR is specified as a dependency, as this has transitive dependencies to almost all of the core dependencies you will need. You are free to update the dependencies section however to include only the dependencies you need.

The project also contains a sample process, under src/main/resources, in the com.sample package, and a kmodule.xml configuration file under the META-INF folder. The kmodule.xml defines which resources (processes, rules, etc.) are to be loaded as part of your project. In this case, it is defining a kbase called "kbase" that will load all the resources in the com.sample folder:

<kmodule xmlns="http://www.drools.org/xsd/kmodule">
  <kbase name="kbase" packages="com.sample"/>
</kmodule>

Finally, it also contains a Java class that can be used to execute the sample process. It will first create a kbase called "kbase" (by inspecting the kmodule.xml file and thus loading the sample.bpmn process) and then use a RuntimeManager to get access to a KieSession and TaskService. In this case, it is used to start a process and then complete the tasks created by this process one by one.

The Kie Navigator is a new view in the Eclipse Tooling as of version 6.3. The Kie Navigator View is accessed from the Eclipse Window->Show View main menu:


In order to use the Kie Navigator View, you must first define an Application Server in the WST Servers View. So, initially the Kie Navigator View will look like this:


Clicking on the link “Use the Servers View to create a new server…” will open the Servers View where a new server definition can be created. Management of the server, including startup and shutdown is done from here. Note that Drools/jBPM requires certain additional JVM and server startup options, which must be added to the server startup configuration. Once a new server has been defined, open the server configuration page (double click on the newly created server entry) and the server Overview page is opened:


Clicking the “Open launch configuration” link opens the following dialog:


Here the user can enter the app server and JVM arguments to properly configure startup of the Kie web service. See the Drools/jBPM documentation for more information about these arguments.

Alternatively, the app server and Kie web service application can be started from a command-line using either the provided Ant demo scripts or any other custom startup script. Note that starting from the Servers view may cause the app server to be shut down when exiting Eclipse. A server can also be configured in Eclipse for external management (see the “Server Behavior” section in the above screenshot.)

Once the server has been configured and started, the Kie Navigator View will recognize the server and attempt to communicate with the Kie web service. The view now looks something like this:


In this screenshot several nodes have been expanded to show all possible situations. At the root of this view is the app server. The Kie Navigator View is designed to support multiple servers, but each must obviously be configured a different hostname and/or HTTP port number. This, for example, allows management of development, test and production servers.

Below the server level are Organizational Units and Repositories. Repositories that are not currently associated with an Organizational Unit appear directly under the Server root node. Below the Organizational Unit level are the associated Repositories, and below the Repositories are Projects contained in the Repository.

A Repository can either be available () or unavailable () in the Workspace; a Repository is only available if it has been “imported” (see Context Menus, below) from the Kie web server.

Similarly, a Project can either be available () or unavailable () depending on whether it has been “imported”. When a Project has been imported, it behaves exactly the same as if it were being viewed in the Eclipse Project Explorer or Navigator; that is, all of the same menu actions available in the Project Explorer are also available in the Kie Navigator View. Also, all of the icon decorators and labels on project folders are the same as in Project Explorer.

This section describes the context menu actions available for each type of node in the Kie Navigator tree.

This section describes all of the property pages for each entry type in the Kie Navigator tree.


  • Server Name:the server name as defined in the WST Servers Viewer. This can not be changed.

  • Host Name:the name of the machine on which the app server is running. This is also managed from the WST Servers Viewer.

  • Username/Password:login credentials for the Kie web app. This is used to make REST calls to the Kie web service.

  • Trust connections to this Server:if a host is not known as a trusted site, the ssh protocol will prompt the user to verify that this is a trusted site. Setting this checkbox disables the prompt. The host can also be entered into the ssh configuration as a trusted site to avoid this problem.

  • KIE Application Name:the name of the Kie web app; the Kie Navigator will try the following application names by default to determine the app name:

    • kie-wb

    • kie-drools-wb

    • kie-jbpm-wb

    • business-central

    • drools-console

    • jbpm-console

    • jboss-brms

    However, since the user has the option of renaming the Kie web app during installation, Kie Navigator may not be able to discover the actual name. This field is intended for the case where the web app name has been user-defined.

  • Use default Git Repository Path:when this checkbox is set, repositories will be cloned into the directory configured by Git (see the Eclipse User Preferences for Git.) When unchecked, the directory used in the following field will be used instead.

  • Git Repository Path:the directory to use for cloning repositories from this server; this field is only enabled if the “Use default Git Repository Path” checkbox is unset. Note that since it is possible to have many servers (e.g. production, test, etc.) with a similar organizational structure, the chances of repository name collisions are high. It is therefore suggested to use a different repository directory for each server. By default, the server name is appended to the default Git repository path, to give a unique directory name for each server.

This section describes how to debug processes using the jBPM Eclipse plugin. This means that the current state of your running processes can be inspected and visualized during the execution. Note that we currently don't allow you to put breakpoints on the nodes within a process directly. You can however put breakpoints inside any Java code you might have (i.e. your application code that is invoking the engine or invoked by the engine, listeners, etc.) or inside rules (that could be evaluated in the context of a process). At these breakpoints, you can then inspect the internal state of all your process instances.

When debugging the application, you can use the following debug views to track the execution of the process:

The process instances view shows the process instances currently running in the selected ksession. To be able to use the process instances view, first open the Process Instances view (Window - Show View - Other ... and under the Drools category select Process Instances and Process Instance). Tip: it might be useful to drag the Process Instance view to the Outline View and slightly enlarge it, as shown in the screenshot below, so you can see both the Process Instances and Process Instance views at the same time.

Next, use a (regular) Java breakpoint to stop your application at a specific point (for example right after starting a new process instance). In the Debug perspective, select the ksession you would like to inspect, and the Process Instances view should show the process instances that are currently active inside that ksession. For example, the screenshot below shows one running process instance (with id "1"). When double-clicking a process instance, the process instance viewer will graphically show the progress of that process instance. An example where the process instance is waiting for a human actor to perform "Task 1" is shown below.

The audit view can be used to show the all the events inside an audit log in a tree-based manner. An audit log is an XML-based log file which contains a log of all the events that occurred while executing a specific ksession. To create a logger, use KieServices to create a new logger and attach it to a ksession. Be sure to close the logger after usage.

KieRuntimeLogger logger = KieServices.Factory.get().getLoggers()
    .newThreadedFileLogger(ksession, "mylogfile", 1000);
// do something with the ksession here
logger.close();
      

To be able to use the Audit View, first open it (Window - Show View - Other ... and under the Drools category select Audit). To open up a log file in the audit view, open the selected log file in the audit view (using the "Open Log" action in the top right corner), or simply drag and drop the log file from the Package Explorer or Navigator into the audit view. A tree-based view is generated based on the data inside the audit log. An event is shown as a subnode of another event if the child event is caused by (a direct consequence of) the parent event. An example is shown below.

From Eclipse, you can synchronize your local workspace with one or more repositories that are managed inside the workbench application. This enables collaboration between developers using Eclipse and users of the web-based workbench (business analysts or end users for example). Synchronization between the workbench repositories and your local version of these projects is done using Git (a popular distributed source code version control system).

When creating and executing processes inside Eclipse, you are creating them on your local file system. You can however also import an existing repository from the Workbench, apply changes and push these changes back into the Workbench repositories. We are using existing Git tools for this. Note that this section will describe how to do this using the EGit tooling (Eclipse Tooling for Git which comes by default with most versions of Eclipse), but feel free to use your preferred Git tool instead.

To import an existing repository from the workbench, you can use the EGit import wizard. In the File menu, select "Import ..." and in the Git category, select "Projects from Git" and click "Next >". This should open a new dialog where you should select the location of the repository you would like to import. Since we are connecting to a repository that is managed by the workbench application, select "URI" and click "Next >" once more.

Use the following URI to connect to your workbench repositories:

ssh://<hostname>:8001/<repository_name>

For example, if you are running the workbench application on your local host (for example by using the jbpm-installer), and you want to import the jbpm-playground repo, use the following URI:

ssh://localhost:8001/jbpm-playground

Note that you can change the port that is used by the server to provide ssh access to the git repository if necessary, using the system property org.uberfire.nio.git.ssh.port

Fill in the URI of the repository you would like to import, as for example shown below, and click "Next >".


You will be asked to select which branch you would like to import. Select the master branch and click "Next >" again.

Finally, you need to specify where on your local file system you would like this repository to be created. Fill in the directory (you can use the Browse button to select the folder in question, and if necessary you can create a new folder there as well) and click "Next >". This will now download the repository to the folder you just selected.


You still need to import the repository you just downloaded as a project in your Eclipse workspace. Select "Import as general project" and after clicking "Next >", give it a name and click "Finish". After doing so, your workspace should now contain your repository, and you should be able to browse, open and edit the various assets inside.


When you import a repository, it will download all the projects that are inside that repository. It is however useful to mount one specific project as a separate Java project in Eclipse. When you do this, Eclipse will be able to interpret the information in the project pom.xml file (that you created in the workbench), download and include any dependencies you specified, compile any Java classes you have in your project (that you for example created with the data modeler), etc.

To do so, right-click on one of the projects in your repository project and select "Import ..." and under the Maven category, select "Existing Maven Projects" (as shown below) and click Next.


In the next page, you should see the pom.xml of the project you selected. Click Finish.


If your project requires some of the jBPM libraries to correctly compile and/or execute any Java classes in your project (for example if you have test classes in your project that start up a jBPM engine and execute some tests for your project, or if you are using the data modeler, which will add some annotations to the generated Java classes), you still need to add the jBPM libraries to the classpath of your project. To do so, simply convert your project into a jBPM project, which will add the jBPM library to your project's classpath. Right-click your project and select "Configure -> Convert to jBPM Project". Your project should now have a jBPM Library added to its classpath (it might be necessary to clean your project to pick up this change and recompile all Java classes).


The Eclipse BPMN 2.0 Modeler allows you to specify business processes, choreographies, etc. using the BPMN 2.0 XML syntax (including BPMNDI for the graphical information). The editor itself is based on the Eclipse Graphiti framework and the Eclipse BPMN 2.0 EMF meta-model.

Features:

The BPMN2 Modeler project is being developed at eclipse.org, sponsored by Red Hat/JBoss. Red Hat understands the benefits of developing software in the community, and therefore, the Eclipse BPMN 2.0 Modeler was developed not just for the jBPM project only, but it can be used in a much broader context and is fully spec compliant. jBPM-specific features are developed as part of a separate jBPM Target Runtime. We welcome other organizations in contributing to this modeler as well and (re)using the generic functionality and/or defining their own target runtime if necessary. Not only is this a good thing for the community, but it also leaves the path open for the jBPM suite to evolve as new features are requested by customers.

Many thanks go out to the people at Codehoop that did a great job in creating a first version of this editor.

The jBPM installer is capable of downloading and installing an Eclipse installation, including the Eclipse BPMN2 Modeler and the Drools and jBPM Eclipse plugin (with a full jBPM runtime preconfigured).

You can however also download and install the jBPM Eclipse Plugin manually. To do so, you need Eclipse 3.6.2 (Helios) or newer. To install, startup Eclipse and install the Eclipse BPMN 2.0 Modeler from the following update site (from menu Help -> Install new software and then add the update site in question by clicking the Add button, filling in a name and the correct URL as shown below). It will automatically download all other dependencies as well (e.g. Graphiti etc.)

Eclipse 3.6 (Helios): http://download.eclipse.org/bpmn2-modeler/updates/helios

Eclipse 3.7 - 4.2.1 (Indigo - Juno): http://download.eclipse.org/bpmn2-modeler/updates/juno

Eclipse 4.3 (Kepler): http://download.eclipse.org/bpmn2-modeler/updates/kepler

The project is hosted at eclipse.org and open for anyone to contribute. The project home page can he found here:

http://http://eclipse.org/bpmn2-modeler/

Sources are available here (using Eclipse Public License v1.0):

https://git.eclipse.org/c/bpmn2-modeler/org.eclipse.bpmn2-modeler.git

A community forum for posting questions and exchanging ideas is also available here:

http://www.eclipse.org/forums/

A Bugzilla bug tracking system is available for reporting new bugs, or checking the status of existing bugs, here:

https://bugs.eclipse.org/bugs/buglist.cgi?product=BPMN2Modeler

Apache Maven is used by jBPM for two main purposes:

Since version 6, jBPM provides simplified and complete deployment mechanism that is based entirely on Apache Maven artifacts. These artifacts also known as kjars are simple JAR files that include a descriptor for KIE system to produce KieBase and KieSession. Descriptor of the kjar is represented as XML file named kmodule.xml and it can be:

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.drools.org/xsd/kmodule">
</kmodule>

Empty kmodule.xml that provides all defaults for the kjar:

All these and more can be configured manually via kmodule.xml when defaults are not enough. The complete set of elements can be found in xsd schema of kmodule.xml.

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://www.drools.org/xsd/kmodule">
  <kbase name="defaultKieBase" default="true" eventProcessingMode="cloud" equalsBehavior="identity" declarativeAgenda="disabled" scope="javax.enterprise.context.ApplicationScoped" packages="*">
    <ksession name="defaultKieSession" type="stateful" default="true" clockType="realtime" scope="javax.enterprise.context.ApplicationScoped">
        <workItemHandlers>
            <workItemHandler name="CustomTask" type="FQCN_OF_HANDLER" />
        </workItemHandlers>
        <listeners>
            <listener type="FQCN_OF_EVENT_LISTENER" />
        </listeners>
    </ksession>
    <ksession name="defaultStatelessKieSession" type="stateless" default="true" clockType="realtime" scope="javax.enterprise.context.ApplicationScoped"/>
  </kbase>
</kmodule>

As illustrated in the listing above the kmodule.xml provides flexible way of instructing the runtime engine on what should be configured and how. The example above does not present all available options, but these are the most common when working with processes.

Note

Important to note is that when using RuntimeManager, KieSession instances are created by the RuntimeManager instead of by KieContainer but kmodule.xml (or model in general) is aways used as a base of the construction process. KieBase although is always taken from KieContainer.

Kjars are represented same way as any other Maven artifact - by Group Artifact Version which is then represented as ReleaseId in KIE API. This the the only thing required to deploy kjar into runtime environment such as KIE Workbeanch.

When building systems that embed jBPM as workflow engine the simplest way is to configure all dependencies required by jBPM via Apache Maven. jBPM provides set of BOMs (Bill Of Material) to simplify what artifacts needs to be declared. Common way to start with integration of custom application and jBPM is to define dependency management:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <version.org.drools>6.0.0.Final</version.org.drools>
    <version.org.jbpm>6.0.0.Final</version.org.jbpm>
    <hibernate.version>4.2.0.Final</hibernate.version>
    <hibernate.core.version>4.2.0.Final</hibernate.core.version>
    <slf4j.version>1.6.4</slf4j.version>
    <jboss.javaee.version>1.0.0.Final</jboss.javaee.version>
    <logback.version>1.0.9</logback.version>
    <h2.version>1.3.161</h2.version>
    <btm.version>2.1.4</btm.version>
    <junit.version>4.8.1</junit.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <!-- define drools BOM -->
      <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-bom</artifactId>
        <type>pom</type>
        <version>${version.org.drools}</version>
        <scope>import</scope>
      </dependency>
      <!-- define drools BOM -->
      <dependency>
        <groupId>org.jbpm</groupId>
        <artifactId>jbpm-bom</artifactId>
        <type>pom</type>
        <version>${version.org.jbpm}</version>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

Above should be declared in top level pom.xml so all modules that need to use KIE (drools and jBPM) API can access it.

Next, module(s) that would operate on KIE API should declare following dependencies:

    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-flow</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-flow-builder</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-bpmn2</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-persistence-jpa</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-human-task-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-runtime-manager</artifactId>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>

Above are the main runtime dependencies, regardless of where the application is deployed (application server, servlet container, standalone app). A good practice is to test the workflow components to ensure they work properly before actual deployment and thus following test dependencies should be defined:

    <!-- test dependencies -->
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-shared-services</artifactId>
      <classifier>btm</classifier>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>${logback.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-entitymanager</artifactId>
      <version>${hibernate.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>${hibernate.core.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <version>${h2.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.codehaus.btm</groupId>
      <artifactId>btm</artifactId>
      <version>${btm.version}</version>
      <scope>test</scope>
    </dependency>

Last but not least, define the JBoss Maven repository for artifacts resolution:

  <repositories>
    <repository>
      <id>jboss-public-repository-group</id>
      <name>JBoss Public Repository Group</name>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <updatePolicy>daily</updatePolicy>
      </snapshots>
    </repository>
  </repositories>

That should allow to configure jBPM in your application and provide access to KIE API to operate on processes, rules, events.

jBPM 6 comes with out of the box integration with CDI (Contexts and Dependency Injection). Although most of the API can be used in CDI world there are some dedicated modules that are designed especially for CDI containers. The most important one is jbpm-services-cdi that provides cdi wrappers on top of jbpm services, these shall be used in most of the cases were CDI is available for jBPM integration. It provides following set of services:

These services are first class citizens for CDI world so they are available for injection in any other CDI bean.

Service responsible for deploying DeploymentUnits into runtime environment. By deploying given deployment unit becomes ready for execution and has RuntimeManager created for it.DeploymentService can next be used to retrieve:

Deployment service stores the deployed units by default in memory and thus in case of a need to restore all previously deployed units, component that uses deployment service needs to store that information itself. Common places for such a store are database, file system, repository of some sort, etc. Deployment service will fire CDI events on deployment and undeployment to allow application components to react real time to these events to be able to store deployments or remove them from the store when they are undeployed.

use CDI observer mechanism to get notification on above events. First to save deployments in the store of your choice:

    public void saveDeployment(@Observes @Deploy DeploymentEvent event) {
        // store deployed unit info for further needs 
        DeployedUnit deployedUnit = event.getDeployedUnit();
    }

next to remove it when it was undeployed

    public void removeDeployment(@Observes @Undeploy DeploymentEvent event) {
        // remove deployment with id event.getDeploymentId()
    }

Due to the fact that there might be several implementation of DeploymentService use of qualifiers is needed to instruct CDI container which one shall be injected. jBPM comes with two out of the box:

The general practice is that every implementation of DeploymentService should come with dedicated implementation of DeploymentUnit as these two provided out of the box.

FormProviderService provides access to form representations usually displayed on UI for both process forms and user task forms. It is built on concept of isolated FormProviders that can provide different capabilities and be backed by different technologies. FormProvider interface describes contract for the implementations

public interface FormProvider {

    int getPriority();

    String render(String name, ProcessDesc process, Map<String, Object> renderContext);

    String render(String name, Task task, ProcessDesc process, Map<String, Object> renderContext);
}

Implementations of FormProvider interface should always define priority as this is the main driver for the FormProviderService to ask for the content of the form of a given provider. FormProviderService will collect all available providers and iterate over them asking for the form content (rendered) in their priority order. The lower the number the higher priority it gets during evaluation, e.g. provider with priority 5 will be evaluated before provider with priority 10. FormProviderService will iterate over available providers as long as one delivers the content. In the worse case scenario, simple text based forms will be returned.

jBPM comes with following FormProviders out of the box:

To make use of jbpm-services-cdi in your system you'll need to provide some beans for the out of the box services to satisfy all dependencies they have. There are several beans that depends on actual scenario

When running in JEE environment like an JBoss Application Server following producer bean should satisfy all requirements of the jbpm-services-cdi

public class EnvironmentProducer { 
   
    @PersistenceUnit(unitName = "org.jbpm.domain")
    private EntityManagerFactory emf;

    @Inject
    @Selectable
    private UserGroupInfoProducer userGroupInfoProducer;

    @Inject
    @Kjar
    private DeploymentService deploymentService;

    @Produces
    public EntityManagerFactory getEntityManagerFactory() {
        return this.emf;
    }

    @Produces
    public org.kie.api.task.UserGroupCallback produceSelectedUserGroupCalback() {
        return userGroupInfoProducer.produceCallback();
    }

    @Produces
    public UserInfo produceUserInfo() {
        return userGroupInfoProducer.produceUserInfo();
    }

    @Produces
    @Named("Logs")
    public TaskLifeCycleEventListener produceTaskAuditListener() {
        return new JPATaskLifeCycleEventListener(true);
    }

    @Produces
    public DeploymentService getDeploymentService() {
        return this.deploymentService;
    }

    @Produces
    public IdentityProvider produceIdentityProvider {
        return new IdentityProvider() {
             // implement IdentityProvider
        };
    }
}

Then beans.xml for the application should enable proper alternative for user group callback (that will be taken based on @Selectable qualifier)

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd">

  <alternatives>
    <class>org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer</class>
  </alternatives>

</beans>

Optionally there can be several other producers provided to deliver:

These components can be provided by implementing following interfaces

/**
 * Allows to provide custom implementations to deliver WorkItem name and WorkItemHandler instance pairs
 * for the runtime.
 * <br/>
 * It will be invoked by RegisterableItemsFactory implementation (especially InjectableRegisterableItemsFactory 
 * in CDI world) for every KieSession. Recommendation is to always produce new instances to avoid unexpected 
 * results. 
 *
 */
public interface WorkItemHandlerProducer {

    /**
     * Returns map of (key = work item name, value work item handler instance) of work items 
     * to be registered on KieSession
     * <br/>
     * Parameters that might be given are as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * 
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return map of work item handler instances (recommendation is to always return new instances when this method is invoked)
     */
    Map<String, WorkItemHandler> getWorkItemHandlers(String identifier, Map<String, Object> params);
}

and

/**
 * Allows do define custom producers for know EventListeners. Intention of this is that there might be several 
 * implementations that might provide different listener instance based on the context they are executed in. 
 * <br/>
 * It will be invoked by RegisterableItemsFactory implementation (especially InjectableRegisterableItemsFactory 
 * in CDI world) for every KieSession. Recommendation is to always produce new instances to avoid unexpected 
 * results.
 *
 * @param <T> type of the event listener - ProcessEventListener, AgendaEventListener, WorkingMemoryEventListener
 */
public interface EventListenerProducer<T> {

    /**
     * Returns list of instances for given (T) type of listeners
     * <br/>
     * Parameters that might be given are as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return list of listener instances (recommendation is to always return new instances when this method is invoked)
     */
    List<T> getEventListeners(String identifier, Map<String, Object>  params);
}

Beans implementing these two interfaces will be collected on runtime and consulted when building KieSession by RuntimeManager. See RuntimeManager section for more details on this.

A complete runnable example of application built with CDI can be found here.

RuntimeManager itself can be injected as CDI bean into any other CDI bean within the application. It has then requirement to get RungimeEnvironment properly produces to allow RuntimeManager to be correctly initialized. RuntimeManager comes with three predefined strategies and each of them gets CDI qualifier so it can be referenced:

Producer that was defined in Configuration section should be now enhanced with producer methods to provide RuntimeEnvironment

public class EnvironmentProducer { 
   
    //add same producers as for services

    @Produces
    @Singleton
    @PerRequest
    @PerProcessInstance
    public RuntimeEnvironment produceEnvironment(EntityManagerFactory emf) {
        
        RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
                .newDefaultBuilder()
                .entityManagerFactory(emf)
                .userGroupCallback(getUserGroupCallback())
                .registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
                .addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
                .addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
                .get();
        return environment;
    }
}

In this example single producer method is capable of providing RuntimeEnvironment for all strategies of RuntimeManager by specifying all qualifiers on the method level.

Once complete producer is available, RuntimeManager can be injected into application's CDi bean

public class ProcessEngine {

    @Inject
    @Singleton
    private RuntimeManager singletonManager;

    public void startProcess() {
        
        RuntimeEngine runtime = singletonManager.getRuntimeEngine(EmptyContext.get());
        KieSession ksession = runtime.getKieSession();
        
        ProcessInstance processInstance = ksession.startProcess("UserTask");
        
        singletonManager.disposeRuntimeEngine(runtime);     
    }
}

That's all what needs to be configured to make use of CDI power with jBPM.

As an alternative to DeploymentService, RuntimeManagerFactory can be injected and then RuntimeManager instance can be created manually by the application. In such case EnvironmentProducer stays same as for DeploymentService and following is an example of simple ProcessEngine bean

public class ProcessEngine {

    @Inject
    private RuntimeManagerFactory managerFactory;
    
    @Inject
    private EntityManagerFactory emf;
    
    @Inject
    private BeanManager beanManager;

    public void startProcess() {
        RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
                .newDefaultBuilder()
                .entityManagerFactory(emf)
                .addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
                .addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
                .registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
                .get();
        
        RuntimeManager manager = managerFactory.newSingletonRuntimeManager(environment);
        RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get());
        KieSession ksession = runtime.getKieSession();
        
        ProcessInstance processInstance = ksession.startProcess("UserTask");
        
        manager.disposeRuntimeEngine(runtime);
        manager.close();     
    }

}

jBPM can be configured in many ways with Spring though the two most frequenlty used approaches are:

While both approaches are tested and valid, which one to chose is a matter of the system functionaltiy. Before selecting one of the approache the most important question to ask is:

Will my system run multiple runtime managers at the same time?

If the asnwer to this question is no, then go ahead with direct Runtime Manager API as it will be the simplest way to use jBPM within your application. But when answer is yes, then go ahead with jbpm services as they encapsulate runtime manager API with best practices by providing dynamic runtime environment for your BPM logic - also known as execution server.

This is the standard (and the simplest) way to get up and running with jBPM in your application. You only configure it once and run as part of the application. With the RuntimeManager usage, both process engine and task service will be managed in complete synchronization, meaning there is no need from end user to deal with "plumbing" code to make these two work together.

To provide spring based way of setting up jBPM, few factory beans where added:

FactoryBeans provide standard way to configure Spring application spring xml though there are not custom spring xml tags equivalent for them.

Following section aims at giving complete spring configuration for single runtime manager wihtin spring application context.

  1. Setup entity manager factory and transaction manager:

    
    <bean id="jbpmEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="org.jbpm.persistence.spring.jta"/>
    </bean>
    
    <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices"></bean>
    
    <bean id="BitronixTransactionManager" factory-method="getTransactionManager"
          class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown" />
      
    <bean id="jbpmTxManager" class="org.springframework.transaction.jta.JtaTransactionManager">
      <property name="transactionManager" ref="BitronixTransactionManager" />
      <property name="userTransaction" ref="BitronixTransactionManager" />
    </bean>
    

    with this we have ready persistence configuration that gives us:

  2. Configure resource that we are going to use - business process

    
    <bean id="process" factory-method="newClassPathResource" class="org.kie.internal.io.ResourceFactory">
      <constructor-arg>
        <value>jbpm/processes/sample.bpmn</value>
      </constructor-arg>
    </bean>
    

    this configures single process that will be available for execution - sample.bpmn that will be taken from class path. This is the simplest way to get your processes included when trying out jbpm.

  3. Configure RuntimeEnvironment with our infrastructure (entity manager, transaction manager, resources)

    
    <bean id="runtimeEnvironment" class="org.kie.spring.factorybeans.RuntimeEnvironmentFactoryBean">
      <property name="type" value="DEFAULT"/>
      <property name="entityManagerFactory" ref="jbpmEMF"/>
      <property name="transactionManager" ref="jbpmTxManager"/>
      <property name="assets">
        <map>
          <entry key-ref="process"><util:constant static-field="org.kie.api.io.ResourceType.BPMN2"/></entry>
        </map>
      </property>
    </bean>
    

    that gives us default runtime environment ready to be used to create instance of a RuntimeManager.

  4. Create RuntimeManager with the environment we just setup

    
    <bean id="runtimeManager" class="org.kie.spring.factorybeans.RuntimeManagerFactoryBean" destroy-method="close">
      <property name="identifier" value="spring-rm"/>
      <property name="runtimeEnvironment" ref="runtimeEnvironment"/>
    </bean>
    

    with just four steps you are ready to execute your processes with Spring and jBPM 6, utilizing EntityManagerFactory and JTA transaction manager.

    Complete spring configuration file can be found here.

This is just one configuration setup that jBPM 6 supports - JTA transaction manager and EntityManagerFactory, others are:

  • JTA and SharedEntityManager

  • Local Persistence Unit and EntityManagerFactory

  • Local Persistence Unit and SharedEntityManager

For more details about difference configuration options look at the example configuration files and test cases.

In case more dynamic nature is required in your Spring application then more appropriate could be to build up so called execution server based on jbpm services. jBPM services has been designed in a way to make them framework agnostic and in case framework specific addons are required they will be brought by additional module. So the code logic of the services is embedded in jbpm-kie-services. These are pure java services and by that can be easily consumed by Spring application.

Dynamic nature means that processes (And other assets like data model, rules, forms, etc) can be added and removed without restarting application.

There is almost no code involved to completely configure jBPM services in spring besides single interface that needs to be implemented - IdentityProvider that depends on your security configuration. One built with Spring Security can be like following though it might not cover all features one can have for Spring application.


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.kie.internal.identity.IdentityProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;

public class SpringSecurityIdentityProvider implements IdentityProvider {

	public String getName() {
		
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (auth != null && auth.isAuthenticated()) {
			return auth.getName();
		}
		return "system";
	}

	public List<String> getRoles() {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		if (auth != null && auth.isAuthenticated()) {
			List<String> roles = new ArrayList<String>();
			
			for (GrantedAuthority ga : auth.getAuthorities()) {
				roles.add(ga.getAuthority());
			}
			
			return roles;
		}
		
		return Collections.emptyList();
	}

	public boolean hasRole(String role) {
		return false;
	}

}

As usual, first thing to start with is transaction configuration:


<context:annotation-config />
<tx:annotation-driven />
<tx:jta-transaction-manager />                

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

Next configuration of JPA and persistence follows:


<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="transactionManager">
   <property name="persistenceXmlLocation" value="classpath:/META-INF/jbpm-persistence.xml" />
</bean>

Configure security and user/group information providers


<util:properties id="roleProperties" location="classpath:/roles.properties" />
 
<bean id="userGroupCallback" class="org.jbpm.services.task.identity.JBossUserGroupCallbackImpl">
  <constructor-arg name="userGroups" ref="roleProperties"></constructor-arg>
</bean>

<bean id="identityProvider" class="org.jbpm.spring.SpringSecurityIdentityProvider"/>

Configure runtime manager factory that is Spring context aware and by that can interact with spring container in correct way and supporting services (transactional command service and task service)


<bean id="runtimeManagerFactory" class="org.kie.spring.manager.SpringRuntimeManagerFactoryImpl">
  <property name="transactionManager" ref="transactionManager"/>
  <property name="userGroupCallback" ref="userGroupCallback"/>
</bean>

<bean id="transactionCmdService" class="org.jbpm.shared.services.impl.TransactionalCommandService">
  <constructor-arg name="emf" ref="entityManagerFactory"></constructor-arg>
</bean>

<bean id="taskService" class="org.kie.spring.factorybeans.TaskServiceFactoryBean" destroy-method="close">
  <property name="entityManagerFactory" ref="entityManagerFactory"/>
  <property name="transactionManager" ref="transactionManager"/>
  <property name="userGroupCallback" ref="userGroupCallback"/>
  <property name="listeners">
    <list>
      <bean class="org.jbpm.services.task.audit.JPATaskLifeCycleEventListener">
        <constructor-arg value="true"/>
      </bean>
    </list>
  </property>
</bean>

Configure jBPM services as spring beans


<!-- definition service -->
<bean id="definitionService" class="org.jbpm.kie.services.impl.bpmn2.BPMN2DataServiceImpl"/>

<!-- runtime data service -->
<bean id="runtimeDataService" class="org.jbpm.kie.services.impl.RuntimeDataServiceImpl">
  <property name="commandService" ref="transactionCmdService"/>
  <property name="identityProvider" ref="identityProvider"/>
  <property name="taskService" ref="taskService"/>
</bean>

<!-- -- deployment service -->
<bean id="deploymentService" class="org.jbpm.kie.services.impl.KModuleDeploymentService" depends-on="entityManagerFactory" init-method="onInit">
  <property name="bpmn2Service" ref="definitionService"/>
  <property name="emf" ref="entityManagerFactory"/>
  <property name="managerFactory" ref="runtimeManagerFactory"/>
  <property name="identityProvider" ref="identityProvider"/>
  <property name="runtimeDataService" ref="runtimeDataService"/>
</bean>

<!-- process service -->
<bean id="processService" class="org.jbpm.kie.services.impl.ProcessServiceImpl" depends-on="deploymentService">
  <property name="dataService" ref="runtimeDataService"/>
  <property name="deploymentService" ref="deploymentService"/>
</bean>

<!-- user task service -->
<bean id="userTaskService" class="org.jbpm.kie.services.impl.UserTaskServiceImpl" depends-on="deploymentService">
  <property name="dataService" ref="runtimeDataService"/>
  <property name="deploymentService" ref="deploymentService"/>
</bean>

<!-- register runtime data service as listener on deployment service so it can receive notification about deployed and undeployed units -->
<bean id="data" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="deploymentService">
  <property name="targetObject" ref="deploymentService"></property>
  <property name="targetMethod"><value>addListener</value></property>
  <property name="arguments">
  <list>
      <ref bean="runtimeDataService"/>
  </list>
  </property>
</bean>

And this is all is needed to build fully featured execution server with Spring and jBPM services. A complete Spring web application with this setup can be found here.

jBPM since version 6.2 provides out of the box integration layer with Enterprise Java Beans (EJB) for both local and remote interaction.

Ejb services are brought by following modules:

EJB layer is based on jbpm services and thus provides almost same capabilities as the core module though there are some imiliations when it comes to remote interfaces. Main difference is for the DeploymentService that has been limited for remote ejb service to following methods:

Main rationale behind is to avoid returning runtime objects such as RuntimeManager over EJB remote as it won't bring any value because it will be "disconnected" state.

All other services do provide exact same set of functionality as core module.

Ejb services as an extension of core services provide EJB based execution semantic and based on various EJB specific features.

Transactions

Transaction is managed by EJB container thus there is no need to setup any sort of transaction manager or user transaction within application code.

Identity provider

Identity provider by default is backed by EJBContext and will rely on caller principal information for both name and roles. When inspecting IdentityProvider interface there are two methods related to roles:

This means that ejb must be secured according to JEE security practices to authentiate and authorize users so valid information will be available. In case no authentication/authorization is configured for EJB services an anonymous user is always assumed.

In addition to that, EJB services acept CDI sytly injection for IdentityProvider in case another (non ejb) security model is used. Simply create valid CDI bean that implements org.kie.internal.identity.IdentityProvider and make it available for injection with application and such implementation will take precedence over EJBContext based identity provider.

Deployment synchronization

Deployment synchronization is enabled by default and will attempt to synchronize any deployments every 3 seconds. It is implemented as ejb singleton with container managed concurrency and lock type set to write. Under the covers it utilizes EJB TimerService to schedule the synchronization jobs.

EJB Scheduler Service

jBPM uses scheduler service to deal with time based activities such as timer events, deadlines, etc. When running in EJB environment and EJB Timer Service based scheduler will be used. It will be automatically registered for all instances of RuntimeManager. When it comes to cluster support application server specific configuration might be required.

UserGroupCallback and UserInfo selection

UserGroupCallback and UserInfo might differ for various applications and thus should be sort of pluggable. With EJB we could not make it directly available for injections as they could not be injected with common type so there is another mechanism that allows to select one of provided out of the box implementation or to give a custom one. This mechanism is based on ssytem properties:

System properties can either be added to the startup configuration of the server (jvm) which is recommended or be set programmatically before services will be used - for example with custom @Startup bean that will configure it properly for selected callback and user info.

A example application that utilizes EJB services can be found here.

Remote EJB services are defined as dedicated remote interfaces that extends core services:

These can be used similar way as local interfaces except for handling custom types. Custom types can be defined:

Globally available types do not require any special handling as they will be available for EJB container when remote requests are handled - marshalling of incoming data. Though local custom types won't be visible by default to EJB container as they are not on application classpath. Thus special handling of such types is required.

EJB services provides easy yet rather powerful mechanism to resolve the issue - it comes with two additional types:

These special objects will perform eager serialization to bytes using ObjectInputStream to remove the need of serialization from the EJB client/container. Though it might be worse in case of performance it does overcome much more complecated handling of class loaders on EJB container side to allow use of custom types defined in the project.

Here is an example code needed to work with local types and remote EJB:

// start a process with custom types via remote EJB

Map<String, Object> parameters = new RemoteMap();
Person person = new org.jbpm.test.Person("john", 25, true);
parameters.put("person", person);

Long processInstanceId = processService.startProcess(deploymentUnit.getIdentifier(), "custom-data-project.work-on-custom-data", parameters);

// fetch task data and complete task with custom types via remote EJB
Map<String, Object> data = userTaskService.getTaskInputContentByTaskId(taskId);
        
Person fromTaskPerson = data.get("_person");
fromTaskPerson.setName("John Doe");
        
RemoteMap outcome = new RemoteMap();
outcome.put("person_", fromTaskPerson);
        
userTaskService.complete(taskId, "john", outcome);

Similar way RemoteObject can be used for example to send evnet to process instance:


// send event with custom type via remote EJB
Person person = new org.jbpm.test.Person("john", 25, true);

RemoteObject myObject = new RemoteObject(person);

processService.signalProcessInstance(processInstanceId, "MySignal", myObject);

These illustrates how to wrap custom data when interacting with remote EJB services. Next section will introduce how to make a connection to a remote service vai client code.

Remote client support is provided by implemetation of ClientServiceFactory interface that is facede for application server specific code:


/**
 * Generic service factory used for remote look ups that are usually container specific.
 *
 */
public interface ClientServiceFactory {
	
	/**
	 * Returns unique name of given factory implementation
	 * @return
	 */
	String getName();

	/**
	 * Returns remote view of given service interface from selected application
	 * @param application application identifier on the container
	 * @param serviceInterface remote service interface to be found
	 * @return
	 * @throws NamingException
	 */
	<T> T getService(String application, Class<T> serviceInterface) throws NamingException;
}

Implementations can be dynamically registered using ServiceLoader mechanism and by default there is only one available for JBoss AS/EAP/Wildfly. Each ClientServiceFactory must provide name which will be used to register it within the client registry so it can be then easily looked up.

Here is a code used to get hold of default JBoss based remote client:


// get hold of valid client service factory
ClientServiceFactory factory = ServiceFactoryProvider.getProvider("JBoss");

// application is the name known to application server aka module name
String application = "sample-war-ejb-app";

// get given service out of the factory
DeploymentServiceEJBRemote deploymentService = factory.getService(application, DeploymentServiceEJBRemote.class);

With service available all know to its interface methods are ready to be used.

When working with JBoss AS and remote client you can add following maven dependency to bring in all EJB client libraries:


<dependency>
  <groupId>org.jboss.as</groupId>
  <artifactId>jboss-as-ejb-client-bom</artifactId>
  <version>7.2.0.Final</version> <!-- use valid version for the server you run on -->
  <optional>true</optional>
  <type>pom</type>
</dependency>

All core jBPM JARs (and core dependencies) are OSGi-enabled. That means that they contain MANIFEST.MF files (in the META-INF directory) that describe their dependencies etc. These manifest files are automatically generated by the build. You can plug these JARs directly into an OSGi environment.

OSGi is a dynamic module system for declarative services. So what does that mean? Each JAR in OSGi is called a bundle and has its own Classloader. Each bundle specifies the packages it exports (makes publicly available) and which packages it imports (external dependencies). OSGi will use this information to wire the classloaders of different bundles together; the key distinction is you don't specify what bundle you depend on, or have a single monolithic classpath, instead you specify your package import and version and OSGi attempts to satisfy this from available bundles.

It also supports side by side versioning, so you can have multiple versions of a bundle installed and it'll wire up the correct one. Further to this Bundles can register services for other bundles to use. These services need initialisation, which can cause ordering problems - how do you make sure you don't consume a service before its registered? OSGi has a number of features to help with service composition and ordering. The two main ones are the programmatic ServiceTracker and the XML based Declarative Services. There are also other projects that help with this; Spring DM, iPOJO, Gravity.

The following jBPM JARs are OSGi-enabled:

Some more advanced topics

jBPM provides the ability to create and use domain-specific task nodes in your business processes. This simplifies development when you're creating business processes that contain tasks dealing with other technical systems.

When using jBPM, we call these domain-specific task nodes "custom work items" or (custom) "service nodes". There are two separate aspects to creating and using custom work items:

With regards to a BPMN2 process, custom work items are certain types of <task> nodes. In most cases, custom work items are <task> nodes in a BPMN2 process definition, although they can also be used with certain other task type nodes such as, among others, <serviceTask> or <sendTask> nodes.

Users can thus easily define their own set of domain-specific service nodes and integrate them with the process language. For example, the next figure shows an example of a healthcare-related BPMN2 process. The process includes domain-specific service nodes for measuring blood pressure, prescribing medication, notifying care providers and following-up on the patient.

Before moving on to an example, this section explains what custom work items and custom work item handlers are.

A work item handler is a Java class used to execute (or abort) work items. That also means that the class implements the org.kie.runtime.instance.WorkItemHandler interface. While jBPM provides some custom WorkItemHandler instances (listed below), a Java developer with a minimal knowledge of jBPM can easily create a new work item handler class with its own custom business logic.

Among others, jBPM offers the following WorkItemHandler implementations:

There are a many more WorkItemHandler implementations present in the jbpm-workitems module. If you're looking for specific integration logic with Twitter, for example, we recommend you take a look at the classes made available there.

In general, a WorkItemHandler's .executeWorkItem(...) and .abortWorkItem(...) methods will do the following:

WorkItemManager.completeWorkItem(long workItemId, Map<String, Object> results)
WorkItemManager.abortWorkItem(long workItemId)

In order to make sure that your custom work item handler is used for a particular process instance, it's necessary to register the work item handler before starting the process. This makes the engine aware of your WorkItemHandler so that the engine can use it for the proper node. For example:

ksession.getWorkItemManager().registerWorkItemHandler("Notification",
    new NotificationWorkItemHandler());

The ksession variable above is a StatefulKnowledgeSession (and also a KieSession) instance. The example code above comes from the example that we will go through in the next session.

Work item handler life cycle management

Work item handler is registered on kie session and then can be used whenever process engine encounters a node that should be handled by that handler. Depending on the implementation of the handler (e.g. some handler might keep state or depend on some resources such as data base connection) there might be a need to maintain life cycle of the handler. To ease the way of doing that jBPM comes with two additional interfaces that handler might implement:

Closeable interface is handled for all use cases, while Cacheable is available only when RuntimeManager is used. RuntimeManager provides caching capabilities via its CacheManager (available via InternalRuntimeManager in case self removal is required).

Let's start by showing you how to include a simple work item for sending notifications. A work item is defined by a unique name and includes additional parameters that describe the work in more detail. Work items can also return information after they have been executed, specified as results.

Our notification work item could be defined using a work definition with four parameters and no results. For example:

We've created our work item definition and configured it, so now we can start using it in our processes. The process editor contains a separate section in the palette where the different service nodes that have been defined for the project appear.

Using drag and drop, a notification node can be created inside your process. The properties can be filled in using the properties view.

Besides any custom properties, the following three properties are available for all work items:

Here is an example that creates a domain specific node to execute Java, asking for the class and method parameters. It includes a custom java.gif icon and consists of the following files and resulting screenshot:


import org.drools.core.process.core.datatype.impl.type.StringDataType;
[
  // the Java Node work item located in:
  // project/src/main/resources/META-INF/JavaNodeDefinition.wid
  [
    "name" : "JavaNode",
    "parameters" : [
      "class" : new StringDataType(),
      "method" : new StringDataType(),
    ],
    "displayName" : "Java Node",
    "icon" : "icons/java.gif"
  ]
]

// located in: project/src/main/resources/META-INF/drools.rulebase.conf
drools.workDefinitions = JavaNodeDefinition.wid WorkDefinitions.conf

// icon for java.gif located in:
// project/src/main/resources/icons/java.gif

Once we've created our Notification work item definition (see the sections above), we can then create a custom implementation of a work item handler that will contain the logic to send the notification.

In order to execute our Notification work items, we first create a NotificationWorkItemHandler that implements the WorkItemHandler interface:

This WorkItemHandler sends a notification as an email and then notifies the WorkItemManager that the work item has been completed.

Note that not all work items can be completed directly. In cases where executing a work item takes some time, execution can continue asynchronously and the work item manager can be notified later.

In these situations, it might also be possible that a work item is aborted before it has been completed. The WorkItemHandler.abortWorkItem(...) method can be used to specify how to abort such work items.

WorkItemHandler instances need to be registered with the WorkItemManager in order to be used. In this case, we need to register an instance of our NotificationWorkItemHandler in order to use it with our process containing a Notification work item. We can do that like this:


StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ksession.getWorkItemManager().registerWorkItemHandler(
  "Notification",                                          (1)
  new NotificationWorkItemHandler()                        (2)
);
  

1

This is the drools name of the <task> (or other task type) node. See below for an example.

2

This is the instance of our custom work item handler instance!

If we were to look at the BPMN2 syntax for our process with the Notification process, we would see something like the following example. Note the use of the tns:taskName attribute in the <task> node. This is necessary for the WorkItemManager to be able to see which WorkItemHandler instance should be used with which task or work item.


<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
...
             xmlns:tns="http://www.jboss.org/drools">

...

  <process isExecutable="true" id="myCustomProcess" name="Domain-Specific Process" >

...

    <task id="_5" name="Notification Task" tns:taskName="Notification" >

...

A lot of these domain-specific services are generic, and can be reused by a lot of different users. Think for example about integration with Twitter, doing file system operations or sending email. Once such a domain-specific service has been created, you might want to make it available to other users so they can easily import and start using it.

A service repository allows you to import services by browsing the repository looking for services you might need and importing these services into your workspace. These will then automatically be added to your palette and you can start using them in your processes. You can also import additional artefacts like for example an icon, any dependencies you might need, a default handler that will be used to execute the service (although you're always free to override the default, for example for testing), etc.

To browse the repository, open the wizard to import services, point it to the right location (this could be to a directory in your file system but also a public or private URL) and select the services you would like to import. For example, in Eclipse, right-click your project that contains your processes and select "Configure ... -> Import jBPM services ...". This will open up a repository browser. In the URL field, fill in the URL of your repository (see below for the URL of the public jBPM repository that hosts some common service implementations out-of-the-box), or use the "..." button to browse to a folder on your file system. Click the Get button to retrieve the contents of that repository.

Select the service you would like to import and then click the Import button. Note that the Eclipse wizard allows you to define whether you would like to automatically configure the service (so it shows up in the palette of your processes), whether you would also like to download any dependencies that might be needed for executing the service and/or whether you would like to automatically register the default handler, so make sure to mark the right checkboxes before importing your service (if you are unsure what to do, leaving all check boxes marked is probably best).

After importing your service, (re)open your process diagram and the new service should show up in your palette and you can start using it in your process. Note that most services also include documentation on how to use them (e.g. what the different input and output parameters are) when you select them browsing the service repository.

Click on the image below to see a screencast where we import the Twitter service in a new jBPM project and create a simple process with it that sends an actual tweet. Note that you need the necessary Twitter keys and secrets to be able to programmatically send tweets to your Twitter account. How to create these is explained here, but once you have these, you can just drop them in your project using a simple configuration file.

Figure 21.1. 


We are building a public service repository that contains predefined services that people can use out-of-the-box if they want to:

http://docs.jboss.org/jbpm/v6.0/repository/

This repository contains some integrations for common services like Twitter integration or file system operations that you can import. Simply point the import wizard to this URL to start browsing the repository.

If you have an implementation of a common service that you would like to contribute to the community, do not hesitate to contact someone from the development team. We are always looking for contributions to extend our repository.

You can set up your own service repository and add your own services by creating a configuration file that contains the necessary information (this is an extended version of the normal work definition configuration file as described earlier in this chapter) and putting the necessary files (like an icon, dependencies, documentation, etc.) in the right folders.

The extended configuration file contains the normal properties (like name, parameters, results and icon), with some additional ones. For example, the following extended configuration file describes the Twitter integration service (as shown in the screencast above):


import org.drools.core.process.core.datatype.impl.type.StringDataType;
[
  [
    "name" : "Twitter",
    "description" : "Send a Twitter message",
    "parameters" : [
      "Message" : new StringDataType()
    ],
    "displayName" : "Twitter",
    "eclipse:customEditor" : "org.drools.eclipse.flow.common.editor.editpart.work.SampleCustomEditor",
    "icon" : "twitter.gif",
    "category" : "Communication",
    "defaultHandler" : "org.jbpm.process.workitem.twitter.TwitterHandler",
    "documentation" : "index.html",
    "dependencies" : [
      "file:./lib/jbpm-twitter.jar",
      "file:./lib/twitter4j-core-2.2.2.jar"
    ]
  ]
]

The root of your repository should also contain an index.conf file that references all the folders that should be processed when searching for services on the repository. Each of those folders should then contain:

You can create your own hierarchical structure, because if one of those folders also contains an index.conf file, that will be used to scan additional sub-folders. Note that the hierarchical structure of the repository is not shown when browsing the repository using the import wizard, as the category property in the configuration file is used for that.

The Workbench provides two ways of installing services from the user defined service repositories:

Technical exceptions happen when a technical component of a business process acts in an unexpected way. When using Java based systems, this often results in a literal Java Exception being thrown by the system.

Technical components used in a process can fail in a way that can not be described using BPMN2. In this case, it's important to handle these exceptions in expected ways.

The following types of code might throw exceptions:

However, those are somewhat abstract definitions. We can narrow down the places at which an exception might be thrown. Technical exceptions can occur at the following points:

It is much easier to ensure correct exception handling for <task> and other task-type nodes that use WorkItemHandler implementations, than for code executed directly in a <scriptTask>.

Exceptions thrown by <scriptTask> can cause the process to fail in an unrecoverable fashion. While there are certain things that you can do to contain the damage, a process that has failed in this way can not be restarted or otherwise recovered. This also applies for other nodes in a process definition that contain script code in the node definition, such as the <onEntry> and <onExit> elements.

When jBPM engine does throw an exception generated by the code in a <scriptTask> the exception thrown is a special Java exception called the WorkflowRuntimeException that contains information about the process.

WorkItemHandler classes are used when your process interacts with other technical systems. For an introduction to them and how to use them in processes, please see the Domain-specific Processes chapter.

While you can build exception handling into your own WorkItemhandler implementations, there are also two handler decorator classes that you can use to wrap a WorkItemhandler implementation.

These two wrapper classes include logic that is executed when an exception is thrown during the execution (or abortion) of a work item.


While the two classes described above should cover most cases involving exception handling, a Java developer with some experience with jBPM should be able to create a WorkItemHandler that executes custom code upon an exception.

If you do decide to write a custom WorkItemHandler that includes exception handling logic, keep the following checklist in mind:

  1. Are you catching all possible exceptions that you want to (and no more, or less)?

  2. Are you making sure to either complete or abort the work item after an exception has been caught? If not, are there mechanisms to retry the process later? Or are incomplete process instances acceptable?

  3. >

    What other actions should be taken when an exception is caught? Do you want to simply log the exception, or is it also important to interact with other technical systems? Do you want to trigger a (BPMN2) subprocess that will handle the exception?

Important

When you use the WorkItemManager to signal that the work item has been completed or aborted, make sure to do that after you've sent any signals to the process instance. Depending on how you've defined your process, calling WorkItemManager.completeWorkItem(...) or WorkItemManager.abortWorkItem(...) will trigger the completion of the process instance. This is because the these methods trigger the jBPM process engine to continue the process flow.

In the next section, we'll describe an example that uses the SignallingTaskHandlerDecorator to signal an event subprocess when a work item handler throws an exception.

We'll go through one example in this section, and then look quickly at how you can change it to get the behavior you want. The example involves an <error> event that's caught by an (Error) Event SubProcess.

When an Error Event is thrown, the containing process will be interrupted. This means that after the process flow attached to the error event has executed, the following will happen:

The example we'll go through contains an <error>, but at the end of the section, we'll show how you can change the process to use a <signal> instead.

Let's look at the BPMN2 process definition first. Besides the definition of the process, the BPMN2 elements defined before the actual process definition are also important. Here's an image of the BPMN2 process that we'll be using in the example:


The BPMN2 process fragment below is part of the process shown above, and contains some notes on the different BPMN2 elements.

Note

If you're viewing this on a web browser, you may need to widen or narrow your browser window in order to see the "callout" or note numbers on the right hand side of the code.

  <itemDefinition id="_stringItem" structureRef="java.lang.(1)String"/>
  <message id="_message" itemRef="_stringItem"/>           (2)

  <interface id="_serviceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
    <operation id="_serviceOperation" name="throwException">
      <inMessageRef>_message</inMessageRef>                (2)
    </operation>
  </interface>

  <error id="_exception" errorCode="code" structureRef="_ex(3)ceptionItem"/>

  <itemDefinition id="_exceptionItem" structureRef="org.kie(4).api.runtime.process.WorkItem"/>
  <message id="_exceptionMessage" itemRef="_exceptionItem"/(4)>

  <interface id="_handlingServiceInterface" name="org.jbpm.examples.exceptions.service.ExceptionService">
    <operation id="_handlingServiceOperation" name="handleException">
      <inMessageRef>_exceptionMessage</inMessageRef>       (4)
    </operation>
  </interface>

  <process id="ProcessWithExceptionHandlingError" name="Service Process" isExecutable="true" processType="Private">
    <!-- properties -->
    <property id="serviceInputItem" itemSubjectRef="_string(1)Item"/>
    <property id="exceptionInputItem" itemSubjectRef="_exce(4)ptionItem"/>

    <!-- main process -->
    <startEvent id="_1" name="Start" />
    <serviceTask id="_2" name="Throw Exception" implementation="Other" operationRef="_serviceOperation">

    <!-- rest of the serviceTask element and process definition... -->

    <subProcess id="_X" name="Exception Handler" triggeredByEvent="true" >
      <startEvent id="_X-1" name="subStart">
        <dataOutput id="_X-1_Output" name="event"/>
        <dataOutputAssociation>
          <sourceRef>_X-1_Output</sourceRef>
          <targetRef>exceptionInputItem</targetRef>        (4)
        </dataOutputAssociation>
        <errorEventDefinition id="_X-1_ED_1" errorRef="_exc(3)eption" />
      </startEvent>

      <!-- rest of the subprocess definition... -->

    </subProcess>

  </process>
  

1

This <itemDefinition> element defines a data structure that we then use in the serviceInputItem property in the process.

2

This <message> element (1rst reference) defines a message that has a String as its content (as defined by the <itemDefintion> element on line above). The <interface> element below it refers to it (2nd reference) in order to define what type of content the service (defined by the <interface>) expects.

3

This <error> element (1rst reference) defines an error for use later in the process: an Event SubProcess is defined that is triggered by this error (2nd reference). The content of the error is defined by the <itemDefintion> element defined below the <error> element.

4

This <itemDefintion> element (1rst reference) defines an item that contains a WorkItem instance. The <message> element (2nd reference) then defines a message that uses this item definition to define its content. The <interface> element below that refers to the <message> definition (3rd reference) in order to define the type of content that the service expects.

In the process itself, a <property> element (4th reference) is defined as having the content defined by the initial <itemDefintion>. This is helpful because it means that the Event SubProcess can then store the error it receives in that property (5th reference).

Caution

When you're using a <serviceTask> to call a Java class, make sure to double check the class name in your BPMN2 definition! A small typo there can cost you time later when you're trying to figure out what went wrong.

Now that BPMN2 process definition is (hopefully) a little clearer, we can look at how to set up jBPM to take advantage of the above BPMN2.

In the (BPMN2) process definition above, we define two different <serviceTask> activities. The org.jbpm.bpmn2.handler.ServiceTaskHandler class is the default task handler class used for <serviceTask> tasks. If you don't specify a WorkItemHandler implementation for a <serviceTask>, the ServiceTaskHandler class will be used.

In the code below, you'll see that we actually wrap or decorate the ServiceTaskHandler class with a SignallingTaskHandlerDecorator instance. We do this in order to define the what happens when the ServiceTaskHandler throws an exception.

In this case, the ServiceTaskHandler will throw an exception because it's configured to call the ExceptionService.throwException method, which throws an exception. (See the _handlingServiceInterface <interface> element in the BPMN2.)

In the code below, we also configure which (error) event is sent to the process instance by the SignallingTaskHandlerDecorator instance. The SignallingTaskHandlerDecorator does this when an exception is thrown in a task. In this case, since we've defined an <error> with the error code code in the BPMN2, we set the signal to Error-code.


import java.util.HashMap;
import java.util.Map;

import org.jbpm.bpmn2.handler.ServiceTaskHandler;
import org.jbpm.bpmn2.handler.SignallingTaskHandlerDecorator;
import org.jbpm.examples.exceptions.service.ExceptionService;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;

public class ExceptionHandlingErrorExample {

    public static final void main(String[] args) {
        runExample();
    }

    public static ProcessInstance runExample() {
        KieSession ksession = createKieSession();

        String eventType = "Error-code";                   (1)
        SignallingTaskHandlerDecorator signallingTaskWrappe(2)r 
            = new SignallingTaskHandlerDecorator(ServiceTaskHandler.class, eventType);
        signallingTaskWrapper.setWorkItemExceptionParameter(3)Name(ExceptionService.exceptionParameterName);
        ksession.getWorkItemManager().registerWorkItemHandler("Service Task", signallingTaskWrapper);

        Map<String, Object> params = new HashMap<String, Object>();
        params.put("serviceInputItem", "Input to Original Service");
        ProcessInstance processInstance = ksession.startProcess("ProcessWithExceptionHandlingError", params);
        
        return processInstance;
    }

    private static KieSession createKieSession() {
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ExceptionHandlingWithError.bpmn2"), ResourceType.BPMN2);
        KieBase kbase = kbuilder.newKnowledgeBase();
        return kbase.newKieSession();
    }
  

1

Here we define the name of the event that will be sent to the process instance if the wrapped WorkItemHandler implementation throws an exception. The eventType string is used when instantiating the SignallingTaskHandlerDecorator class.

2

Then we construct an instance of the SignallingTaskHandlerDecorator class. In this case, we simply give it the class name of the WorkItemHandler implementation class to instantiate, but another constructor is available that we can pass an instance of a WorkItemHandler implementation to (necessary if the WorkItemHandler implementation does not have a no-argument constructor).

3

When an exception is thrown by the wrapped WorkItemHandler, the SignallingTaskHandlerDecorator saves it as a parameter in the WorkItem instance with a parameter name that we configure the SignallingTaskHandlerDecorator to give it (see the code below for the ExceptionService).

In the example above, the thrown Error Event interrupts the process: no other flows or activities are executed once the Error Event has been thrown.

However, when a Signal Event is processed, the process will continue after the Signal Event SubProcess (or whatever other activities that the Signal Event triggers) has been executed. Furthermore, this implies that the the process will not end up in an aborted state, unlike a process that throws an Error Event.

In the process above, we use the <error> element in order to be able to use an Error Event:

  <error id="_exception" errorCode="code" structureRef="_exceptionItem"/>

When we want to use a Signal Event instead, we remove that line and use a <signal> element:

   <signal id="exception-signal" structureRef="_exceptionItem"/> 

However, we must also change all references to the "_exception" <error> so that they now refer to the "exception-signal" <signal>.

That means that the <errorEventDefintion> element in the <startEvent>,

   <errorEventDefinition id="_X-1_ED_1" errorRef="_exception" /> 

must be changed to a <signalEventDefintion> which would like like this:

   <signalEventDefinition id="_X-1_ED_1" signalRef="exception-signal"/> 

In short, we have to make the following changes to the <startEvent> in the Event SubProcess:

In this section, we'll briefly describe what's possible when dealing with <scriptTask> nodes that throw exceptions, and then quickly go through an example (also available in the jbpm-examples module) that illustrates this.

If you're reading this, then you probably already have a problem: you're either expecting to run into this problem because there are scripts in your process definition that might throw an exception, or you're already running a process instance with scripts that are causing a problem.

Unfortunately, if you're running into this problem, then there is not much you can do. The only thing that you can do is retrieve more information about exactly what's causing the problem. Luckily, when a <scriptTask> node causes an exception, the exception is then wrapped in a WorkflowRuntimeException.

What type of information is available? The WorkflowRuntimeException instance will contain the information outlined in the following table. All of the fields listed are available via the normal get* methods.


The following code illustrates how to extract extra information from a process instance that throws a WorkflowRuntimeException exception instance.


import org.jbpm.workflow.instance.WorkflowRuntimeException;
import org.kie.api.KieBase;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.process.ProcessInstance;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.io.ResourceFactory;

public class ScriptTaskExceptionExample {

    public static final void main(String[] args) {
        runExample();
    }

    public static void runExample() {
        KieSession ksession = createKieSession();
        Map<String, Object> params = new HashMap<String, Object>();
        String varName = "var1";
        params.put( varName , "valueOne" );
        try { 
            ProcessInstance processInstance = ksession.startProcess("ExceptionScriptTask", params);
        } catch( WorkflowRuntimeException wfre ) { 
            String msg = "An exception happened in "
                    + "process instance [" + wfre.getProcessInstanceId()
                    + "] of process [" + wfre.getProcessId()
                    + "] in node [id: " + wfre.getNodeId() 
                    + ", name: " + wfre.getNodeName()
                    + "] and variable " + varName + " had the value [" + wfre.getVariables().get(varName)
                    + "]";
            System.out.println(msg);
        }
    }
    
    private static KieSession createKieSession() {
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add(ResourceFactory.newClassPathResource("exceptions/ScriptTaskException.bpmn2"), ResourceType.BPMN2);
        KieBase kbase = kbuilder.newKnowledgeBase();
        return kbase.newKieSession();
    }
 
}

Business Exceptions are exceptions that are designed and managed in the BPMN2 specification of a business process. In other words, Business Exceptions are exceptions which happen at the process or workflow level, and are not related to the technical components.

Many of the elements in BPMN2 related to Business Exceptions are related to Compensation and Business Transactions. Compensation, in particular, is complexer than many other parts of the BPMN2 specification.

Full support for compensation and business transactions is expected with the release of jBPM 6.1 or 6.2. Once that has been implemented, this section will contain more information about using those BPMN2 features with jBPM.

The following attempts to briefly describe Compensation and Business Transaction related elements in BPMN2. For more complete information about these elements and their uses, see the BPMN2 specification, Bruce Silver's book BPMN Method and Style or any of the other available books about the use of BPMN2.

Table 22.3. BPMN2 Exception Handling Elements

BPMN2 Element typesDescription
Errors

Error Events can be used to signal when a process has encountered an unexpected situation: signalling an error is often called throwing an error.

Boundary Error Events in a different part of the process can then be used to catch the error and initiate a sequence of activities to handle the exception.

Errors themselves can be extended with extra information that is passed from the throwing to catching event. This is done with the use of an Item Definition.

Compensation

Exception handling activities associated with the normal activities in a Business Transaction are triggered by Compensation Events.

There are 3 types of compensation events: Intermediate (a.k.a. Boundary) (catch) events, Start (catch) events, and Intermediate or End (throw) events.

Compensation Boundary (catch) events may only be attached to activities (e.g. tasks) that could cause an exception. These Boundary events are then associated (not linked!) with a Task that will be executed if the Boundary event catches a (thrown) Compensation signal.

Start (catch) events are used when defining an Compensation Event SubProcess, which requires them in order to be able to catch a (thrown) Compensation signal.

Compensation Intermediate and End events are used in order to throw Compensation Events. These events often follow decision nodes that determine whether the workflow executed up to that point has succeeded. If not, the path including the Intermediate or End Event is chosen in order to trigger Compensatoin for the activities that did not succeed.


BPMN2 contains a number of constructs to model exceptions in business processes. There are several advantages to doing exception handling at the business process level (as opposed to handling it with code):

  • Transparency
    • Being able to quickly see what happens in exceptional situations means that the results and performance of a process is more easily monitored and measured.
    • It also increases how easily a process can be implemented as well as how maintainable a process definition is.
  • Business Logic Isolation
    • Again, the idea behind using a business process is to isolate the business logic from the technical code. This simplifies the complexity of the system and increases how quickly you can create new business processes and change existing ones.
    • Implementing exception handling at a technical level often takes more time because it's often complexer and specific to a system.

Case management and its relation to BPM is a hot topic nowadays. There definitely seems to be a growing need amongst end users for more flexible and adaptive business processes, without ending up with overly complex solutions. Everyone seems to agree that using a process-centric approach only in many cases leads to complex solutions that are hard to maintain. The "knowledge workers" no longer want to be locked into rigid processes but wants to have the power and flexibility to regain more control over the process themselves.

The term case management is often used in that context. Without trying to give a precise definition of what it might or might not mean, as this has been a hot topic for discussion, it refers to the basic idea that many applications in the real world cannot really be described completely from start to finish (including all possible paths, deviations, exceptions, etc.). Case management takes a different approach: instead of trying to model what should happen from start to finish, let's give the end user the flexibility to decide what should happen at runtime. In its most extreme form for example, case management doesn't even require any process definition at all. Whenever a new case comes in, the end user can decide what to do next based on all the case data.

A typical example can be found in healthcare (clinical decision support to be more precise), where care plans can be used to describe how patients should be treated in specific circumstances, but people like general practitioners still need to have the flexibility to add additional steps and deviate from the proposed plan, as each case is unique. And there are similar examples in claim management, help desk support, etc.

So, should we just throw away our BPM system then? No! Even at its most extreme form (where we don't model any process up front), you still need a lot of the other features a BPM system (usually) provides: there still is a clear need for audit logs, monitoring, coordinating various services, human interaction (e.g. using task forms), analysis, etc. And, more importantly, many cases are somewhere in between, or might even evolve from case management to more structured business process over time (when we for example try to extract common approaches from many cases). If we can offer flexibility as part of our processes, can't we let the users decide how and where they would like to apply it?

Let me give you two examples that show how you can add more and more flexibility to your processes. The first example shows a care plan that shows the tasks that should be performed when a patient has high blood pressure. While a large part of the process is still well-structured, the general practitioner can decide himself which tasks should be performed as part of the sub-process. And he also has the ability to add new tasks during that period, tasks that were not defined as part of the process, or repeat tasks multiple times, etc. The process uses an ad-hoc sub-process to model this kind of flexibility, possibly augmented with rules or event processing to help in deciding which fragments to execute.


The second example actually goes a lot further than that. In this example, an internet provider could define how cases about internet connectivity problems will be handled by the internet provider. There are a number of actions the case worker can select from, but those are simply small process fragments. The case worker is responsible for selecting what to do next and can even add new tasks dynamically. As you can see, there is not process from start to finish anymore, but the user is responsible for selecting which process fragments to execute.


And in its most extreme form, we even allow you to create case instances without a process definition, where what needs to be performed is selected purely at runtime. This however doesn't mean you can't figure out anymore what 's actually happening. For example, meetings can be very ad hoc and dynamic, but we usually want a log of what was actually discussed. The following screenshot shows how our regular audit view can still be used in this case, and the end user could then for example get a lot more info about what actually happened by looking at the data associated with each of those steps. And maybe, over time, we can even automate part of that by using a semi-structured process.


In the following text, we will refer to two types of "multi-threading": logical and technical. Technical multi-threading is what happens when multiple threads or processes are started on a computer, for example by a Java or C program. Logical multi-threading is what we see in a BPM process after the process reaches a parallel gateway, for example. From a functional standpoint, the original process will then split into two processes that are executed in a parallel fashion.

Of course, the jBPM engine supports logical multi-threading: for example, processes that include a parallel gateway. We've chosen to implement logical multi-threading using one thread: a jBPM process that includes logical multi-threading will only be executed in one technical thread. The main reason for doing this is that multiple (technical) threads need to be be able to communicate state information with each other if they are working on the same process. This requirement brings with it a number of complications. While it might seem that multi-threading would bring performance benefits with it, the extra logic needed to make sure the different threads work together well means that this is not guaranteed. There is also the extra overhead incurred because we need to avoid race conditions and deadlocks.

In general, the jBPM engine executes actions in serial. For example, when the engine encounters a script task in a process, it will synchronously execute that script and wait for it to complete before continuing execution. Similarly, if a process encounters a parallel gateway, it will sequentially trigger each of the outgoing branches, one after the other. This is possible since execution is almost always instantaneous, meaning that it is extremely fast and produces almost no overhead. As a result, the user will usually not even notice this. Similarly, action scripts in a process are also synchronously executed, and the engine will wait for them to finish before continuing the process. For example, doing a Thread.sleep(...) as part of a script will not make the engine continue execution elsewhere but will block the engine thread during that period.

The same principle applies to service tasks. When a service task is reached in a process, the engine will also invoke the handler of this service synchronously. The engine will wait for the completeWorkItem(...) method to return before continuing execution. It is important that your service handler executes your service asynchronously if its execution is not instantaneous.

An example of this would be a service task that invokes an external service. Since the delay in invoking this service remotely and waiting for the results might be too long, it might be a good idea to invoke this service asynchronously. This means that the handler will only invoke the service and will notify the engine later when the results are available. In the mean time, the process engine then continues execution of the process.

Human tasks are a typical example of a service that needs to be invoked asynchronously, as we don't want the engine to wait until a human actor has responded to the request. The human task handler will only create a new task (on the task list of the assigned actor) when the human task node is triggered. The engine will then be able to continue execution on the rest of the process (if necessary) and the handler will notify the engine asynchronously when the user has completed the task.

The simplest way to run multiple processes is to run them all using one knowledge session. However, there are cases in which it's necessary to run multiple processes in different knowledge sessions, even in different (technical) threads. Both are supported by jBPM.

When we add persistence (using a database, for example) to a situation in which we have multiple knowledge sessions (and processes), there is a guideline that users should be aware of. The following paragraphs explain why this guideline is important to follow.

For example, a user could have a situation in which there are 2 (or more) threads running, each with its own knowledge session instance. On each thread, jBPM processes are being started using the local knowledge session instance.

In this use case, a race condition exists in which both thread A and thread B will have coincidentally simultaneously finished a process. At this point, because persistence is being used, both thread A and B will be committing changes to the database. If row-level locks are not possible, then the following situation can occur:

This is a deadlock situation which the database and application will not be able to solve. However, if row-level locks are possible (and enabled!!) in the database (and tables used), then this situation will not occur.

In version 6, jBPM introduces new component called jbpm executor which provides quite advanced features for asynchronous execution. It delivers generic environment for background execution of commands. Commands are nothing more than business logic encapsulated within simple interface. It does not have any process runtime related information, that means no need to complete work items, or anything of that sort. It purely focuses on the business logic to be executed. It receives data via CommandContext and returns results of the execution with ExecutionResults.

Before looking into details on jBPM support for asynchronous execution let's look at what are the common requirements for such execution:

When confronting these requirements with the "simple async handler" (executed as separate thread) you can directly notice that all of these would need to be implemented all over again by different systems. Due to that a common, generic component has been provided out of the box to simplify and empower usage.

jBPM executor operates on commands, which are essential piece of code that is going to be executed as background job.

/**
 * Executor's Command are dedicated to contain purely business logic that should be executed. 
 * It should not have any reference to underlying process engine and should not be concerned
 * with any process runtime related logic such us completing work item, sending signals, etc.
 * <br/>
 * Information that are taken from process will be delivered as part of data instance of 
 * <code>CommandContext</code>. Depending on the execution context that data can vary but 
 * in most of the cases following will be given:
 * <ul>
 *  <li></li>
 *  <li>businessKey - usually unique identifier of the caller</li>
 *  <li>callbacks - FQCN of the <code>CommandCollback</code> that shall be used on command completion</li>
 * </ul>
 * When executed as part of the process (work item handler) additional data can be expected:
 * <ul>
 *  <li>workItem - the actual work item that is being executed with all its parameters</li>
 *  <li>processInstanceId - id of the process instance that triggered this work</li>
 *  <li>deploymentId - if given process instance is part of an active deployment</li>
 * </ul>
 * Important note about implementations is that it shall always be possible to be initialized with default constructor
 * as executor service is an async component so it will initialize the command on demand using reflection.
 * In case there is a heavy logic on initialization it should be placed in another service implementation that 
 * can be looked up from within command.
 */
public interface Command {
    
    /**
     * Executed this command's logic.
     * @param ctx - contextual data given by the executor service
     * @return returns any results in case of successful execution
     * @throws Exception in case execution failed and shall be retried if possible
     */
    public ExecutionResults execute(CommandContext ctx) throws Exception;
}

Looking at the interface above, there is no specific integration with the jBPM runtime engine, it's decoupled from it to put main focus on the actual logic that shall be executed as part of that command rather to worry about integration with process engine. This design promotes reuse of already existing logic by simply wrapping it with Command implementation.

Input data is transferred from process engine to command via CommandContext. It acts purely as data transfer object and puts single requirement on the data it holds - all objects must be serializable.

/**
 * Data holder for any contextual data that shall be given to the command upon execution.
 * Important note that every object that is added to the data container must be serializable 
 * meaning it must implement <code>java.io.Seriazliable</code>
 *
 */
public class CommandContext implements Serializable {

    private static final long serialVersionUID = -1440017934399413860L;
    private Map<String, Object> data;

    public CommandContext() {
        data  = new HashMap<String, Object>();
    }

    public CommandContext(Map<String, Object> data) {
        this.data = data;
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

    public Map<String, Object> getData() {
        return data;
    }

    public Object getData(String key) {
        return data.get(key);
    }

    public void setData(String key, Object value) {
        data.put(key, value);
    }

    public Set<String> keySet() {
        return data.keySet();
    }

    @Override
    public String toString() {
        return "CommandContext{" + "data=" + data + '}';
    }
}

Next outcome is provided to process engine via ExecutionResults, which is very similar in nature to the CommandContext and acts as data transfer object.

/**
 * Data holder for command's result data. Whatever command produces should be placed in
 * this results so they can be later on referenced by name by the requester - e.g. process instance.
 *
 */
public class ExecutionResults implements Serializable {

    private static final long serialVersionUID = -1738336024526084091L;
    private Map<String, Object> data = new HashMap<String, Object>();

    public ExecutionResults() {
    }

    public void setData(Map<String, Object> data) {
        this.data = data;
    }

    public Map<String, Object> getData() {
        return data;
    }

    public Object getData(String key) {
        return data.get(key);
    }

    public void setData(String key, Object value) {
        data.put(key, value);
    }

    public Set<String> keySet() {
        return data.keySet();
    }

    @Override
    public String toString() {
        return "ExecutionResults{" + "data=" + data + '}';
    }
    
    
}

Executor covers all requirements listed above and provides user interface as part of jbpm console and kie workbench (kie-wb) applications.


Above screenshot illustrates history view of executor's job queue. As can be seen on it there are several options available:

  • view details of the job

  • cancel given job

  • create new job

jBPM (again in version 6) provides an out of the box async work item handler that is backed by the jbpm executor. So by default all features that executor delivers will be available for background execution within process instance. AsyncWorkItemHandler can be configured in two ways:

Option 1 is by default configured for jbpm console and kie-wb web applications and is registered under async name in every ksession that is bootstrapped within the applications. So whenever there is a need to execute some logic asynchronously following needs to be done at modeling time (using jbpm web designer):

Next follow regular way to complete process modeling. Note that all data inputs will be transferred to executor so they must be serializable.

Second option allows to register different instances of AsyncWorkItemHandler for different work items. Since it's registered for dedicated work item most likely the command will be dedicated to that work item as well. If so CommandClass can be specified on registration time instead of requiring it to be set as work item parameters. To register such handlers for jbpm console or kie-wb additional class is required to inform what shall be registered. A CDI bean that implements WorkItemHandlerProducer interface needs to be provided and placed on the application classpath so CDI container will be able to find it. Then at modeling time TaskName property needs to be aligned with those used at registration time.

The following features were added to jBPM 6.5

The following features were added to jBPM 6.4

The general look and feel in the entire workbench has been updated to adopt PatternFly. The update brings a cleaner, lightweight and more consistent user experience throughout every screen. Allowing users focus on the data and the tasks by removing all uncessary visual elements. Interactions and behaviors remain mostly unchanged, limiting the scope of this change to visual updates.


The following features were added to jBPM 6.3.

The core process engine has always contained the flexibility to model adaptive and flexible processes. These kinds of features are typically also required in the context of case management. To simplify picking up some of these more advanced features, we created a (wrapper) API that exposes some of these features in a simple API. Note that this API simply relies on other existing features / API and can easily be extended. The API and implementation is added as part of a new jbpm-case-mgmt module.

The remote REST API for accessing the workbench received the following extensions:

The following features were added to the jBPM core on top of 6.1.

  1. Lazy initialization of runtime engine components by RuntimeManager to make runtime engine creation lightweight

    RuntimeEngine has been enhanced to lazy initialize its components (KieSession, TaskService, AuditService) to improve overall performance of retriveing RuntimeEngine instances from RuntimeManager.

  2. Life cycle management for work item handlers and event listeners

    Handler and listeners can implement additional interface to be managed by runtime engine, see work item handler life cycle management for more details.

  3. Deployments are now by default stored in data base (as deployment descriptors) to servive server restarts

    Prior to verion 6.2 deployments that were handled by DeploymentService implementation were not persisted so they required to be handled separately - in case of kie-workbench they were stored inside system.git repo. With version 6.2 deployment service will persist that information directly into db which will make it easier in many cases including clustering as it will not require VFS clustering (Zookeeper and Helix) setup.

  4. Extension to deployment descriptor to specify classes (by FQCN) that should be added to JAXB context for remote interfaces interaction

    Deployment descriptor accept new set of elements

    <remoteable-classes>
       ...
       <remotable-class>org.jbpm.test.CustomClass</remotable-class>
       ...
    </remoteable-classes>

  5. Classpath scanning for classes to be included in JAXB context for remote interfaces interaction

    Classes annotated with javax.xml.bind.annotation.XmlRootElement and org.kie.api.remote.Remotable will be automatically added to JAXB context of given deployment as soon as they are defined as project dependency. At the same time all classes included in project itself are also added to deployment's JAXB context.

  6. jbpm executor has been enhanced to provide support for:

    • requeue failed jobs so they can be executed once the error that caused them to is resolved.

    • reoccuring jobs that allows single definition to be repeatedly invoked based on time intervals, e.g. daily jobs to clean up history log tables. See this article for details and example.

  7. CRON support for intermediate and boundary timer events

  8. Enhanced support for multi instance activities to support completion condition as MVEL expression

jBPM 6.1 comes with a ton of smaller improvements and bug fixes (done over the last few months on top of 6.0.1.Final), and also includes some important new features, adding to the foundation delivered as part of jBPM 6.0.

The execution engine itself has (mostly) remained the same, although we've done various improvements in the following areas:

The task service has been refactored significantly as well, and the TaskService APIs have been moved to the public kie-api. Although the TaskService interfaces themselves haven't changed a lot, the internal implementation has been simplified. Auditing for the task-related operations (similar to the runtime engine auditing) has been added.

By default, a local task service will always be used by a ksession to perform various task-related operations (creating a task, being notified when a task is completed). Setting up a remote singleton task service and connecting multiple ksessions to this (using Mina or HornetQ) as was possible in jBPM5 is no longer possible, as it introduces more challenges that it brings advantages. Since the jBPM execution service now also provides a remote API for all task-related operations, we believe this setup is no longer necessary, and has been replaced by the use of a local task service in all use cases.

The workbench has had a big overhaul using a new base project called UberFire. UberFire is inspired by Eclipse and provides a clean, extensible and flexible framework for the workbench. The end result is not only a richer experience for our end users, but we can now develop more rapidly with a clean component based architecture. If you like he Workbench experience you can use UberFire today to build your own web based dashboard and console efforts.

As well as the move to a UberFire the other biggest change is the move from JCR to Git; there is an utility project to help with migration. Git is the most scalable and powerful source repository bar none. JGit provides a solid OSS implementation for Git. This addresses the continued performance problems with the various JCR implementations, which would slow down once the number of files and number of versions become too high. There has been a big "low tech" drive, to remove complexity. Everything is now stored as a file, including meta data. The database is only there to provide fast indexing and search. So importing and exporting is all standard Git and external sites, like GitHub, can be used to exchange repositories.

In 5.x developers would work with their own source repository and then push JCR, via the team provider. This team provider was not full featured and not available outside Eclipse. Git enables our repository to work any existing Git tool or team provider. While not yet supported in the UI, this will be added over time, it is possible to connect to the repo and tag and branch and restore things.


The Guvnor brand leaked too much from its intended role; such as the authoring metaphors, like Decision Tables, being considered Guvnor components instead of Drools components. This wasn't helped by the monolithic projects structure used in 5.x for Guvnor. In 6.0 Guvnor 's focus has been narrowed to encapsulates the set of UberFire plugins that provide the basis for building a web based IDE. Such as Maven integration for building and deploying, management of Maven repositories and activity notifications via inboxes. Drools and jBPM build workbench distributions using Uberfire as the base and including a set of plugins, such as Guvnor, along with their own plugins for things like decision tables, guided editors, BPMN2 designer, human tasks.

The "Model Structure" diagram outlines the new project anatomy. The Drools workbench is called KIE-Drools-WB. KIE-WB is the uber workbench that combines all the Guvnor, Drools and jBPM plugins. The jBPM-WB is ghosted out, as it doesn't actually exist, being made redundant by KIE-WB.


Important

KIE Drools Workbench and KIE Workbench share a common set of components for generic workbench functionality such as Project navigation, Project definitions, Maven based Projects, Maven Artifact Repository. These common features are described in more detail throughout this documentation.

The two primary distributions consist of:

  • KIE Drools Workbench

    • Drools Editors, for rules and supporting assets.

    • jBPM Designer, for Rule Flow and supporting assets.

  • KIE Workbench

    • Drools Editors, for rules and supporting assets.

    • jBPM Designer, for BPMN2 and supporting assets.

    • jBPM Console, runtime and Human Task support.

    • jBPM Form Builder.

    • BAM.

Workbench highlights:

  • New flexible Workbench environment, with perspectives and panels.

  • New packaging and build system following KIE API.

    • Maven based projects.

    • Maven Artifact Repository replaces Global Area, with full dependency support.

  • New Data Modeller replaces the declarative Fact Model Editor; bringing authoring of Java classes to the authoring environment. Java classes are packaged into the project and can be used within rules, processes etc and externally in your own applications.

  • Virtual File System replaces JCR with a default Git based implementation.

    • Default Git based implementation supports remote operations.

    • External modifications appear within the Workbench.

  • Incremental Build system showing, near real-time validation results of your project and assets.

The editors themselves are largely unchanged; however of note imports have moved from the package definition to individual editors so you need only import types used for an asset and not the package as a whole.