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);
|
}
|
}
|
}
|