Phân tích quá trình khởi tạo Instance Quy trình trong Activiti 7

Hãy xem xét một sơ đồ quy trình đơn giản:


<process id="demo" name="demo" isExecutable="true">
    <startEvent id="sid-aee4f5b6-6b26-423d-85c3-499659fb523b"/>
    <manualTask id="sid-f10234c0-9056-4b68-8422-f967d08c1cac" activiti:exclusive="true" name="Nhiệm vụ thủ công 1"/>
    <manualTask id="sid-fad46cbb-9529-4685-83d1-95cf96dba9dc" activiti:exclusive="true" name="Nhiệm vụ thủ công 2">
        <extensionElements>
            <activiti:executionListener event="end" class="com.example.demo222.MyExecutionListener"/>
        </extensionElements>
    </manualTask>
    <userTask id="sid-7049a00c-8eb8-4018-9210-dba9ece4dcf7" name="Nhiệm vụ người dùng" activiti:assignee="zhangsan">
        <extensionElements>
            <activiti:taskListener event="complete" class="com.example.demo222.MyTaskCompleteListener"/>
        </extensionElements>
    </userTask>
    <endEvent id="sid-5be26610-d40f-4745-87a9-f20462595a45"/>
    <sequenceFlow id="sid-3b11c1ae-cdc2-4899-9014-0aa6ecc948a3" sourceRef="sid-aee4f5b6-6b26-423d-85c3-499659fb523b" targetRef="sid-f10234c0-9056-4b68-8422-f967d08c1cac"/>
    <sequenceFlow id="sid-04d1ef58-433d-4bd5-ac73-dec836ee2856" sourceRef="sid-f10234c0-9056-4b68-8422-f967d08c1cac" targetRef="sid-fad46cbb-9529-4685-83d1-95cf96dba9dc"/>
    <sequenceFlow id="sid-428c8fef-fe25-4c09-8d27-78f109f8312f" sourceRef="sid-fad46cbb-9529-4685-83d1-95cf96dba9dc" targetRef="sid-7049a00c-8eb8-4018-9210-dba9ece4dcf7"/>
    <sequenceFlow id="sid-fb1b8968-35e5-4fde-85c9-8c8dbafcf75b" sourceRef="sid-7049a00c-8eb8-4018-9210-dba9ece4dcf7" targetRef="sid-5be26610-d40f-4745-87a9-f20462595a45"/>
</process>
  

Quy trình này bao gồm hai nhiệm vụ thủ công và một nhiệm vụ người dùng.

Phụ thuộc


<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.1.0.M6</version>
</dependency>
  

Bộ lắng nghe sự kiện thực thi


package com.example.demo222;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;

public class MyExecutionListener implements ExecutionListener {
    @Override
    public void notify(DelegateExecution execution) {
        throw new RuntimeException("Lỗi từ ExecutionListener");
    }
}
  

Bộ lắng nghe nhiệm vụ


package com.example.demo222;

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;

public class MyTaskCompleteListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        throw new RuntimeException("Lỗi từ TaskListener");
    }
}
  

Khởi tạo quy trình


package com.example.demo222;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class WorkflowStartTest {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;

    @Test
    void deployProcess() {
        repositoryService.createDeployment()
                .addClasspathResource("processes/demo.bpmn20.xml")
                .name("myProcess")
                .key("myProcessKey")
                .tenantId("tenant123")
                .deploy();
    }

    @Test
    public void startProcessInstance() {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKeyAndTenantId("myProcessKey", "tenant123");
        System.out.println("Đã khởi tạo: " + processInstance.getId());
    }
}
  

Với một quy trình đơn giản và mã nguồn như trên, tôi kỳ vọng quy trình sẽ khởi tạo thành công. Tuy nhiên, quá trình khởi tạo đã thất bại.


java.lang.RuntimeException: Lỗi từ ExecutionListener
	at com.example.demo222.MyExecutionListener.notify(MyExecutionListener.java:9)
    ... (stack trace omitted for brevity) ...
  

Trước đây, tôi luôn nghĩ rằng việc khởi tạo quy trình và việc chuyển đổi giữa các nhiệm vụ là hai quá trình tách biệt. Quy trình sẽ tiếp tục chạy sau khi khởi tạo, và việc chuyển đổi nhiệm vụ sẽ diễn ra sau đó, hai quá trình này không ảnh hưởng lẫn nhau. Tuy nhiên, tôi đã nhầm. Lỗi xảy ra trong quá trình thực thi các node của quy trình dẫn đến việc khởi tạo quy trình thất bại. Chỉ sau khi xem xét mã nguồn, tôi mới nhận ra sai lầm của mình. Quá trình khởi tạo quy trình sẽ tiếp tục thực thi cho đến khi gặp một node mà nó không thể tự động xử lý (ví dụ: User Task) thì mới dừng lại.

