This is the 5th day of my participation in the August More Text Challenge

@[toc]

Activi7 Workflow classic combat

Activiti7

Activiti is the most widely used open source workflow engine and was launched in May 2010. Before we get to Activiti, let’s first understand what workflow is.

1.1 WorkFlow WorkFlow

There is an official definition of what a workflow is: it is a fully automated business process that transfers and executes documents, information, or tasks between different actors according to a set of ready-made rules. In fact, to put it bluntly, it is a complete approval process in business. For example, employee entry, leave, business trip, procurement, etc., and some key business such as order application, contract review, etc., these processes are a workflow.

For workflow, the traditional way of processing often requires someone to hold various kinds of documents, between multiple executive departments approval. When we started using software to help with this kind of approval process, workflow systems began to emerge. Workflow system can reduce a lot of offline communication costs and improve work efficiency.

Workflow engines began to appear with workflow systems. In order to realize such flow control, we usually use the status field to track the flow changes before there is a special workflow engine. For example, for an employee’s leave request, we will define some states such as applied, reviewed by the group leader, reviewed by the department manager, etc., and then control different business behaviors through these states. For example, the department manager role can only see the orders approved by the group leader and the days of leave exceed 3 days, etc.

This implementation is relatively simple to implement, and is also a very common way in software systems. However, this method of flow control through the status field still has its drawbacks.

On the one hand: the whole process is not clearly defined. Business processes are scattered across business phases and it is very difficult to see how the entire process is defined from a code perspective.

On the other hand: When the process changes, the code written this way requires a very large change. For example, if three-level approval is to be increased to four-level approval or even collaborative approval, a large number of changes need to be made in the approval process at all business stages.

It is from these pain points that workflow engines are created. With the workflow engine, the entire approval process can be designed in one place and the business process can remain unchanged when the approval process changes. In this way, the adaptability of business systems has been greatly improved.

The idea of engines is everywhere. We have the Drools rules engine, which allows business rules to be centrally defined and modified without application changes. Aviator expression engine, can quickly calculate the result of an expression. Search engines, quick unified search and so on. Its core idea is to extract the commonness between businesses and reduce the impact of business changes on programs.

1.2 Activiti workflow engine

Activiti is the most widely used open source workflow engine. Activiti’s website iswww.activiti.orgThe latest release is Activiti Cloud 7.1.0-M11 after two major releases of 6.x and 5.x.

It can extract complex business processes from business systems and define them using a special modeling language, BPMN2.0. Business processes are executed according to predefined processes, and the entire implementation process is managed by Activiti to reduce the amount of work required to modify the business system due to process changes, thereby reducing the cost of system development and maintenance and improving the robustness of the system. So with Activiti, the focus is on two steps, first defining the process using BPMN and then implementing the process using the Activiti framework.

1.3 Modeling language BPMN

When it comes to BPMN, we start with BPM. BPM stands for Business Process Managemenet. Is a standardized way to construct an end-to-end business process to continuously improve the efficiency of an organization’s business. BPM courses are included in common business management education, such as EMBA and MBA.

With BPM requirements, BPM software emerges. IT is an IT tool that promotes the integration and adjustment between people, between people and systems, and between systems of business domain solutions according to the changes of business environment in enterprises. By modeling, automating, managing, monitoring and optimizing the whole life cycle of enterprise business processes, enterprises can reduce costs and increase profits. BPM software is widely used in enterprises, where business processes can be managed using BPM. Such as enterprise personnel office management, procurement process management, document approval process management, financial management and so on.

BPMN is the Business Process Model And Notation, a modeling standard used to describe Business processes. BPMN was first proposed by BPMI(BusinessProcess Management Initiative) scheme. It consists of a set of standard business process modeling notations. Business processes can be defined quickly using BPMN.

BPMN was first released in May 2004. In September 2005, it was incorporated into OMG(The Object Managemenet Group). OMG released the final version of BPMN2.0 in January 2011. BPMN is a BPM standard widely accepted by major BPM vendors. Activiti uses BPMN2.0 for process modeling and process execution management.

The entire BPMN is a set of symbols that describe the various events that occur in a business process. BPMN describes a complete business process by wiring between these symbolic events.

A complete BPMN graphical process is ultimately described in XML. Typically, the BPMN process is eventually saved as a.bPMn file that can then be opened and viewed using a text editor. There is specialized software to convert graphics to XML files.

We will cover how to configure a workflow later in the field.

1.4 Activiti usage Steps

Typically using Activiti involves the following steps:

  • Deploying Activiti: Activiti contains a bunch of JARS, so you need to integrate business systems with Activiti’s environment for deployment.
  • Define a process: Define a business process.bPMN file using Activiti’s modeling tools.
  • Deploy the process definition: Use the API provided by Activiti to store the process definition content and perform the process summary in Acitivti to query the definition content. Activiti stores business processes through a database.
  • Start ProcessInstance: ProcessInstance is also called ProcessInstance. Starting a process instance starts the operation of a business process. For example, after an employee submits a leave application, a process instance can be opened to facilitate subsequent actions such as approval.
  • Users query tasks: Since all system processes are managed by Activiti, activiti can query the current step of the process. Activiti also manages the tasks that current users need to perform, eliminating the need for developers to write their own SQL queries.
  • Tasks for users: Once a user has checked his or her to-do list, he or she can perform a task, and if that task needs to be completed by another user, Activiti can help push the workflow forward.
  • End of process: When a task is completed and there is no next task node, the process instance is completed.

With that in mind, let’s get into the real thing.

2. Activiti environment setup

The basic environment you need to use Activiti includes JDK 8 or above; You then need a database to hold the process definition data, mysql 5 or above is recommended.

2.1 Installing plug-ins

Development tool IDEA, in which you need to install Activiti’s process definition tool plug-in actiBPM. Currently, the plugin has not been updated since November 2014, and the IDEA version is only supported until 2019.1. The plug-in of the new version of IDEA cannot be searched from the plug-in market. To install, search for the actiBPM plug-in at plugins.jetbrains.com/, download it locally, and install it locally.

Once installed, you can use the plug-in to edit the.bPMn files in your project to define the business process. However, this file, as described earlier, is essentially an XML text file, so you still need to know more about how XML is configured.

2.2 Initializing database tables

Activiti supports a variety of databases, including:

Database type version JDBC Connection Example instructions
h2 1.3.168 jdbc:h2:tcp://localhost/activiti Default configured database
mysql 5.1.21 jdbc:mysql://localhost:3306/activiti? autoReconnect=true Test using mysql-connector-Java driver
oracle 11.2.0.1.0 jdbc:oracle:thin:@localhost:1521:xe
postgres 8.1 jdbc:postgresql://localhost:5432/activiti
db2 DB2 10.1 using db2jcc4 jdbc:db2://localhost:50000/activiti
mssql 2008 using sqljdbc4 jdbc:sqlserver://localhost:1433/activiti

We chose mysql database here. Next, follow these steps to initialize the data tables required by Activiti.

1- Create a database activiti in mysql that will be used to create activiti-related tables.

