刘光辉
14 小时以前 34981c30a78e8bbd7791131059a9210f9928b62c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package jnpf.flowable.util;
 
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
 
/**
 * 流程状态回写必须先于业务事件的源码级回归检查。
 */
public class FlowStatusEventOrderChecks {
 
    private static final Path REPOSITORY_ROOT = Paths.get("").toAbsolutePath().normalize();
 
    public static void main(String[] args) throws IOException {
        String operatorUtil = read(
                "jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/util/OperatorUtil.java");
        String taskController = read(
                "jnpf-flowable/jnpf-flowable-controller/src/main/java/jnpf/flowable/controller/TaskController.java");
        String operatorController = read(
                "jnpf-flowable/jnpf-flowable-controller/src/main/java/jnpf/flowable/controller/OperatorController.java");
        String autoAuditJob = read(
                "jnpf-flowable/jnpf-flowable-biz/src/main/java/jnpf/flowable/job/AutoAuditJob.java");
 
        String queuedEvent = extractMethod(operatorUtil, "public void handleTaskStatusThenEvent()");
        requireOrder(queuedEvent, "handleTaskStatus();", "handleEvent();",
                "queued events must run after form flow-state synchronization");
 
        String directEvent = extractMethod(operatorUtil,
                "public void handleTaskStatusThenEvent(FlowModel flowModel, Integer eventStatus)");
        requireOrder(directEvent, "handleTaskStatus();", "flowUtil.event(flowModel, eventStatus);",
                "direct events must run after form flow-state synchronization");
 
        requireCount(operatorUtil, "handleEvent();", 1,
                "OperatorUtil callers must use the status-then-event entry point");
        forbid(taskController, "operatorUtil.handleEvent();",
                "TaskController must not dispatch queued events directly");
        forbid(taskController, "flowUtil.event(",
                "TaskController must not dispatch direct events without state synchronization");
        forbid(operatorController, "operatorUtil.handleEvent();",
                "OperatorController must not dispatch queued events directly");
        forbid(operatorController, "flowUtil.event(",
                "OperatorController must not dispatch direct events without state synchronization");
        forbid(autoAuditJob, "operatorUtil.handleEvent();",
                "AutoAuditJob must not dispatch queued events directly");
    }
 
    private static String read(String relativePath) throws IOException {
        return new String(Files.readAllBytes(REPOSITORY_ROOT.resolve(relativePath)), StandardCharsets.UTF_8);
    }
 
    private static String extractMethod(String source, String marker) {
        int start = source.indexOf(marker);
        if (start < 0) {
            return "";
        }
        int bodyStart = source.indexOf('{', start);
        int depth = 0;
        for (int index = bodyStart; index < source.length(); index++) {
            char current = source.charAt(index);
            if (current == '{') {
                depth++;
            } else if (current == '}') {
                depth--;
                if (depth == 0) {
                    return source.substring(start, index + 1);
                }
            }
        }
        return "";
    }
 
    private static void requireOrder(String source, String first, String second, String message) {
        int firstIndex = source.indexOf(first);
        int secondIndex = source.indexOf(second);
        if (firstIndex < 0 || secondIndex < 0 || firstIndex >= secondIndex) {
            throw new AssertionError(message);
        }
    }
 
    private static void requireCount(String source, String value, int expected, String message) {
        int count = 0;
        int index = 0;
        while ((index = source.indexOf(value, index)) >= 0) {
            count++;
            index += value.length();
        }
        if (count != expected) {
            throw new AssertionError(message + ": expected=" + expected + ", actual=" + count);
        }
    }
 
    private static void forbid(String source, String forbidden, String message) {
        if (source.contains(forbidden)) {
            throw new AssertionError(message);
        }
    }
}