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 ea1b80e58..26071666f 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 @@ -233,6 +233,11 @@ public class LiteFlowChainELBuilder { } } + public LiteFlowChainELBuilder setNamespace(String nameSpace){ + this.chain.setNamespace(nameSpace); + return this; + } + /** * EL表达式校验 * @param elStr EL表达式 diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java b/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java index ad64b36e5..e54c6af2f 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java @@ -32,6 +32,10 @@ public interface ChainConstant { String LANGUAGE = "language"; + String NAMESPACE = "namespace"; + + String DEFAULT_NAMESPACE = "default"; + String VALUE = "value"; String ANY = "any"; 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 a60c3222c..bee3fa8ba 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,6 +13,7 @@ import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Tuple; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.*; +import com.yomahub.liteflow.common.ChainConstant; import com.yomahub.liteflow.enums.ChainExecuteModeEnum; import com.yomahub.liteflow.enums.InnerChainTypeEnum; import com.yomahub.liteflow.enums.ParseModeEnum; @@ -283,7 +284,11 @@ public class FlowExecutor { } public List executeRouteChain(Object param, Class... contextBeanClazzArray){ - return this.executeWithRoute(param, null, contextBeanClazzArray, null); + return this.executeWithRoute(null, param, null, contextBeanClazzArray, null); + } + + public List executeRouteChain(String namespace, Object param, Class... contextBeanClazzArray){ + return this.executeWithRoute(namespace, param, null, contextBeanClazzArray, null); } public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) { @@ -291,23 +296,35 @@ public class FlowExecutor { } public List executeRouteChain(Object param, Object... contextBeanArray){ - return this.executeWithRoute(param, null, null, contextBeanArray); + return this.executeWithRoute(null, param, null, null, contextBeanArray); + } + + public List executeRouteChain(String namespace, Object param, Object... contextBeanArray){ + return this.executeWithRoute(namespace, param, null, null, contextBeanArray); } public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class... contextBeanClazzArray) { return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null); } - public List executeRouteChainWithRid(String chainId, Object param, String requestId, Class... contextBeanClazzArray) { - return this.executeWithRoute(param, requestId, contextBeanClazzArray, null); + public List executeRouteChainWithRid(Object param, String requestId, Class... contextBeanClazzArray) { + return this.executeWithRoute(null, param, requestId, contextBeanClazzArray, null); + } + + public List executeRouteChainWithRid(String namespace, Object param, String requestId, Class... contextBeanClazzArray) { + return this.executeWithRoute(namespace, param, requestId, contextBeanClazzArray, null); } public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) { return this.execute2Resp(chainId, param, requestId, null, contextBeanArray); } - public List executeRouteChainWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) { - return this.executeWithRoute(param, requestId, null, contextBeanArray); + public List executeRouteChainWithRid(Object param, String requestId, Object... contextBeanArray) { + return this.executeWithRoute(null, param, requestId, null, contextBeanArray); + } + + public List executeRouteChainWithRid(String namespace, Object param, String requestId, Object... contextBeanArray) { + return this.executeWithRoute(namespace, param, requestId, null, contextBeanArray); } // 调用一个流程并返回Future,允许多上下文的传入 @@ -353,8 +370,8 @@ public class FlowExecutor { return LiteflowResponse.newMainResponse(slot); } - private List executeWithRoute(Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray){ - List slotList = doExecuteWithRoute(param, requestId, contextBeanClazzArray, contextBeanArray); + private List executeWithRoute(String namespace, Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray){ + List slotList = doExecuteWithRoute(namespace, param, requestId, contextBeanClazzArray, contextBeanArray); return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList()); } @@ -522,15 +539,23 @@ public class FlowExecutor { MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath); } - private List doExecuteWithRoute(Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray){ + private List doExecuteWithRoute(String namespace, Object param, String requestId, Class[] contextBeanClazzArray, Object[] contextBeanArray){ if (FlowBus.needInit()) { init(true); } - List routeChainList = FlowBus.getChainMap().values().stream().filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList()); + if (StrUtil.isBlank(namespace)){ + namespace = ChainConstant.DEFAULT_NAMESPACE; + } + + String finalNamespace = namespace; + List routeChainList = FlowBus.getChainMap().values().stream() + .filter(chain -> chain.getNamespace().equals(finalNamespace)) + .filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList()); if (CollUtil.isEmpty(routeChainList)){ - throw new RouteChainNotFoundException("cannot find any route chain"); + String errorMsg = StrUtil.format("no route found for namespace[{}]", finalNamespace); + throw new RouteChainNotFoundException(errorMsg); } String finalRequestId; @@ -601,7 +626,7 @@ public class FlowExecutor { } }).filter(Objects::nonNull).collect(Collectors.toList()); - LOG.info("There are {} chains that matched the route.", resultSlotList.size()); + LOG.info("chain namespace:[{}], total size:[{}], matched size:[{}]", namespace, routeChainList.size(), resultSlotList.size()); return resultSlotList; } 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 f381be1cf..fef1c6515 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 @@ -40,6 +40,8 @@ public class Chain implements Executable{ private boolean isCompiled = true; + private String namespace; + public Chain(String chainName) { this.chainId = chainName; } @@ -195,4 +197,12 @@ public class Chain implements Executable{ public void setCompiled(boolean compiled) { isCompiled = compiled; } + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java index ab0fb928f..9c3df1206 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.parser.helper; +import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.databind.JsonNode; @@ -24,6 +25,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Function; import static com.yomahub.liteflow.common.ChainConstant.*; @@ -310,9 +312,11 @@ public class ParserHelper { // 构建chainBuilder String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); + String namespace = chainNode.get(NAMESPACE) == null? DEFAULT_NAMESPACE : chainNode.get(NAMESPACE).textValue(); + JsonNode routeJsonNode = chainNode.get(ROUTE); - LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); + LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace); // 如果有route这个标签,说明是决策表chain // 决策表链路必须有route和body这两个标签 @@ -339,9 +343,11 @@ public class ParserHelper { // 构建chainBuilder String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); + String namespace = StrUtil.blankToDefault(e.attributeValue(NAMESPACE), DEFAULT_NAMESPACE); + Element routeElement = e.element(ROUTE); - LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); + LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace); // 如果有route这个标签,说明是决策表chain // 决策表链路必须有route和body这两个标签 @@ -364,6 +370,7 @@ public class ParserHelper { } } + builder.build(); } diff --git a/liteflow-core/src/main/resources/dtd/liteflow.dtd b/liteflow-core/src/main/resources/dtd/liteflow.dtd index 8698963e2..334cee794 100644 --- a/liteflow-core/src/main/resources/dtd/liteflow.dtd +++ b/liteflow-core/src/main/resources/dtd/liteflow.dtd @@ -14,9 +14,12 @@ class CDATA #IMPLIED file CDATA #IMPLIED language (qlexpress|groovy|js|python|lua|aviator|java) #IMPLIED + enable (true|false) #IMPLIED > \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/RouteSpringbootNamespaceTest.java b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/RouteSpringbootNamespaceTest.java new file mode 100644 index 000000000..762db6a48 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/RouteSpringbootNamespaceTest.java @@ -0,0 +1,66 @@ +package com.yomahub.liteflow.test.namespace; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.NoMatchedRouteChainException; +import com.yomahub.liteflow.exception.RouteChainNotFoundException; +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.function.Executable; +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; +import java.util.List; + +/** + * springboot环境EL常规的例子测试 + * + * @author Bryan.Zhang + */ +@TestPropertySource(value = "classpath:/namespace/application.properties") +@SpringBootTest(classes = RouteSpringbootNamespaceTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.namespace.cmp" }) +public class RouteSpringbootNamespaceTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // n1 space中的两个链路都能匹配 + @Test + public void testNamespaceRoute1() throws Exception { + List responseList = flowExecutor.executeRouteChain("n1", 15, DefaultContext.class); + LiteflowResponse response1 = responseList.stream().filter( + liteflowResponse -> liteflowResponse.getChainId().equals("r_chain1") + ).findFirst().orElse(null); + + assert response1 != null; + Assertions.assertTrue(response1.isSuccess()); + Assertions.assertEquals("b==>a", response1.getExecuteStepStr()); + + LiteflowResponse response2 = responseList.stream().filter( + liteflowResponse -> liteflowResponse.getChainId().equals("r_chain2") + ).findFirst().orElse(null); + + assert response2 != null; + Assertions.assertTrue(response2.isSuccess()); + Assertions.assertEquals("a==>b", response2.getExecuteStepStr()); + } + + // n1这个namespace中没有规则被匹配上 + @Test + public void testNamespaceRoute2() throws Exception { + Assertions.assertThrows(NoMatchedRouteChainException.class, () -> flowExecutor.executeRouteChain("n1", 8, DefaultContext.class)); + } + + // 没有n3这个namespace + @Test + public void testNamespaceRoute3() throws Exception { + Assertions.assertThrows(RouteChainNotFoundException.class, () -> flowExecutor.executeRouteChain("n3", 8, DefaultContext.class)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/ACmp.java new file mode 100644 index 000000000..f2e91ff48 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/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.namespace.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-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/BCmp.java new file mode 100644 index 000000000..36455d7c7 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/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.namespace.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-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R1.java b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R1.java new file mode 100644 index 000000000..f18471f93 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.namespace.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeBooleanComponent; + +@LiteflowComponent("r1") +public class R1 extends NodeBooleanComponent { + @Override + public boolean processBoolean() throws Exception { + int testInt = this.getRequestData(); + return testInt >= 10 && testInt <= 20; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R2.java b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R2.java new file mode 100644 index 000000000..b0fcf95fa --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/java/com/yomahub/liteflow/test/namespace/cmp/R2.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.namespace.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeBooleanComponent; + +@LiteflowComponent("r2") +public class R2 extends NodeBooleanComponent { + @Override + public boolean processBoolean() throws Exception { + int testInt = this.getRequestData(); + return testInt > 100; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/application.properties b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/application.properties new file mode 100644 index 000000000..850ec86f5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=namespace/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/flow.el.xml new file mode 100644 index 000000000..8a2aeaa6f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-routechain/src/test/resources/namespace/flow.el.xml @@ -0,0 +1,30 @@ + + + + + + r1 + + + THEN(b,a); + + + + + + OR(r1,r2) + + + THEN(a,b); + + + + + + r2 + + + THEN(b,b); + + + \ No newline at end of file