CREATE DATABASE activiti DEFAULT CHARACTER SET utf8;
Copy the code

Then, Activiti relies on 25 business tables, and activiti provides tools to generate the tables we need.

2- Create a Maven project, BasicDemo, and introduce the following dependencies in POM.xml:

<properties>
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
    <activiti.version>7.1.0. M6</activiti.version>
    <activiti.cloud.version>7.0.0. Walk</activiti.cloud.version>
    <mysql.version>8.0.20</mysql.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-engine</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <! -- BPMN model processing -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-model</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <! -- BPMN -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-converter</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <! -- BPMN json data conversion -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-json-converter</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <! -- BPMN layout -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-layout</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <! -- Activiti Cloud support -->
    <dependency>
        <groupId>org.activiti.cloud</groupId>
        <artifactId>activiti-cloud-services-api</artifactId>
        <version>${activiti.cloud.version}</version>
    </dependency>
    <! -- Mysql driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>${mysql.version}</version>
    </dependency>
    <! -- mybatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <! -- Link pool -->
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <! -- log start -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
</dependencies>
Copy the code

3- Add log4J logging configuration

Log4j is used for logging, so you need to configure logging by creating a log4j.properties file in the Resources directory

# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE debug info warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE
# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p % 30.30C %x - %m\n
# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=f:\act\activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p % 30.30C %x - %m\n
Copy the code

4- Add the Activiti profile

By default, Activiti uses mysql to create tables. You need to create a configuration file activiti.cfg. XML to define data source information.

Create the activiti.cfg.xml file in the Resources directory.

Note that this directory is the default location of the classpath. This is the directory and file activiti reads by default.

It is possible to create in another directory, but you need to specify the directory and name of the file at build time.

The basic content of the configuration file is as follows: – Several namespaces are defined.


      
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>
Copy the code

5- Configure in activiti.cfg.xml

We can add basic configuration about the database in Activiti.cfg.xml


      
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <! -- Link pool DBCP -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/activiti? serverTimezone=GMT%2B8" />
        <property name="username" value="root" />
        <property name="password" value="root" />
        <property name="maxActive" value="3" />
        <property name="maxIdle" value="1" />
    </bean>

    <bean id="processEngineConfiguration"
          class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <! -- Reference data source is already set -->
        <property name="dataSource" ref="dataSource" />
        <! -- Activiti database table processing strategy -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>
Copy the code

Note: 1, the processEngineConfiguration is best not to modify the name. This is the default Bean name that Activiti reads.

2, may also directly in the processEngineConfiguration configuration jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword several properties.

3. The databaseSchemaUpdate property is configured by following the source code a bit:

The default is false; Check the table structure of the database. If the table structure is not checked, an exception will be thrown

Create-drop: creates a table structure when the engine starts and deletes it when the engine finishes processing.

True: Creates a complete table organization and updates the table structure if necessary.

6- Write Java programs to generate tables.

Create a test class that calls Activiti’s utility classes to directly generate the database tables activiti needs. The code is as follows:

package com.roy;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.junit.Test;

/ * * *@author: the loulan *@date: Created in 2021/4/7 *@description: * * /

public class TestCreateTable {
    /** * Generate activiti database table */
        @Test
    public void testCreateDbTable(a) {
        // Default creation mode
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        // Generic creation method, specifying the configuration file name and Bean name
// ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml", "processEngineConfiguration");
// ProcessEngine processEngine1 = processEngineConfiguration.buildProcessEngine();System.out.println(processEngine); }}Copy the code

Note: You can see from this code what our previous defaults are doing. ProcessEngines. GetDefaultProcessEngine () this line of code will read the classpath: by default the activiti. CFG. XML and activiti – context. Two XML configuration files. And from the spring container loading called processEngineConfiguration Bean.

Executing this script completes the mysql table structure creation. If the execution is normal, you can see that a large number of SQL statements are executed and a log line is finally printed

org.activiti.engine.impl.ProcessEngineImpl@77307458
Copy the code

This indicates that the engine was created successfully. You can also see the 25 tables used by Activiti in mysql.

These table bodies can also often be exported to SQL files and migrated directly. However, porting as a SQL file is generally not recommended, given the potential for tweaks between versions.

2.3 Table structure interpretation

As you can see from the tables you just created, activiti’s tables all start with act_. The second section shows the purpose of the table. The purpose also corresponds to the service API.

ACT_RE: ‘RE’ stands for repository. This prefixed table contains process definitions and process static resources (images, rules, and so on). ACT_RU: ‘RU’ stands for runtime. These run-time tables contain running data for process instances, tasks, variables, asynchronous tasks, and more. Activiti saves this data only during the process instance execution and deletes the records at the end of the process. This way the table can stay very small and very fast at runtime. ACT_HI: ‘HI’ stands for history. These tables contain historical data, such as historical process instances, variables, tasks, and so on. ACT_GE: GE stands for general. Common data used in different scenarios

A complete database table does the following:

Classification table The name of the table explain
General data
[ACT_GE_BYTEARRAY] Common process definitions and process resources
[ACT_GE_PROPERTY] System related Attributes
Process history
[ACT_HI_ACTINST] Historical process instances
[ACT_HI_ATTACHMENT] Historical process attachments
[ACT_HI_COMMENT] Historical illustrative information
[ACT_HI_DETAIL] Details of the process execution in history
[ACT_HI_IDENTITYLINK] History of user relationships during process runs
[ACT_HI_PROCINST] Historical process instances
[ACT_HI_TASKINST] Historical task instances
[ACT_HI_VARINST] Historical information about variables in a process run
Process definition table
[ACT_RE_DEPLOYMENT] Deployment Unit Information
[ACT_RE_MODEL] Model information
[ACT_RE_PROCDEF] Deployed process definitions
Run instance table
[ACT_RU_EVENT_SUBSCR] Run time event
[ACT_RU_EXECUTION] Runtime process execution instance
[ACT_RU_IDENTITYLINK] Runtime user relationship information, which stores information about task nodes and participants
[ACT_RU_JOB] Run time job
[ACT_RU_TASK] Runtime task
[ACT_RU_VARIABLE] Run time varying meter

2.4 Activiti core classes

Once we get ProcessEngine, we can take a quick look at its methods

These services are the core service implementation classes for Activiti. Most of the core business functions surrounding Activiti come through these services.

The name of the service The service function
RepositoryService Activiti’s resource management class
RuntimeService Activiti’s process run management class
TaskService Activiti’s task management class
HistoryService Activiti’s history management class
ManagerService Activiti’s engine management class

Brief introduction:

RepositoryService

Activiti resource Management class provides management and control of process distribution packages and process-defined operations. A business flowchart designed using a workflow modeling tool requires the service to deploy the contents of the process definition file to a computer.

In addition to deploying process definitions, you can: query the release packages and process definitions in the engine.

Suspend or activate the distribution, corresponding to all and specific process definitions. Pause means they can’t do anything anymore, and activate is the reverse. Get multiple resources, such as files included in a release package, or flow charts automatically generated by the engine.

Get a POJO version of the process definition that can be used to parse the process through Java rather than XML.

RuntimeService

Activiti’s process run management class. You can get a lot of information about process execution from this service class

TaskService

Activiti’s task management class. You can get information about tasks from this class.

HistoryService

Activiti’s history management class allows you to query historical information. When a process is executed, the engine stores a lot of data (depending on the configuration), such as when the process instance started, who participated in the task, when the task was completed, the execution path of each process instance, and so on. The service obtains this data mainly through the query function.

ManagementService

Activiti’s Engine Management class provides management and maintenance capabilities for the Activiti process engine, which are not used in workflow-driven applications and are primarily used for routine maintenance of the Activiti system.

Getting started with Activiti

In this chapter, we create an Activiti workflow and start it. Understand the basic Activiti development process.

The main steps for creating an Activiti workflow include the following:

  1. Define the process. According to the BPMN specification, the entire process is described using the process definition tool
  2. Deployment process. Load the drawn BPMN process definition file into the database to generate the relevant table data
  3. Start the process. Use Java code to manipulate the contents of a database table.

3.1 Process symbol details

Let’s take a look at common symbols used in process design. The basic symbols of BPMN2.0 mainly include the following categories:

  • Events in the Event

Events are the core objects that drive the development of workflow and are often seen in the process customization of workflow.

  • Activities in the Activity

Activity is a generic term for a job or task. An activity can be a task or a subprocess of the current process. And there are different types of activities. Such as UserTask defined Activiti, ScriptTask, ServiceTask, MailTask and so on a variety of types. These activities form the backbone of the overall business process.

  • The GateWay of GateWay

Gateways are used to process roles that determine the business direction of the workflow. There are several common gateways to know about:

  • Exclusive gateway

Only one path will be selected. When the process reaches the gateway, it calculates the output streams one by one in the order of the output streams. When the result of the condition is true, the output streams of the current gateway continue to be executed.

If more than one line calculation structure is true, the route with the first value true is executed. The engine throws an exception if none of the gateway calculations result in true.

The exclusive gateway needs to be used in conjunction with conditional order flow. The default attribute specifies the default order flow that will be executed when all conditions are not met.

  • A parallel gateway

All paths will be selected simultaneously.

Execute all output sequential streams in parallel, creating a parallel execution path for each sequential stream. Finally, continue down until all execution paths have been completed.

  • Inclusive gateway

Multiple routes can be executed simultaneously. It is a combination of exclusive gateway and parallel gateway.

Conditions can be set on the gateway to evaluate expressions on each route. When the expression evaluates to true, a parallel line is created and execution continues. Finally, when all the lines that need to be executed have been executed, proceed down.

  • The event gateway

Set specifically for intermediate capture events. Allows you to set multiple output streams to point to multiple different intermediate capture events. When the process executes to the event gateway, the process is in the wait state and needs to wait for the corresponding event to be thrown to change the wait state to the active state.

  • Flow to the Flow

A flow is a wire connecting two process nodes and represents an association between processes.

Note: Activiti’s BPMN notation is an extension of the standard notation. You can create a new BPMN file and look at the workflow diagrams provided by the actBPM tool.

3.2 Customize a simple leave process

Create a BPMN directory in the Resources directory, and create a Bomn file in the resources directory with the name Leave to indicate a Leave process.

Then we drag and drop the following flowchart from the flowchart

Note here that each module can be selected to set its properties on the left.

Here, Assignee attributes denote the leader of the task. Here we set the responsible worker for creating the business trip application; Department manager in charge of approval and setting; Financer in charge of financial approval setting.

When you’re done, remember to click on the blank page of the process and set the properties for the entire process on the left.

In this way, our entire employee travel application process has been defined. As mentioned earlier, this file is actually an XML file, so it can be opened directly with a text editor. The whole document looks something like this

The root node is the Definitions node. Within this node, you can define multiple workflow Process nodes. This means that multiple workflows can be defined in a single diagram, but it is generally recommended that a file contain only one process definition to simplify maintenance. The XMLNS and tagetNamespace attributes are mandatory in the Definitions node.

The file then contains a process definition section described by the tag and a process layout definition section described by the tag < BPMnDI :BPMNDiagram >. Used to define the layout information of the workflow and flowchart, respectively.

If the file is closed and opened again, garbled Chinese characters may appear. This is due to character set problems with IDEA. In this case, modify the CONFIGURATION file of IDEA. In IDEA, select Help -> Edit Custom VM Options menu, add a line -dfile. encoding=UTF-8 configuration at the end of the open file, and restart IDEA.

3.3 Deployment Leave Process

Next, you need to deploy the processes defined in the designer into Activiti’s database. Activiti provides apis to deploy process definition BPMN and PNG files into Activiti.

To get PNG image files, you can either take screenshots directly or export them using IDEA. XML. Right-click the file and choose Diagrams -> Shwo BPMN 2.0 Diagrams. You can open the image tool and click the Export button at the top to export a PNG file.

During process deployment, you can upload a BPMN file and a PNG file, or upload the two files as a ZIP package.

public class ActivitiDemo {

    /** * The deployment process defines the file upload mode */
    @Test
    public void testDeployment(a){
// create ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// Get RepositoryService instance
        RepositoryService repositoryService = processEngine.getRepositoryService();
// 3. Use RepositoryService to deploy the repository
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("bpmn/Leave.bpmn") // Add the BPMN resource
  // PNG resource names have conventions. Leave. [key] [PNG | JPG | | GIF SVG] or Leave. [PNG | JPG | | GIF SVG]
            .addClasspathResource("bpmn/Leave.myLeave.png")  // Add a PNG resource
                .name("Leave Application Process")
                .deploy();
// 4. Output deployment information
        System.out.println("Process Deployment ID:" + deployment.getId());
        System.out.println("Process Deployment Name:" + deployment.getName());
    }

    /** * Zip file upload mode */
    @Test
    public void deployProcessByZip(a) {
        // Define the zip input stream
        InputStream inputStream = this
                .getClass()
                .getClassLoader()
                .getResourceAsStream(
                        "bpmn/Leave.zip");
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        / / get the repositoryService
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine
                .getRepositoryService();
        // Process deployment
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
        System.out.println("Process Deployment ID:" + deployment.getId());
        System.out.println("Process Deployment Name:"+ deployment.getName()); }}Copy the code

The most important thing to do is go to the repositoryService. After execution, you can see the deployment status in the log:

Process Deployment ID: 1 Process deployment Name: Leave application processCopy the code

In addition, from the log, we can analyze that three data tables were operated during the whole deployment process:

  • The ACT_RE_Deployment process defines the deployment table, adding one record per deployment
  • Act_re_procdef process definition table, to which a record is added with each new process definition deployed. The key in the record is the most important field in the process definition.
  • Act_ge_bytearray Process resource table. Each process definition corresponds to two resource records, BPMN and PNG.

Multiple process definitions can be deployed in one deployment i.e. the data in ACT_RE_Deployment and ACT_RE_procdef is one-to-many. In real development, however, one deployment at a time is not recommended as a process.

3.4 Starting the process instance

Once a business process is deployed to Activiti, it is ready to use. For example, once the travel application process is deployed, you can start a process for a travel application. The execution of a process is managed by the RuntimeService service.

 /** * Start the process instance */
    @Test
    public void testStartProcess(a){
// create ProcessEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// get RunTimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
// 3. Start the process according to the process definition Id
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey("myLeave");
// Output the content
        System.out.println("Process definition ID:" + processInstance.getProcessDefinitionId());
        System.out.println("Process instance ID:" + processInstance.getId());
        System.out.println("Active Id:" + processInstance.getActivityId());
    }

Copy the code

As a result, you can see how the process instance looks:

Process definition ID: myLeave:1:4 Process instance ID: 2501 Current activity ID: NULLCopy the code

Moving on to the log, you can see the data tables involved in this process:

  • Act_hi_actinst Process instance execution history
  • History of participating users in the act_hi_identitylink process
  • Act_hi_procinst Process instance history information
  • Act_hi_taskinst Process task history
  • Act_ru_execution process execution information
  • The act_ru_identitylink process participant information
  • Act_ru_task Task information

3.5 Task Query

Once the process is started, the task owner can query his or her current to-do list. Task-related services are managed by TaskService.

/** * Query the current individual task to be performed */ @test public void testFindPersonalTaskList() {// Task leader String assignee = "worker"; ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); / / create the TaskService TaskService TaskService = processEngine. GetTaskService (); / / according to the key process and Task, head of the query Task List < Task > List = taskService. CreateTaskQuery () processDefinitionKey (" myLeave ") / / process the key .taskassignee (assignee)// Query only the task. list(); For (Task Task: list) {System. Out. Println (" process instance id: "+ Task. GetProcessInstanceId ()); System.out.println(" task id: "+ task.getid ()); System.out.println(" Task.getassignee () : "+ task.getassignee ()); System.out.println(" task name: "+ task.getName()); }}Copy the code

When the execution is complete, you can see a list of tasks for the current process

Process instance ID: 2501 Task ID: 2505 Task owner: worker Task name: Create a leave applicationCopy the code

After the current leave process starts, it is time to wait for the worker user to submit the leave application. The actual leave application process should start with the worker submitting the leave application, but in the Activiti workflow, it starts with the starter event. This relationship needs to be clarified.

3.6 Process Task Processing

After finding the agent task, the task leader can select the task to process and complete the task.

// Complete the task
    @Test
    public void completTask(a){
// Get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        获取taskService
        TaskService taskService = processEngine.getTaskService();

// Query the task according to the process key and the task owner
// Return a task object
        Task task = taskService.createTaskQuery()
                .processDefinitionKey("myLeave") / / process the Key
                .taskAssignee("worker")  // The person in charge of the query
                .singleResult();

// Complete task. Parameter: task ID
        taskService.complete(task.getId());
    }


Copy the code

After this task was completed, the leave process was moved to the next step, which was approved by the department manager. You can then use different users to drive the process to its end.

In fact, in the process of completing the approval task, there are some other supplementary operations for the taskId. For example, add comments, add attachments, add subtasks, add candidate leaders, and so on. Take a look at the taskService API.

3.7 Querying Flow Information

This step enables you to query information about the process, including the process definition, process deployment, and process version.

    /** * Query process definition */
    @Test
    public void queryProcessDefinition(a){
        // Get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
// Get ProcessDefinitionQuery
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
// Query all current process definitions
// Conditional: processDefinitionKey =evection
/ / orderByProcessDefinitionVersion sorted by version
/ / desc flashbacks
// list returns the collection
        List<ProcessDefinition> definitionList = processDefinitionQuery.processDefinitionKey("myLeave")
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
// Output process definition information
        for (ProcessDefinition processDefinition : definitionList) {
            System.out.println("Process definition ID ="+processDefinition.getId());
            System.out.println("Process definition name="+processDefinition.getName());
            System.out.println("Process definition key="+processDefinition.getKey());
            System.out.println("Process definition Version="+processDefinition.getVersion());
            System.out.println("Process Deployment ID ="+processDefinition.getDeploymentId()); }}Copy the code

The query result is displayed

Process definition ID =myLeave:1:4 Process definition Name = Employee leave approval process Key =myLeave Process definition Version=1 Process deployment ID =1Copy the code

3.8 Deletion Process

public void deleteDeployment(a) {
		// Process deployment ID
		String deploymentId = "1";
		
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    // Get repositoryService through the process engine
		RepositoryService repositoryService = processEngine
				.getRepositoryService();
		// Delete the process definition. If the process definition has an existing process instance started, the deletion will fail
		repositoryService.deleteDeployment(deploymentId);
		// Set true to delete a cascading process definition. The process can be deleted even if the process instance is started. Set false to non-cascading deletion
		//repositoryService.deleteDeployment(deploymentId, true);
	}

Copy the code

Note:

1. Only the process definition is deleted, not the history table information

2. When deleting a task, you can pass in a Boolean variable cascade to indicate whether to cascade the task. The default value is false, indicating common deletion.

If a running process exists in the process, an error message will be displayed when you delete the process. However, you can delete all the processes and related records. After removing the unfinished process nodes, you can remove the process definition information completely.

In project development, cascading deletes are usually only available to administrators.

3.9 Downloading Process Resources

Process resource files can be uploaded during process execution. We had uploaded both the BPMN and the PNG image describing the BPMN when we deployed the process, and we could also upload resource files during the process execution. If other users want to view these resource files, they can download them from the database.

But files are stored in the database as BLOBs, and to get Blob files, you can use JDBC. You can also use the API provided by Activiti to assist. We use activiti to achieve this.

The commons-io dependency is introduced first

<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

Copy the code

Process resources can then be obtained through process definition objects. Here are the BPMN and PNG files we uploaded earlier

import org.apache.commons.io.IOUtils;

@Test
    public void deleteDeployment(a){
// Get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        获取repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
// Delete the deployment information based on the deployment ID. If you want to cascade the deletion, add the second parameter, true
        repositoryService.deleteDeployment("1");
    }

    public void  queryBpmnFile(a) throws IOException {
// get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// get repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
// Get ProcessDefinitionQuery, set query criteria, get the desired process definition
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("myLeave")
                .singleResult();
// 4. Obtain the deployment ID through the process definition information
        String deploymentId = processDefinition.getDeploymentId();
// 5. RepositoryService is used to repositoryService the image information and BPMN information
// PNG image stream
        InputStream pngInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
// Stream of BPMN files
        InputStream bpmnInput = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());
// create an OutputStream
        File file_png = new File("d:/myLeave.png");
        File file_bpmn = new File("d:/myLeave.bpmn");
        FileOutputStream bpmnOut = new FileOutputStream(file_bpmn);
        FileOutputStream pngOut = new FileOutputStream(file_png);
// Input stream, output stream conversion
        IOUtils.copy(pngInput,pngOut);
        IOUtils.copy(bpmnInput,bpmnOut);
// Close the stream
        pngOut.close();
        bpmnOut.close();
        pngInput.close();
        bpmnInput.close();
    }


Copy the code

Note: filename in access to resources, PNG image resource file name is processDefinition getDiagramResourceName (), he comes from ACT_RE_PROCDEF DGRM_RESOURCE_NAME fields in a table. The value of this field is determined from the filename suffix when deploying the process. Supported formats for [ResourceName]. [key] [PNG | JPG | | GIF SVG] or [ResourceName] [PNG | JPG | | GIF SVG]

While BPMN file filename is processDefinition. GetResourceName (), he comes from ACT_RE_PROCDEF RESOURCE_NAME fields in a table.

3.10 Viewing Process History Information

The history of the process is stored in activiti’s ACT_hi_ * table, which allows you to query the history of the process execution. You need to view the history through the HistoryService.

/** * View historical information */
    @Test
    public void findHistoryInfo(a){
// Get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/ / get HistoryService
        HistoryService historyService = processEngine.getHistoryService();
// Obtain the query object of the actinst table
        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
// Query all historical information about a process based on InstanceId
        instanceQuery.processInstanceId("25001");
Call DefinitionId for historical information about a process
// instanceQuery.processDefinitionId("myLeave:1:22504");
/ / increase the sort operation, orderByHistoricActivityInstanceStartTime sort asc ascending according to start time
        instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// Query all contents
        List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
/ / output
        for (HistoricActivityInstance hi : activityInstanceList) {
            System.out.println(hi.getActivityId());
            System.out.println(hi.getActivityName());
            System.out.println(hi.getProcessDefinitionId());
            System.out.println(hi.getProcessInstanceId());
            System.out.println("< = = = = = = = = = = = = = = = = = = = = = = = = = = >"); }}Copy the code

In this way, you can query the processing results of previous steps

_2 StartEvent myLeave: 504, 25001 and < = = = = = = = = = = = = = = = = = = = = = = = = = = > _3 create leave application myLeave: 25001 and 504 < = = = = = = = = = = = = = = = = = = = = = = = = = = > _4 department manager approval myLeave: and 504 25001 < = = = = = = = = = = = = = = = = = = = = = = = = = = >Copy the code

Note: 1. About the historical information of a process, note that if a process is deleted in a cascade manner, the historical information will be deleted along with it. However, historical information is not deleted in common deletion mode.

2. There are different types of historical information. You can use historyService to build different types of Query objects to obtain results.

3.11 Chapter Summary

In this chapter, you’ve walked through the basics of a workflow and got a general idea of how Activiti works.

The power of activiti is that its workflow definition, built around BPMN2.0, provides a complete set of back-end workflow capabilities. These backend functions can form a stable daemon that only needs to provide different BPMN definition files for different business processes without modifying the backend code. In addition, when the business process changes, only the process definition in the BPMN file needs to be modified, and the corresponding background code basically does not need to be changed.

However, it is important to note that Activiti only provides back-end functionality, and does not have a compatible front-end integration. And, when a task or a workflow to do some detailed operations, or need to pass in some specific business parameters, and these business parameters, or need a system to carry out the overall processing. So activiti is a powerful workflow engine, but it’s still a bit short of a complete workflow system.

In addition, it is critical to learn activiti that you have 25 tables in your database, which ultimately contains all of the data in your workflow. In practice, it is almost impossible for us to fully understand the data content of these 25 tables, but we still need to understand some key operational data, which is an important basis for us to master the running state of the whole workflow engine in the future.

Now that we have a general understanding of Activiti, let’s dive into some of Activiti’s advanced features.

Activiti advanced

4.1 Process definition and process instance

ProcessDefinition and ProcessInstance ProcessInstance are two very important concepts in Activiti. Their relationship is similar to the concept of classes and objects in JAVA.

A ProcessDefinition is a workflow defined in a BPMN file and is a set of work specifications. For example, the leave process we defined earlier. ProcessInstance ProcessInstance refers to a concrete business process. For example, if an employee initiates a leave, a process instance of leave will be instantiated, and each of the different process instances will not affect each other.

In the table structure behind the scenes, there are many tables that contain fields for process definition ProcessDefinetion and ProcessInstance ProcessInstance. The field defined by the process is usually PROC_DEF_ID, and the field for the process instance is usually PROC_INST_ID.

4.1.1 Add Businesskey when starting the process instance

In our previous simple case, the key code to start a process instance was this line.

ProcessInstance processInstance = runtimeService              .startProcessInstanceByKey("myLeave");

Copy the code

When we went to see the startProcessInstanceByKey this method, will see the realization of this method has several overloading method, can pass some different parameters. Several of the important parameters include

  • String processDefinitionKey: The unique key of the process definition cannot be empty
  • String businessKey: Unique key associated in the context of each thread instance. This is also the focus of this chapter.
  • Map

    variables: Process variables passed in thread instances. This process variable can be used in the entire process instance, as described later.
    ,object>
  • String tenantId: tenantId, which is Activiti’s multi-tenant design. Each tenant can obtain a relatively independent operating environment.

In this chapter we will introduce the businessKey. This is a very important convenience that Activiti provides to tie Activiti’s workflow to the real business.

For example, when we need to approve a business order, the details of the order are not in activiti’s data, but they do need to be viewed for approval. At this point, the businessKey can be used to associate the order ID, so that in the business system, the order details can be associated with the order ID, and the approval personnel can quickly refer to it.

For actual business consolidation, the businessKey can be designed in different data formats, such as key information comma concatenation, or even JSON, depending on the business scenario. The only thing to note is that the database length of this field is 255, which does not exceed the database length limit.

Next, let’s look at how to get this business key during process instance execution:

@Test
    public void queryProcessInstance(a) {
        // The process defines the key
        String processDefinitionKey = "myLeave";
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        / / get RunTimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
        List<ProcessInstance> list = runtimeService
                .createProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)//
                .list();

        for (ProcessInstance processInstance : list) {
            System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
            System.out.println("Process instance ID:"
                    + processInstance.getProcessInstanceId());
            System.out.println("Owning process definition ID:"
                    + processInstance.getProcessDefinitionId());
            System.out.println("Whether execution has been completed:" + processInstance.isEnded());
            System.out.println("Pause or not:" + processInstance.isSuspended());
            System.out.println("Current Active identifier:" + processInstance.getActivityId());
            System.out.println("Business Keywords:"+processInstance.getBusinessKey()); }}Copy the code

Through the final line of processInstance. GetBusinessKey () will be able to get to the business keywords in the current process instance. In the database, the BUSINESS_KEY field in the ACT_RU_execution table is used to hold this business key.

4.1.2 Suspending and activating process instances

We’ve tested how to delete a process before, and there are many times when we just need to temporarily stop a process and resume it after a while. For example, the reimbursement approval process is not accepted at the end of the month, the loan approval process is not accepted at the end of the year, or the after-sales reimbursement process is not accepted on non-working days, etc. At this time, the process can be suspended. Suspended processes are no longer executed.

When you suspend a process, there are two ways to do this.

One is to suspend the entire Process Definition, so that all Process instances under this Process Definition are suspended and cannot be continued

/** * All process instances hang and activate */
    @Test
    public void SuspendAllProcessInstance(a){
/ / get processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
//        获取repositoryService
        RepositoryService repositoryService = processEngine.getRepositoryService();
// Query the object defined by the process
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().
                processDefinitionKey("myEvection").
                singleResult();
// Get whether all instances of the current process definition are paused
        boolean suspended = processDefinition.isSuspended();
// Process definition ID
        String processDefinitionId = processDefinition.getId();
// Determine if it is paused
        if(suspended){
Parameter 1: process definition ID, parameter 2: Whether to activate, and parameter 3: activation time
            repositoryService.activateProcessDefinitionById(processDefinitionId,
                    true.null
            );
            System.out.println("Process Definition:"+processDefinitionId+", activated");
        }else{
Parameter 1: process definition ID, parameter 2: whether to suspend, parameter 3: pause time
            repositoryService.suspendProcessDefinitionById(processDefinitionId,
                    true.null);
            System.out.println("Process Definition:"+processDefinitionId+", suspended"); }}Copy the code

Another way is to suspend a specific process instance. For example, suspend a problematic leave application and activate it after data adjustment is completed. Continuing to execute a process in the pending state will throw an exception

/** * A single process instance is suspended and activated */
    @Test
    public void SuspendSingleProcessInstance(a){
/ / get processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
// Query the object defined by the process
        ProcessInstance processInstance = runtimeService.
                createProcessInstanceQuery().
                processInstanceId("15001").
                singleResult();
// Get whether all instances of the current process definition are paused
        boolean suspended = processInstance.isSuspended();
// Process definition ID
        String processInstanceId = processInstance.getId();
// Determine if it is paused
        if(suspended){
// If it is paused, the activation operation can be performed. Parameter: process definition ID
            runtimeService.activateProcessInstanceById(processInstanceId);
            System.out.println("Process Definition:"+processDefinitionId+", activated");
        }else{
// If it is active, it can be paused with the process definition ID
            runtimeService.suspendProcessInstanceById( processInstanceId);
            System.out.println("Process Definition:"+processDefinitionId+", suspended"); }}/** * Tests complete personal tasks */
    @Test
    public void completTask(a){
// Get the engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// Get the TaskService for the operation task
        TaskService taskService = processEngine.getTaskService();
// Complete the task. Parameter: process instance ID. Complete the zhangsan task
        Task task = taskService.createTaskQuery()
                .processInstanceId("15001")
                .taskAssignee("rose")
                .singleResult();

        System.out.println("Process instance ID ="+task.getProcessInstanceId());
        System.out.println("Task Id ="+task.getId());
        System.out.println("Task Leader ="+task.getAssignee());
        System.out.println("Task Name ="+task.getName());
        taskService.complete(task.getId());
    }

Copy the code

4.2 Process Variables

Process variables also play an important role in Activiti. The leave process we defined before did not use process variables, and each step is very fixed. However, when we need to realize some complex business processes, such as the approval of department manager within 3 days, and the approval of general manager over 3 days, process variables need to be used.

Note: This process variable is somewhat similar to the business keyword introduced earlier in that it can carry business information. These are also available through the Activiti API. In general, however, you should minimize business information in process variables to reduce code intrusion into activiti workflows by business code.

As described in the previous chapter, the type of the process variable is Map<String,Object>. Therefore, process variables are much more powerful than business keywords. Variable values are not just strings, but POJO objects as well. But when you need to put a POJO object into a process variable, note that the object must implement the serialization interface serializable.

4.2.1 Scope of process variables

The scope of a variable can be set to Global or Local.

