From 4abeabbcb1e2a60746beea982bd8add13a432c0a Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Mon, 8 Apr 2024 12:23:15 +0800 Subject: [PATCH] =?UTF-8?q?feature=20#I9DQDU=20=E5=85=81=E8=AE=B8=E5=AF=B9?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E7=BB=84=E4=BB=B6=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=85=A8=E5=B1=80=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builder/el/LiteFlowChainELBuilder.java | 79 ++++++++++++++++--- .../yomahub/liteflow/flow/element/Chain.java | 26 ++++++ .../liteflow/property/LiteflowConfig.java | 15 ++++ .../yomahub/liteflow/util/LOGOPrinter.java | 2 +- .../liteflow/springboot/LiteflowProperty.java | 11 +++ .../LiteflowPropertyAutoConfiguration.java | 1 + ...itional-spring-configuration-metadata.json | 7 ++ .../META-INF/liteflow-default.properties | 1 + .../UnCheckNodeSpringbootTest.java | 46 +++++++++++ .../liteflow/test/uncheckNode/cmp/ACmp.java | 20 +++++ .../liteflow/test/uncheckNode/cmp/BCmp.java | 21 +++++ .../liteflow/test/uncheckNode/cmp/XCmp.java | 19 +++++ .../uncheckNode/application.properties | 2 + .../test/resources/uncheckNode/flow.el.xml | 7 ++ 14 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/UnCheckNodeSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/XCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/flow.el.xml 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 a503822ba..ab8d62473 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,10 +1,7 @@ package com.yomahub.liteflow.builder.el; import cn.hutool.core.collection.CollUtil; -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 cn.hutool.core.util.*; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.ql.util.express.DefaultContext; @@ -23,6 +20,8 @@ import com.yomahub.liteflow.flow.element.condition.AndOrCondition; import com.yomahub.liteflow.flow.element.condition.NotCondition; 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.util.ElRegexUtil; import java.util.ArrayList; @@ -154,10 +153,6 @@ public class LiteFlowChainELBuilder { throw new RouteELInvalidException("the route EL can only be a boolean node, or an AND or OR expression."); } - if (Objects.isNull(routeExecutable)){ - throw new QLException(StrUtil.format("parse route el fail,el:[{}]", routeEl)); - } - // 把主要的condition加入 this.route = routeExecutable; return this; @@ -186,6 +181,14 @@ public class LiteFlowChainELBuilder { throw new FlowSystemException(errMsg); } + this.chain.setEl(elStr); + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + // 如果设置了不检查Node是否存在,那么这里是不解析的 + if (BooleanUtil.isFalse(liteflowConfig.getCheckNodeExists())){ + this.chain.setCompiled(false); + return this; + } + List errorList = new ArrayList<>(); try { DefaultContext context = new DefaultContext<>(); @@ -285,7 +288,7 @@ public class LiteFlowChainELBuilder { * 解析 EL 表达式,查找未定义的 id 并构建错误信息 * @param elStr el 表达式 */ - private String buildDataNotFoundExceptionMsg(String elStr) { + private static String buildDataNotFoundExceptionMsg(String elStr) { String msg = String.format("[node/chain is not exist or node/chain not register]\n EL: %s", StrUtil.trim(elStr)); try { @@ -336,6 +339,62 @@ public class LiteFlowChainELBuilder { } return msg; } - // #endregion + + public static void buildUnCompileChain(Chain chain){ + if (StrUtil.isBlank(chain.getEl())){ + throw new FlowSystemException(StrUtil.format("no el content in this unCompile chain[{}]", chain.getChainId())); + } + + // 如果chain已经有Condition了,那说明已经解析过了,这里只对未解析的chain进行解析 + if (CollUtil.isNotEmpty(chain.getConditionList())){ + return; + } + + List errorList = new ArrayList<>(); + try { + DefaultContext context = new DefaultContext<>(); + + // 这里一定要先放chain,再放node,因为node优先于chain,所以当重名时,node会覆盖掉chain + // 往上下文里放入所有的chain,是的el表达式可以直接引用到chain + FlowBus.getChainMap().values().forEach(chainItem -> context.put(chainItem.getChainId(), chainItem)); + + // 往上下文里放入所有的node,使得el表达式可以直接引用到nodeId + FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.getNode(nodeId))); + + // 放入当前主chain的ID + context.put(ChainConstant.CURR_CHAIN_ID, chain.getChainId()); + + // 解析el成为一个Condition + // 为什么这里只是一个Condition,而不是一个List呢 + // 这里无论多复杂的,外面必定有一个最外层的Condition,所以这里只有一个,内部可以嵌套很多层,这点和以前的不太一样 + Condition condition = (Condition) EXPRESS_RUNNER.execute(chain.getEl(), context, errorList, true, true); + + if (Objects.isNull(condition)){ + throw new QLException(StrUtil.format("parse el fail,el:[{}]", chain.getEl())); + } + + // 把主要的condition加入 + chain.setConditionList(CollUtil.toList(condition)); + + // 把chain的isCompiled设置为true + chain.setCompiled(true); + + FlowBus.addChain(chain); + } catch (QLException e) { + // EL 底层会包装异常,这里是曲线处理 + if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) { + // 构建错误信息 + String msg = buildDataNotFoundExceptionMsg(chain.getEl()); + throw new ELParseException(msg); + }else if (ObjectUtil.isNotNull(e.getCause())){ + throw new ELParseException(e.getCause().getMessage()); + }else{ + throw new ELParseException(e.getMessage()); + } + } catch (Exception e) { + String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId()); + throw new ELParseException(errMsg + e.getMessage()); + } + } } 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 236cbc568..f381be1cf 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 @@ -9,6 +9,8 @@ package com.yomahub.liteflow.flow.element; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.BooleanUtil; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.exception.ChainEndException; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; @@ -34,6 +36,10 @@ public class Chain implements Executable{ private List conditionList = new ArrayList<>(); + private String el; + + private boolean isCompiled = true; + public Chain(String chainName) { this.chainId = chainName; } @@ -81,6 +87,10 @@ public class Chain implements Executable{ // 执行chain的主方法 @Override public void execute(Integer slotIndex) throws Exception { + if (BooleanUtil.isFalse(isCompiled)) { + LiteFlowChainELBuilder.buildUnCompileChain(this); + } + if (CollUtil.isEmpty(conditionList)) { throw new FlowSystemException("no conditionList in this chain[" + chainId + "]"); } @@ -169,4 +179,20 @@ public class Chain implements Executable{ public void setRouteItem(Executable routeItem) { this.routeItem = routeItem; } + + public String getEl() { + return el; + } + + public void setEl(String el) { + this.el = el; + } + + public boolean isCompiled() { + return isCompiled; + } + + public void setCompiled(boolean compiled) { + isCompiled = compiled; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java b/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java index 8696a34ab..ea8b99185 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java @@ -118,6 +118,9 @@ public class LiteflowConfig { //是否快速加载规则,如果快速加载规则意味着不用copyOnWrite机制了 private Boolean fastLoad; + //检查node是否存在 + private Boolean checkNodeExists; + public Boolean getEnableMonitorFile() { return enableMonitorFile; } @@ -490,4 +493,16 @@ public class LiteflowConfig { public void setFastLoad(Boolean fastLoad) { this.fastLoad = fastLoad; } + + public Boolean getCheckNodeExists() { + if (ObjectUtil.isNull(checkNodeExists)){ + return Boolean.TRUE; + }else{ + return checkNodeExists; + } + } + + public void setCheckNodeExists(Boolean checkNodeExists) { + this.checkNodeExists = checkNodeExists; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/LOGOPrinter.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/LOGOPrinter.java index c2d634a43..0b487b59c 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/util/LOGOPrinter.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/LOGOPrinter.java @@ -31,7 +31,7 @@ public class LOGOPrinter { str.append(" | |___ | | | | | |__|_____| _| | |__| |_| |\\ V V / \n"); str.append(" |_____|___| |_| |_____| |_| |_____\\___/ \\_/\\_/ \n\n"); str.append(" Version: " + VERSION_NO + "\n"); - str.append(" This Is My Rule.\n"); + str.append(" This Is Our Rule.\n"); str.append(" website:https://liteflow.cc\n"); str.append(" wechat:bryan_31\n"); str.append( diff --git a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java index 63271ce03..085571564 100644 --- a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java +++ b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java @@ -94,6 +94,9 @@ public class LiteflowProperty { //是否快速加载规则,如果快速加载规则意味着不用copyOnWrite机制了 private boolean fastLoad; + //是否检查节点存在 + private boolean checkNodeExists; + public boolean isEnableMonitorFile() { return enableMonitorFile; } @@ -311,4 +314,12 @@ public class LiteflowProperty { public void setFastLoad(boolean fastLoad) { this.fastLoad = fastLoad; } + + public boolean isCheckNodeExists() { + return checkNodeExists; + } + + public void setCheckNodeExists(boolean checkNodeExists) { + this.checkNodeExists = checkNodeExists; + } } diff --git a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java index 1a7a092cc..61629477e 100644 --- a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java +++ b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java @@ -49,6 +49,7 @@ public class LiteflowPropertyAutoConfiguration { liteflowConfig.setParallelLoopExecutorClass(property.getParallelLoopExecutorClass()); liteflowConfig.setFallbackCmpEnable(property.isFallbackCmpEnable()); liteflowConfig.setFastLoad(property.isFastLoad()); + liteflowConfig.setCheckNodeExists(property.isCheckNodeExists()); liteflowConfig.setEnableLog(liteflowMonitorProperty.isEnableLog()); liteflowConfig.setQueueLimit(liteflowMonitorProperty.getQueueLimit()); liteflowConfig.setDelay(liteflowMonitorProperty.getDelay()); diff --git a/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json index f5eb0bec5..05a3bf082 100644 --- a/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -159,6 +159,13 @@ "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", "defaultValue": false }, + { + "name": "liteflow.check-node-exists", + "type": "java.lang.Boolean", + "description": "Whether to check the existence of a node or not.", + "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", + "defaultValue": true + }, { "name": "liteflow.monitor.enable-log", "type": "java.lang.Boolean", diff --git a/liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties b/liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties index 5f69bdbb2..f860fc981 100644 --- a/liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties +++ b/liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties @@ -17,6 +17,7 @@ liteflow.node-executor-class=com.yomahub.liteflow.flow.executor.DefaultNodeExecu liteflow.print-execution-log=true liteflow.fallback-cmp-enable=false liteflow.fast-load=false +liteflow.check-node-exists=true liteflow.parallel-max-workers=16 liteflow.parallel-queue-limit=512 liteflow.parallel-loop-executor-class=com.yomahub.liteflow.thread.LiteFlowDefaultParallelLoopExecutorBuilder diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/UnCheckNodeSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/UnCheckNodeSpringbootTest.java new file mode 100644 index 000000000..6bccce000 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/UnCheckNodeSpringbootTest.java @@ -0,0 +1,46 @@ +package com.yomahub.liteflow.test.uncheckNode; + +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.test.uncheckNode.cmp.XCmp; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; + +import javax.annotation.Resource; + +/** + * springboot环境不检查Node存在与否测试 + * + * @author Bryan.Zhang + */ +@TestPropertySource(value = "classpath:/uncheckNode/application.properties") +@SpringBootTest(classes = UnCheckNodeSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.uncheckNode.cmp" }) +public class UnCheckNodeSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // X不存在,能启动就算成功 + @Test + public void testUncheckNode1() throws Exception { + + } + + // X不存在,但是启动好加入进去,就可以 + @Test + public void testUncheckNode2() throws Exception { + LiteFlowNodeBuilder.createCommonNode().setId("x").setClazz(XCmp.class).build(); + + LiteflowResponse response = flowExecutor.execute2Resp("chain1"); + Assertions.assertTrue(response.isSuccess()); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/ACmp.java new file mode 100644 index 000000000..fd412889c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/ACmp.java @@ -0,0 +1,20 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.uncheckNode.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/BCmp.java new file mode 100644 index 000000000..6ff340d2d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/BCmp.java @@ -0,0 +1,21 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.uncheckNode.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/XCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/XCmp.java new file mode 100644 index 000000000..31f03e955 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/uncheckNode/cmp/XCmp.java @@ -0,0 +1,19 @@ +/** + *

Title: liteflow

+ *

Description: 轻量级的组件式流程框架

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.uncheckNode.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +public class XCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("XCmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/application.properties new file mode 100644 index 000000000..b4ccad578 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/application.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=uncheckNode/flow.el.xml +liteflow.check-node-exists=false \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/flow.el.xml new file mode 100644 index 000000000..3c29ee6ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/uncheckNode/flow.el.xml @@ -0,0 +1,7 @@ + + + + + THEN(a,b,x); + + \ No newline at end of file