diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomStatefulException.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomStatefulException.java new file mode 100644 index 000000000..10cda69b6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomStatefulException.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.parallelLoop; + +import com.yomahub.liteflow.exception.LiteFlowException; + +/** + * 用户自定义带状态码的异常 + */ +public class CustomStatefulException extends LiteFlowException { + + public CustomStatefulException(String code, String message) { + super(code, message); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomThreadExecutor.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomThreadExecutor.java new file mode 100644 index 000000000..72773f38a --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/CustomThreadExecutor.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.parallelLoop; + +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.thread.ExecutorBuilder; + +import java.util.concurrent.ExecutorService; + +public class CustomThreadExecutor implements ExecutorBuilder { + + @Override + public ExecutorService buildExecutor() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + // 只有在非spring的场景下liteflowConfig才会为null + if (ObjectUtil.isNull(liteflowConfig)) { + liteflowConfig = new LiteflowConfig(); + } + return buildDefaultExecutor(liteflowConfig.getParallelMaxWorkers(), liteflowConfig.getParallelMaxWorkers(), + liteflowConfig.getParallelQueueLimit(), "customer-loop-thead-"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/ParallelLoopELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/ParallelLoopELDeclSpringbootTest.java new file mode 100644 index 000000000..bb3278517 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/ParallelLoopELDeclSpringbootTest.java @@ -0,0 +1,117 @@ +package com.yomahub.liteflow.test.parallelLoop; + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.LiteFlowException; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +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.junit4.SpringRunner; + +import javax.annotation.Resource; +import java.util.List; +import java.util.regex.Pattern; + +/** + * springboot环境EL异步循环测试 + * + * @author zhhhhy + */ +@RunWith(SpringRunner.class) +@TestPropertySource(value = "classpath:/parallelLoop/application.properties") +@SpringBootTest(classes = ParallelLoopELDeclSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.parallelLoop.cmp" }) +public class ParallelLoopELDeclSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + //测试并行FOR循环,循环次数直接在el中定义 + @Test + public void testParallelLoop1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assert.assertTrue(response.isSuccess()); + } + + //测试并行FOR循环,循环次数由For组件定义 + @Test + public void testParallelLoop2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assert.assertTrue(response.isSuccess()); + } + + //测试并行FOR循环中的BREAK组件能够正常发挥作用 + @Test + public void testParallelLoop3() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assert.assertTrue(response.isSuccess()); + } + + //测试并行FOR循环中,主线程是否会正常等待所有并行子项完成后再继续执行 + @Test + public void testParallelLoop4() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assert.assertTrue(response.isSuccess()); + } + + @Test + //测试并行FOR循环中,某个并行子项抛出异常 + public void testParallelLoop5() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assert.assertFalse(response.isSuccess()); + Assert.assertEquals("300", response.getCode()); + Assert.assertNotNull(response.getCause()); + Assert.assertTrue(response.getCause() instanceof LiteFlowException); + Assert.assertNotNull(response.getSlot()); + } + + //并行的条件循环 + @Test + public void testParallelLoop6() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assert.assertTrue(response.isSuccess()); + } + + //并行的迭代循环 + @Test + public void testParallelLoop7() throws Exception { + List list = ListUtil.toList("1", "2", "3", "4", "5"); + LiteflowResponse response = flowExecutor.execute2Resp("chain7", list); + Assert.assertTrue(response.isSuccess()); + } + + //测试并行FOR循环中的index + @Test + public void testParallelLoop8() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assert.assertTrue(response.isSuccess()); + String regex = "(?!.*(.).*\\1)[0-4]{5}"; //匹配不包含重复数字的0-4的5位数字 + Pattern pattern = Pattern.compile(regex); + //e1,e2,e3分别并行执行5次,因此单个循环的顺序可以是任意的 + Assert.assertTrue(pattern.matcher(context.getData("loop_e1")).matches()); + Assert.assertTrue(pattern.matcher(context.getData("loop_e2")).matches()); + Assert.assertTrue(pattern.matcher(context.getData("loop_e3")).matches()); + } + + + //测试自定义线程池配置是否生效 + @Test + public void testParallelLoop9() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assert.assertTrue(response.isSuccess()); + Assert.assertTrue(context.getData("threadName").toString().startsWith("customer-loop-thead")); + } + + + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/cmp/CmpConfig.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/cmp/CmpConfig.java new file mode 100644 index 000000000..83fedcabf --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/parallelLoop/cmp/CmpConfig.java @@ -0,0 +1,120 @@ +package com.yomahub.liteflow.test.parallelLoop.cmp; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.slot.DefaultContext; +import com.yomahub.liteflow.test.exception.CustomStatefulException; + +import java.util.Iterator; +import java.util.List; + +@LiteflowComponent +public class CmpConfig { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a") + public void processA(NodeComponent bindCmp) { + System.out.println("ACmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b") + public void processB(NodeComponent bindCmp) { + + System.out.println("BCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "c") + public void processC(NodeComponent bindCmp) { + System.out.println("CCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "d") + public void processD(NodeComponent bindCmp) { + DefaultContext context = bindCmp.getFirstContextBean(); + String key = "test"; + if (context.hasData(key)) { + int count = context.getData(key); + context.setData(key, ++count); + } else { + context.setData(key, 1); + } + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "e") + public void processE(NodeComponent bindCmp) { + synchronized (this){ + DefaultContext context = bindCmp.getFirstContextBean(); + String key = StrUtil.format("{}_{}", "loop", bindCmp.getTag()); + if (context.hasData(key)) { + String loopStr = context.getData(key); + String loopStrReturn = StrUtil.format("{}{}", loopStr, bindCmp.getLoopIndex()); + context.setData(key, loopStrReturn); + } else { + context.setData(key, bindCmp.getLoopIndex().toString()); + } + } + } + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "f") + public void processF(NodeComponent bindCmp){ + try { + System.out.println("FCmp start to sleep 5s"); + Thread.sleep(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + System.out.println("FCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "g") + public void processG(NodeComponent bindCmp){ + if(bindCmp.getLoopIndex()==1){ + throw new CustomStatefulException("300", "chain execute custom stateful execption"); + } + System.out.println("GCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "h") + public void processH(NodeComponent bindCmp){ + DefaultContext context = bindCmp.getFirstContextBean(); + context.setData("threadName", Thread.currentThread().getName()); + System.out.println("HCmp executed!"); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "it", nodeType = NodeTypeEnum.ITERATOR) + public Iterator processIT(NodeComponent bindCmp) { + List list = bindCmp.getRequestData(); + return list.iterator(); + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = "x", nodeType = NodeTypeEnum.FOR) + public int processX(NodeComponent bindCmp) { + return 3; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK, nodeId = "y", nodeType = NodeTypeEnum.BREAK) + public boolean processY(NodeComponent bindCmp) { + DefaultContext context = bindCmp.getFirstContextBean(); + int count = 0; + if(context.hasData("test")) { + count = context.getData("test"); + } + return count > 3; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE, nodeId = "z", nodeType = NodeTypeEnum.WHILE) + public boolean processZ(NodeComponent bindCmp) { + DefaultContext context = bindCmp.getFirstContextBean(); + String key = "test"; + if (context.hasData(key)) { + int count = context.getData("test"); + return count < 5; + } + else { + return true; + } + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/application.properties new file mode 100644 index 000000000..ebf6a6eda --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/application.properties @@ -0,0 +1,4 @@ +liteflow.rule-source=parallelLoop/flow.xml +liteflow.parallel-max-workers = 10 +liteflow.parallel-queue-limit = 1024 +liteflow.parallel-loop-executor-class =com.yomahub.liteflow.test.parallelLoop.CustomThreadExecutor \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/flow.xml new file mode 100644 index 000000000..9f02a00e3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/parallelLoop/flow.xml @@ -0,0 +1,46 @@ + + + + FOR(2).DO(THEN(a,b,c)); + + + + FOR(x).parallel(true).DO(THEN(a,b,c)); + + + + FOR(100).parallel(true).DO(THEN(a,b,d)).BREAK(y); + + + + FOR(x).parallel(true).DO(THEN(a,b,f)); + + + + FOR(x).parallel(true).DO(THEN(a,b,g)); + + + + WHILE(z).parallel(true).DO(THEN(a,d)); + + + + ITERATOR(it).parallel(true).DO(THEN(a,b)); + + + + FOR(5).parallel(true).DO( + WHEN( + THEN(a,e.tag("e1")), + THEN(c,e.tag("e2")), + THEN(b,e.tag("e3")) + ) + ); + + + + + FOR(x).parallel(true).DO(THEN(a,b,h)); + + + \ No newline at end of file