  • Global variables

    This is the default scope of the process variable and represents a complete process instance. The name of a Global variable must be unique. If the same variable name is set, the later value overrides the previous value.

  • The Local variable

    The Local variable is only scoped to the scope of a task or execution instance, not as large as the process instance. The Local variable is used in different tasks or execution instances, so the scope of different variables is not affected by each other, and the variable name can be the same. The Local variable name can also be the same as the Global variable name without impact.

4.2.2 Using process variables

Once the process variables are defined, they can be used throughout the process definition. For example, you can use assignee on certain task attributes such as assignee, or {assignee} on certain connections, or {day<3} on certain connections.

These process variables can be used in Activiti using UEL expressions. UEL expressions can directly fetch the value of a variable, evaluate an expression with a Boolean result, and directly use properties of certain objects. For example, for the leave process created previously, if the department manager’s review is to be implemented within 3 days and the general manager’s review is to be added over 3 days, the following adjustments can be made:

1) The number of business trips is greater than or equal to 3 connection conditions

It can also be named using object arguments, such as evection. Num:

2) The number of business trips is less than 3 connection conditions

You can also use object parameter names, such as:

4.2.3 Setting the Global Process Variable

When process variables are used in the process definition, the corresponding process variables need to be set in the background JAVA code. In fact, you can design your own process variables in many parts of the process execution.

