From 852c18ac0b6dfb843533ce69ba6a70943a26a4bf Mon Sep 17 00:00:00 2001 From: luoyi <972849752@qq.com> Date: Mon, 30 Jun 2025 17:14:22 +0800 Subject: [PATCH] =?UTF-8?q?enhancement=20#IBQCWB=20FlowExecutor=20?= =?UTF-8?q?=E5=85=A5=E5=8F=A3=E6=94=AF=E6=8C=81=E6=89=A7=E8=A1=8C=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20EL=20=E8=A1=A8=E8=BE=BE=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builder/el/LiteFlowChainELBuilder.java | 16 +- .../yomahub/liteflow/core/FlowExecutor.java | 51 +- .../com/yomahub/liteflow/flow/FlowBus.java | 647 +++++++++--------- .../yomahub/liteflow/flow/element/Chain.java | 334 ++++----- .../BaseNodeInstanceIdManageSpi.java | 6 +- .../yomahub/liteflow/util/ElRegexUtil.java | 13 +- .../test/base/BaseELSpringbootTest.java | 23 +- 7 files changed, 589 insertions(+), 501 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java index 0a4f426d7..f19286306 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java @@ -1,7 +1,10 @@ package com.yomahub.liteflow.builder.el; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.*; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.ql.util.express.DefaultContext; @@ -27,7 +30,10 @@ import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.property.LiteflowConfigGetter; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; /** @@ -36,6 +42,7 @@ import java.util.*; * @author Bryan.Zhang * @author Jay li * @author jason + * @author luo yi * @since 2.8.0 */ public class LiteFlowChainELBuilder { @@ -268,6 +275,11 @@ public class LiteFlowChainELBuilder { } } + public LiteFlowChainELBuilder setElMd5(String md5) { + this.chain.setElMd5(md5); + return this; + } + public void build() { this.chain.setRouteItem(this.route); this.chain.setConditionList(this.conditionList); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java index e79dd56f6..37d9e4925 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java @@ -13,8 +13,10 @@ import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Tuple; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; +import cn.hutool.crypto.digest.MD5; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.common.ChainConstant; +import com.yomahub.liteflow.common.entity.ValidationResp; import com.yomahub.liteflow.enums.ChainExecuteModeEnum; import com.yomahub.liteflow.enums.ParseModeEnum; import com.yomahub.liteflow.exception.*; @@ -40,6 +42,7 @@ import com.yomahub.liteflow.slot.Slot; import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder; import com.yomahub.liteflow.spi.holder.PathContentParserHolder; import com.yomahub.liteflow.thread.ExecutorHelper; +import com.yomahub.liteflow.util.ElRegexUtil; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -51,6 +54,7 @@ import java.util.stream.Collectors; * 流程规则主要执行器类 * * @author Bryan.Zhang + * @author luo yi */ public class FlowExecutor { @@ -252,8 +256,9 @@ public class FlowExecutor { * * @param elStr EL 表达式 * @return LiteflowResponse + * @throws ELParseException */ - public LiteflowResponse execute2RespWithEL(String elStr) { + public LiteflowResponse execute2RespWithEL(String elStr) throws Exception { return this.execute2RespWithEL(elStr, null, null, DefaultContext.class); } @@ -263,8 +268,9 @@ public class FlowExecutor { * @param elStr EL 表达式 * @param param 入参 * @return LiteflowResponse + * @throws ELParseException */ - public LiteflowResponse execute2RespWithEL(String elStr, Object param) { + public LiteflowResponse execute2RespWithEL(String elStr, Object param) throws Exception { return this.execute2RespWithEL(elStr, param, null, DefaultContext.class); } @@ -276,8 +282,9 @@ public class FlowExecutor { * @param requestId 请求 ID * @param contextBeanClazzArray 上下文 Class * @return LiteflowResponse + * @throws ELParseException */ - public LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Class... contextBeanClazzArray) { + public LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Class... contextBeanClazzArray) throws Exception { return this.execute2RespWithEL(elStr, param, requestId, contextBeanClazzArray, null); } @@ -289,8 +296,9 @@ public class FlowExecutor { * @param requestId 请求 ID * @param contextBeanArray 上下文对象 * @return LiteflowResponse + * @throws ELParseException */ - public LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Object... contextBeanArray) { + public LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Object... contextBeanArray) throws Exception { return this.execute2RespWithEL(elStr, param, requestId, null, contextBeanArray); } @@ -303,14 +311,35 @@ public class FlowExecutor { * @param contextBeanClazzArray 上下文 Class 数组 * @param contextBeanArray 上下文对象数组 * @return LiteflowResponse + * @throws ELParseException */ - private LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray) { - // 调用表达式构造 chain,并且返回 UUID 作为 chainId - String chainId = IdUtil.fastSimpleUUID(); - LiteFlowChainELBuilder.createChain() - .setChainId(chainId) - .setEL(elStr) - .build(); + private LiteflowResponse execute2RespWithEL(String elStr, Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray) throws Exception { + // 规范化 el 表达式 + String normalizedEl = ElRegexUtil.normalize(elStr); + + // 校验 EL 是否正常 + ValidationResp validationResp = LiteFlowChainELBuilder.validateWithEx(normalizedEl); + + if (!validationResp.isSuccess()) { + // 实际封装的是 ELParseException 类型 + throw validationResp.getCause(); + } + + // 计算 EL MD5 值,并检查对应的 chain 是否已加载到内存中 + String elMd5 = MD5.create().digestHex(normalizedEl); + + String chainId; + + if (StrUtil.isEmpty(chainId = FlowBus.getChainIdByElMd5(elMd5))) { + // 调用表达式构造 chain,并且返回 UUID 作为 chainId + chainId = IdUtil.fastSimpleUUID(); + LiteFlowChainELBuilder.createChain() + .setChainId(chainId) + .setEL(normalizedEl) + .setElMd5(elMd5) + .build(); + } + return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, contextBeanArray); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index 0e16b183a..4eea59284 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -19,13 +19,13 @@ import com.yomahub.liteflow.core.ComponentInitializer; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.core.ScriptComponent; import com.yomahub.liteflow.core.proxy.DeclWarpBean; +import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil; import com.yomahub.liteflow.enums.FlowParserTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.enums.ParseModeEnum; import com.yomahub.liteflow.exception.ComponentCannotRegisterException; import com.yomahub.liteflow.exception.NullNodeTypeException; import com.yomahub.liteflow.flow.element.Chain; -import com.yomahub.liteflow.flow.element.Condition; import com.yomahub.liteflow.flow.element.Node; import com.yomahub.liteflow.lifecycle.LifeCycleHolder; import com.yomahub.liteflow.log.LFLog; @@ -43,14 +43,10 @@ import com.yomahub.liteflow.spi.ContextAware; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder; import com.yomahub.liteflow.util.CopyOnWriteHashMap; -import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * 流程元数据类 @@ -58,384 +54,395 @@ import java.util.stream.Stream; * @author Bryan.Zhang * @author DaleLee * @author Jay li + * @author luo yi */ public class FlowBus { - private static final LFLog LOG = LFLoggerManager.getLogger(FlowBus.class); + private static final LFLog LOG = LFLoggerManager.getLogger(FlowBus.class); - private static final Map chainMap; + private static final Map chainMap; - private static final Map nodeMap; + private static final Map nodeMap; - private static final Map fallbackNodeMap; + private static final Map fallbackNodeMap; - private static final AtomicBoolean initStat = new AtomicBoolean(false); + private static final Map elMd5Map; - static { - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - if (liteflowConfig.getFastLoad()){ - chainMap = new HashMap<>(); - nodeMap = new HashMap<>(); - fallbackNodeMap = new HashMap<>(); - }else{ - chainMap = new CopyOnWriteHashMap<>(); - nodeMap = new CopyOnWriteHashMap<>(); - fallbackNodeMap = new CopyOnWriteHashMap<>(); - } - } + private static final AtomicBoolean initStat = new AtomicBoolean(false); - public static Chain getChain(String id) { - return chainMap.get(id); - } + static { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + if (liteflowConfig.getFastLoad()) { + chainMap = new HashMap<>(); + nodeMap = new HashMap<>(); + fallbackNodeMap = new HashMap<>(); + elMd5Map = new HashMap<>(); + } else { + chainMap = new CopyOnWriteHashMap<>(); + nodeMap = new CopyOnWriteHashMap<>(); + fallbackNodeMap = new CopyOnWriteHashMap<>(); + elMd5Map = new CopyOnWriteHashMap<>(); + } + } - // 这一方法主要用于第一阶段chain的预装载 - public static void addChain(String chainName) { - if (!chainMap.containsKey(chainName)) { - chainMap.put(chainName, new Chain(chainName)); - } - } + public static Chain getChain(String id) { + return chainMap.get(id); + } - // 这个方法主要用于第二阶段的替换chain - public static void addChain(Chain chain) { - //如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainBuildLifeCycleList())){ - LifeCycleHolder.getPostProcessChainBuildLifeCycleList().forEach( - postProcessAfterChainBuildLifeCycle -> postProcessAfterChainBuildLifeCycle.postProcessBeforeChainBuild(chain) - ); - } + // 这一方法主要用于第一阶段chain的预装载 + public static void addChain(String chainName) { + if (!chainMap.containsKey(chainName)) { + chainMap.put(chainName, new Chain(chainName)); + } + } - chainMap.put(chain.getChainId(), chain); + // 这个方法主要用于第二阶段的替换chain + public static void addChain(Chain chain) { + //如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainBuildLifeCycleList())) { + LifeCycleHolder.getPostProcessChainBuildLifeCycleList().forEach( + postProcessAfterChainBuildLifeCycle -> postProcessAfterChainBuildLifeCycle.postProcessBeforeChainBuild(chain) + ); + } - //如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainBuildLifeCycleList())){ - LifeCycleHolder.getPostProcessChainBuildLifeCycleList().forEach( - postProcessAfterChainBuildLifeCycle -> postProcessAfterChainBuildLifeCycle.postProcessAfterChainBuild(chain) - ); - } - } + chainMap.put(chain.getChainId(), chain); - public static boolean containChain(String chainId) { - return chainMap.containsKey(chainId); - } + elMd5Map.put(chain.getElMd5(), chain.getChainId()); - public static boolean needInit() { - return initStat.compareAndSet(false, true); - } + //如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainBuildLifeCycleList())) { + LifeCycleHolder.getPostProcessChainBuildLifeCycleList().forEach( + postProcessAfterChainBuildLifeCycle -> postProcessAfterChainBuildLifeCycle.postProcessAfterChainBuild(chain) + ); + } + } - public static boolean containNode(String nodeId) { - return nodeMap.containsKey(nodeId); - } + public static boolean containChain(String chainId) { + return chainMap.containsKey(chainId); + } - public static void addManagedNode(String nodeId) { - ContextAware contextAware = ContextAwareHolder.loadContextAware(); - if (contextAware.hasBean(nodeId)){ - addManagedNode(nodeId, contextAware.getBean(nodeId)); - } - } + public static boolean needInit() { + return initStat.compareAndSet(false, true); + } - /** - * 添加已托管的节点(如:Spring、Solon 管理的节点) - * @param nodeId nodeId - * @param nodeComponent nodeComponent - */ - public static void addManagedNode(String nodeId, NodeComponent nodeComponent) { - // 根据class来猜测类型 - NodeTypeEnum type = NodeTypeEnum.guessType(nodeComponent.getClass()); + public static boolean containNode(String nodeId) { + return nodeMap.containsKey(nodeId); + } - if (type == null) { - throw new NullNodeTypeException(StrUtil.format("node type is null for node[{}]", nodeId)); - } + public static void addManagedNode(String nodeId) { + ContextAware contextAware = ContextAwareHolder.loadContextAware(); + if (contextAware.hasBean(nodeId)) { + addManagedNode(nodeId, contextAware.getBean(nodeId)); + } + } - Node node = new Node(ComponentInitializer.loadInstance() - .initComponent(nodeComponent, type, nodeComponent.getName(), nodeId)); - put2NodeMap(nodeId, node); - addFallbackNode(node); - } + /** + * 添加已托管的节点(如:Spring、Solon 管理的节点) + * + * @param nodeId nodeId + * @param nodeComponent nodeComponent + */ + public static void addManagedNode(String nodeId, NodeComponent nodeComponent) { + // 根据class来猜测类型 + NodeTypeEnum type = NodeTypeEnum.guessType(nodeComponent.getClass()); - /** - * 添加 node - * @param nodeId 节点id - * @param name 节点名称 - * @param type 节点类型 - * @param cmpClazz 节点组件类 - */ - public static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz) { - addNode(nodeId, name, type, cmpClazz, null, null); - } + if (type == null) { + throw new NullNodeTypeException(StrUtil.format("node type is null for node[{}]", nodeId)); + } - /** - * 添加 node - * @param nodeId 节点id - * @param name 节点名称 - * @param nodeType 节点类型 - * @param cmpClazzStr 节点组件类路径 - */ - public static void addNode(String nodeId, String name, NodeTypeEnum nodeType, String cmpClazzStr) { - Class cmpClazz; - try { - cmpClazz = Class.forName(cmpClazzStr); - } - catch (Exception e) { - throw new ComponentCannotRegisterException(e.getMessage()); - } - addNode(nodeId, name, nodeType, cmpClazz, null, null); - } + Node node = new Node(ComponentInitializer.loadInstance() + .initComponent(nodeComponent, type, nodeComponent.getName(), nodeId)); + put2NodeMap(nodeId, node); + addFallbackNode(node); + } - /** - * 添加脚本 node - * @param nodeId 节点id - * @param name 节点名称 - * @param nodeType 节点类型 - * @param script 脚本 - * @param language 语言 - */ - public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script, - String language) { + /** + * 添加 node + * + * @param nodeId 节点id + * @param name 节点名称 + * @param type 节点类型 + * @param cmpClazz 节点组件类 + */ + public static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz) { + addNode(nodeId, name, type, cmpClazz, null, null); + } + + /** + * 添加 node + * + * @param nodeId 节点id + * @param name 节点名称 + * @param nodeType 节点类型 + * @param cmpClazzStr 节点组件类路径 + */ + public static void addNode(String nodeId, String name, NodeTypeEnum nodeType, String cmpClazzStr) { + Class cmpClazz; + try { + cmpClazz = Class.forName(cmpClazzStr); + } catch (Exception e) { + throw new ComponentCannotRegisterException(e.getMessage()); + } + addNode(nodeId, name, nodeType, cmpClazz, null, null); + } + + /** + * 添加脚本 node + * + * @param nodeId 节点id + * @param name 节点名称 + * @param nodeType 节点类型 + * @param script 脚本 + * @param language 语言 + */ + public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script, + String language) { LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - // 如果是PARSE_ONE_ON_FIRST_EXEC模式,则不进行脚本加载,而是直接把脚本内容放到node中 + // 如果是PARSE_ONE_ON_FIRST_EXEC模式,则不进行脚本加载,而是直接把脚本内容放到node中 if (liteflowConfig.getParseMode().equals(ParseModeEnum.PARSE_ONE_ON_FIRST_EXEC)) { - List nodes = LiteflowMetaOperator.getNodesInAllChain(nodeId); - if (CollectionUtil.isNotEmpty(nodes)) { - nodes.forEach(node -> { + List nodes = LiteflowMetaOperator.getNodesInAllChain(nodeId); + if (CollectionUtil.isNotEmpty(nodes)) { + nodes.forEach(node -> { node.setCompiled(false); node.setScript(script); }); - } + } - Node node = new Node(nodeId, name, nodeType, script, language); - nodeMap.put(nodeId, node); - } else { - addScriptNodeAndCompile(nodeId, name, nodeType, script, language); + Node node = new Node(nodeId, name, nodeType, script, language); + nodeMap.put(nodeId, node); + } else { + addScriptNodeAndCompile(nodeId, name, nodeType, script, language); } } - /** - * 添加脚本 node,并且编译脚本 - * @param nodeId nodeId - * @param name name - * @param type type - * @param script script content - * @param language language - * @return NodeComponent instance - */ - public static NodeComponent addScriptNodeAndCompile(String nodeId, String name, NodeTypeEnum type, String script, - String language) { - addNode(nodeId, name, type, ScriptComponent.ScriptComponentClassMap.get(type), script, language); - return nodeMap.get(nodeId).getInstance(); - } + /** + * 添加脚本 node,并且编译脚本 + * + * @param nodeId nodeId + * @param name name + * @param type type + * @param script script content + * @param language language + * @return NodeComponent instance + */ + public static NodeComponent addScriptNodeAndCompile(String nodeId, String name, NodeTypeEnum type, String script, + String language) { + addNode(nodeId, name, type, ScriptComponent.ScriptComponentClassMap.get(type), script, language); + return nodeMap.get(nodeId).getInstance(); + } - private static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz, String script, - String language) { - try { - // 判断此类是否是声明式的组件,如果是声明式的组件,就用动态代理生成实例 - // 如果不是声明式的,就用传统的方式进行判断 - List cmpInstanceList = new ArrayList<>(); - if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)) { - // 如果是spring体系,把原始的类往spring上下文中进行注册,那么会走到ComponentScanner中 - // 由于ComponentScanner中已经对原始类进行了动态代理,出来的对象已经变成了动态代理类,所以这时候的bean已经是NodeComponent的子类了 - // 所以spring体系下,无需再对这个bean做二次代理 - // 非spring体系下,从2.11.4开始不再支持声明式组件 - List declWarpBeanList = DeclComponentParserHolder.loadDeclComponentParser().parseDeclBean(cmpClazz, nodeId, name); + private static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz, String script, + String language) { + try { + // 判断此类是否是声明式的组件,如果是声明式的组件,就用动态代理生成实例 + // 如果不是声明式的,就用传统的方式进行判断 + List cmpInstanceList = new ArrayList<>(); + if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)) { + // 如果是spring体系,把原始的类往spring上下文中进行注册,那么会走到ComponentScanner中 + // 由于ComponentScanner中已经对原始类进行了动态代理,出来的对象已经变成了动态代理类,所以这时候的bean已经是NodeComponent的子类了 + // 所以spring体系下,无需再对这个bean做二次代理 + // 非spring体系下,从2.11.4开始不再支持声明式组件 + List declWarpBeanList = DeclComponentParserHolder.loadDeclComponentParser().parseDeclBean(cmpClazz, nodeId, name); - cmpInstanceList = declWarpBeanList.stream().map( - declWarpBean -> (NodeComponent)ContextAwareHolder.loadContextAware().registerDeclWrapBean(nodeId, declWarpBean) - ).collect(Collectors.toList()); - } - else { - // 以node方式配置,本质上是为了适配无spring的环境,如果有spring环境,其实不用这么配置 - // 这里的逻辑是判断是否能从spring上下文中取到,如果没有spring,则就是new instance了 - // 如果是script类型的节点,因为class只有一个,所以也不能注册进spring上下文,注册的时候需要new Instance - if (!type.isScript()) { - cmpInstanceList = ListUtil - .toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz)); - } - // 如果为空 - if (cmpInstanceList.isEmpty()) { - NodeComponent cmpInstance = (NodeComponent) cmpClazz.newInstance(); - cmpInstanceList.add(cmpInstance); - } - } - // 进行初始化component - cmpInstanceList = cmpInstanceList.stream() - .map(cmpInstance -> ComponentInitializer.loadInstance() - .initComponent(cmpInstance, type, name, - cmpInstance.getNodeId() == null ? nodeId : cmpInstance.getNodeId())) - .collect(Collectors.toList()); + cmpInstanceList = declWarpBeanList.stream().map( + declWarpBean -> (NodeComponent) ContextAwareHolder.loadContextAware().registerDeclWrapBean(nodeId, declWarpBean) + ).collect(Collectors.toList()); + } else { + // 以node方式配置,本质上是为了适配无spring的环境,如果有spring环境,其实不用这么配置 + // 这里的逻辑是判断是否能从spring上下文中取到,如果没有spring,则就是new instance了 + // 如果是script类型的节点,因为class只有一个,所以也不能注册进spring上下文,注册的时候需要new Instance + if (!type.isScript()) { + cmpInstanceList = ListUtil + .toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz)); + } + // 如果为空 + if (cmpInstanceList.isEmpty()) { + NodeComponent cmpInstance = (NodeComponent) cmpClazz.newInstance(); + cmpInstanceList.add(cmpInstance); + } + } + // 进行初始化component + cmpInstanceList = cmpInstanceList.stream() + .map(cmpInstance -> ComponentInitializer.loadInstance() + .initComponent(cmpInstance, type, name, + cmpInstance.getNodeId() == null ? nodeId : cmpInstance.getNodeId())) + .collect(Collectors.toList()); - // 初始化Node,把component放到Node里去 - List nodes = cmpInstanceList.stream().map(Node::new).collect(Collectors.toList()); + // 初始化Node,把component放到Node里去 + List nodes = cmpInstanceList.stream().map(Node::new).collect(Collectors.toList()); - for (int i = 0; i < nodes.size(); i++) { - Node node = nodes.get(i); - NodeComponent cmpInstance = cmpInstanceList.get(i); - // 如果是脚本节点,则还要加载script脚本 - if (type.isScript()) { - if (StrUtil.isNotBlank(script)) { - node.setScript(script); - node.setLanguage(language); - ((ScriptComponent) cmpInstance).loadScript(script, language); - } - else { - String errorMsg = StrUtil.format("script for node[{}] is empty", nodeId); - throw new ScriptLoadException(errorMsg); - } - } - String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId(); - put2NodeMap(activeNodeId, node); - addFallbackNode(node); - } - } - catch (Exception e) { - String error = StrUtil.format("component[{}] register error", - StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name)); - LOG.error(e.getMessage()); - throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage())); - } - } + for (int i = 0; i < nodes.size(); i++) { + Node node = nodes.get(i); + NodeComponent cmpInstance = cmpInstanceList.get(i); + // 如果是脚本节点,则还要加载script脚本 + if (type.isScript()) { + if (StrUtil.isNotBlank(script)) { + node.setScript(script); + node.setLanguage(language); + ((ScriptComponent) cmpInstance).loadScript(script, language); + } else { + String errorMsg = StrUtil.format("script for node[{}] is empty", nodeId); + throw new ScriptLoadException(errorMsg); + } + } + String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId(); + put2NodeMap(activeNodeId, node); + addFallbackNode(node); + } + } catch (Exception e) { + String error = StrUtil.format("component[{}] register error", + StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name)); + LOG.error(e.getMessage()); + throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage())); + } + } - public static Node getNode(String nodeId) { - return nodeMap.get(nodeId); - } + public static Node getNode(String nodeId) { + return nodeMap.get(nodeId); + } - public static Map getNodeMap() { - return nodeMap; - } + public static Map getNodeMap() { + return nodeMap; + } - public static Map getChainMap() { - return chainMap; - } + public static Map getChainMap() { + return chainMap; + } - public static Node getFallBackNode(NodeTypeEnum nodeType){ - String key = StrUtil.format("FB_{}", nodeType.name()); - return fallbackNodeMap.get(key); - } + public static Node getFallBackNode(NodeTypeEnum nodeType) { + String key = StrUtil.format("FB_{}", nodeType.name()); + return fallbackNodeMap.get(key); + } - public static void cleanCache() { - chainMap.clear(); - nodeMap.clear(); - fallbackNodeMap.clear(); - cleanScriptCache(); - } + public static void cleanCache() { + chainMap.clear(); + nodeMap.clear(); + fallbackNodeMap.clear(); + elMd5Map.clear(); + cleanScriptCache(); + } - public static void cleanScriptCache() { - // 如果引入了脚本组件SPI,则还需要清理脚本的缓存 - try { - ScriptExecutorFactory.loadInstance().cleanScriptCache(); - } - catch (ScriptSpiException ignored) { - } - } + public static void cleanScriptCache() { + // 如果引入了脚本组件SPI,则还需要清理脚本的缓存 + try { + ScriptExecutorFactory.loadInstance().cleanScriptCache(); + } catch (ScriptSpiException ignored) { + } + } - public static void refreshFlowMetaData(FlowParserTypeEnum type, String content) throws Exception { - if (type.equals(FlowParserTypeEnum.TYPE_EL_XML)) { - new LocalXmlFlowELParser().parse(content); - } - else if (type.equals(FlowParserTypeEnum.TYPE_EL_JSON)) { - new LocalJsonFlowELParser().parse(content); - } - else if (type.equals(FlowParserTypeEnum.TYPE_EL_YML)) { - new LocalYmlFlowELParser().parse(content); - } - } + public static void refreshFlowMetaData(FlowParserTypeEnum type, String content) throws Exception { + if (type.equals(FlowParserTypeEnum.TYPE_EL_XML)) { + new LocalXmlFlowELParser().parse(content); + } else if (type.equals(FlowParserTypeEnum.TYPE_EL_JSON)) { + new LocalJsonFlowELParser().parse(content); + } else if (type.equals(FlowParserTypeEnum.TYPE_EL_YML)) { + new LocalYmlFlowELParser().parse(content); + } + } - public static boolean removeChain(String chainId) { - if (containChain(chainId)) { - chainMap.remove(chainId); - return true; - } - else { - String errMsg = StrUtil.format("cannot find the chain[{}]", chainId); - LOG.error(errMsg); - return false; - } - } + public static String getChainIdByElMd5(String elMd5) { + return elMd5Map.get(elMd5); + } - public static void removeChain(String... chainIds) { - Arrays.stream(chainIds).forEach(FlowBus::removeChain); - } + public static boolean removeChain(String chainId) { + if (containChain(chainId)) { + Chain removedChain = chainMap.remove(chainId); + // 移除 elMd5 对应的 chainId + elMd5Map.remove(removedChain.getElMd5()); + return true; + } else { + String errMsg = StrUtil.format("cannot find the chain[{}]", chainId); + LOG.error(errMsg); + return false; + } + } - // 移除节点 - public static boolean removeNode(String nodeId) { - return nodeMap.remove(nodeId) != null; - } + public static void removeChain(String... chainIds) { + Arrays.stream(chainIds).forEach(FlowBus::removeChain); + } - // 判断是否是降级组件,如果是则添加到 fallbackNodeMap - private static void addFallbackNode(Node node) { - NodeComponent nodeComponent = node.getInstance(); - FallbackCmp fallbackCmp = AnnoUtil.getAnnotation(nodeComponent.getClass(), FallbackCmp.class); - if (fallbackCmp == null) { - return; - } + // 移除节点 + public static boolean removeNode(String nodeId) { + return nodeMap.remove(nodeId) != null; + } - NodeTypeEnum nodeType = node.getType(); - String key = StrUtil.format("FB_{}", nodeType.name()); - fallbackNodeMap.put(key, node); - } + // 判断是否是降级组件,如果是则添加到 fallbackNodeMap + private static void addFallbackNode(Node node) { + NodeComponent nodeComponent = node.getInstance(); + FallbackCmp fallbackCmp = AnnoUtil.getAnnotation(nodeComponent.getClass(), FallbackCmp.class); + if (fallbackCmp == null) { + return; + } + + NodeTypeEnum nodeType = node.getType(); + String key = StrUtil.format("FB_{}", nodeType.name()); + fallbackNodeMap.put(key, node); + } // 重新加载脚本 - public static void reloadScript(String nodeId, String script) { - Node node = getNode(nodeId); - if (node == null || !node.getType().isScript()) { - return; - } + public static void reloadScript(String nodeId, String script) { + Node node = getNode(nodeId); + if (node == null || !node.getType().isScript()) { + return; + } // 更新元数据模版中的脚本 - node.setScript(script); + node.setScript(script); - // 更新Chain中的Node中的脚本 - LiteflowMetaOperator.getNodesInAllChain(nodeId).forEach(n -> n.setScript(script)); + // 更新Chain中的Node中的脚本 + LiteflowMetaOperator.getNodesInAllChain(nodeId).forEach(n -> n.setScript(script)); - ScriptExecutorFactory.loadInstance() - .getScriptExecutor(node.getLanguage()) - .load(nodeId, script); - } + ScriptExecutorFactory.loadInstance() + .getScriptExecutor(node.getLanguage()) + .load(nodeId, script); + } - // 卸载脚本节点 - public static boolean unloadScriptNode(String nodeId) { - Node node = getNode(nodeId); - if (node == null || !node.getType().isScript()) { - return false; - } - // 卸载脚本 - ScriptExecutorFactory.loadInstance() - .getScriptExecutor(node.getLanguage()) - .unLoad(nodeId); - // 移除脚本 - return removeNode(nodeId); - } + // 卸载脚本节点 + public static boolean unloadScriptNode(String nodeId) { + Node node = getNode(nodeId); + if (node == null || !node.getType().isScript()) { + return false; + } + // 卸载脚本 + ScriptExecutorFactory.loadInstance() + .getScriptExecutor(node.getLanguage()) + .unLoad(nodeId); + // 移除脚本 + return removeNode(nodeId); + } - // 重新加载规则 - public static void reloadChain(String chainId, String elContent) { - reloadChain(chainId, elContent, null); - } + // 重新加载规则 + public static void reloadChain(String chainId, String elContent) { + reloadChain(chainId, elContent, null); + } - public static void reloadChain(String chainId, String elContent, String routeContent) { - LiteFlowChainELBuilder.createChain().setChainId(chainId).setEL(elContent).setRoute(routeContent).build(); - } + public static void reloadChain(String chainId, String elContent, String routeContent) { + LiteFlowChainELBuilder.createChain().setChainId(chainId).setEL(elContent).setRoute(routeContent).build(); + } - public static void clearStat(){ - initStat.set(false); - } + public static void clearStat() { + initStat.set(false); + } - private static void put2NodeMap(String nodeId, Node node){ - // 如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessNodeBuildLifeCycleList())){ - LifeCycleHolder.getPostProcessNodeBuildLifeCycleList().forEach( - postProcessAfterNodeBuildLifeCycle -> postProcessAfterNodeBuildLifeCycle.postProcessBeforeNodeBuild(node) - ); - } + private static void put2NodeMap(String nodeId, Node node) { + // 如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessNodeBuildLifeCycleList())) { + LifeCycleHolder.getPostProcessNodeBuildLifeCycleList().forEach( + postProcessAfterNodeBuildLifeCycle -> postProcessAfterNodeBuildLifeCycle.postProcessBeforeNodeBuild(node) + ); + } - nodeMap.put(nodeId, node); + nodeMap.put(nodeId, node); - // 如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessNodeBuildLifeCycleList())){ - LifeCycleHolder.getPostProcessNodeBuildLifeCycleList().forEach( - postProcessAfterNodeBuildLifeCycle -> postProcessAfterNodeBuildLifeCycle.postProcessAfterNodeBuild(node) - ); - } - } + // 如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessNodeBuildLifeCycleList())) { + LifeCycleHolder.getPostProcessNodeBuildLifeCycleList().forEach( + postProcessAfterNodeBuildLifeCycle -> postProcessAfterNodeBuildLifeCycle.postProcessAfterNodeBuild(node) + ); + } + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Chain.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Chain.java index 489749f39..62f6e1abb 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Chain.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Chain.java @@ -10,6 +10,7 @@ package com.yomahub.liteflow.flow.element; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.BooleanUtil; +import cn.hutool.crypto.digest.MD5; import com.alibaba.ttl.TransmittableThreadLocal; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.common.ChainConstant; @@ -30,207 +31,206 @@ import java.util.List; * * @author Bryan.Zhang * @author jason + * @author luo yi */ -public class Chain implements Executable{ +public class Chain implements Executable { - private static final LFLog LOG = LFLoggerManager.getLogger(Chain.class); + private static final LFLog LOG = LFLoggerManager.getLogger(Chain.class); - private String chainId; + private String chainId; - private Executable routeItem; + private Executable routeItem; - private List conditionList = new ArrayList<>(); + private List conditionList = new ArrayList<>(); - private String el; + private String el; - private boolean isCompiled = true; + private boolean isCompiled = true; - private String namespace = ChainConstant.DEFAULT_NAMESPACE; + private String namespace = ChainConstant.DEFAULT_NAMESPACE; + + private String elMd5; private String threadPoolExecutorClass; - private final TransmittableThreadLocal runtimeIdTL = new TransmittableThreadLocal<>(); + private final TransmittableThreadLocal runtimeIdTL = new TransmittableThreadLocal<>(); - public Chain(String chainName) { - this.chainId = chainName; - } + public Chain(String chainName) { + this.chainId = chainName; + } - public Chain() { - } + public Chain() { + } - public Chain(String chainName, List conditionList) { - this.chainId = chainName; - this.conditionList = conditionList; - } + public Chain(String chainName, List conditionList) { + this.chainId = chainName; + this.conditionList = conditionList; + } - public List getConditionList() { - return conditionList; - } + public List getConditionList() { + return conditionList; + } - public void setConditionList(List conditionList) { - this.conditionList = conditionList; - } + public void setConditionList(List conditionList) { + this.conditionList = conditionList; + } - /** - * @deprecated 请使用{@link #getChainId()} - * @return chainId - */ - @Deprecated - public String getChainName() { - return chainId; - } + /** + * @return chainId + * @deprecated 请使用{@link #getChainId()} + */ + @Deprecated + public String getChainName() { + return chainId; + } - /** - * @param chainName chainId - * @deprecated 请使用 {@link #setChainId(String)} - */ - @Deprecated - public void setChainName(String chainName) { - this.chainId = chainName; - } + /** + * @param chainName chainId + * @deprecated 请使用 {@link #setChainId(String)} + */ + @Deprecated + public void setChainName(String chainName) { + this.chainId = chainName; + } - public String getChainId() { - return chainId; - } + public String getChainId() { + return chainId; + } - public void setChainId(String chainId) { - this.chainId = chainId; - } + public void setChainId(String chainId) { + this.chainId = chainId; + } - // 执行chain的主方法 - @Override - public void execute(Integer slotIndex) throws Exception { - //生成runtimeId - this.runtimeIdTL.set(System.nanoTime()); + // 执行chain的主方法 + @Override + public void execute(Integer slotIndex) throws Exception { + //生成runtimeId + this.runtimeIdTL.set(System.nanoTime()); - //如果EL还未编译,则进行编译 - if (BooleanUtil.isFalse(isCompiled)) { - LiteFlowChainELBuilder.buildUnCompileChain(this); - } + //如果EL还未编译,则进行编译 + if (BooleanUtil.isFalse(isCompiled)) { + LiteFlowChainELBuilder.buildUnCompileChain(this); + } - if (CollUtil.isEmpty(conditionList)) { - throw new FlowSystemException("no conditionList in this chain[" + chainId + "]"); - } - Slot slot = DataBus.getSlot(slotIndex); - try { - //如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainExecuteLifeCycleList())){ - LifeCycleHolder.getPostProcessChainExecuteLifeCycleList().forEach( - postProcessChainExecuteLifeCycle -> postProcessChainExecuteLifeCycle.postProcessBeforeChainExecute(chainId, slot) - ); - } + if (CollUtil.isEmpty(conditionList)) { + throw new FlowSystemException("no conditionList in this chain[" + chainId + "]"); + } + Slot slot = DataBus.getSlot(slotIndex); + try { + //如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainExecuteLifeCycleList())) { + LifeCycleHolder.getPostProcessChainExecuteLifeCycleList().forEach( + postProcessChainExecuteLifeCycle -> postProcessChainExecuteLifeCycle.postProcessBeforeChainExecute(chainId, slot) + ); + } - // 设置主ChainId - slot.setChainId(chainId); - slot.addChainInstance(this); - // 执行主体Condition - for (Condition condition : conditionList) { - condition.setCurrChainId(chainId); - condition.execute(slotIndex); - } - } - catch (ChainEndException e) { - // 这里单独catch ChainEndException是因为ChainEndException是用户自己setIsEnd抛出的异常 - // 是属于正常逻辑,所以会在FlowExecutor中判断。这里不作为异常处理 - throw e; - } - catch (Exception e) { - // 这里事先取到exception set到slot里,为了方便finally取到exception - slot.setException(e); - throw e; - }finally { - //如果有生命周期则执行相应生命周期实现 - if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainExecuteLifeCycleList())){ - LifeCycleHolder.getPostProcessChainExecuteLifeCycleList().forEach( - postProcessChainExecuteLifeCycle -> postProcessChainExecuteLifeCycle.postProcessAfterChainExecute(chainId, slot) - ); - } - runtimeIdTL.remove(); - } - } + // 设置主ChainId + slot.setChainId(chainId); + slot.addChainInstance(this); + // 执行主体Condition + for (Condition condition : conditionList) { + condition.setCurrChainId(chainId); + condition.execute(slotIndex); + } + } catch (ChainEndException e) { + // 这里单独catch ChainEndException是因为ChainEndException是用户自己setIsEnd抛出的异常 + // 是属于正常逻辑,所以会在FlowExecutor中判断。这里不作为异常处理 + throw e; + } catch (Exception e) { + // 这里事先取到exception set到slot里,为了方便finally取到exception + slot.setException(e); + throw e; + } finally { + //如果有生命周期则执行相应生命周期实现 + if (CollUtil.isNotEmpty(LifeCycleHolder.getPostProcessChainExecuteLifeCycleList())) { + LifeCycleHolder.getPostProcessChainExecuteLifeCycleList().forEach( + postProcessChainExecuteLifeCycle -> postProcessChainExecuteLifeCycle.postProcessAfterChainExecute(chainId, slot) + ); + } + runtimeIdTL.remove(); + } + } - public void executeRoute(Integer slotIndex) throws Exception { - if (routeItem == null) { - throw new FlowSystemException("no route condition or node in this chain[" + chainId + "]"); - } - Slot slot = DataBus.getSlot(slotIndex); - try { - // 设置主ChainName - slot.setChainId(chainId); + public void executeRoute(Integer slotIndex) throws Exception { + if (routeItem == null) { + throw new FlowSystemException("no route condition or node in this chain[" + chainId + "]"); + } + Slot slot = DataBus.getSlot(slotIndex); + try { + // 设置主ChainName + slot.setChainId(chainId); - // 执行决策路由 - routeItem.setCurrChainId(chainId); - routeItem.execute(slotIndex); + // 执行决策路由 + routeItem.setCurrChainId(chainId); + routeItem.execute(slotIndex); - boolean routeResult = routeItem.getItemResultMetaValue(slotIndex); + boolean routeResult = routeItem.getItemResultMetaValue(slotIndex); - slot.setRouteResult(routeResult); - } - catch (ChainEndException e) { - throw e; - } - catch (Exception e) { - slot.setException(e); - throw e; - } - } + slot.setRouteResult(routeResult); + } catch (ChainEndException e) { + throw e; + } catch (Exception e) { + slot.setException(e); + throw e; + } + } - @Override - public ExecuteableTypeEnum getExecuteType() { - return ExecuteableTypeEnum.CHAIN; - } + @Override + public ExecuteableTypeEnum getExecuteType() { + return ExecuteableTypeEnum.CHAIN; + } - @Override - public void setId(String id) { - this.chainId = id; - } + @Override + public void setId(String id) { + this.chainId = id; + } - @Override - public String getId() { - return chainId; - } + @Override + public String getId() { + return chainId; + } - @Override - public void setTag(String tag) { - //do nothing - } + @Override + public void setTag(String tag) { + //do nothing + } - @Override - public String getTag() { - return null; - } + @Override + public String getTag() { + return null; + } - public Executable getRouteItem() { - return routeItem; - } + public Executable getRouteItem() { + return routeItem; + } - public void setRouteItem(Executable routeItem) { - this.routeItem = routeItem; - } + public void setRouteItem(Executable routeItem) { + this.routeItem = routeItem; + } - public String getEl() { - return el; - } + public String getEl() { + return el; + } - public void setEl(String el) { - this.el = el; - } + public void setEl(String el) { + this.el = el; + } - public boolean isCompiled() { - return isCompiled; - } + public boolean isCompiled() { + return isCompiled; + } - public void setCompiled(boolean compiled) { - isCompiled = compiled; - } + public void setCompiled(boolean compiled) { + isCompiled = compiled; + } - public String getNamespace() { - return namespace; - } + public String getNamespace() { + return namespace; + } - public void setNamespace(String namespace) { - this.namespace = namespace; - } + public void setNamespace(String namespace) { + this.namespace = namespace; + } public String getThreadPoolExecutorClass() { return threadPoolExecutorClass; @@ -240,7 +240,15 @@ public class Chain implements Executable{ this.threadPoolExecutorClass = threadPoolExecutorClass; } - public Long getRuntimeId(){ - return runtimeIdTL.get(); - } + public Long getRuntimeId() { + return runtimeIdTL.get(); + } + + public String getElMd5() { + return elMd5 == null ? MD5.create().digestHex(el) : elMd5; + } + + public void setElMd5(String elMd5) { + this.elMd5 = elMd5; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/instanceId/BaseNodeInstanceIdManageSpi.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/instanceId/BaseNodeInstanceIdManageSpi.java index b88183623..b18ef6467 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/instanceId/BaseNodeInstanceIdManageSpi.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/instanceId/BaseNodeInstanceIdManageSpi.java @@ -1,7 +1,6 @@ package com.yomahub.liteflow.flow.instanceId; import cn.hutool.core.collection.CollUtil; -import cn.hutool.crypto.digest.MD5; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.element.Chain; import com.yomahub.liteflow.flow.element.Condition; @@ -9,11 +8,14 @@ import com.yomahub.liteflow.flow.element.Node; import com.yomahub.liteflow.flow.entity.InstanceInfoDto; import com.yomahub.liteflow.util.JsonUtil; import org.apache.commons.lang.StringUtils; + import java.util.*; + import static com.yomahub.liteflow.util.SerialsUtil.generateShortUUID; /** * @author lhh + * @author luo yi * @since 2.13.0 */ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManageSpi { @@ -156,7 +158,7 @@ public abstract class BaseNodeInstanceIdManageSpi implements NodeInstanceIdManag public void setNodesInstanceId(Condition condition, Chain chain) { NodeInstanceIdManageSpi nodeInstanceIdManageSpi = NodeInstanceIdManageSpiHolder.getInstance().getNodeInstanceIdManageSpi(); - String elMd5 = MD5.create().digestHex(chain.getEl()); + String elMd5 = chain.getElMd5(); String chainId = chain.getChainId(); List instanceIdFile = nodeInstanceIdManageSpi.readInstanceIdFile(chainId); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/ElRegexUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/ElRegexUtil.java index 8598f0531..aa758b544 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/util/ElRegexUtil.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/ElRegexUtil.java @@ -1,7 +1,5 @@ package com.yomahub.liteflow.util; -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.exception.ParseException; import java.util.regex.Matcher; @@ -56,4 +54,15 @@ public class ElRegexUtil { public static boolean isAbstractChain(String elStr) { return Pattern.compile(REGEX_ABSTRACT_HOLDER).matcher(elStr).find(); } + + /** + * 规范化 EL + * @param elStr + * @return String + */ + public static String normalize(String elStr) { + // 替换 EL 中多余空格,并在末尾保留分号 + return elStr.replaceAll("\\s", "").replaceFirst(";*$", ";"); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/BaseELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/BaseELSpringbootTest.java index be0accbeb..d0f7d56e5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/BaseELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/BaseELSpringbootTest.java @@ -1,6 +1,7 @@ package com.yomahub.liteflow.test.base; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; @@ -16,6 +17,7 @@ import javax.annotation.Resource; * springboot环境EL常规的例子测试 * * @author Bryan.Zhang + * @author luo yi */ @TestPropertySource(value = "classpath:/base/application.properties") @SpringBootTest(classes = BaseELSpringbootTest.class) @@ -64,8 +66,27 @@ public class BaseELSpringbootTest extends BaseTest { // 入参执行 EL 表达式 @Test public void testBase6() throws Exception { - LiteflowResponse response = flowExecutor.execute2RespWithEL("THEN(a,b,c)"); + LiteflowResponse response = flowExecutor.execute2RespWithEL("THEN(a, b,c);;"); Assertions.assertTrue(response.isSuccess()); + + LiteflowResponse response1 = flowExecutor.execute2RespWithEL("THEN(\na, \tb,c);"); + Assertions.assertTrue(response1.isSuccess()); + + Assertions.assertEquals(response.getChainId(), response1.getChainId()); + } + + // 入参执行 EL 表达式,测试移除 chain + @Test + public void testBase7() throws Exception { + LiteflowResponse response = flowExecutor.execute2RespWithEL("THEN(a,b, \nc);;"); + Assertions.assertTrue(response.isSuccess()); + + FlowBus.removeChain(response.getChainId()); + + LiteflowResponse response1 = flowExecutor.execute2RespWithEL("THEN(a,b, c);"); + Assertions.assertTrue(response1.isSuccess()); + + Assertions.assertNotEquals(response.getChainId(), response1.getChainId()); } }