Business Process Management with Activiti 7 and Spring Boot 2.1
Business Process Management is a way a company manages its business processes effectively and contributes to continuous improvement of its processes. There are many BPM tools available nowadays spanning from fully supported enterprise products vs Open Source, On-premise vs Cloud.
The one I’m going to talk about is Activiti, a leading lightweight, java-centric open-source BPMN engine supporting real-world process automation needs. In this article, we can learn how Activiti v7 can be built and deployed as a Spring Boot application.
Spring Boot and Activiti in a micro-services world plays nicely together. Spring Boot provides the capability to get a production-ready service up and running in no time making it a powerful component in a distributed micro services architecture. Activiti gives the capability to implement a work flow involving human interactions and also integrate with various micro services to achieve a certain goal.
Getting Started
Let’s start implementing a user registration process implemented in BPMN2.0 standard. Here’s the diagram:
Here’s the description of the process:
- The process starts by sending the user registration for approval
- A user task is created and assigned against the person by whom the approval needs to be carried out.
- The process then waits till the approval task is completed, which is done by a user
- Once the approval task is done, a service task is initiated to inform the user micro service about the approval status
- If the registration gets approved, a task is assigned to the user to update his profile. The process waits till the task is completed by the user
- The process ends in case of a rejection or when the user completes updating his profile
The BPMN for this process can be found here
Let’s start creating a Maven project, add the dependancies required to run Spring Boot, Activiti and database. For simplicity we will use in memory H2 database (which obviously can be changed to use the database you wish to use)
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Now create the spring boot application
@SpringBootApplication
public class ProcessServer {
public static void main(String[] args) {
SpringApplication.run(ProcessServer.class, args);
}
}
Now we have to get the process running. Drop the process definition xml to the src/main/resources/processes/ folder which is the default folder to make the processes to deploy automatically when the application starts.
Create a service method which tells the Activiti engine to start a process instance upon request. This makes use of the ProcessRuntime to initiate a process instance.
public ProcessInstance startRegistrationProcess(UserRegistrationRequest userRegistrationRequest) {
return processRuntime.start(ProcessPayloadBuilder
.start()
.withProcessDefinitionKey(USER_REGISTRATION_PROCESS)
.withVariable(VAR_REGISTRATION_REQUEST,
userRegistrationRequest)
.build());
}
Next step is to give the ability to complete a task assigned to a user. For user management, we use spring security InMemoryUserDetailsManager with some hard coded users with Basic Auth, which can very well be replaced with any other user management system like LDAP in a production system.
@Bean
public UserDetailsService myUserDetailsService() {
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
String[][] usersGroupsAndRoles = {
{"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
{"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
{"admin", "password", "ROLE_ACTIVITI_ADMIN"},
};
for (String[] user : usersGroupsAndRoles) {
List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
}
return inMemoryUserDetailsManager;
}
Completing a task makes use of TaskRuntime by Activiti which can accept the task id and perform the user validation before completing a task.
taskRuntime.complete(TaskPayloadBuilder.complete()
.withTaskId(taskId)
.withVariable(VAR_REGISTRATION_REQUEST, contentToProcess)
.withVariable("comment", taskRequest.getComment())
.withVariable("approved", taskRequest.isApproved()).build());
Ok. Next step is to expose this as a REST API so these methods can be invoked by other micro services. For that we can create end points to:
- Start the process instance
@PostMapping(path = "/start",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<AppResponse> startRegistrationProcess(@RequestBody UserRegistrationRequest userRegistrationRequest)
- List tasks for a user
@GetMapping(path = "/tasks",
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<AppResponse> getTaskDefinitions(Authentication authentication)
- Complete task assigned to a user
@PostMapping(path = "/tasks/{taskId}",
consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<AppResponse> completeTask(@PathVariable String taskId, @RequestBody TaskCompletionRequest taskRequest)
Now let’s run the application using mvn spring-boot:run which starts the spring boot application and deploys the process.
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.10.RELEASE)2020-10-24 15:30:49.303 INFO 23101 --- [nio-9080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 23 ms
Testing
Testing can be done by invoking REST APIs. Let’s use Postman to do that by giving following requests. Obviously we need to give basic authentication while accessing these APIs.
- Start the process instance
POST http://localhost:9080/process/secure/user-registration/start{
"userId":"salaboy",
"approverId": "ryandawsonuk"
}
- List tasks for user
GET http://localhost:9080/process/secure/user-registration/tasks
- Complete task assigned to a user
POST http://localhost:9080/process/secure/user-registration/tasks/c0986138-15ce-11eb-862b-62a95c6a97fa{
"approved": true,
"comment": "Comment...."
}
Next steps
What we have seen in this example is the demonstration of a simple task. There are much more things that Activiti can do like performing a business rules validation, sending email notifications, consuming/publishing events, listening to task statuses etc.
Code for this example can be found in my Github Repository.