Dưới đây là phân tích chi tiết quá trình khởi tạo một instance quy trình thông qua mã nguồn:

Sau khi tạo một instance quy trình, một đối tượng ExecutionEntity (chính là ProcessInstance) sẽ được trả về.

Instance quy trình (ExecutionEntity) sau đó được đưa vào Agenda.

Một ContinueProcessOperation được thêm vào Agenda để tiếp tục xử lý. Hãy xem xét chi tiết ContinueProcessOperation.

Trong ví dụ này, quy trình có tổng cộng 9 FlowElement.

Tại thời điểm này, FlowElement hiện tại (được biểu diễn bởi currentFlowElement trong mã) là StartEvent.

Vì nó là một FlowNode, logic xử lý node sẽ được thực thi tiếp theo.

Theo mặc định, các node được thực thi đồng bộ. Nếu bạn chỉ định activiti:async="true" trong định nghĩa nhiệm vụ, nó sẽ được thực thi không đồng bộ.

Ngoài ra, chúng ta cũng biết rằng các bộ lắng nghe được gọi đồng bộ trong quá trình thực thi quy trình. Chi tiết có thể xem tại org.activiti.engine.impl.bpmn.listener.ListenerNotificationHelper#executeExecutionListeners.

Ở đây, chúng ta liên quan đến các Behavior. Trong ví dụ này, có các loại Behavior sau:

Behavior tương ứng với node StartNoneStartEventActivityBehavior, hành vi của nó là rời khỏi hoạt động.

Việc rời khỏi hoạt động được thực hiện bằng cách thêm một TakeOutgoingSequenceFlowsOperation vào Agenda.

Chi tiết có thể xem tại org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation#leaveFlowNode.

Trước đó chúng ta đã xử lý FlowNode, bây giờ hãy xem xét SequenceFlow.

FlowElement tiếp theo được đặt làm currentFlowElement, và sau đó là Context.getAgenda().planContinueProcessOperation(execution);.

Sau khi rời khỏi hoạt động hiện tại, chúng ta sẽ đến hoạt động tiếp theo. Trong ví dụ này, hoạt động tiếp theo là Manual Task.

Manual Task là một hoạt động được bỏ qua trực tiếp.

Khi đến nhiệm vụ thủ công thứ hai, một ngoại lệ đã xảy ra trong quá trình thực thi bộ lắng nghe, dẫn đến việc khởi tạo quy trình thất bại. Giả sử không có ngoại lệ và quy trình tiếp tục, nó sẽ gặp một User Task.

Trong trường hợp xử lý UserTaskActivityBehavior, theo quy tắc thông thường, hoạt động sẽ không rời đi ngay lập tức, mà sẽ dừng lại ở đó.

Do đó, dựa trên quy trình được trình bày trong ví dụ này, chúng ta có thể rút ra các điểm sau:

  1. Instance quy trình ExecutionEntity được tạo và đưa vào Agenda để tiếp tục thực thi.
  2. Một quy trình bao gồm nhiều FlowElement, được chia thành hai loại chính: FlowNodeSequenceFlow. FlowNode có liên quan đến ActivityBehavior tương ứng.
  3. Bắt đầu từ FlowElement hiện tại, nếu đó là FlowNode, thì hành vi liên quan sẽ được thực thi; nếu đó là SequenceFlow, thì việc thực thi sẽ tiếp tục.

Quá trình chỉ có thể dừng lại ở FlowNode. Các FlowNode như StartEventManualTask có hành vi là rời khỏi hoạt động, trong khi các hoạt động như UserTask sẽ dừng lại.

Vì vậy, việc học hỏi từ mã nguồn giúp chúng ta rút ra hai kiến thức quan trọng:

  1. Trong quá trình khởi tạo instance quy trình, không chỉ đơn thuần là khởi tạo. Hệ thống sẽ tiếp tục thực thi các hoạt động cho đến khi gặp một hoạt động mà nó không thể tự động vượt qua. Nếu không có ngoại lệ nào xảy ra trong giai đoạn này, việc khởi tạo quy trình mới được coi là thành công.
  2. Các bộ lắng nghe (listeners) được thực thi đồng bộ. Nếu một ngoại lệ xảy ra trong bộ lắng nghe, nó sẽ ảnh hưởng đến việc thực thi hoạt động. Điều này tương tự như ví dụ trên, nơi ngoại lệ trong bộ lắng nghe của nhiệm vụ thủ công đã khiến việc khởi tạo instance quy trình thất bại.
  3. Theo mặc định, các hoạt động được thực thi đồng bộ.

Thẻ: Activiti bpmn Java Spring Boot workflow

Đăng vào ngày 28 tháng 7 lúc 09:07