How to configure spring activiti
Spring Activiti is a lightweight workflow and Business Process Management (BPM) platform designed for managing and automating business processes. Built on top of the Activiti BPMN engine, it integrates seamlessly with the Spring ecosystem, making it easy to embed within Java applications.
Activiti provides support for BPMN 2.0 (Business Process Model and Notation), which is an open standard for modeling business processes. By using BPMN, developers and business analysts can collaborate on defining workflows in a visual and standard format. The BPMN diagrams can be exported as XML files and directly used within Spring Activiti to define the workflow of tasks, decisions, and other activities.
How BPMN Files Drive User Workflow
BPMN files are central to driving user workflows in Activiti. Here's how it works:
BPMN Process Definition: A BPMN file defines the entire process, including tasks, decision gateways, and sequence flows. It outlines both user-driven tasks (e.g., approvals) and system-driven tasks (e.g., automated notifications or service calls).
Process Deployment: The BPMN file, typically an XML file, is deployed to the Activiti engine, which reads the definition and instantiates processes as needed.
User Task Assignment: BPMN files include "User Tasks," which require human intervention. When the workflow reaches these tasks, it waits for a user to complete them before moving to the next step. These tasks can be assigned to specific users or groups.
Event-Driven Execution: As users interact with the workflow (e.g., completing tasks, making decisions), the Activiti engine evaluates the BPMN definitions and proceeds to the next activity. This can include branching logic (e.g., decision gateways), triggering sub-processes, or sending notifications.
By leveraging BPMN with Spring Activiti, developers can build scalable, maintainable, and easy-to-extend business processes that align with industry-standard process modeling practices.
Below is an example of how we get actions from BPMN files in the FFQMS system
public List<String> getTaskActions(Batch batch, Lot lot, List<RoleCategory> roleCategories, Map userData, List<String> states) {
List actions = new ArrayList();
String organizationId = userData.getOrDefault("manufacturerId", "").toString();
String type = "MODULE";
if (TextUtils.isEmpty(organizationId)) {
organizationId = userData.getOrDefault("labId", "1").toString();
type = "LAB";
}
String instanceId = batch == null ? lot.getTaskId() : batch.getTaskId();
String state = batch == null ? lot.getState().getName() : batch.getState().getName();
Task task = taskService.createTaskQuery().taskId(instanceId).singleResult();
String finalOrganizationId = organizationId;
String finalType = type;
if ((task == null || roleCategories.stream().noneMatch((roleCategory ->
task.getAssignee().contains(roleCategory.getRoleName() + "_" + finalOrganizationId + "_" + finalType +
"_" + roleCategory.getCategory().getName()))))) {
return actions;
}
if (task == null) return actions;
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
Collection<FlowElement> flowElements = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()).getProcess(processInstance.getName()).getFlowElements();
for (FlowElement element : flowElements) {
if (element instanceof SequenceFlow && state.toLowerCase().equals(((SequenceFlow) element).getSourceRef().toLowerCase())) {
if (!states.contains(((SequenceFlow) element).getTargetRef())) {
actions.add(((SequenceFlow) element).getTargetRef());
}
}
}
return actions;
}
The method getTaskActions
returns the list of actions possible at the current stage of the workflow to the end user using different parameters passed to the method.
Below shows the notify method of spring activiti that is responsible for handling the delegation of tasks in a workflow related to Batch
processing. It processes a task event, parses the task description, and updates the Batch
entity's state and task assignment based on specific conditions. This method is part of a larger workflow management system, where it interacts with various services like BatchManager
, StateManager
, RoleCategoryManager
, and TaskService
.
public void notify(DelegateTask delegateTask) {
ObjectMapper mapper = new ObjectMapper();
HashMap map;
try {
map = mapper.readValue(delegateTask.getDescription(), HashMap.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
// Retrieve the Batch entity
Batch batch = getBatchManager().findById(Long.parseLong(map.getOrDefault("batchId", 0).toString()));
// Retrieve the State entity
State state = getStateManager().findByName(delegateTask.getName());
// Retrieve RoleCategories based on categoryId and state
List<RoleCategory> roleCategories = getRoleCategoryManager().findAllByCategoryIdAndState(
Long.parseLong(map.getOrDefault("categoryId", 0).toString()), state.getId());
if (delegateTask.getEventName().equals("create")) {
// Get organization ID and assign task roles
String orgId = map.getOrDefault("manufacturerId", "").toString();
ArrayList<String> assigneeList = new ArrayList<>();
for(RoleCategory roleCategory : roleCategories) {
RoleCategoryType type = roleCategory.getRoleCategoryType();
String assigneeString = roleCategory.getRoleName() + "_" + orgId + "_" + type.name() + "_" +
roleCategory.getCategory().getName();
assigneeList.add(assigneeString);
}
// Update Batch entity and assign task
batch.setTaskId(delegateTask.getId());
batch.setState(state);
delegateTask.setAssignee(String.join(",", assigneeList));
delegateTask.setOwner(batch.getId().toString());
// Handling specific task definition keys
Map<String, Object> variables = new HashMap<>();
if (delegateTask.getTaskDefinitionKey().equals("batchToDispatch") || delegateTask.getTaskDefinitionKey().equals("rejected")) {
getTaskService().complete(delegateTask.getId());
}
if (delegateTask.getTaskDefinitionKey().equals("batchSampleLabTestDone") || delegateTask.getTaskDefinitionKey().equals("selfTestedBatch")) {
String sampleTestResult = delegateTask.getVariable("sampleTestResult").toString();
variables.put(delegateTask.getTaskDefinitionKey() + "_rejected", false);
variables.put(delegateTask.getTaskDefinitionKey() + "_batchToDispatch", false);
if (sampleTestResult.equals(SampleTestResult.TEST_FAILED.name())) {
variables.put(delegateTask.getTaskDefinitionKey() + "_rejected", true);
} else {
variables.put(delegateTask.getTaskDefinitionKey() + "_batchToDispatch", true);
}
getTaskService().complete(batch.getTaskId(), variables);
}
}
// Save updated Batch entity
getBatchManager().save(batch);
}
Key Responsibilities
Parsing Task Description: The method reads and parses a JSON description of the task using Jackson's
ObjectMapper
. This JSON contains relevant data likebatchId
,categoryId
, andmanufacturerId
.Retrieving Batch and State: It retrieves the relevant
Batch
entity using thebatchId
from the parsed JSON, and the correspondingState
of the task usingStateManager
.Assigning Task Roles: The method determines the roles responsible for the task based on the
categoryId
andstate
. It assigns the task to specific users based on their roles and updates the task’s assignee list.Task Completion Based on Conditions: Depending on the task's definition key, it completes the task, updates variables, and handles scenarios like "batch rejection" or "batch ready for dispatch."
Key Features
JSON Parsing with
ObjectMapper
:The task description is parsed from a JSON string into a
HashMap
using Jackson'sObjectMapper
. This allows the method to access task-related data, such asbatchId
andmanufacturerId
, to proceed with processing.
Batch and State Retrieval:
The method retrieves the
Batch
entity usingbatchId
and the correspondingState
usingStateManager
. These are essential for updating the state of the batch and assigning the task correctly.
Role-Based Task Assignment:
The method assigns the task to users based on their role categories. It constructs the assignee string using a combination of the role name, organization ID, and role category type. The task is then assigned to these users using the
setAssignee
method.
Conditional Task Completion:
The method checks the task's definition key to decide if it should automatically complete the task. For tasks like "batch dispatch" or "batch rejection," the task is completed immediately.
For tasks like "batch sample lab test," it handles the test result and sets additional variables based on whether the sample test passed or failed. The task is completed with these variables.
Updating and Saving Batch:
The method updates the
Batch
entity with the new task ID and state. Once the processing is complete, it saves the updated batch using theBatchManager
.
Code Explanation
1. Parsing Task Description
map = mapper.readValue(delegateTask.getDescription(), HashMap.class);
The task's description is a JSON string that contains key details about the task (e.g.,
batchId
,categoryId
). This line parses the description into aHashMap
, allowing easy access to these values.
2. Retrieving Batch and State
Batch batch = getBatchManager().findById(Long.parseLong(map.getOrDefault("batchId", 0).toString()));
State state = getStateManager().findByName(delegateTask.getName());
The method retrieves the
Batch
entity by converting thebatchId
from the JSON map into aLong
and using theBatchManager
.It also retrieves the
State
entity corresponding to the task's name using theStateManager
.
3. Assigning Task to Users
ArrayList<String> assigneeList = new ArrayList<>();
for (RoleCategory roleCategory : roleCategories) {
RoleCategoryType type = roleCategory.getRoleCategoryType();
String assigneeString = roleCategory.getRoleName() + "_" + orgId + "_" + type.name() + "_" +
roleCategory.getCategory().getName();
assigneeList.add(assigneeString);
}
delegateTask.setAssignee(String.join(",", assigneeList));
The method constructs a list of assignee strings for the task based on the role category, organization ID, and category name.
The assignees are then set on the task using the
setAssignee
method.
4. Completing Task Based on Conditions
if (delegateTask.getTaskDefinitionKey().equals("batchToDispatch") || delegateTask.getTaskDefinitionKey().equals("rejected")) {
getTaskService().complete(delegateTask.getId());
}
Depending on the task definition key, the task may be completed immediately, especially for "batch dispatch" or "rejected" tasks.
For more complex tasks like "batchSampleLabTestDone," additional variables are set based on the sample test result before completing the task.
Conclusion
The notify
method is a crucial part of the workflow engine, managing task assignment and completion based on batch processing states. It interacts with multiple components to ensure tasks are assigned to the right users and completed as per the defined workflow. This method integrates JSON parsing, role-based task assignment, and conditional logic to handle various scenarios in batch processing workflows.
Last updated