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 1294248a0..33820bdcd 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 @@ -20,6 +20,7 @@ import com.yomahub.liteflow.core.ScriptComponent; import com.yomahub.liteflow.core.proxy.DeclWarpBean; 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; @@ -53,6 +54,7 @@ import java.util.stream.Stream; * * @author Bryan.Zhang * @author DaleLee + * @author Jay li */ public class FlowBus { @@ -183,8 +185,16 @@ public class FlowBus { */ public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script, String language) { - addNode(nodeId, name, nodeType, ScriptComponent.ScriptComponentClassMap.get(nodeType), script, language); - } + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + + // 如果是PARSE_ONE_ON_FIRST_EXEC模式,则不进行脚本加载,而是直接把脚本内容放到node中 + if (liteflowConfig.getParseMode().equals(ParseModeEnum.PARSE_ONE_ON_FIRST_EXEC)) { + Node node = new Node(nodeId, name, nodeType, script, language); + nodeMap.put(nodeId, node); + } else { + addNode(nodeId, name, nodeType, ScriptComponent.ScriptComponentClassMap.get(nodeType), script, language); + } + } private static void addNode(String nodeId, String name, NodeTypeEnum type, Class cmpClazz, String script, String language) { @@ -245,6 +255,7 @@ public class FlowBus { } String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId(); + node.setCompiled(true); put2NodeMap(activeNodeId, node); addFallbackNode(node); } @@ -258,7 +269,16 @@ public class FlowBus { } public static Node getNode(String nodeId) { - return nodeMap.get(nodeId); + Node node = nodeMap.get(nodeId); + + if (!Objects.isNull(node) && !node.isCompiled()) { + addNode(nodeId, node.getName(), node.getType(), ScriptComponent.ScriptComponentClassMap.get(node.getType()), + node.getScript(), node.getLanguage()); + // 编译完脚本节点需重新获取 + return nodeMap.get(nodeId); + } else { + return node; + } } // 获取某一个 chainId 下的所有 nodeId diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java index ad0cb1730..2e1e9db80 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java @@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantLock; * * @author Bryan.Zhang * @author luo yi + * @author Jay li */ public class Node implements Executable, Cloneable, Rollbackable{ @@ -61,6 +62,8 @@ public class Node implements Executable, Cloneable, Rollbackable{ private String currChainId; + private boolean isCompiled = true; + // node 的 isAccess 结果,主要用于 WhenCondition 的提前 isAccess 判断,避免 isAccess 方法重复执行 private TransmittableThreadLocal accessResult = new TransmittableThreadLocal<>(); @@ -91,6 +94,17 @@ public class Node implements Executable, Cloneable, Rollbackable{ this.clazz = instance.getClass().getName(); } + + public Node(String nodeId, String name, NodeTypeEnum nodeType, String script, String language) { + this.id = nodeId; + this.name = name; + this.type = nodeType; + this.script = script; + this.language = language; + this.isCompiled = false; + } + + @Override public String getId() { return id; @@ -457,6 +471,14 @@ public class Node implements Executable, Cloneable, Rollbackable{ this.language = language; } + public boolean isCompiled() { + return isCompiled; + } + + public void setCompiled(boolean compiled) { + isCompiled = compiled; + } + @Override public T getItemResultMetaValue(Integer slotIndex) { return instance.getItemResultMetaValue(slotIndex); diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELParseModeTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELParseModeTest.java new file mode 100644 index 000000000..824aa3bca --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/LiteflowXmlScriptQLExpressELParseModeTest.java @@ -0,0 +1,72 @@ +package com.yomahub.liteflow.test.script.qlexpress; + +import cn.hutool.core.io.resource.ResourceUtil; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.enums.FlowParserTypeEnum; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +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 org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; + +/** + * PARSE_ONE_ON_FIRST_EXEC 第一次执行时解析脚本节点 + * 测试springboot下的脚本组件 + * + * @author jay li + * @since 2.12.4 + */ +@ExtendWith(SpringExtension.class) +@TestPropertySource(value = "classpath:/xml-script/application-parse-first.properties") +@SpringBootTest(classes = LiteflowXmlScriptQLExpressELParseModeTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.script.qlexpress.cmp" }) +public class LiteflowXmlScriptQLExpressELParseModeTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 测试普通脚本节点 + @Test + public void testScript1() { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals(Integer.valueOf(6), context.getData("s1")); + } + + // 测试条件脚本节点 + @Test + public void testScript2() { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>s2[条件脚本]==>b", response.getExecuteStepStr()); + } + + @Test + public void testScript3() throws Exception { + // 根据配置,加载的应该是flow.xml,执行原来的规则 + LiteflowResponse responseOld = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(responseOld.isSuccess()); + Assertions.assertEquals("d==>s2[条件脚本]==>b", responseOld.getExecuteStepStr()); + // 更改规则,重新加载,更改的规则内容从flow_update.xml里读取,这里只是为了模拟下获取新的内容。不一定是从文件中读取 + String newContent = ResourceUtil.readUtf8Str("classpath: /xml-script/flow_update.el.xml"); + // 进行刷新 + FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, newContent); + + // 重新执行chain2这个链路,结果会变 + LiteflowResponse responseNew = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(responseNew.isSuccess()); + Assertions.assertEquals("d==>s2[条件脚本_改]==>a==>s3[普通脚本_新增]", responseNew.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application-parse-first.properties b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application-parse-first.properties new file mode 100644 index 000000000..87dc7fede --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/resources/xml-script/application-parse-first.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=xml-script/flow.el.xml +liteflow.parse-mode=PARSE_ONE_ON_FIRST_EXEC \ No newline at end of file