1) Set variables when starting the process

The process variable is set when the process instance is started, and the scope of the process variable is the entire process instance. This is equivalent to the Global scope. Core code:

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);

Copy the code
2) Set variables during task handling

The process variable can only be used by other nodes after the task is completed. Its scope is the whole process instance. If the key of the set process variable already has the same name in the process instance, the variable set later will replace the variable set earlier. Core code:

taskService.complete(task.getId(),map);

Copy the code

Note: If the currently executing task ID does not exist, an exception will be thrown and the process variable will fail to be set.

3) Set through the current process instance

Global variables are set by the process instance ID, which must not be completed.

  @Test
    public void setGlobalVariableByExecutionId(a){
// Current process instance execution ID, usually set to the currently executing process instance
        String executionId="2601";
/ / get processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/ / get RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
// Create a travel POJO object
        Evection evection = new Evection();
// Set the days
        evection.setNum(3d);
// Set the process variable by the process instance ID
        runtimeService.setVariable(executionId, "myLeave", evection);
// Set multiple values at once
// runtimeService.setVariables(executionId, variables)
    }

Copy the code

Note: ececutionId must be the execution ID of the currently unfinished process instance. Typically this ID sets the ID of the process instance. Process variable design is finished, but can be by runtimeService. GetVariable () to obtain the process variables

