enhancement #I821F1 使用 Jackson 序列化对象检测 chain 是否存在循环引用
This commit is contained in:
parent
04cf0ae7f4
commit
f64b97c7a2
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue