enhancement #I821F1 使用 Jackson 序列化对象检测 chain 是否存在循环引用

This commit is contained in:
luoyi 2023-09-27 20:40:29 +08:00
parent 04cf0ae7f4
commit f64b97c7a2
3 changed files with 32 additions and 96 deletions

View File

@ -4,15 +4,15 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.exception.DataNotFoundException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.exception.*;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Condition;
@ -34,6 +34,8 @@ public class LiteFlowChainELBuilder {
private static final LFLog LOG = LFLoggerManager.getLogger(LiteFlowChainELBuilder.class);
private static ObjectMapper objectMapper =new ObjectMapper();
private Chain chain;
/**
@ -109,15 +111,12 @@ public class LiteFlowChainELBuilder {
return this;
}
/**
* <p>原来逻辑从 FlowBus 中获取相应的 chain如果 EL 表达式中出现嵌套引用 chain那么在构建 Condition 的时候可能会出现 chain 死循环引用情况</p>
* <p>故删掉从 FlowBus 中获取的逻辑直接使用新的 {@link LiteFlowChainELBuilder} 对象</p>
*
* @param chainId
* @return LiteFlowChainELBuilder
*/
public LiteFlowChainELBuilder setChainId(String chainId) {
this.chain.setChainId(chainId);
if (FlowBus.containChain(chainId)) {
this.chain = FlowBus.getChain(chainId);
} else {
this.chain.setChainId(chainId);
}
return this;
}
@ -199,6 +198,17 @@ public class LiteFlowChainELBuilder {
if (CollUtil.isNotEmpty(errorList)) {
throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
}
// 对每一个 chain 进行循环引用检测
try {
objectMapper.writeValueAsString(this.chain);
} catch (Exception e) {
e.printStackTrace();
if (e instanceof JsonMappingException) {
throw new CyclicDependencyException(StrUtil.format("There is a circular dependency in the chain[{}], please check carefully.", chain.getChainId()));
} else {
throw new ParseException(e.getMessage());
}
}
}
/**

View File

@ -15,7 +15,9 @@ import com.yomahub.liteflow.enums.InnerChainTypeEnum;
import com.yomahub.liteflow.exception.*;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.element.*;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.Rollbackable;
import com.yomahub.liteflow.flow.entity.CmpStep;
import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
import com.yomahub.liteflow.log.LFLog;
@ -193,9 +195,6 @@ public class FlowExecutor {
}
}
// 检查构建生成的 chain 的有效性
checkValidOfChain();
// 执行钩子
if (isStart) {
FlowInitHook.executeHook();
@ -215,80 +214,6 @@ public class FlowExecutor {
}
}
/**
* 检查 chain 的有效性同时重新构建 FlowBus chain将其子 chain 引用连起来
* @throws CyclicDependencyException
*/
private void checkValidOfChain() {
// 存储已经构建完的有效的 chain 对应 Id
Set<String> validChainIdSet = new HashSet<>();
// 遍历所有解析的 chain
for (Chain rootChain : FlowBus.getChainMap().values()) {
// 不存在 validChainIdSet 中的 chain说明还未检查
if (!validChainIdSet.contains(rootChain.getChainId())) {
// rootChain 相关联的 chain ID
Set<String> associatedChainIdSet = new HashSet<>();
// 检查 chain 的有效性是否存在死循环情况
checkValidOfChain(rootChain, associatedChainIdSet);
// 检查完当前 chain 能走到这里说明当前相关的 chain 是有效的
validChainIdSet.addAll(associatedChainIdSet);
}
}
}
/**
* 检查 chain 的有效性
* @param currentChain 当前遍历到的 chain 节点
* @param associatedChainIdSet rootChain 相关联的 chainId 集合
* @throws CyclicDependencyException
*/
private void checkValidOfChain(Chain currentChain, Set<String> associatedChainIdSet) {
// 判断 associatedChainIdSet 中是否已经存在对应的 chain
if (associatedChainIdSet.add(currentChain.getChainId())) {
// Set 中不存在则说明可能是父 chain 或者子 chain 未引用自身又或者子 chain 未引用其父 chain继续判断其子 chain
for (Condition condition : currentChain.getConditionList()) {
// 遍历所有 executable 列表
for (Executable executable : condition.getExecutableList()) {
// 只需判断 chain因为只有 chain 才会存在死循环依赖情况
if (executable instanceof Chain) {
// 能执行到此处必能从 FlowBus 中获取到对应的 chain故无需做非空判断
Chain childrenChain = FlowBus.getChainMap().get(executable.getId());
// 递归检查 chain 有效性
checkValidOfChain(childrenChain, associatedChainIdSet);
// 重新构建 chain condition 列表
((Chain) executable).setConditionList(childrenChain.getConditionList());
}
}
}
} else {
String errorMessage = StrUtil.format("There is a circular dependency in the chain[{}], please check carefully.", currentChain.getChainId());
LOG.error(errorMessage);
// chain 重复说明子 chain 中引用了自身或其父 chain存在死循环情况
throw new CyclicDependencyException(errorMessage);
}
}
// 此方法就是从原有的配置源主动拉取新的进行刷新
// 和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {

View File

@ -8,23 +8,23 @@
package com.yomahub.liteflow.flow.element;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.flow.executor.NodeExecutorHelper;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.flow.executor.NodeExecutorHelper;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.exception.FlowSystemException;
/**
* Node节点实现可执行器 Node节点并不是单例的每构建一次都会copy出一个新的实例
@ -47,6 +47,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
private String language;
@JsonIgnore
private NodeComponent instance;
private String tag;