4) Through the current task Settings
@Test
	public void setGlobalVariableByTaskId(a){
		
		// Id of the current to-do task
		String taskId="1404";
/ / get processEngine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
		TaskService taskService = processEngine.getTaskService();
		Evection evection = new Evection();
		evection.setNum(3);
		// Set process variables by task
		taskService.setVariable(taskId, "evection", evection);
		// Set multiple values at once
		//taskService.setVariables(taskId, variables)
	}

Copy the code

Note: The task ID must be the ID of the current to-do task, which exists in act_ru_task. If the task has ended, an error is reported. Process variables can also be obtained using TaskService.getvariable ().

Matters needing attention

1. An error is reported if the process variable name does not exist in the UEL expression.

2. If the value of the process variable in the UEL expression is NULL, the process does not execute according to the UEL expression, and the process ends.

3. If all UEL expressions do not meet the conditions, the flow ends

4. If no condition is set on the line, the line with small flow number will be followed

5. Setting the flow variable will insert the record in act_ru_variable and act_HI_VARinst.

4.2.4 Setting the Local flow variable

Local process variables can also have multiple Settings.

1) Set during task handling

Set the local process variable during task handling. The local process variable can be used only before the task is completed. You can query the local process variable by querying historical tasks. Key code:

// Set the local variable scoped to the task
   taskService.setVariablesLocal(taskId, variables);
