Introduction to Flowable


1. What is Flowable

Flowable is a lightweight business process engine written in Java. The Flowable process engine can be used to deploy BPMN 2.0 process definitions (the industry XML standard for defining processes), create process instances of these process definitions, perform queries, access running or historical process instances and related data, and more. This section will step through the concepts and apis with an example that you can use in your own development environment.

Flowable can be flexibly added to your application/service/architecture. A Flowable library published as a JAR can be added to an application or service to embed the engine. Distribution as a JAR makes it easy to add Flowable to any Java environment: Java SE; Servlet containers such as Tomcat, Jetty, or Spring; Java EE servers like JBoss or WebSphere, and so on. Alternatively, HTTP calls can also be made using the Flowable REST API. There are also many Flowable applications (Flowable Modeler, Flowable Admin, Flowable IDM, and Flowable Task) that provide directly usable UIs that can use processes and tasks.

What all of the approaches to using Flowable have in common is the core engine. The core engine is a collection of services and provides apis for managing and executing business processes. The following tutorial starts with an introduction to setting up and using the core engine. Subsequent chapters build on the knowledge gained in previous chapters.


2. Flowable and Activiti

Flowable, launched in 2016 based on Activiti.



Second, the start

1. Build a command-line program

The example we’ll build is a simple Holiday Request process:

  • The employee asked for a few days’ leave
  • The manager approves or rejects the application
  • We simulate registering the application to an external system and email the results to the employee


1.1. Create a process engine

Create maven project

Create a Maven project named holiday-Request and add dependencies:

<! <groupId>org.flowable</groupId> <artifactId> Flowable </artifactId> </artifactId> The < version > 6.3.0 < / version > < / dependency > <! --MySQL driver, MySQL database is used here, if using other database, need to introduce the corresponding dependency. --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency>Copy the code


Create database table

  • Create a database flowable_demo.

  • Create a normal Java class: HolidayRequest

/ * * *@AuthorThree points *@Date 2020/5/2
 * @DescriptionCreate table */
