From 09792996286e851bafa3a3b40cebd49a6b69b67d Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 30 Jul 2024 11:05:30 +0800 Subject: [PATCH 1/3] =?UTF-8?q?bug=20#IAERN6=20=E9=9A=90=E5=BC=8F=E5=AD=90?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=B5=8C=E5=A5=97=E6=8A=A5=E9=94=99=EF=BC=8C?= =?UTF-8?q?=E5=AF=B9refNode=E7=9A=84=E5=BA=95=E5=B1=82=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 41 +++++++++++-------- .../asyncNode/AsyncNodeELSpringbootTest.java | 18 ++++---- .../liteflow/test/asyncNode/cmp/DCmp.java | 3 +- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 02140db44..111466bea 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -30,6 +30,7 @@ import com.yomahub.liteflow.util.JsonUtil; import java.lang.reflect.Method; import java.util.Date; +import java.util.Stack; /** * 普通组件抽象类 @@ -67,13 +68,7 @@ public abstract class NodeComponent{ /** 当前对象为单例,注册进spring上下文,但是node实例不是单例,这里通过对node实例的引用来获得一些链路属性 **/ - private final TransmittableThreadLocal refNodeTL = new TransmittableThreadLocal<>(); - - /** - ******************* 以下的属性为线程附加属性******************** - * 线程属性是指每一个request的值都是不一样的 - * 这里NodeComponent是单例,所以要用ThreadLocal来修饰 - */ + private final TransmittableThreadLocal> refNodeStackTL = new TransmittableThreadLocal<>(); public NodeComponent() { // 反射判断是否重写了rollback方法 @@ -225,7 +220,7 @@ public abstract class NodeComponent{ // 是否结束整个流程(不往下继续执行) public boolean isEnd() { - Boolean isEnd = this.refNodeTL.get().getIsEnd(); + Boolean isEnd = this.getRefNode().getIsEnd(); if (ObjectUtil.isNull(isEnd)) { return false; }else { @@ -235,15 +230,15 @@ public abstract class NodeComponent{ // 设置是否结束整个流程 public void setIsEnd(boolean isEnd) { - this.refNodeTL.get().setIsEnd(isEnd); + this.getRefNode().setIsEnd(isEnd); } public void setIsContinueOnError(boolean isContinueOnError) { - this.refNodeTL.get().setIsContinueOnErrorResult(isContinueOnError); + this.getRefNode().setIsContinueOnErrorResult(isContinueOnError); } public Integer getSlotIndex() { - return this.refNodeTL.get().getSlotIndex(); + return this.getRefNode().getSlotIndex(); } public Slot getSlot() { @@ -327,7 +322,7 @@ public abstract class NodeComponent{ } public String getTag() { - return this.refNodeTL.get().getTag(); + return this.getRefNode().getTag(); } public MonitorBus getMonitorBus() { @@ -385,15 +380,27 @@ public abstract class NodeComponent{ } public Node getRefNode() { - return this.refNodeTL.get(); + return this.refNodeStackTL.get().peek(); } public void setRefNode(Node refNode) { - this.refNodeTL.set(refNode); + if (this.refNodeStackTL.get() == null){ + Stack stack = new Stack<>(); + stack.push(refNode); + this.refNodeStackTL.set(stack); + }else{ + if (!this.refNodeStackTL.get().peek().equals(refNode)){ + this.refNodeStackTL.get().push(refNode); + } + } } public void removeRefNode() { - this.refNodeTL.remove(); + if (this.refNodeStackTL.get().size() > 1) { + this.refNodeStackTL.get().pop(); + }else{ + this.refNodeStackTL.remove(); + } } public T getCmpData(Class clazz) { @@ -408,11 +415,11 @@ public abstract class NodeComponent{ } public Integer getLoopIndex() { - return this.refNodeTL.get().getLoopIndex(); + return this.getRefNode().getLoopIndex(); } public T getCurrLoopObj() { - return this.refNodeTL.get().getCurrLoopObject(); + return this.getRefNode().getCurrLoopObject(); } @Deprecated diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java index 37e0413d2..00c2e840b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java @@ -32,7 +32,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { /***** * 标准chain 嵌套选择 嵌套子chain进行执行 验证了when情况下 多个node是并行执行 验证了默认参数情况下 when可以加载执行 **/ - @Test + //@Test public void testAsyncFlow1() { LiteflowResponse response = flowExecutor.execute2Resp("chain1", "it's a base request"); Assertions.assertTrue(response.isSuccess()); @@ -40,7 +40,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 这个和test1有点类似,只不过进一步验证了步骤 - @Test + //@Test public void testAsyncFlow2() { LiteflowResponse response = flowExecutor.execute2Resp("chain2", "it's a base request"); Assertions.assertTrue( @@ -50,14 +50,14 @@ public class AsyncNodeELSpringbootTest extends BaseTest { .contains(response.getExecuteStepStr())); } - @Test + //@Test public void testAsyncFlow3() { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "it's a base request"); Assertions.assertTrue(response.isSuccess()); } // 测试errorResume,默认的errorResume为false,这里测试默认的 - @Test + //@Test public void testAsyncFlow3_1() { LiteflowResponse response = flowExecutor.execute2Resp("chain3-1", "it's a base request"); Assertions.assertFalse(response.isSuccess()); @@ -65,14 +65,14 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 测试errorResume,默认的errorResume为false,这里设置为true - @Test + //@Test public void testAsyncFlow3_2() { LiteflowResponse response = flowExecutor.execute2Resp("chain3-2", "it's a base request"); Assertions.assertTrue(response.isSuccess()); } // 相同group的并行组,会合并,并且errorResume根据第一个when来,这里第一个when配置了不抛错 - @Test + //@Test public void testAsyncFlow4() { LiteflowResponse response = flowExecutor.execute2Resp("chain4", "it's a base request"); // 因为不记录错误,所以最终结果是true @@ -86,7 +86,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 相同group的并行组,会合并,并且errorResume根据第一个when来,这里第一个when配置了会抛错 - @Test + //@Test public void testAsyncFlow5() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain5", "it's a base request"); // 整个并行组是报错的,所以最终结果是false @@ -100,7 +100,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 不同group的并行组,不会合并,第一个when的errorResume是false,会抛错,那第二个when就不会执行 - @Test + //@Test public void testAsyncFlow6() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain6", "it's a base request"); // 第一个when会抛错,所以最终结果是false @@ -114,7 +114,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 不同group的并行组,不会合并,第一个when的errorResume是true,不会报错,那第二个when还会继续执行,但是第二个when的errorResume是false,所以第二个when会报错 - @Test + //@Test public void testAsyncFlow7() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain7", "it's a base request"); // 第二个when会抛错,所以最终结果是false diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java index 1773200ef..5c3458f8d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java @@ -9,7 +9,8 @@ public class DCmp extends NodeComponent { @Override public void process() throws Exception { - Thread.sleep(1000); + System.out.println(Thread.currentThread().getName()); + Thread.sleep(199000); DefaultContext context = this.getFirstContextBean(); synchronized (NodeComponent.class) { if (context.hasData("check")) { From f0e0cb0707016d64512e3a3e909f1a65a94b249e Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 15 Aug 2024 00:20:47 +0800 Subject: [PATCH 2/3] =?UTF-8?q?bug=20#IAE8J3=20=E9=9A=90=E5=BC=8F=E5=AD=90?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=B8=AD=E5=86=8D=E6=AC=A1=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E9=9A=90=E5=BC=8F=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 5 ++-- .../subflow/NestedImplicitSubFlowTest.java | 29 +++++++++++++++++++ .../liteflow/test/subflow/cmp3/VCmp.java | 20 +++++++++++++ .../subflow/nestedImplicitSubFlow.properties | 1 + .../subflow/nestedImplicitSubFlow.xml | 14 +++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/NestedImplicitSubFlowTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/cmp3/VCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.xml diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 111466bea..6cc841574 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -68,7 +68,7 @@ public abstract class NodeComponent{ /** 当前对象为单例,注册进spring上下文,但是node实例不是单例,这里通过对node实例的引用来获得一些链路属性 **/ - private final TransmittableThreadLocal> refNodeStackTL = new TransmittableThreadLocal<>(); + private final ThreadLocal> refNodeStackTL = new ThreadLocal<>(); public NodeComponent() { // 反射判断是否重写了rollback方法 @@ -389,7 +389,8 @@ public abstract class NodeComponent{ stack.push(refNode); this.refNodeStackTL.set(stack); }else{ - if (!this.refNodeStackTL.get().peek().equals(refNode)){ + Node compareNode = this.refNodeStackTL.get().peek(); + if (!compareNode.equals(refNode)) { this.refNodeStackTL.get().push(refNode); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/NestedImplicitSubFlowTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/NestedImplicitSubFlowTest.java new file mode 100644 index 000000000..588ce3bd9 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/NestedImplicitSubFlowTest.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.test.subflow; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +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; + +@TestPropertySource(value = "classpath:/subflow/nestedImplicitSubFlow.properties") +@SpringBootTest(classes = SubflowXMLELSpringBootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.subflow.cmp1","com.yomahub.liteflow.test.subflow.cmp3" }) +public class NestedImplicitSubFlowTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testNested(){ + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "it's a request"); + Assertions.assertTrue(response.isSuccess()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/cmp3/VCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/cmp3/VCmp.java new file mode 100644 index 000000000..063589396 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/subflow/cmp3/VCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.subflow.cmp3; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.flow.LiteflowResponse; +import org.springframework.stereotype.Component; + +@Component("v") +public class VCmp extends NodeComponent { + + @Override + public void process() { + String data = this.getTag(); + LiteflowResponse resp = this.invoke2Resp(data, null); + + if (!resp.isSuccess()){ + throw new RuntimeException("implicit sub flow exception"); + } + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.properties new file mode 100644 index 000000000..99a94d876 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.properties @@ -0,0 +1 @@ +liteflow.rule-source=subflow/nestedImplicitSubFlow.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.xml new file mode 100644 index 000000000..6b3181263 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/subflow/nestedImplicitSubFlow.xml @@ -0,0 +1,14 @@ + + + + THEN(v.tag("subChain1")); + + + + THEN(v.tag("subChain2")); + + + + THEN(a, b); + + \ No newline at end of file From dd8f75f1d1677e6c99c737306e7e54c428433baa Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 15 Aug 2024 00:23:29 +0800 Subject: [PATCH 3/3] =?UTF-8?q?bug=20#IAE8J3=20=E9=9A=90=E5=BC=8F=E5=AD=90?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=B8=AD=E5=86=8D=E6=AC=A1=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E9=9A=90=E5=BC=8F=E5=AD=90=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../asyncNode/AsyncNodeELSpringbootTest.java | 18 +++++++++--------- .../liteflow/test/asyncNode/cmp/DCmp.java | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java index 00c2e840b..37e0413d2 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java @@ -32,7 +32,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { /***** * 标准chain 嵌套选择 嵌套子chain进行执行 验证了when情况下 多个node是并行执行 验证了默认参数情况下 when可以加载执行 **/ - //@Test + @Test public void testAsyncFlow1() { LiteflowResponse response = flowExecutor.execute2Resp("chain1", "it's a base request"); Assertions.assertTrue(response.isSuccess()); @@ -40,7 +40,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 这个和test1有点类似,只不过进一步验证了步骤 - //@Test + @Test public void testAsyncFlow2() { LiteflowResponse response = flowExecutor.execute2Resp("chain2", "it's a base request"); Assertions.assertTrue( @@ -50,14 +50,14 @@ public class AsyncNodeELSpringbootTest extends BaseTest { .contains(response.getExecuteStepStr())); } - //@Test + @Test public void testAsyncFlow3() { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "it's a base request"); Assertions.assertTrue(response.isSuccess()); } // 测试errorResume,默认的errorResume为false,这里测试默认的 - //@Test + @Test public void testAsyncFlow3_1() { LiteflowResponse response = flowExecutor.execute2Resp("chain3-1", "it's a base request"); Assertions.assertFalse(response.isSuccess()); @@ -65,14 +65,14 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 测试errorResume,默认的errorResume为false,这里设置为true - //@Test + @Test public void testAsyncFlow3_2() { LiteflowResponse response = flowExecutor.execute2Resp("chain3-2", "it's a base request"); Assertions.assertTrue(response.isSuccess()); } // 相同group的并行组,会合并,并且errorResume根据第一个when来,这里第一个when配置了不抛错 - //@Test + @Test public void testAsyncFlow4() { LiteflowResponse response = flowExecutor.execute2Resp("chain4", "it's a base request"); // 因为不记录错误,所以最终结果是true @@ -86,7 +86,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 相同group的并行组,会合并,并且errorResume根据第一个when来,这里第一个when配置了会抛错 - //@Test + @Test public void testAsyncFlow5() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain5", "it's a base request"); // 整个并行组是报错的,所以最终结果是false @@ -100,7 +100,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 不同group的并行组,不会合并,第一个when的errorResume是false,会抛错,那第二个when就不会执行 - //@Test + @Test public void testAsyncFlow6() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain6", "it's a base request"); // 第一个when会抛错,所以最终结果是false @@ -114,7 +114,7 @@ public class AsyncNodeELSpringbootTest extends BaseTest { } // 不同group的并行组,不会合并,第一个when的errorResume是true,不会报错,那第二个when还会继续执行,但是第二个when的errorResume是false,所以第二个when会报错 - //@Test + @Test public void testAsyncFlow7() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain7", "it's a base request"); // 第二个when会抛错,所以最终结果是false diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java index 5c3458f8d..1773200ef 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/DCmp.java @@ -9,8 +9,7 @@ public class DCmp extends NodeComponent { @Override public void process() throws Exception { - System.out.println(Thread.currentThread().getName()); - Thread.sleep(199000); + Thread.sleep(1000); DefaultContext context = this.getFirstContextBean(); synchronized (NodeComponent.class) { if (context.hasData("check")) {