// Complete the task
   taskService.complete(taskId);

Copy the code
2) Through the current task Settings
@Test
public void setLocalVariableByTaskId(a){
// Id of the current to-do task
    String taskId="1404";
/ / get processEngine
    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    TaskService taskService = processEngine.getTaskService();
    Evection evection = new Evection ();
    evection.setNum(3d);
// Set process variables by task
    taskService.setVariableLocal(taskId, "evection", evection);
// Set multiple values at once
    //taskService.setVariablesLocal(taskId, variables)
}

Copy the code

Note: The task ID must be the ID of the current to-do task and must exist in act_ru_task

4.3 the gateway

Gateways are important components used to control the flow of processes and are often used in conjunction with process variables.

4.3.1 ExclusiveGateway

An exclusive gateway to implement decisions in the process. When the process executes to this gateway, all branches determine whether the condition is true and execute the branch if true,

Note: Exclusive gateways select only one branch that is true for execution. If both branch conditions are true, the exclusive gateway selects the branch with the smaller ID to execute.

Why an exclusive gateway?

It is possible to implement branching without an exclusive gateway, for example, by setting a branching condition on the condition of the connection.

Disadvantages of setting conditions on the wire: If none of the conditions are met, the process ends (an exception).

If an exclusive gateway is used to determine the direction of a branch, the following would be used:

The system throws an exception if all conditions of the outgoing line are not met.

org.activiti.engine.ActivitiException: No outgoing sequence flow of the exclusive gateway 'exclusivegateway1' could be selected for continuing the process

Copy the code

4.3.2 ParallelGateway ParallelGateway

Parallel gateways allow a process to be split into multiple branches and to be brought together. The functions of parallel gateways are based on incoming and outgoing sequential flows:

Fork: All outgoing sequential streams after parallel, creating a concurrent branch for each sequential stream.

Join convergence: All branches that arrive at the parallel gateway wait to enter the branch until all branches that enter the sequential flow arrive and the process passes through the convergence gateway.

Note that if the same parallel gateway has multiple incoming and multiple outgoing sequential flows, it has both branching and converging capabilities. In this case, the gateway will converge all incoming sequential streams before splitting them into parallel branches.

The main difference from other gateways is that parallel gateways do not resolve conditions. Even if conditions are defined in the sequential flow, they are ignored.

Note: Both the technical manager and the project manager will be asked for approval. The condition on the connection is ignored.

The technical manager and project manager are two execution branches, with two records of technical manager and project manager in the ACT_RU_EXECUTION table, and one record of ACT_RU_EXECUTION representing the process instance.

When the tasks of technical manager and project manager are all completed, they are converged at the convergence point and parallelGateway is passed through parallelGateway.

Parallel gateway is often used in service applications for countersigning tasks, that is, tasks jointly handled by multiple participants.

4.3.3 Including gateway InclusiveGateway

The inclusion gateway can be regarded as a combination of the exclusive gateway and the parallel gateway.

As with exclusive gateways, you can define conditions on outgoing sequential flows, and the containing gateway will parse them. But the main difference is that the inclusion gateway can select more than one sequential stream, just like a parallel gateway.

The functionality that includes gateways is based on incoming and outgoing sequential flows:

Branching: All conditions of the outgoing sequence stream are resolved, and the sequence stream that results in true continues execution in parallel, creating a branch for each sequence stream.

Convergence: All parallel branches arrive at the inclusion gateway and enter a waiting state until each branch that enters the sequential flow containing the process token arrives. This is the biggest difference with parallel gateways. In other words, the include gateway will only wait for incoming sequential streams that have been selected for execution. After convergence, the process continues through the containment gateway.

Note: If the number of leave days exceeds 3 days, the project manager and hr manager need to approve together. The number of days for leave is less than 3, which needs to be approved by the technical manager and the personnel manager.

All eligible branches will converge later.

4.3.4 EventGateway EventGateway

The event gateway allows you to determine the direction of flow based on events. Each outgoing sequential flow of the gateway is connected to an intermediate capture event. When a process reaches an event-based gateway, the gateway enters a wait state: execution is paused. At the same time, a relative event subscription is created for each outgoing sequential stream.

The outgoing sequential flows of the event gateway are different from normal sequential flows in that they do not actually “execute”, but instead let the process engine decide which events the process executing to the event gateway needs to subscribe to. Consider the following conditions:

  1. The event gateway must have two or more outgoing sequential flows;
  2. After the event gateway, only the intermediateCatchEvent type can be used (Activiti does not support connection to ReceiveTask based on the event gateway)
  3. Intermediate capture events connected to the event gateway must have only one entry sequence stream.

IntermediateCatchEvent used in conjunction with the event gateway:

This event supports multiple event types:

Message Event: indicates the Message Event

Singal Event: indicates a signal Event

Timer Event: indicates a Timer Event

Define the process using the event gateway:

4.4 Personal task management

4.4.1 Assign task managers

In the previous simple example, we have been able to specify the leader of a task by configuring Assignee attributes. However, in our previous example, it was simple to configure fixed task people like worker, Manager, finacer, etc. But in the real world, it’s not always the same person. It may be a system user corresponding to a job or a role. At this point, this fixed allocation is very inflexible. At this point, you can use UEL expressions in conjunction with process variables to flexibly specify. For example:

This assIGNEE0 corresponds to a process variable in Activiti.

Configured as ${user.assignee} means that the attribute value is obtained through user’s getter method

It can also be configured to use the specific method ${user.getUserId()}.

It can even be easily used in conjunction with Spring. Such as ${ldapService. FindManagerForEmployee (emp)} ldapService is a bean, spring container findManagerForEmployee is one of the bean method, Emp is activiti process variables, an emp as a parameter to ldapService. FindManagerForEmployee method.

Once the process is configured, it can be used with process variables. Such as:

 /** * set the process manager */
    @Test
    public void assigneeUEL(a){
// Get the process engine
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
/ / get RuntimeService
        RuntimeService runtimeService = processEngine.getRuntimeService();
// Set the value of assignee, and the user can set the execution of the process on the interface
        Map<String,Object> assigneeMap = new HashMap<>();
        assigneeMap.put("assignee0"."Zhang");
        assigneeMap.put("assignee1"."Manager Li");
        assigneeMap.put("assignee2"."General Manager Wang");
        assigneeMap.put("assignee3"."Zhao Finance");
// Start the process instance, and also set the value of assignee defined by the process
        runtimeService.startProcessInstanceByKey("myEvection1",assigneeMap);
/ / output
        System.out.println(processEngine.getName());
    }