public class HolidayRequest {
    public static void main(String[] args) {
        / / 1, to create ProcessEngineConfiguration instance, the instance can adjust the process engine configuration and Settings
        ProcessEngineConfiguration cfg=new StandaloneProcessEngineConfiguration()
                / / 2, usually USES the XML configuration files to create ProcessEngineConfiguration, directly with the method of code here
                //3. Configure database parameters
                .setJdbcUrl("jdbc:mysql://localhost:3306/flowable_demo? useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8&nullCatalogMeansCurrent=true")
                .setJdbcUsername("root")
                .setJdbcPassword("root")
                .setJdbcDriver("com.mysql.jdbc.Driver")
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
        //4. Initialize the ProcessEngine instanceProcessEngine processEngine=cfg.buildProcessEngine(); }}Copy the code


1.1.3, run,

Running this class, you will find that 34 tables have been created in the database flowable_demo:


1.1.4 Description of the created table

Flowable naming rules:

  • ACT_RE_* : ‘RE’ indicates repository. RepositoryService Table operated by the interface. The table with this prefix contains static information, such as process definition, resources of the process (images, rules, etc.).
  • ACT_RU_* : ‘RU’ represents Runtime. This is the runtime table that stores the runtime data on process variables, user tasks, variables, jobs, and so on. Flowable stores only run-time data during the execution of the instance, which is deleted when the process instance ends. This ensures that these runtime tables are small and fast.
  • ACT_ID_* : ‘ID’ indicates identity(organization). These tables contain identifying information, such as users, user groups, and so on.
  • ACT_HI_* : ‘HI’ indicates history. These are the tables that contain historical data such as finished process instances, variables, tasks, and so on.
  • ACT_GE_* : Normal data, data used in all kinds of situations.

34 Table description:

Classification table The name of the table Table shows that
General Data (2) ACT_GE_BYTEARRAY Common process definitions and process resources
ACT_GE_PROPERTY System dependent attribute
Process History (8) ACT_HI_ACTINST Historical process instance
ACT_HI_ATTACHMENT History of the process attachment
ACT_HI_COMMENT Illustrative information from history
ACT_HI_DETAIL Details of the historical process running
ACT_HI_IDENTITYLINK Historical user relationships during process running
ACT_HI_PROCINST Historical process instance
ACT_HI_TASKINST Historical task instance
ACT_HI_VARINST Historical process running variable information
User Group Table (9) ACT_ID_BYTEARRAY Binary data table
ACT_ID_GROUP User group information table
ACT_ID_INFO Details table of user information
ACT_ID_MEMBERSHIP Table of relationships between people and groups
ACT_ID_PRIV Permissions on the table
ACT_ID_PRIV_MAPPING Table of user or group permission relationships
ACT_ID_PROPERTY Property sheet
ACT_ID_TOKEN Table of system login logs
ACT_ID_USER The users table
Process Definition Table (3) ACT_RE_DEPLOYMENT Deployment unit Information
ACT_RE_MODEL Model information
ACT_RE_PROCDEF Deployed process definitions
Run instance table (10) ACT_RU_DEADLETTER_JOB A list of running tasks
ACT_RU_EVENT_SUBSCR Runtime event
ACT_RU_EXECUTION Runtime process execution instance
ACT_RU_HISTORY_JOB History task sheet
ACT_RU_IDENTITYLINK Runtime user relationship information
ACT_RU_JOB Runtime job table
ACT_RU_SUSPENDED_JOB Pause table
ACT_RU_TASK Runtime task list
ACT_RU_TIMER_JOB Schedule schedule
ACT_RU_VARIABLE Run time variable scale
Other tables (2) ACT_EVT_LOG Event log table
ACT_PROCDEF_INFO Process definition information


1.1.4 Log configuration

During the above run, you can also see that the console is reporting an error message because the log is not configured correctly:

Flowable uses SLF4J as its internal logging framework. We use Log4J as an implementation of SLF4J. So add the following dependencies to the pom.xml file:

<dependency> <groupId>org.slf4j</groupId> <artifactId> slf44J -api</artifactId> <version>1.7.21</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.21</version> </dependency>Copy the code


Create log4j configuration file log4j.properties in SRC /resouce:

log4j.rootLogger=DEBUG, CA

log4j.appender.CA=org.apache.log4j.ConsoleAppender
log4j.appender.CA.layout=org.apache.log4j.PatternLayout
log4j.appender.CA.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
Copy the code


Running again, you can see the log prompt about engine startup and creating the database table structure:


1.2 deployment process definition

The process to build is a very simple leave process. The Flowable engine requires processes to be defined in BPMN 2.0 format, which is an XML standard widely accepted by the industry.

In Flowable terms, we refer to this as a process definition. A process definition can start multiple process instances. A process definition can be thought of as a blueprint for repeating the process. In this example, the process definition defines the steps for leave, and a process instance corresponds to a leave request made by an employee.

BPMN 2.0 is stored as XML and contains a visual section: it defines in a standard way how each step type (human task, automatic service invocation, and so on) is rendered and how it connects to each other. Thus, the BPMN 2.0 standard enables technical and business people to communicate business processes in a way that both sides can understand.

The process we will use is defined as:


Description of process definition:

  • Let’s assume that starting the process requires some information, such as the name of the employee, the length of time off, and the description. Of course, these can be modeled separately as the first step in the process. But if you treat them as “input information” to the process, you can guarantee that a process instance will only be created when the actual request is made. Otherwise (commit as the first step in the process), the user might change his mind and cancel before commit, but the process instance has already been created. In some scenarios, this can affect important metrics (such as how many applications have been initiated but not completed), depending on business goals.

  • The circle on the left is called a Start event. This is the starting point for a process instance.

  • The first rectangle is a User task. This is the step in the process that a human user takes. In this example, the manager needs to approve or reject the application.

  • Depending on the manager’s decision, the exclusive gateway (diamond with a fork) routes the process instance to the approval or rejection path.

  • If approved, the application needs to be registered with an external system and followed by another user task to inform the applicant of the manager’s decision. You can also send an email instead.

  • If it is rejected, an email is sent to the employee to notify him.

Generally, such a process definition is established using a visual modeling tool such as Flowable Designer(Eclipse) or Flowable Web Modeler(Web application).

Here we write the XML directly to familiarize ourselves with BPMN 2.0 and its concepts.

Here is the BPMN 2.0 XML corresponding to the flowchart shown above. Only the “process part” is included. If you use a graphical modeling tool, the actual XML file will also contain a “visualization section” that describes graphical information, such as the coordinates of the various elements in the process definition (all graphical information is contained in the BPMNDiagram tag of the XML as a child of the Definitions tag).

Create a holiday-request.bpmn20.xml file in the SRC /main/resources folder:

<? The XML version = "1.0" encoding = "utf-8"? > <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:flowable="http://flowable.org/bpmn" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.flowable.org/processdef"> <process id="holiday-request" name="Holiday Request" isExecutable="true"> <! -- startEvent: start of process instance --> <startEvent id="startEvent"/> <! <sequenceFlow sourceRef="startEvent" targetRef="approveTask"/> <! -- User tasks: <userTask ID ="approveTask" name="Approve or reject request"/> <sequenceFlow sourceRef="approveTask" targetRef="decision"/> <! <exclusiveGateway ID ="decision"/> <sequenceFlow sourceRef="decision" targetRef="externalSystemCall"> <! ConditionExpression xsi:type="tFormalExpression"> <! ConditionExpression xsi:type="tFormalExpression"> -- conditional expression: is shorthand for ${approved == true} --> <! [CDATA[ ${approved} ]]> </conditionExpression> </sequenceFlow> <sequenceFlow sourceRef="decision" targetRef="sendRejectionMail"> <conditionExpression xsi:type="tFormalExpression"> <! [CDATA[ ${!approved} ]]> </conditionExpression> </sequenceFlow> <! -- Service Task, an automatic activity, <serviceTask id="externalSystemCall" name="Enter Holidays in externalSystem" flowable:class="edu.hpu.process.CallExternalSystemDelegate"/> <userTask id="holidayApprovedTask" name="Holiday Approve!" /> <sequenceFlow sourceRef="holidayApprovedTask" targetRef="approveEnd"/> <serviceTask id="sendRejectionMail" name="Send  out rejection email" flowable:class="edu.hpu.process.SendRejectionMail"/> <sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/> <! <endEvent id="rejectEnd"/> < process> </definitions>Copy the code


  • Each step (called an activity in BPMN 2.0 terminology) has an ID attribute that provides it with a unique identifier in the XML file. All activities can be given a name to improve the readability of the flowchart.

  • Activities are connected by a sequence flow, which is a directed arrow in the flowchart. When a process instance is executed, execution flows sequentially from the launch event to the next activity.

  • The sequential flows that leave the exclusive gateway (diamond with X) are special: they all define conditions in the form of expressions. When the execution of the process instance reaches this gateway, the condition is evaluated and the first sequential flow that evaluates to true is used. This is what it means to be exclusive: to choose only one. If different routing policies are required, you can use other types of gateways.

  • The expression used as a condition here is ${approved}, which is short for ${approved == true}. The variable ‘approved’ is called a process variable. Process variables are persistent data that is stored with the process instance and can be used throughout the lifecycle of the process instance. In this case, we need to set the process variable at a specific place (when the manager user task is submitted or, in Flowable terms, complete), because this is not data that can be retrieved when the process instance is started.

Now that we have the process BPMN 2.0 XML file, we need to deploy it to the engine. Deploying a process definition means:

  • The process engine stores the XML file in the database so that it can be retrieved when needed.

  • The process definition is transformed into an internal, executable object model that can be used to start process instances.

To deploy the process definition to the Flowable engine, you need to use RepositoryService, which is available from the ProcessEngine object. With RepositoryService, you can create a new Deployment from the path of the XML file and call the deploy() method to actually execute:

       // Create RepositoryService instance
        RepositoryService repositoryService=processEngine.getRepositoryService();
        // Load the process
        Deployment deployment=repositoryService.createDeployment()
                .addClasspathResource("holiday-request.bpmn20.xml")
                .deploy();
Copy the code


We can now validate the process definitions that have been deployed in the engine through API queries. ProcessDefinitionQuery object created by RepositoryService.

        // Query the process definition
        ProcessDefinition processDefinition=repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        System.out.println("Found process definition : "+processDefinition.getName());
Copy the code

Run:


The XML file has been stored in the database:


1.3 start the process instance

Now that the process definition is deployed in the process engine, you can use this process definition as a “blueprint” to start the process instance.

  • To start a process instance, you need to provide some initialization process variables. Typically, these variables can be obtained either through the form presented to the user or through the REST API when the process is automatically triggered by another system. In this example, we simply enter some data on the command line using the java.util.Scanner class:
       //1, get the process initialization variable
        Scanner scanner = new Scanner(System.in);

        System.out.println("Who are you?");
        String employee = scanner.nextLine();

        System.out.println("How many holidays do you want to request?");
        Integer nrOfHolidays = Integer.valueOf(scanner.nextLine());

        System.out.println("Why do you need them?");
        String description = scanner.nextLine();
Copy the code
  • Next, we start a process instance using the RuntimeService. The collected data is passed as a java.util.Map instance, where the keys are the identifiers that are later used to get variables. The process instance is started using key (among other things). The key is the ID attribute set in the BPMN 2.0 XML file, in this case, holiday-request.
<process id="holiday-request" name="Holiday Request" isExecutable="true">
Copy the code
     //2. Start a process instance with RuntimeService
        RuntimeService runtimeService=processEngine.getRuntimeService();
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("employee", employee);
        variables.put("nrOfHolidays", nrOfHolidays);
        variables.put("description", description);
        ProcessInstance processInstance=runtimeService.startProcessInstanceByKey("holiday-request",variables);
Copy the code

After the process instance is started, an execution is created and placed on the launch event. From here, the execution moves down a sequential flow to the user task approved by the manager and performs the user task behavior. This behavior creates a task in the database that can be found later using the query. The user task is in a wait state, and the engine stops execution and returns to the API call.

Input process initialization variables:


Insert data into the database


Insert data into database:


1.4 transactions in Flowable

In Flowable, database transactions play a key role in ensuring data consistency and resolving concurrency issues. When the Flowable API is called, by default, all operations are synchronous and under the same transaction. This means that when the method call returns, a transaction is started and committed.

After the process starts, there is a database transaction that continues from the start of the process instance to the next wait state. In this case, it’s the first user task. When the engine reaches the user task, the state is persisted to the database, the transaction is committed, and the API call is returned.

In Flowable, when a process instance is running, there is always a database transaction that continues from one wait state to the next. After the data is persisted, it may remain in the database for a long time, even years, until an API call causes the process instance to continue executing. Note that when the process is in the wait state, no computing or memory resources are consumed until the next APi call.

In this example, when the first user task completes, a database transaction is started, starting with the user task and going through the exclusive gateway (automatic logic) until the second user task. Or take another path directly to the end.


1.5. Query and complete tasks

In a more practical application, a user interface would be provided for employees and managers to log in and view a list of tasks. You can see the process instance data stored as process variables and decide how to operate the task. In this example, we simulate the task list by making API calls that are usually called in the background by UI-driven services.

We have not configured the transactor for the user task.

  • Assign the first task to the “Managers” group and the second user task to the submitter of the leave application.
<! -- assign task to manager group --> <userTask ID ="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
Copy the code
<! -- assigned to the approver for leave approval, ${employee} dynamically assigned using the process variable, passed when the process instance is started --> <userTask ID ="holidayApprovedTask" name="Holiday Approve!" flowable:assignee="${employee}"/>
Copy the code


  • To get the actual list of tasks, create a TaskQuery using a TaskService. This query is configured to return only tasks from the ‘Managers’ group:
        // Run the TaskService command to query the tasks of the Manager group
        TaskService taskService=processEngine.getTaskService();
        List<Task> tasks=taskService.createTaskQuery().taskCandidateGroup("managers").list();
        System.out.println("You have " + tasks.size() + " tasks:");
        for (int i=0; i<tasks.size(); i++) {
            System.out.println((i+1) + ")" + tasks.get(i).getName());
        }
Copy the code


  • Use collection subscripts to get variables for a particular process instance and output them on the console
        // Use the collection subscript to get the variables of a particular process instance and output them in the console
        System.out.println("Which task would you like to complete?");
        int taskIndex = Integer.valueOf(scanner.nextLine());
        Task task=tasks.get(taskIndex-1);
        Map<String,Object> processVariables=taskService.getVariables(task.getId());
        System.out.println(processVariables.get("employee") + " wants " +
                processVariables.get("nrOfHolidays") + " of holidays. Do you approve this?");
Copy the code


  • The manager can finish the task now. In real development, a form is usually submitted by the user. The data in the form is passed as process variables. Here, we pass a map with the ‘approved’ variable (this name is important because it will be used later in the condition of the sequential flow) to simulate when we complete the task:
        // The manager completes the task
        boolean approved=scanner.nextLine().toLowerCase().equals("y");
        variables = new HashMap<String, Object>();
        variables.put("approved", approved);
        // The task is completed and will select one of the two paths away from the exclusive gateway based on the 'approved' process variable
        taskService.complete(task.getId(),variables);
Copy the code

The last thing missing is that the service invoked by the service task is not implemented:

<! -- serviceTask, an automatic activity that calls some services --> <serviceTask id="externalSystemCall" name="Enter holidays in external system" flowable:class="edu.hpu.process.CallExternalSystemDelegate"/>
Copy the code
  • Create a class that implements the JavaDelegate interface and implements the Execute method, which can write a lot of business logic. Here we’re just printing something out from the console:
/ * * *@AuthorThree points *@Date 2020/5/3
 * @Description* /
public class CallExternalSystemDelegate implements JavaDelegate {
    public void execute(DelegateExecution delegateExecution) {
        System.out.println("Calling the external system for employee "
                + delegateExecution.getVariable("employee")); }}Copy the code

Run:

Startup process:


  • View the task


  • To complete the task


  • Perform automatic logic

At this point, a simulated leave process is complete.


1.6. Use historical Data

One of the reasons to choose a process engine such as Flowable is that it automatically stores audit data or historical data for all process instances. This data can be used to create reports that give insight into how the organization is performing, where bottlenecks are, and so on.

Get the HistoryService from ProcessEngine and create a query for historical Activities.

 // Get the HistoryService instance
        HistoryService historyService=processEngine.getHistoryService();
        // Add search criteria
        List<HistoricActivityInstance> activities =
                historyService.createHistoricActivityInstanceQuery()
                        // Select a specific instance
                        .processInstanceId(processInstance.getId())
                        // Select the completed ones
                        .finished()
                        // In ascending order by instance completion time
                        .orderByHistoricActivityInstanceEndTime().asc()
                        .list();

        for (HistoricActivityInstance activity : activities) {
            System.out.println(activity.getActivityId() + " took "
                    + activity.getDurationInMillis() + " milliseconds");
        }
Copy the code




Reference:

[1] : Flowable BPMN User Manual (V 6.3.0) [2] : Flowable Learning (ⅰ) — Create a simple Flowable process example [3] : The difference between Activiti and Flowable [4] : Flowable database table description [5] : BPMN2.0 based Workflow (Workflow) [6] : BPMN2.0 / Flowable [7] : BPMN2.0 specification [8] : Chapter 3 BPMN2.0