Spring Boot Integration activiti Quick Start Demo

HBLOG
4 min readMay 10, 2024

--

1. What is activiti?

Activiti is a workflow engine, which can extract complex business processes from the business system and define them using the special modeling language BPMN 2.0, and the business processes are executed according to the pre-defined processes, so as to realize the management of the process flow of the system, reduce the workload of upgrading the business system due to process changes, thereby improving the robustness of the system, and also reducing the cost of system development and maintenance.

Activiti’s core service components

  1. RepositoryService: provides a series of management process deployments and process definitionsAPI。
  2. RuntimeService: manages and controls process instances while the process is running.
  3. TaskService: manages process tasks, such as task reminders, task completion, and task creation.
  4. IdentityService: Provides an API for managing process role data, including user groups, users, and the relationships between them.
  5. ManagementService: provides services that manage and maintain the process engine.
  6. HistoryService: Perform operations on the historical data of the process, including querying and deleting the historical data.
  7. FormService: a form service.

Business process model BPMN xml configuration file

An XML file, and Activiti parses it to understand what we’re trying to do. You can use the ideal plugin [Activiti BPMN visualizer] to edit this file, and the specific use of the plugin can be googled, so I won’t describe it here

2. MySQL database environment construction

Why do you need MySQL?

This is because activiti generates a lot of tables for process executions. Tables are all ACT At the beginning, it is used to record the recorded data generated by the workflow! This is shown in the figure below

Introduction to the table structure,forexample

Install

docker run --name docker-mysql-5.7 -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:5.7

initialize

create database activity;

Default user and password

msyql username:root
mysql password:123456

3. Code engineering

Purpose: to achieve process-driven

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>activiti</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
</project>

application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/activity?characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.properties.hibernate.hbm2ddl.auto=update
spring.jpa.show-sql=true
server.session.timeout=10
server.tomcat.uri-encoding=UTF-8
server.port=8088

test.bpmn20.xml

<?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:activiti="http://activiti.org/bpmn" 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:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/testm1510735932336" id="m1510735932336" name="">
<process id="leave" isExecutable="true" isClosed="false" processType="None">
<startEvent id="_2" name="StartEvent"/>
<endEvent id="_3" name="EndEvent"/>
<userTask id="approve" name="manager approve" activiti:assignee="${approve}"/>
<exclusiveGateway id="_5" name="ExclusiveGateway"/>
<sequenceFlow id="_6" sourceRef="approve" targetRef="_5"/>
<sequenceFlow id="_7" name="pass" sourceRef="_5" targetRef="_3">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${pass}]]></conditionExpression>
</sequenceFlow>
<userTask id="application" name="apply submit" activiti:assignee="${apply}"/>
<sequenceFlow id="_9" sourceRef="_2" targetRef="application"/>
<sequenceFlow id="_10" sourceRef="application" targetRef="approve"/>
<userTask id="modify" name="modify apply" activiti:assignee="${apply}"/>
<sequenceFlow id="_12" name="fail " sourceRef="_5" targetRef="modify">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${!pass}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="_13" sourceRef="modify" targetRef="approve"/>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_leave">
<bpmndi:BPMNPlane bpmnElement="leave" id="BPMNPlane_leave">
<bpmndi:BPMNShape bpmnElement="_2" id="BPMNShape__2">
<omgdc:Bounds height="35.0" width="35.0" x="15.0" y="60.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="BPMNShape__3">
<omgdc:Bounds height="35.0" width="35.0" x="630.0" y="63.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="approve" id="BPMNShape_approve">
<omgdc:Bounds height="55.0" width="85.0" x="315.0" y="50.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="BPMNShape__5">
<omgdc:Bounds height="40.0" width="40.0" x="505.0" y="60.5"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="application" id="BPMNShape_application">
<omgdc:Bounds height="55.0" width="85.0" x="135.0" y="50.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="modify" id="BPMNShape_modify">
<omgdc:Bounds height="55.0" width="85.0" x="315.0" y="150.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_6" id="BPMNEdge__6">
<omgdi:waypoint x="400.0" y="77.0"/>
<omgdi:waypoint x="505.0" y="80.5"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7">
<omgdi:waypoint x="545.0" y="80.5"/>
<omgdi:waypoint x="630.0" y="80.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9">
<omgdi:waypoint x="50.0" y="77.0"/>
<omgdi:waypoint x="135.0" y="77.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10">
<omgdi:waypoint x="220.0" y="77.0"/>
<omgdi:waypoint x="315.0" y="77.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_12" id="BPMNEdge__12">
<omgdi:waypoint x="525.0" y="100.5"/>
<omgdi:waypoint x="525.0" y="177.0"/>
<omgdi:waypoint x="400.0" y="177.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_13" id="BPMNEdge__13">
<omgdi:waypoint x="357.0" y="150.0"/>
<omgdi:waypoint x="357.0" y="105.0"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>

controller

package com.et.activiti.controller;
import com.et.activiti.service.ActivityConsumerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class HelloWorldController {
@Autowired
ActivityConsumerService activityConsumerService;
@RequestMapping(value="/startActivityDemo",method= RequestMethod.GET)
public boolean startActivityDemo(){
return activityConsumerService.startActivityDemo();
}
}

service

package com.et.activiti.service;
import org.activiti.engine.task.TaskQuery;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

public interface ActivityConsumerService {
public boolean startActivityDemo();

}
package com.et.activiti.service;
import java.util.HashMap;
import java.util.Map;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ActivityConsumerServiceImpl implements ActivityConsumerService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Override
public boolean startActivityDemo() {
System.out.println("method startActivityDemo begin....");
Map<String, Object> map = new HashMap<String, Object>();
map.put("apply", "zhangsan");
map.put("approve", "lisi");
//flow start
ExecutionEntity pi1 = (ExecutionEntity) runtimeService.startProcessInstanceByKey("leave", map);
String processId = pi1.getId();
pi1.getExecutions().forEach(row->{
String taskId = row.getTasks().get(0).getId();
taskService.complete(taskId, map);//complete first step
Task task = taskService.createTaskQuery().processInstanceId(processId).singleResult();
String taskId2 = task.getId();
map.put("pass", false);
taskService.complete(taskId2, map);//refuse apply
System.out.println("method startActivityDemo end....");
});
return false;
}
}

The above are just some key codes. For all codes, please see the code repository below.

code repository

4. Testing

You may need to login first, because activiti integrates Spring security by default, the default username user, and password are automatically generated when the program starts, as shown below

Using generated security password: 86f027ef-93c2-46ee-a054-c1ada840e64d

5. References

--

--