Copy the code

After executing, you can see the data from the map in the ACT_ru_variable table

Matters needing attention

Because expression assignment is used, expression execution must be guaranteed during task execution. Otherwise an Activiti exception will be thrown.

4.5 Group Task Assignment

4.5.1 Set multiple candidate responsible persons

We’ve been able to give the task a lot of flexibility. But there is another very common set of requirements that cannot be supported in everyday work. For example, an order contract needs to be signed by a department manager. However, there are multiple department managers in the company, and the business only needs to find any one of them to complete the approval. In this scenario, we cannot set the person in charge by setting process variables. This is where you need to use another tool provided by Activiti – task Candidate Users.

At this point, you can set multiple candidates, candidate-uses, separated by commas.

This can be seen in the BPMN file

<userTask activiti:candidateUsers="lisi,wangwu" activiti:exclusive="true" id="_3" name="Manager approval"/>

Copy the code

This sets up a set of candidates for the task.

4.5.2 Group task handling process

After candidates are assigned to tasks, they are then required to take the initiative to claim their business and process it.

1. Query group tasks

To query the current to-do list of a specified candidate. Candidates cannot handle tasks immediately and need to claim business first.

@Test
    public void findGroupTaskList(a) {
       // The process defines the key
       String processDefinitionKey = "evection3";
       // Task candidate
       String candidateUser = "lisi";
        / / get processEngine
       ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
       / / create the TaskService
       TaskService taskService = processEngine.getTaskService();
       // Query group tasks
       List<Task> list = taskService.createTaskQuery()
              .processDefinitionKey(processDefinitionKey)
              .taskCandidateUser(candidateUser)// Based on the candidate query
              .list();
       for (Task task : list) {
           System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
           System.out.println("Process instance ID:" + task.getProcessInstanceId());
           System.out.println("Task ID:" + task.getId());
           System.out.println("Mission leader:" + task.getAssignee());
           System.out.println("Task Name:"+ task.getName()); }}Copy the code
2. Claim collection task

All candidates in this group of tasks can be picked. Turn the candidate’s group tasks into individual tasks. The candidate becomes the person in charge of the task. If you do not want to handle the task after picking up, the person in charge can also return the individual task that has been picked to the group, turning the individual task into a group task.

Candidate claim Group task

@Test
    public void claimTask(a){
         / / get processEngine
       ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
       TaskService taskService = processEngine.getTaskService();
       // Id of the task to be picked
       String taskId = "6302";
       // Task candidate ID
       String userId = "lisi";
       // Pick task
       // The user can pick even if the user is not a candidate (it is recommended to check eligibility when picking)
       // Verify that the user is qualified to pick tasks
       Task task = taskService.createTaskQuery()
              .taskId(taskId)
              .taskCandidateUser(userId)// Based on the candidate query
              .singleResult();
       if(task! =null) {// Pick task
           taskService.claim(taskId, userId);
           System.out.println("Task picked up successfully"); }}Copy the code

Note: In Activiti, you can claim a hot dance even if the user is not a candidate. Therefore, it is recommended to check whether they are qualified in the business.

Once a task is claimed, it has a specific leader. This task cannot be queried by other candidates.

3. Query personal tasks

The same as the original process

4. Personal tasks

The same as the original process

5. Return group tasks

If the individual does not want to handle the task group, you can return the task group after claiming, after returning the user is no longer the person in charge of the task.

/* * Return group tasks, from individual tasks to group tasks, and can also be handed over */
@Test
public void setAssigneeToGroupTask(a) {
    / / get processEngine
       ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
   // Use TaskService to query tasks
   TaskService taskService = processEngine.getTaskService();
   // Current to-do task
   String taskId = "6004";
   // Task manager
   String userId = "zhangsan2";
    // Check if userId is responsible for taskId, if so, return group task
   Task task = taskService
       .createTaskQuery()
       .taskId(taskId)
       .taskAssignee(userId)
       .singleResult();
    if(task ! =null) {
       // If set to null, the group task is returned without a responsible person
       taskService.setAssignee(taskId, null); }}Copy the code

Note: As you can see from this code, it is actually possible to assign responsibility to a task directly, even if the delegated user is not a candidate.

This is the transition process.

Database table operations

Example Query the current task execution table

SELECT * FROM act_ru_task 

Copy the code

Task execution table, which records the currently executed task. As the task is currently a group task, all assignee is empty, and this field is the ID of the user to be picked after the task is picked

Query Task Participant

SELECT * FROM act_ru_identitylink

Copy the code

Task participant records the current reference task user or group. If the current task has a candidate, the candidate record will be inserted into the table. If there are several candidates, several candidates will be inserted

Corresponding to act_ru_identitylink is a history table, act_hi_identitylink, which inserts records into act_ru_identitylink at the same time as inserting records into the history table. Task to complete

Activiti integration with Spring

The basic idea behind the Activiti/Spring integration is that the ProcessEngine class, which is Activiti’s core, is managed by the Spring container.

Core POM dependencies:

 <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>7.0.0. Walk</version>
    </dependency>
    <dependency>

Copy the code

Then create the activiti-spring.xml file in your classpath

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <! -- Data source -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="maxActive" value="3"/>
        <property name="maxIdle" value="1"/>
    </bean>
    <! Workflow Engine configuration bean -->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <! -- Data source -->
        <property name="dataSource" ref="dataSource"/>
        <! Spring transaction Manager -->
        <property name="transactionManager" ref="transactionManager"/>
        <! -- Database policy -->
        <property name="databaseSchemaUpdate" value="drop-create"/>
    </bean>
    <! -- Process Engine -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
    <! Service -->
    <bean id="repositoryService" factory-bean="processEngine" factory-method="getRepositoryService"/>
    <! -- Process run service -->
    <bean id="runtimeService" factory-bean="processEngine"  factory-method="getRuntimeService"/>
    <! Service -->
    <bean id="taskService" factory-bean="processEngine" factory-method="getTaskService"/>
    <! Service -->
    <bean id="historyService" factory-bean="processEngine" factory-method="getHistoryService"/>
    <! -- Transaction manager -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <! -- notice -- -- >
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <! -- Communication behavior -->
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
            <tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
        </tx:attributes>
    </tx:advice>
</beans>

Copy the code

You can then reference the corresponding service directly from the Spring container to perform specific business operations.

/** Test activiti integration with Spring successfully **/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:activiti-spring.xml")
 public class ActivitiTest {
     @Autowired
     private RepositoryService repositoryService;
     
     @Test
     public void test01(a){
         System.out.println("Deployment Object :"+repositoryService); }}Copy the code

Let’s take a look at the Activiti/Spring integration loading process.

1. Load the activiti-spring. XML configuration file

2, load SpringProcessEngineConfiguration object, the object it is injected into the dataSource object and the transactionManager object need to rely on.

3, loading ProcessEngineFactoryBean factory to create the ProcessEngine object, and ProcessEngineFactoryBean factory need to di processEngineConfiguration object.

The processEngine object is responsible for creating our Service object to simplify the Activiti development process.