From 5c6b8387dd145fa2a514f1ccdf6c4c4b4975431e Mon Sep 17 00:00:00 2001 From: luoyi <972849752@qq.com> Date: Mon, 15 Jan 2024 12:26:33 +0800 Subject: [PATCH 01/20] =?UTF-8?q?bug=20#I8MXHX=20=E4=BF=AE=E5=A4=8D=20When?= =?UTF-8?q?Condition=20=E4=B8=8B=E7=9A=84=20node=20=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=20isAccess=20=E6=96=B9=E6=B3=95=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/flow/element/Node.java | 22 ++++++++++++------- .../strategy/AllOfParallelExecutor.java | 7 ------ .../strategy/AnyOfParallelExecutor.java | 16 -------------- .../strategy/ParallelStrategyExecutor.java | 14 +++++++----- .../strategy/SpecifyParallelExecutor.java | 17 -------------- 5 files changed, 22 insertions(+), 54 deletions(-) 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 269a4dd62..50ec99f48 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 @@ -21,8 +21,6 @@ import com.yomahub.liteflow.flow.executor.NodeExecutor; import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; 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.slot.DataBus; import com.yomahub.liteflow.slot.Slot; @@ -30,6 +28,7 @@ import com.yomahub.liteflow.slot.Slot; * Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例 * * @author Bryan.Zhang + * @author luo yi */ public class Node implements Executable, Cloneable, Rollbackable{ @@ -57,6 +56,9 @@ public class Node implements Executable, Cloneable, Rollbackable{ private String currChainId; + // node 的 isAccess 结果,主要用于 WhenCondition 的提前 isAccess 判断,避免 isAccess 方法重复执行 + private boolean accessResult; + private TransmittableThreadLocal loopIndexTL = new TransmittableThreadLocal<>(); private TransmittableThreadLocal currLoopObject = new TransmittableThreadLocal<>(); @@ -125,16 +127,13 @@ public class Node implements Executable, Cloneable, Rollbackable{ throw new FlowSystemException("there is no instance for node id " + id); } - Slot slot = DataBus.getSlot(slotIndex); try { // 把线程属性赋值给组件对象 instance.setSlotIndex(slotIndex); instance.setRefNode(this); - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - // 判断是否可执行,所以isAccess经常作为一个组件进入的实际判断要素,用作检查slot里的参数的完备性 - if (instance.isAccess()) { + if (accessResult || instance.isAccess()) { LOG.info("[O]start component[{}] execution", instance.getDisplayName()); // 这里开始进行重试的逻辑和主逻辑的运行 @@ -142,8 +141,7 @@ public class Node implements Executable, Cloneable, Rollbackable{ .buildNodeExecutor(instance.getNodeExecutorClass()); // 调用节点执行器进行执行 nodeExecutor.execute(instance); - } - else { + } else { LOG.info("[X]skip component[{}] execution", instance.getDisplayName()); } // 如果组件覆盖了isEnd方法,或者在在逻辑中主要调用了setEnd(true)的话,流程就会立马结束 @@ -253,6 +251,14 @@ public class Node implements Executable, Cloneable, Rollbackable{ return currChainId; } + public boolean getAccessResult() { + return accessResult; + } + + public void setAccessResult(boolean accessResult) { + this.accessResult = accessResult; + } + public void setLoopIndex(int index) { this.loopIndexTL.set(index); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java index 3ca243fbd..dd5f518e6 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java @@ -1,12 +1,10 @@ package com.yomahub.liteflow.flow.parallel.strategy; -import com.yomahub.liteflow.flow.element.Executable; import com.yomahub.liteflow.flow.element.condition.WhenCondition; import com.yomahub.liteflow.flow.parallel.WhenFutureObj; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; /** * 完成全部任务 @@ -31,9 +29,4 @@ public class AllOfParallelExecutor extends ParallelStrategyExecutor { } - //在allOf这个场景中,不需要过滤 - @Override - protected Stream filterAccess(Stream stream, Integer slotIndex) { - return stream; - } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java index 6f4c19257..ca1475dec 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java @@ -1,12 +1,10 @@ package com.yomahub.liteflow.flow.parallel.strategy; -import com.yomahub.liteflow.flow.element.Executable; import com.yomahub.liteflow.flow.element.condition.WhenCondition; import com.yomahub.liteflow.flow.parallel.WhenFutureObj; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; /** * 完成任一任务 @@ -31,18 +29,4 @@ public class AnyOfParallelExecutor extends ParallelStrategyExecutor { } - //在anyOf这个场景中,需要过滤掉isAccess为false的场景 - //因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了 - //换句话说,就是anyOf这个场景,isAccess会被执行两次 - @Override - protected Stream filterAccess(Stream stream, Integer slotIndex) { - return stream.filter(executable -> { - try { - return executable.isAccess(slotIndex); - } catch (Exception e) { - LOG.error("there was an error when executing the when component isAccess", e); - return false; - } - }); - } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java index b8199419e..0cf8f2bd0 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.enums.ParallelStrategyEnum; import com.yomahub.liteflow.exception.WhenExecuteException; import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.Node; import com.yomahub.liteflow.flow.element.condition.FinallyCondition; import com.yomahub.liteflow.flow.element.condition.PreCondition; import com.yomahub.liteflow.flow.element.condition.WhenCondition; @@ -92,22 +93,23 @@ public abstract class ParallelStrategyExecutor { protected Stream filterWhenTaskList(List executableList, Integer slotIndex) { // 1.先进行过滤,前置和后置组件过滤掉,因为在 EL Chain 处理的时候已经提出来了 // 2.过滤 isAccess 为 false 的情况,因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了 - Stream stream = executableList.stream() + // 3.为避免同一个 node 的 isAccess 方法重复执行,给 node 设置 isAccess 方法执行结果 + return executableList.stream() .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)) .filter(executable -> { try { - return executable.isAccess(slotIndex); + boolean access = executable.isAccess(slotIndex); + if (executable instanceof Node) { + ((Node) executable).setAccessResult(access); + } + return access; } catch (Exception e) { LOG.error("there was an error when executing the when component isAccess", e); return false; } }); - return filterAccess(stream, slotIndex); } - //过滤isAccess的抽象接口方法 - protected abstract Stream filterAccess(Stream stream, Integer slotIndex); - /** * 获取 WHEN 所需线程池 * @param whenCondition diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java index c345a83fd..e9a78ef9a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java @@ -1,14 +1,12 @@ package com.yomahub.liteflow.flow.parallel.strategy; import cn.hutool.core.collection.CollUtil; -import com.yomahub.liteflow.flow.element.Executable; import com.yomahub.liteflow.flow.element.condition.WhenCondition; import com.yomahub.liteflow.flow.parallel.WhenFutureObj; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; -import java.util.stream.Stream; /** * 完成指定任务执行器,使用 ID 进行比较 @@ -77,19 +75,4 @@ public class SpecifyParallelExecutor extends ParallelStrategyExecutor { } - //在must这个场景中,需要过滤掉isAccess为false的场景 - //因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了 - //换句话说,就是must这个场景,isAccess会被执行两次 - @Override - protected Stream filterAccess(Stream stream, Integer slotIndex) { - return stream.filter(executable -> { - try { - return executable.isAccess(slotIndex); - } catch (Exception e) { - LOG.error("there was an error when executing the when component isAccess", e); - return false; - } - }); - } - } From cfc1994fd47816b602de3638a840c99b922f137e Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 18 Jan 2024 11:48:25 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=88=902.11.4.1?= =?UTF-8?q?=EF=BC=8C=E8=A1=A5=E4=B8=81=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parallel/strategy/ParallelStrategyExecutor.java | 10 +--------- .../java/com/yomahub/liteflow/test/base/cmp/ACmp.java | 1 - .../java/com/yomahub/liteflow/test/base/cmp/CCmp.java | 5 +++++ 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java index b8199419e..701c74188 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java @@ -93,15 +93,7 @@ public abstract class ParallelStrategyExecutor { // 1.先进行过滤,前置和后置组件过滤掉,因为在 EL Chain 处理的时候已经提出来了 // 2.过滤 isAccess 为 false 的情况,因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了 Stream stream = executableList.stream() - .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)) - .filter(executable -> { - try { - return executable.isAccess(slotIndex); - } catch (Exception e) { - LOG.error("there was an error when executing the when component isAccess", e); - return false; - } - }); + .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)); return filterAccess(stream, slotIndex); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/ACmp.java index c2361cacb..c33792217 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/ACmp.java @@ -17,5 +17,4 @@ public class ACmp extends NodeComponent { 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/base/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/CCmp.java index 6b6f84b41..f744fdbb8 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/CCmp.java @@ -18,4 +18,9 @@ public class CCmp extends NodeComponent { System.out.println("CCmp executed!"); } + @Override + public boolean isAccess() { + System.out.println("hello"); + return true; + } } From 9677b128cad64f4747fbb4a2d05308105ff8fd03 Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Thu, 18 Jan 2024 15:59:46 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E5=85=81=E8=AE=B8EL=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E9=87=8C=E8=AE=BE=E7=BD=AE=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builder/el/LiteFlowChainELBuilder.java | 1 + .../el/operator/RetryTimesOperator.java | 30 +++++ .../liteflow/common/ChainConstant.java | 2 + .../element/condition/RetryCondition.java | 87 ++++++++++++++ .../RetryTimesELDeclMultiSpringbootTest.java | 108 ++++++++++++++++++ .../test/retryTimes/cmp/CmpConfig.java | 82 +++++++++++++ .../retryTimes/application.properties | 1 + .../src/test/resources/retryTimes/flow.el.xml | 43 +++++++ .../RetryTimesELDeclSpringbootTest.java | 104 +++++++++++++++++ .../liteflow/test/retryTimes/cmp/ACmp.java | 12 ++ .../liteflow/test/retryTimes/cmp/BCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/CCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/DCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/FCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/ICmp.java | 23 ++++ .../liteflow/test/retryTimes/cmp/NCmp.java | 17 +++ .../retryTimes/application.properties | 1 + .../src/test/resources/retryTimes/flow.el.xml | 43 +++++++ .../test/retryTimes/RetryTimesTest.java | 103 +++++++++++++++++ .../liteflow/test/retryTimes/cmp/ACmp.java | 11 ++ .../liteflow/test/retryTimes/cmp/BCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/CCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/DCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/FCmp.java | 15 +++ .../liteflow/test/retryTimes/cmp/ICmp.java | 21 ++++ .../liteflow/test/retryTimes/cmp/NCmp.java | 16 +++ .../src/test/resources/retryTimes/flow.el.xml | 52 +++++++++ .../retryTimes/RetryTimesSpringbootTest.java | 100 ++++++++++++++++ .../liteflow/test/retryTimes/cmp/ACmp.java | 12 ++ .../liteflow/test/retryTimes/cmp/BCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/CCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/DCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/FCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/ICmp.java | 23 ++++ .../liteflow/test/retryTimes/cmp/NCmp.java | 17 +++ .../retryTimes/application.properties | 1 + .../src/test/resources/retryTimes/flow.el.xml | 43 +++++++ .../retryTimes/RetryTimesSpringbootTest.java | 105 +++++++++++++++++ .../liteflow/test/retryTimes/cmp/ACmp.java | 12 ++ .../liteflow/test/retryTimes/cmp/BCmp.java | 18 +++ .../liteflow/test/retryTimes/cmp/CCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/DCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/FCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/ICmp.java | 23 ++++ .../liteflow/test/retryTimes/cmp/NCmp.java | 17 +++ .../retryTimes/application.properties | 1 + .../src/test/resources/retryTimes/flow.el.xml | 42 +++++++ .../test/retryTimes/RetryTimesSpringTest.java | 101 ++++++++++++++++ .../liteflow/test/retryTimes/cmp/ACmp.java | 12 ++ .../liteflow/test/retryTimes/cmp/BCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/CCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/DCmp.java | 17 +++ .../liteflow/test/retryTimes/cmp/FCmp.java | 16 +++ .../liteflow/test/retryTimes/cmp/ICmp.java | 23 ++++ .../liteflow/test/retryTimes/cmp/NCmp.java | 17 +++ .../test/resources/retryTimes/application.xml | 23 ++++ .../src/test/resources/retryTimes/flow.el.xml | 43 +++++++ 57 files changed, 1704 insertions(+) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/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 0f1a65ad7..f749aee94 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 @@ -92,6 +92,7 @@ public class LiteFlowChainELBuilder { EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PARALLEL, Object.class, new ParallelOperator()); + EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY_TIMES, Object.class, new RetryTimesOperator()); } public static LiteFlowChainELBuilder createChain() { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java new file mode 100644 index 000000000..72ac3dded --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java @@ -0,0 +1,30 @@ +package com.yomahub.liteflow.builder.el.operator; + +import com.yomahub.liteflow.builder.el.operator.base.BaseOperator; +import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.flow.element.Condition; +import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.condition.RetryCondition; +import com.yomahub.liteflow.flow.element.condition.ThenCondition; +import com.yomahub.liteflow.flow.element.condition.WhileCondition; + +/** + * + * @author Rain + * @since 2.11.5 + * + */ +public class RetryTimesOperator extends BaseOperator { + @Override + public Condition build(Object[] objects) throws Exception { + OperatorHelper.checkObjectSizeGtTwo(objects); + Executable executable = OperatorHelper.convert(objects[0], Executable.class); + Integer retryTimes = OperatorHelper.convert(objects[1], Integer.class); + RetryCondition retryCondition = new RetryCondition(); + retryCondition.addExecutable(executable); + retryCondition.setRetryTimes(retryTimes); + return retryCondition; + } + +} 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 884b8b157..977a9371a 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 @@ -94,4 +94,6 @@ public interface ChainConstant { String EXTENDS = "extends"; + String RETRY_TIMES = "retryTimes"; + } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java new file mode 100644 index 000000000..6f4bbaf99 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java @@ -0,0 +1,87 @@ +package com.yomahub.liteflow.flow.element.condition; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.exception.ChainEndException; +import com.yomahub.liteflow.exception.ELParseException; +import com.yomahub.liteflow.flow.element.Chain; +import com.yomahub.liteflow.flow.element.Condition; +import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.Node; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.slot.DataBus; + +public class RetryCondition extends ThenCondition{ + + private final LFLog LOG = LFLoggerManager.getLogger(this.getClass()); + + private Integer retryTimes; + + public Integer getRetryTimes() { + return retryTimes; + } + + public void setRetryTimes(Integer retryTimes) { + this.retryTimes = retryTimes; + } + + @Override + public void executeCondition(Integer slotIndex) throws Exception { + int retryTimes = this.getRetryTimes() < 0 ? 0 : this.getRetryTimes(); + for (int i = 0; i <= retryTimes; i ++) { + try { + if(i == 0) { + super.executeCondition(slotIndex); + } else { + retry(slotIndex, i); + } + break; + } catch (ChainEndException e) { + throw e; + } catch (Exception e) { + if(i >= retryTimes) { + if(retryTimes > 0) { + String retryFailMsg = StrFormatter.format("retry fail when executing the chain[{}] because {} occurs {}.", + this.getCurrChainId(), this.getCurrentExecutableId(), e); + LOG.error(retryFailMsg); + } + throw e; + } else { + DataBus.getSlot(slotIndex).removeException(); + } + } + } + } + + private void retry(Integer slotIndex, int retryTime) throws Exception { + LOG.info("{} performs {} retry ", this.getCurrentExecutableId(), retryTime); + super.executeCondition(slotIndex); + } + + /** + * 获取当前组件的 id + * + * @return + */ + private String getCurrentExecutableId() { + // retryCondition 只有一个 Executable + Executable executable = this.getExecutableList().get(0); + if (ObjectUtil.isNotNull(executable.getId())) { + // 已经有 id 了 + return executable.getId(); + } + // 定义 id + switch (executable.getExecuteType()) { + // chain 和 node 一般都有 id + case CHAIN: + return ((Chain) executable).getChainId(); + case CONDITION: + return "condition-" + ((Condition) executable).getConditionType().getName(); + case NODE: + return "node-" + ((Node) executable).getType().getCode(); + default: + return "unknown-executable"; + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java new file mode 100644 index 000000000..2ed4fe38e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java @@ -0,0 +1,108 @@ +package com.yomahub.liteflow.test.retryTimes; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.test.rollback.RollbackELDeclMultiSpringbootTest; +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; + + +@ExtendWith(SpringExtension.class) +@TestPropertySource(value = "classpath:/retryTimes/application.properties") +@SpringBootTest(classes = RetryTimesELDeclMultiSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) +public class RetryTimesELDeclMultiSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java new file mode 100644 index 000000000..da8c2cb99 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java @@ -0,0 +1,82 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.annotation.LiteflowRetry; +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 java.util.Iterator; +import java.util.List; + +@LiteflowComponent +public class CmpConfig { + + int flagb = 0; + int flagc = 0; + int flagd = 0; + int flagf = 0; + int flagi = 0; + int flagn = 0; + int flagw = 0; + + @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) { + flagb ++; + System.out.println("BCmp executed!"); + if(flagb < 4) throw new RuntimeException(); + else flagb = 0; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = "c", nodeType = NodeTypeEnum.FOR) + public int processC(NodeComponent bindCmp) { + flagc ++; + System.out.println("CCmp executed!"); + if(flagc < 4) throw new RuntimeException(); + else return 1; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH, nodeId = "d", nodeType = NodeTypeEnum.SWITCH) + public String processD(NodeComponent bindCmp) { + flagd ++; + System.out.println("DCmp executed!"); + if(flagd < 4) throw new RuntimeException(); + else return "a"; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF, nodeId = "f", nodeType = NodeTypeEnum.IF) + public boolean processF(NodeComponent bindCmp) { + System.out.println("FCmp executed!"); + flagf ++; + if(flagf < 4) throw new RuntimeException(); + else return true; + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "i", nodeType = NodeTypeEnum.ITERATOR) + public Iterator processI(NodeComponent bindCmp) { + flagi ++; + if(flagi < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE, nodeId = "n", nodeType = NodeTypeEnum.WHILE) + public boolean processN(NodeComponent bindCmp) { + flagn ++; + System.out.println("NCmp executed!"); + if(flagn < 4) throw new RuntimeException(); + else return flagn == 4 ? true : false; + } + + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties new file mode 100644 index 000000000..ecdb08bea --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..775e7a742 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,43 @@ + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java new file mode 100644 index 000000000..9a65758dc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java @@ -0,0 +1,104 @@ +package com.yomahub.liteflow.test.retryTimes; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +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:/retryTimes/application.properties") +@SpringBootTest(classes = RetryTimesELDeclSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) +public class RetryTimesELDeclSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java new file mode 100644 index 000000000..46e99dc1f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + @Override + public void process() { + System.out.println("ACmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java new file mode 100644 index 000000000..5a02501b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() { + flag ++; + System.out.println("BCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java new file mode 100644 index 000000000..f6a1014ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeForComponent; + +@LiteflowComponent("c") +public class CCmp extends NodeForComponent { + int flag = 0; + + @Override + public int processFor() throws Exception { + flag ++; + System.out.println("CCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return 1; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java new file mode 100644 index 000000000..319c98cd5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("d") +public class DCmp extends NodeSwitchComponent { + int flag = 0; + + @Override + public String processSwitch() throws Exception { + flag ++; + System.out.println("DCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java new file mode 100644 index 000000000..f9764757e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("f") +public class FCmp extends NodeIfComponent { + int flag = 0; + @Override + public boolean processIf() throws Exception { + System.out.println("FCmp executed!"); + flag ++; + if(flag < 4) throw new RuntimeException(); + else return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java new file mode 100644 index 000000000..3341869d8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Iterator; +import java.util.List; + +@LiteflowComponent("i") +public class ICmp extends NodeIteratorComponent { + int flag = 0; + @Override + public Iterator processIterator() throws Exception { + flag ++; + if(flag < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java new file mode 100644 index 000000000..d4e371647 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@LiteflowComponent("n") +public class NCmp extends NodeWhileComponent { + int flag = 0; + + @Override + public boolean processWhile() throws Exception { + flag ++; + System.out.println("NCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return flag == 4 ? true : false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties new file mode 100644 index 000000000..ecdb08bea --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..775e7a742 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,43 @@ + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java new file mode 100644 index 000000000..b3dec4514 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java @@ -0,0 +1,103 @@ +package com.yomahub.liteflow.test.retryTimes; + + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class RetryTimesTest extends BaseTest { + + private static FlowExecutor flowExecutor; + + @BeforeAll + public static void init() { + LiteflowConfig config = new LiteflowConfig(); + config.setRuleSource("retryTimes/flow.el.xml"); + flowExecutor = FlowExecutorHolder.loadInstance(config); + } + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java new file mode 100644 index 000000000..23ed69a7a --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java @@ -0,0 +1,11 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeComponent; + +public class ACmp extends NodeComponent { + @Override + public void process() { + System.out.println("ACmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java new file mode 100644 index 000000000..d52cfdb2d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeComponent; + +public class BCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() { + flag ++; + System.out.println("BCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java new file mode 100644 index 000000000..9a39cb349 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeForComponent; + +public class CCmp extends NodeForComponent { + int flag = 0; + + @Override + public int processFor() throws Exception { + flag ++; + System.out.println("CCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return 1; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java new file mode 100644 index 000000000..e88942178 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeSwitchComponent; + +public class DCmp extends NodeSwitchComponent { + int flag = 0; + + @Override + public String processSwitch() throws Exception { + flag ++; + System.out.println("DCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java new file mode 100644 index 000000000..bd5601bbb --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeIfComponent; + +public class FCmp extends NodeIfComponent { + int flag = 0; + @Override + public boolean processIf() throws Exception { + System.out.println("FCmp executed!"); + flag ++; + if(flag < 4) throw new RuntimeException(); + else return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java new file mode 100644 index 000000000..058bb3c4e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Iterator; +import java.util.List; + +public class ICmp extends NodeIteratorComponent { + int flag = 0; + @Override + public Iterator processIterator() throws Exception { + flag ++; + if(flag < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java new file mode 100644 index 000000000..68409ab83 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import com.yomahub.liteflow.core.NodeWhileComponent; + +public class NCmp extends NodeWhileComponent { + int flag = 0; + + @Override + public boolean processWhile() throws Exception { + flag ++; + System.out.println("NCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return flag == 4 ? true : false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..685f6ef27 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java new file mode 100644 index 000000000..79b92c2b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java @@ -0,0 +1,100 @@ +package com.yomahub.liteflow.test.retryTimes; + + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +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.noear.solon.annotation.Inject; +import org.noear.solon.test.SolonJUnit5Extension; +import org.noear.solon.test.annotation.TestPropertySource; + +@ExtendWith(SolonJUnit5Extension.class) +@TestPropertySource("classpath:/retryTimes/application.properties") +public class RetryTimesSpringbootTest extends BaseTest { + + @Inject + private FlowExecutor flowExecutor; + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java new file mode 100644 index 000000000..9c5531a86 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.noear.solon.annotation.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-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java new file mode 100644 index 000000000..b4890b4ef --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.noear.solon.annotation.Component; + +@Component("b") +public class BCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() { + flag ++; + System.out.println("BCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java new file mode 100644 index 000000000..069acb496 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeForComponent; +import org.noear.solon.annotation.Component; + +@Component("c") +public class CCmp extends NodeForComponent { + int flag = 0; + + @Override + public int processFor() throws Exception { + flag ++; + System.out.println("CCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return 1; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java new file mode 100644 index 000000000..ccbd72a3e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeSwitchComponent; +import org.noear.solon.annotation.Component; + +@Component("d") +public class DCmp extends NodeSwitchComponent { + int flag = 0; + + @Override + public String processSwitch() throws Exception { + flag ++; + System.out.println("DCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java new file mode 100644 index 000000000..5d13709bb --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeIfComponent; +import org.noear.solon.annotation.Component; + +@Component("f") +public class FCmp extends NodeIfComponent { + int flag = 0; + @Override + public boolean processIf() throws Exception { + System.out.println("FCmp executed!"); + flag ++; + if(flag < 4) throw new RuntimeException(); + else return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java new file mode 100644 index 000000000..922ace500 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.core.NodeIteratorComponent; +import org.noear.solon.annotation.Component; + +import java.util.Iterator; +import java.util.List; + +@Component("i") +public class ICmp extends NodeIteratorComponent { + int flag = 0; + @Override + public Iterator processIterator() throws Exception { + flag ++; + if(flag < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java new file mode 100644 index 000000000..e6b27d11f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeWhileComponent; +import org.noear.solon.annotation.Component; + +@Component("n") +public class NCmp extends NodeWhileComponent { + int flag = 0; + + @Override + public boolean processWhile() throws Exception { + flag ++; + System.out.println("NCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return flag == 4 ? true : false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties new file mode 100644 index 000000000..ecdb08bea --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..775e7a742 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,43 @@ + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java new file mode 100644 index 000000000..24d30a669 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java @@ -0,0 +1,105 @@ +package com.yomahub.liteflow.test.retryTimes; + +import com.yomahub.liteflow.core.FlowExecutor; +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.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:/retryTimes/application.properties") +@SpringBootTest(classes = RetryTimesSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) +public class RetryTimesSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java new file mode 100644 index 000000000..46e99dc1f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("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/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java new file mode 100644 index 000000000..e5ab69d42 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowRetry; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() { + flag ++; + System.out.println("BCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else flag = 0; + } +} \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java new file mode 100644 index 000000000..f6a1014ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeForComponent; + +@LiteflowComponent("c") +public class CCmp extends NodeForComponent { + int flag = 0; + + @Override + public int processFor() throws Exception { + flag ++; + System.out.println("CCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return 1; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java new file mode 100644 index 000000000..319c98cd5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("d") +public class DCmp extends NodeSwitchComponent { + int flag = 0; + + @Override + public String processSwitch() throws Exception { + flag ++; + System.out.println("DCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java new file mode 100644 index 000000000..f9764757e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("f") +public class FCmp extends NodeIfComponent { + int flag = 0; + @Override + public boolean processIf() throws Exception { + System.out.println("FCmp executed!"); + flag ++; + if(flag < 4) throw new RuntimeException(); + else return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java new file mode 100644 index 000000000..3341869d8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Iterator; +import java.util.List; + +@LiteflowComponent("i") +public class ICmp extends NodeIteratorComponent { + int flag = 0; + @Override + public Iterator processIterator() throws Exception { + flag ++; + if(flag < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java new file mode 100644 index 000000000..d4e371647 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@LiteflowComponent("n") +public class NCmp extends NodeWhileComponent { + int flag = 0; + + @Override + public boolean processWhile() throws Exception { + flag ++; + System.out.println("NCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return flag == 4 ? true : false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties new file mode 100644 index 000000000..ecdb08bea --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..b12f5eced --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,42 @@ + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java new file mode 100644 index 000000000..7026672e5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java @@ -0,0 +1,101 @@ +package com.yomahub.liteflow.test.retryTimes; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +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.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; + + +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:/retryTimes/application.xml") +public class RetryTimesSpringTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // THEN测试 + @Test + public void testThen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr()); + } + + // WHEN测试 + @Test + public void testWhen() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // node测试 + @Test + public void testNode() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); + } + + // FOR测试 + @Test + public void testFor() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr()); + } + + // SWITCH测试 + @Test + public void testSwitch() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr()); + } + + // IF测试 + @Test + public void testIf() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr()); + } + + // WHILE测试 + @Test + public void testWhile() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr()); + } + + // ITERATOR测试 + @Test + public void testIterator() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr()); + } + + // 重试失败提示信息测试 + @Test + public void testRetryFail() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr()); + } + + // FINALLY测试 + @Test + public void testFinally() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg"); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("a==>b", response.getExecuteStepStr()); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java new file mode 100644 index 000000000..0dd4114a0 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.retryTimes.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-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java new file mode 100644 index 000000000..a50952f53 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("b") +public class BCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() { + flag ++; + System.out.println("BCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java new file mode 100644 index 000000000..51949af98 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeForComponent; +import org.springframework.stereotype.Component; + +@Component("c") +public class CCmp extends NodeForComponent { + int flag = 0; + + @Override + public int processFor() throws Exception { + flag ++; + System.out.println("CCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return 1; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java new file mode 100644 index 000000000..ee453a2ab --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeSwitchComponent; +import org.springframework.stereotype.Component; + +@Component("d") +public class DCmp extends NodeSwitchComponent { + int flag = 0; + + @Override + public String processSwitch() throws Exception { + flag ++; + System.out.println("DCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java new file mode 100644 index 000000000..d41ed62c9 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeIfComponent; +import org.springframework.stereotype.Component; + +@Component("f") +public class FCmp extends NodeIfComponent { + int flag = 0; + @Override + public boolean processIf() throws Exception { + System.out.println("FCmp executed!"); + flag ++; + if(flag < 4) throw new RuntimeException(); + else return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java new file mode 100644 index 000000000..e936cc096 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + + +import cn.hutool.core.collection.ListUtil; +import com.yomahub.liteflow.core.NodeIteratorComponent; +import org.springframework.stereotype.Component; + +import java.util.Iterator; +import java.util.List; + +@Component("i") +public class ICmp extends NodeIteratorComponent { + int flag = 0; + @Override + public Iterator processIterator() throws Exception { + flag ++; + if(flag < 4) throw new RuntimeException(); + else { + List list = ListUtil.toList("jack"); + return list.iterator(); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java new file mode 100644 index 000000000..5bb3b5865 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.retryTimes.cmp; + +import com.yomahub.liteflow.core.NodeWhileComponent; +import org.springframework.stereotype.Component; + +@Component("n") +public class NCmp extends NodeWhileComponent { + int flag = 0; + + @Override + public boolean processWhile() throws Exception { + flag ++; + System.out.println("NCmp executed!"); + if(flag < 4) throw new RuntimeException(); + else return flag == 4 ? true : false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml new file mode 100644 index 000000000..c1d4af03e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml new file mode 100644 index 000000000..775e7a742 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml @@ -0,0 +1,43 @@ + + + + THEN( a, b ).retryTimes(3); + + + + WHEN( a, b ).retryTimes(3); + + + + THEN( a, b.retryTimes(3) ); + + + + FOR(c).DO(a).retryTimes(3); + + + + SWITCH(d).TO(a).retryTimes(3); + + + + IF(f, a).retryTimes(3); + + + + WHILE(n).DO(a).retryTimes(3); + + + + ITERATOR(i).DO(a).retryTimes(3); + + + + THEN( a, b ).retryTimes(1); + + + + THEN( a, FINALLY(b, a).retryTimes(3) ); + + + \ No newline at end of file From dd9d2014be1210ba34dac08943776c5a0a3958ce Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Mon, 22 Jan 2024 16:12:47 +0800 Subject: [PATCH 04/20] =?UTF-8?q?=E6=96=B0=E7=9A=84=E5=A3=B0=E6=98=8E?= =?UTF-8?q?=E5=BC=8F=E6=94=B9=E9=80=A0=E6=94=AF=E6=8C=81=E4=BD=8E=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84spring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/spring/DeclBeanDefinition.java | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java index 2a30ebb2a..7d4d396b6 100644 --- a/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java @@ -4,6 +4,8 @@ import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.util.ReflectUtil; import com.yomahub.liteflow.annotation.LiteflowMethod; import com.yomahub.liteflow.core.proxy.DeclWarpBean; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; @@ -12,6 +14,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.*; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -23,38 +26,42 @@ import java.util.Map; * @since 2.11.4 */ public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor { + + private final LFLog LOG = LFLoggerManager.getLogger(this.getClass()); @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry; - Map beanDefinitionHolderMap = (Map)ReflectUtil.getFieldValue(defaultListableBeanFactory, "mergedBeanDefinitions"); - beanDefinitionHolderMap.entrySet().stream().filter(entry -> { - Class rawClass = entry.getValue().getResolvableType().getRawClass(); + String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames(); + + Arrays.stream(beanDefinitionNames).filter(beanName -> { + BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); + Class rawClass = getRawClassFromBeanDefinition(beanDefinition); if (rawClass == null){ return false; }else{ return Arrays.stream(rawClass.getMethods()).anyMatch(method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class) != null); } - }).forEach(entry -> { - Class rawClass = entry.getValue().getResolvableType().getRawClass(); + }).forEach(beanName -> { + BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName); + Class rawClass = getRawClassFromBeanDefinition(beanDefinition); List declWarpBeanList = DeclComponentParserHolder.loadDeclComponentParser().parseDeclBean(rawClass); declWarpBeanList.forEach(declWarpBean -> { - GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); - beanDefinition.setBeanClass(DeclWarpBean.class); - beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON); + GenericBeanDefinition newBeanDefinition = new GenericBeanDefinition(); + newBeanDefinition.setBeanClass(DeclWarpBean.class); + newBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON); MutablePropertyValues mutablePropertyValues = new MutablePropertyValues(); mutablePropertyValues.add("nodeId", declWarpBean.getNodeId()); mutablePropertyValues.add("nodeName", declWarpBean.getNodeName()); mutablePropertyValues.add("nodeType", declWarpBean.getNodeType()); mutablePropertyValues.add("rawClazz", declWarpBean.getRawClazz()); mutablePropertyValues.add("methodWrapBeanList", declWarpBean.getMethodWrapBeanList()); - mutablePropertyValues.add("rawBean", entry.getValue()); - beanDefinition.setPropertyValues(mutablePropertyValues); + mutablePropertyValues.add("rawBean", beanDefinition); + newBeanDefinition.setPropertyValues(mutablePropertyValues); defaultListableBeanFactory.setAllowBeanDefinitionOverriding(true); - defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), beanDefinition); + defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), newBeanDefinition); }); - }); } @@ -62,4 +69,19 @@ public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor { public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } + + private Class getRawClassFromBeanDefinition(BeanDefinition beanDefinition){ + try{ + Method method = ReflectUtil.getMethodByName(DeclBeanDefinition.class, "getResolvableType"); + if (method != null){ + Object resolvableType = ReflectUtil.invoke(beanDefinition, method); + return ReflectUtil.invoke(resolvableType, "getRawClass"); + }else{ + return ReflectUtil.invoke(beanDefinition, "getTargetType"); + } + }catch (Exception e){ + LOG.error("An error occurred while obtaining the rowClass.",e); + return null; + } + } } From 1fb4f9902890c429bcd84ad27aa80eca94434571 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Mon, 22 Jan 2024 16:16:11 +0800 Subject: [PATCH 05/20] =?UTF-8?q?bug=20#I8Y0X4=20=E5=B9=B6=E8=A1=8C?= =?UTF-8?q?=E5=BE=AA=E7=8E=AF=E4=B8=AD=E6=9C=89=E5=8F=AF=E8=83=BD=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E7=9A=84ConditionStack=E7=9A=84=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/yomahub/liteflow/slot/Slot.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java index 8bd133c14..e8cbb8cba 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java @@ -11,6 +11,7 @@ import cn.hutool.core.collection.ConcurrentHashSet; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.alibaba.ttl.TransmittableThreadLocal; import com.yomahub.liteflow.exception.NoSuchContextBeanException; import com.yomahub.liteflow.exception.NullParamException; import com.yomahub.liteflow.flow.element.Condition; @@ -92,7 +93,7 @@ public class Slot { private List contextBeanList; - private static final ThreadLocal> conditionStack = ThreadLocal.withInitial(LinkedList::new); + private static final TransmittableThreadLocal> conditionStack = TransmittableThreadLocal.withInitial(ConcurrentLinkedDeque::new); public Slot() { } From b850ebf5e591a271a9f119d313693e0502003143 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Mon, 22 Jan 2024 16:16:27 +0800 Subject: [PATCH 06/20] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E5=88=B02.11.4.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d71032f10..3e880236f 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ - 2.11.4.1 + 2.11.4.2 UTF-8 UTF-8 8 From a132d39502b5f9fb3a2ef6a5da0b3f6e533f88a0 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 23 Jan 2024 14:12:50 +0800 Subject: [PATCH 07/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 25 +++++++++++++++++++++++++ README.zh-CN.md | 24 ++++++++++++++++++++++++ static/img/zsxq-gitee.png | Bin 0 -> 50434 bytes static/img/zsxq-github.png | Bin 0 -> 51092 bytes 4 files changed, 49 insertions(+) create mode 100644 static/img/zsxq-gitee.png create mode 100644 static/img/zsxq-github.png diff --git a/README.md b/README.md index 31d5086b9..cf35b4ffc 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,31 @@ Looking forward to your use! Discord Link: [https://discord.gg/MpdBSBnFTu](https://discord.gg/MpdBSBnFTu) +## LF CLUB Community + +LF CLUB is a premium paid community founded by the author of LiteFlow. + +LF CLUB can help all users of the LiteFlow framework, as well as potential developers who want to use LiteFlow. + +LF CLUB provides the following services: + +**1.Weekly releases of a condensed analysis series for LF. As long as users study along with the Planet series articles, they will definitely be able to fully grasp LF.** + +**2.Provide a Q&A service where members can ask unlimited questions and receive detailed replies and guidance on the same day.** + +**3.Each enrolled user is entitled to two remote one-on-one tutoring sessions per year as part of the remote assistance service.** + +**4.Every 1 to 2 days, there will be updates on LF's current progress and the focus of the next version.** + +The LF CLUB can solve all the problems you encounter when using the LiteFlow framework. It offers a series of courses to help you gain a deep understanding of the LiteFlow framework. Unlike the WeChat community, the LF CLUB prioritizes the importance of questions and provides detailed answers. + +Exclusive content helps you gain a profound understanding without the need to search for answers on other platforms. The author personally teaches, providing expert guidance at your fingertips, eliminating the need to seek help from others. + +To join the LF CLUB, please scan the QR code below or click on the image to go directly to the website. + + + + ## 🦾Sponsor **MISBoot低代码开发平台** diff --git a/README.zh-CN.md b/README.zh-CN.md index 66de111d2..9376a260b 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,6 +57,30 @@ LiteFlow拥有极其详细易懂的文档体系,能帮助你解决在使用框 LiteFlow期待你的了解! +## LF CLUB社区 + +LF CLUB是由LiteFlow作者创办的高级付费社区 + +LF CLUB能帮助到所有LiteFlow框架的使用者,以及想使用LiteFlow的潜在开发者。 + +LF CLUB提供以下服务: + +**1.每周发布一篇LF的解析精华系列。从头开始解析LF,只要跟着星球解析系列走,使用者一定能完全掌握LF。** + +**2.提供答疑服务,会员可以无限制提问,当天必定得到详细的回复和指导建议。** + +**3.每个加入的用户每年提供2次远程一对一答疑,远程协助服务。** + +**4.每1到2天会分享LF目前的进度,以及下一个版本的重点。** + +LF CLUB里能解决你在使用LiteFlow框架时碰到的所有问题,并有系列课程能帮助你深刻理解LiteFlow框架,不同于微信社区,LF CLUB的问题优先级程度是最高的,且答疑非常详细。 + +独家内容帮助深刻理解,不用在其他平台去搜索问题的答案。作者亲授,相当于随时拥有专家在身边,不用再去求助其他人。 + +加入LF CLUB,请扫描以下二维码,或者直接点击图片也可以直达: + + + ## 🦾赞助商 **MISBoot低代码开发平台** diff --git a/static/img/zsxq-gitee.png b/static/img/zsxq-gitee.png new file mode 100644 index 0000000000000000000000000000000000000000..cd26d0ac77a55e0cc5378da2970874fef6279078 GIT binary patch literal 50434 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%0D!L6`7_hT*_vEFF!G!?v^Ya7+5milC1D=M`xNi zA}+y;yc{T4Y~aTNlu(PsR=sbi6h!X#_7xR{K*6oIx18X}-{26RgE~yQSPvZv(T%1V z3mJD39IWtt+8VE#hPxtSOTff1WuliHy51dp1z$ZOns(gps{MEK5_pJ@Ya8nvmMO`1 zJne+no8;B*wWG!KO?2H*jgU!&X3gkm?<(#w4S#Bp@U0;V-~#h!4uObSN9nl9B|t zc`OL=^17KCkMzk7c8t#BW6*f=rqW!yZ`rt?MS0EAD4eLiJSM}=xUJ9sqtyKmm^&qS zHwa;KSIf*&O#J$yiaLfMo--5v=}Huon29=U^_fx6mz8L9F&dE`z1P01YSRO2=XG!B zx{>SMh?90QQFrPIL$9NgF_!73z5b||?+_F=F_1$AZi5U^%Lkpa_R5>mdLe|ZwN}*k zrexJ}rh?$h?Uvy_6T`6u)ABpS(US8Y$HzY71YWEWVu~&x&+N$jN=y_!81MKWfu0sw z^10Ffn1IznDb8UA+A&Y7L$|(7*UThFC8oN5d_5|l7;3HFzq4Q!muMHKFk*S6-n%$c_z zE+G!?>~yG^spk(a|ACMK@6u?v^l<$srYAUlY2aD_-RBfM)1t3lG$d4s__ zVOFi1pfcB{k33B~=K7{2uh2={PVi3BMHtNbo6UyhSZcvi!Bla9p>0<@?WjDNHqpFR z!w)rAD%m2`fUX##WaA$RE>81Td~-5*#YMn=!MwT&qX-xvgAH)e2wYd-PSv{CY+sGu zss5yvEo5AWsT3ewWpxWCE>fKL{{tMT8yXF74t8tV zXj-zWlX^RCfB$Ysw-?C?`E#rq7SbpclGNe_3@5JnY2&C)XFTf}-mH{Y9;4O(`8FES zPFs$dS>ZXQe8uCHW7z(s^|jJa4*RjA33dKE(^H{Rj7sdwgHyr5&CkfR!tN$b3J%_C zVnetV`0N==^vXWF_BpZ#Bgq;@BW1q!@Li_N*C@ih&hXh`l{ipa6_XqexYv4G#`+}G zz5_*W#VQ^ob?*U8G8MSdcmd+|2R~DMPtE4`PXiCli7R%ne`e8CZ5o7dGiD?7OC0!_ z<;fd4%wtKE83T2@Cy#v0odI9hp{#4q9KNY}2bs6s-W_Iyl>>KJ*@04!qcg@LR*Goz z)Wecon_xELF4yC9LGH4_~@9KUdW!rEnMOsBa&46$0%o{(eu7oyhwN~@+-@v%!Po543Y*Jc&^ zT(;xd;u&?&<|!`d<;00>4s=ZY0e}=$n5|feIauS5zKXHqsbVUZ%Y!>h^#zB_)*VEM zbfo9rFt-nXr#=yQ6~y9Jt<-?Sq?ayfKIIvs_|(T;Yk~d!X&>~T8cOW$O#2w^y�$ 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 literal 0 HcmV?d00001 From c8ebd4b2d3e32d5d474bb8a156ac3fb1c1a092f2 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 23 Jan 2024 14:16:03 +0800 Subject: [PATCH 08/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/img/zsxq-gitee.png | Bin 50434 -> 24941 bytes static/img/zsxq-github.png | Bin 51092 -> 24640 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/static/img/zsxq-gitee.png b/static/img/zsxq-gitee.png index cd26d0ac77a55e0cc5378da2970874fef6279078..be9c435ea7f4cb08ebdbd81d30a89af36eba6f15 100644 GIT binary patch literal 24941 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%0D!L6`7_hT*_vEFF!G!?v^Ya7+5milC1D=M`xNi zA}+y;yc{T4Y~aTNlu(PsR=sbi6h!X#_7xR{K*6oIx18X}-{26RgE~yQSPvZv(T%1V z3mJD39IWtt+8VE#hPxtSOTff1WuliHy51dp1z$ZOns(gps{MEK5_pJ@Ya8nvmMO`1 zJne+no8;B*wWG!KO?2H*jgU!&X3gkm?<(#w4S#Bp@U0;V-~#h!4uObSN9nl9B|t zc`OL=^17KCkMzk7c8t#BW6*f=rqW!yZ`rt?MS0EAD4eLiJSM}=xUJ9sqtyKmm^&qS zHwa;KSIf*&O#J$yiaLfMo--5v=}Huon29=U^_fx6mz8L9F&dE`z1P01YSRO2=XG!B zx{>SMh?90QQFrPIL$9NgF_!73z5b||?+_F=F_1$AZi5U^%Lkpa_R5>mdLe|ZwN}*k zrexJ}rh?$h?Uvy_6T`6u)ABpS(US8Y$HzY71YWEWVu~&x&+N$jN=y_!81MKWfu0sw z^10Ffn1IznDb8UA+A&Y7L$|(7*UThFC8oN5d_5|l7;3HFzq4Q!muMHKFk*S6-n%$c_z zE+G!?>~yG^spk(a|ACMK@6u?v^l<$srYAUlY2aD_-RBfM)1t3lG$d4s__ zVOFi1pfcB{k33B~=K7{2uh2={PVi3BMHtNbo6UyhSZcvi!Bla9p>0<@?WjDNHqpFR z!w)rAD%m2`fUX##WaA$RE>81Td~-5*#YMn=!MwT&qX-xvgAH)e2wYd-PSv{CY+sGu zss5yvEo5AWsT3ewWpxWCE>fKL{{tMT8yXF74t8tV zXj-zWlX^RCfB$Ysw-?C?`E#rq7SbpclGNe_3@5JnY2&C)XFTf}-mH{Y9;4O(`8FES zPFs$dS>ZXQe8uCHW7z(s^|jJa4*RjA33dKE(^H{Rj7sdwgHyr5&CkfR!tN$b3J%_C zVnetV`0N==^vXWF_BpZ#Bgq;@BW1q!@Li_N*C@ih&hXh`l{ipa6_XqexYv4G#`+}G zz5_*W#VQ^ob?*U8G8MSdcmd+|2R~DMPtE4`PXiCli7R%ne`e8CZ5o7dGiD?7OC0!_ z<;fd4%wtKE83T2@Cy#v0odI9hp{#4q9KNY}2bs6s-W_Iyl>>KJ*@04!qcg@LR*Goz z)Wecon_xELF4yC9LGH4_~@9KUdW!rEnMOsBa&46$0%o{(eu7oyhwN~@+-@v%!Po543Y*Jc&^ zT(;xd;u&?&<|!`d<;00>4s=ZY0e}=$n5|feIauS5zKXHqsbVUZ%Y!>h^#zB_)*VEM zbfo9rFt-nXr#=yQ6~y9Jt<-?Sq?ayfKIIvs_|(T;Yk~d!X&>~T8cOW$O#2w^y�$ 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 E1)LP09RL6T literal 51092 zcmeFZcTiJn_Xes6f^q~KVxcJ_A|(m}A_NeWB2^HO8kH(YF9AYFMMb)Rv|yu&G-(L| zA}Tdfq?aUw8fhD(1qdN`M}_m<-*4vLxifd>&Yk)GIG!2ZdH3FHt@l}LJ?nY*Gksn4 z1N%?z-?eMk0ZonT2D^4K0lRkX31Hg`{tfFlhz$6@-JS;OD!U5Xc&2vk64<4A{n~9` zt673yh=oxK?Rj|B?JOE?mwR?| zyVWJ$xW@9emPLgv^ik}iS!%6thF4XW->{*+vyrzEft**BGmV^frAeu7pvj74PnQ(f zaC(&yJoneat&zvOnAikD{{HdoSgs&S*gw#LV>k0bfwa4JV-+r69Y z-90_)O8e|J_8r7qqYko-UlNdH{u?dbWxIA`Hz$Npq6hYoZ=P@239NU!-dM4!xj{(R z_4_Q9Z=c={QFr!(<4ci;@i!jD@==DOer^9C)R%>AY0fuB{vMPld*t2D^@)caicZ&B z65KhZKSm6~vxwE{tbb#(g_}+KAMo!)M2(l~pdxQ%MFL-X#JT;m9mx9H0wN=FB7tjF zW{xr_ldI=VHUerT*cxtZzuD-qT(8%K%j=`77cN`1vQTa^|Gb(0&VYLt`P%j+A5QB( zbF+NrdAqaFE(}w2an1>UQ$muwV;q%(3tVM^QRWZ2Na2{6yK~mx4Y*}AYhGSujaL1$ zM(9Q6_@i!t%cIucZx%i*u7d)~rzpPb$4U;9bI0qyqIV2%^U(;Z{Sd`Bv1)(VKY%+6 z3DuwpF~*M>O`jntZ13L?NGM9x!(yynIvP?LFt(QqKeoTY{l$^Sg(nHzW?jM z{YP7jC&mS5N_saAenFfGN90`_^tZ4J?D}ymrPMxB0p*k-rc`V$BP-K1*|4Y6?L9iM zcI=h(j<@fH0=!pSIwYVr4#|LItvU>i8rG02Z9h15+hlm~nRKlAOac$-2sgf9L_W$% zPNLLjXe4~GW`%tM*^0BJ$7Sz_A=CfG+t;s@+c`4EomCrgjcVFS*D^4PM@ddX)xsB0 z=3n5GOinSlkMaR!H-b=GwrFpH3h-eG%f0O;aASDd96H zD%;$8gBNw6DBxH7BOjQ_k+Ge8!FAYegcf5c(O$LWvTv~WqDuj#PJe}DmDd5RmSN!| zumQfYu?zRLw{y_z{Y)-H-;2({Qp?FTNna60;fO31jQQvJLUVy(Qz&+S^3j~@+gs=0 zg3vv&cS8+k*R_v7K$9d35db(<9?r*6j|o z&Q^o37@=5m$FYlA58e8hmjZT8jdjM(Bm9!S6U#+7L>vCimtr>1@+sNpBr2Y> zP;!-#NPQ9b(31GgjrQokX|lWNC60gXAI4(bwyFF9^ZlZwu5aHH4`)lX*R%bA`(vL7 z6*mZ_ljOT?a{;l?fI6rY_p$x`69Bv@k7NZ9d}dZ(efI}Y5i3vLnQveC`gW+7TYJrn z)!zm4JhfV<^v6ctP5kp(&iDwv06xS{D^&-sFvDB--wNN}5RV|SC%|0}1^rJ1QMm)T zRUpEBg<=6<8=f@}$c4+>g4N{$nR}9N_s{ZZXvj zB0DZ{RQb$834jldvCXFzTue>%c%9FOAL~DpFt3X^`IuzYHc?ly9UjV8n5Ia4T*OG| zgY0y1scWnhkBVl8`3i3KIlMb3IZ7>O=SnnFN}c5qVu88c?*fzEI3?(#vZ!i9H&W7C%IG+Mbx%;Y#5hY9ihhK zt7i$lg6Q4H?5U$8(nJJM>Y(o-MlI>uwVVfS_+aOMVlJ@(Sr*55{<4Ww5kCV6RwD++ z_)Od?0q6G0`3#av?WElHs~u!B0Nsa|pR4~;HCc#R$&-43O63416g9Iu6e+Rc)LfSu z8$0@lWO@6+6ZkC5@=W&)WPi!E+S<`|fnn{-QQNV#cC?8bFMf@<_$Vd!F5k+M0ndHx z7+)X3M>getOo-TOX!rNb$H(#NUUMRSF{x*}sGP2)+2ZOkE#_pRXoOj`4ecW$BDxdb zc_t#J;#Fa|b=B%iLx0Yf;!y{{gkaoN(mN`#!aHvHH75D$p@9J}jjb|UqnU#_t9)k@ z5S(QkBKfCQTaL)f9#wGBt|WT2g;;+do0R{&E;d*xm{igkj`Hj4>uF96N7zzFR7YP( zoVt8&WlY$oFxgTksstHR_XlQkZ9xxIKJsq$BqVtBK}+hO@U#~)xjB1+{p(byZqv5m zYPiNm<$I9Ci&b3aWC6+kpBR zWA82UA8FI=p%cO)>S_1&f0n*6qERe8-YG#aj=!L(emwtWchq-cZ}jE(rMG4 z1v7%Tpk91_Ov-CO-g50*^oqyQC-Qzic-`kXk<2SrN~Y9Ln?7Utei0`-^75Cb`{m}_ zId~q#Lv^AgtDV|ql;}TS8QR>;ye5ettDyr4qIsFYTPP{p+Y~F0_Us8$C*DS$zwj1n zcFc`mx3#iouvJbffO}B!ot#V;WV+D5yS>0E?kUNIaPHl{UmPtxkG&dcdjxf3DKS^x zcpo?w!xM*nXep!z;017;vo4;=1THK2(d&N0PoJ62Y|U$B#tmR(@(Le=A?y8_Vq9~c z;%q8X+E(?!A&;G&+=>=yw%a>E`!)jlm^-nXlz*m6d zBYO)V0WV2ruao%4z7`lP({?c})%O;^eHDetu0?eEd8ia^RY{@1EZHZ2K8?Jx<6n4owW6XB}OBXCy_2?_gs4#s%%BTYSor6KJNWm$**`G zk;Yj`bjNzPs`#C@hHX9w+>48PC!E9N_eLIYxkx!Z6qHew-QW%HBdn$^8=rvH5i*=6 zLZR7o(~XrULW^r{2j1=j!mq-$oD)y1(dXNS72N%&6=bnj)qD8|GgC7?Ju`)y0tuHV zZRD~j0)BSnbL#^eeHAS>wH%c^no)J^f8qBni=xpOsicjBda6%L4$My_^(DW7cO5OV zF;ht5B}|?UX34G0BRROKz=)Qxnju5$iP}zc+uF_P^llmC@=W?xV-q<>sr0TJ)~n-Z zFH9GI$%zw4NM3c(;1+LD=a*o2Gf3 zr!nmk0iN{=PLbVzVk$)WiSlhXcvcqC8#1xygETiDH!zHJ#cBJyBK!{|@Zlp+uVp7X zyKjtFcv?_LVTO7ZCDr&Nwz_lBJ15Pfn>K6A4fzdvYOnV8;LCxmaC;9*wyjyxZ9Oxr{sBNag;6<$VdvjrR^l^@U}D$-)Mf9c z|=w>3HZo4C<^QS^NPFPF?R^=*ojQno3J{Xy`e;+BCotwG6!##s%6G)Nfbt0xDUI0 zSMwoEY2<)pnN!SL1XwZ=Xt*bcjrQ=N-WFM_;A6qHq%GJH`=+v5)p3ZqaWmYw6_RJq z@uw?%4c&hy93$1jO-!h=JlD&a)4+u<7_xazGjQ81#ok=@PUCn`*sLe+udqsflq~57 ztAi47$->&nQq%(l4sMY8)09{D^%8$D!zmAgC_GdlFd`bcp-ETI4jZ4Ku(uT#Lcaw* zA?c1`4}ZI)_qRqAxE*TJ?(b?0mdFuyk=or6xASW(vp>`kC^9ExgF3^h&V~}2fet^M zP5ipjm25zSTG5fB(dW(Kr(noi3Wq9ij=tv{LMpW(*yr*14i7kdgXRjJ>22%nL>QWER# zk_@(qeG1nac26oYNiMa=^Wo>AH1Rxg+`K}7Mq$th#18q{u?r~d>Vm!1t>jl3a!-O^NAZ{2UscO0!{wX z6*txCA)Td~x5zO7X%cKuv^$UUFs4r)NjIFjYFg^j4iE^|(fBgh7||oHz<=zs9`Yk(*$^Jtv!_1mSdn zrJ+bx5A(kdJ_%ebNc^E^g7z7I=HZxGcO!Gm3gW%k*fo5{@~T%XMpj&}XR;O_VT)bf zkZlY?)U&f-fA#R1xVX&^l|*j33{K=7Q(RxP&f~0|bV+o_I&4xOl^XJ23|jIGz8Khn zqHVTIO8p@>1hC33ZhEZ03>4ox4v?*83;oZ`qqv8LfsXyokfj@oMn_eZ@YFUR{NYO+4{p|(*6oP}{%IS5` z3|M5~nB(B(3+d`K3ye$&!M$je)zFiF&xsaZ3Q8$`1DgPOX^mLcEWjcpFhl0ffwZ;p zNJas`GH;i>!9(%lPuveAA2=yazv%M8dqaqYvV5%`IYz`-+oL@P(+sod#XsHs{Dz}t z;CTC0ehJu>EwKlV58_0@f~~cM{Hsw$bMK#>EpRu~kp-@9{0swJW2UHpnRd=ukM8rb zb~3)!=v-UlHIDgCRTrg|&hG9$emtbF<}z-x_Y|R^a`Wd3ru#8)k;(=Dx}`&uT3>ow zcbDB;t9c%u>VM`3RguK<5>n;2DtS`PRi6u2UsCt>&DUO}&dtr|tw}MvT^*tCCwi z>6`BMO>%qG-bZ7CXB^j8Tet^bj0mD~^dyTtO->M3%5(4!uX-t0@GAEE2f-WtwJF3r zI7(n&v8}W&;#rzJJaN9r{}w>_)LH$6=TE<;jNrQCKZ!7#4~^=jEKIBi-7rG6h*=F=U@4cw!D@g z80d|-)88KSX05i5(Dk59ncR_E>K$I|RQL8dkb*&Y&iLUWoS9cvySt5Jag%t%h0XO<6lN68A+PJ=OXH59Th=H8#OmtS%_UOHX>c4?OQ=3RjI==U&b-szbM6knjB5{ytRyTY#erq3j)J*pa)c>+)1 zc0>ygd3`8K=3af6D4$bn>JOh51E!=Yt_Wv&{aSfos{9Vz4K|(3ZS}XoeSM_qA0Q=k zXyZN(Zd}rZ)nGH<(&7%vNe~tE4jjG z&sFb4m|mdaG;1oE$bB_tC|;@%!7o<;GI5`qj+D0W4boq+f6$-C%l%;vL(Pu3)j?&8bI7mSUbqJwO7Nj-Pol?)qitgl(tIo@ zR_eMt!Z7CFm-&7!f2k6O1P@f*MB>rclKA_@!S3)qK;i&8b=qd-wTYK)Uc8vw8a!fy z$3gedx{4IyHhc`{M3;WWrE&7c2AeLHeGH~)1zQ}a#q6`MB>G{U=gzFJG-Q>qP@YVl zGex;Z1l*f?xZeIu%EoN?bRK4fZa?zwQ*~3sb<6&(W)W^JN_2*e&R1f0kUIsxgzrPd zqzKF(_X)VQpMuldfL-9=dl0?(nEYdIW*m_}M4W)EvS-sCOOd zZIh9FHx(N7?i{Tb0jGSV#MEp$OeaSy^TOfWej~MW z1ySjEr@PZGp545ZWj;8Zq$J_5veySl+FuTMlN)S3W(FU}^(}L$MkmHZWyF8L1QO1y z_dLmRj$S7<9hsf%4&g^ey%MX!8k@rpqz{e7n4}6g3 z!Y{FGfpLKeQTvxShzS^Wc2zj9WLc@f_k8V1_x(KwW;);fBsy~wsa~`38cHaX3hBIqGeMW!Lg{Tf zI0G!t^%{JU#5_+>H3|~z5o5GTXfP3?AgTMHRNNyZ?d=muTEJXgpx=B527euWllKEskHxJIEM4Lo*Ml}bU4r#D5C?2$a5@+rtVhb_3 zmSRl}`lNlPqGf#R$RnQ^ME4O}p0jlsl*`++Wrrg3+c3mWy`+7TD5uQxW7FppDQT4q+;gX6->`XL+*V@AtC1_pmjAFSVW5A-}TBw8O)}q zxmGP_?qR%?#G%$P0e0s^kuq+@VP1F`0^<*pG~HP_N*Oec?0AmeXI@IWTjrLtItB|p zkT$<3FD1Ca_yn=%@7sqcQxBdOLfJoAQK2i<5e&g}zQs&g{iSA`v~z`>TRWVh&}NS| z$3n}`QBdXp%H9h37^psi_dD{ppWDSWrz&s(Wq-k8wZa}?Yd%0^h97z+3igDjQI#>W z7fN@ydkZ&+R}R!9f`ladmjqogI)#6S9MN?nw?=OBU}ccq4A#yRn00SQMN5K&;M~lC zhKJkKTqp{hP490GfcJDoxHnqv*4Vdg@et(!(3dRt-jE7uo;Y4Ot}?r-cVMjWj`Gc& z5x~iV#msP7!m=VImj(n824zkHZiy z8tv7G(oa<^Jzfi_tI^kh9PMyAgCc*qG3Qb22JVdOHSI z&#vhUds7DOpL(^h^*?UtAI6`hmv73gHxDKo<(MLVte9dyUoJ0edKwY*{;JG}^TQ_# z9r^{kGBAv**ZN&JKkt^YdT4^z1lq-5%(A2Y;8^4rc=#*q$iOIXQ<=x!n zLC&_#;;3_H6VSf_ZdRtuojVSCy3PN1lb|5AG~GLQG49Td{U8;}vJUbwETU|{k1E)m zjTiV&cGtX4gIP58Jow}La-ew`+fu+lpo|n$?-~4(?Bg1pZgM+RrVK+^KA@iZZI0}N zz(&@%_)c9z!*MOuLd=w0u0!s<+)&S)>S|YYuS<;8jx$yh*;(!N$vWDSmK{!%IyW+_ z?3qxFf}4-0UuL0XxM!vRfIAR7xxuQ@bf~ZC>rk&)GZ-jtYxQ2lW#q2 z0M4KG9b)()Y{x1e4&leDocQF>Oe`iGyuy{pfLm#qRe|Kb@4?D^gc1plpTST-dws26 z#CaM|7-e4yD6WJu!)IzY8gsCQ=T>JkZGR%mq%M%di{m1%?Pg-T{Tum&{~%weM(FBs zd@_=LZE7e_D@kYR$1(Km0Kx_=GUM!~!qS9wlTWGXw0(fX36=}r1Jlpd)rdP4MQ7L! zu<$!By&>(3AxZ#y@~c4?{KGI|#=j=4VWBLszIZvT9cXwedH!R-wOWJmuUU@;mS~3; zQtI_u+Iwr_z1ixZSkiO$!UQC*ye(~^1x;xw#mL;S!W?vbYrZW_P1rxn=wCTF;TWj6Q#A|NTh+G{Z-A4|jqAo%z8NUa{r0uG zzRUKL5R^gsa}bvo&NA}GUyN6NuZ&;^*26|)dL@ohLI(`20J()s-eCXUH~E~vL^Ot} zlrm#(E2%{=es_qz1<*(ss1=;ncKW@H4Lql_4Z6iw8v-Y|=rwME2HPvyg!Dn8u5&VyQ;P~mB z)f4zhbHj{@oMTW4>|pWruQv}cv7H6ghJ|;ScOX7j%?%zy^^BqAFXx*ywtdO_r~)^a5jrO9kbybl|tzO()xT? zsdJ{}A#mJ^myI=p0q#-lH6zeCEPfp0@)4X9eM3WKXc^8xb>`N1kqy`v^{(cr?+U#D ziYd8%{u|jK_;)TT^-prlb0;ZwopVtCQe)MDt_}Z)Uig}4|1t*#B!{|l(r<$kOh=kj zCb%%7t6tWw9Pd?jLrq~#aPG3%Od-!zT^H#3+mB-wAwJixE^(Q-!L22(P*P>uz3B3M z2Oe0-bNsKkE~_K~0Dz2jhvRpng;TYCmsd6N16VQ@RxhvP*SboB##iUt12*8M!IM%b z%#nhsg|D48s*N8#t4IJ-^~6e1V}=Mnzr4^)ENf+fCbaN}i5r&wy|Njcq1i{lfOksb zp4rECGq)T8(Pi`hgjr5%=>o$FztUwSGJV%Ct+zI0#aX4W0J;=Wa-i~t_jIZ^o_tJ6 z2<&2W-_`}Le$ThHpeUM>yjoUjq62m?xW7 zC-e?}`kn&z%;)vMs96yFPh_*~w?d4pbmncjQt`a;)Y$1n&v`D2uf`k>`lbPzcW$W% z)Mlwcm#y9P>Y4-AYsa>5hLD6v)~L3t_J&%KJd~f#dFTfq0UJ~u5Zt-$d=rx9sh0*( zf9{n3{gNf3rP`C&XBR`2UgD1hX$^mQvGeHSN+=Aov|Iwa5trQ^ZMxd7otPUW3?w%dE4nC z<-EE5Gao@=k8DnjX4S;QN_XmVqmoBj+4$y}!|HDQU{R$YJx1t!rYDBHY-m$#*n3Rz+eQ+3>yDeQ3un-@901*D8hR3)EKM5jS~TzdgW1 zTjClxp1AD*$1aZ}z)sO4iU2w4n(nFBdlA?t3*^b6`3+h#^uESrcMD!xqAkXmQh!E! zdw7RJZZEnM-nY{#z?3>`nbsmS3d)soV?k$weeR!DD2_>y=?+^Rq3)lV#X;jJB=3+* z&(lY^^KR5ZNWf&F4E(cHWA1e~I2rSB;W#?5CCd*R*DWbrS#H;I1S^J8A~t%DZQv+% zR$9Z|XF`-G810YejtdtKeOB2AaOA)n+dOd1+|4X|)YZ%;M16hw&Y>TSd^I)KU+giu zfWmlUa=zeYCJa)#okIL{UCcY2XO1KVVl9qVIAZ0`GaxqKO6)ga++L;O4q-tr$Z;My3P4W~Q3p>uAq-PmO0R zZQWk(PUbw3=D%!9Pj57uly72LT}M>5L|CQwY^*=JAD-+>4FL!j+UEwvhB){?-cd?t+s*8# z#n=yee~r0K3jDX1{BLLeo5A>hljl&5WdfU)#5b0RCQ+)-%WgWjQCl=?_BMeb+`I`L zvVkp)#5erlx%`gqCWjfmn9_+T{%3B`l6aZS$>??`JMK7wm2BQadAtC{)lxiW{tg5b zQbpNX>}60sWsL1|^zU|I7Fk$ZhPQk}(y}Ig-Wmv6du_2iYxyawjZyVfg7CeDR zObl42bvHV(&&*8Qv_9rf0^4^pPDbB50ZURS;C=h?Dn;unp{vk=cZ?KgcVcNlujOPy zID@QXkUgi>{vur6J}?CcI7w7lOlkOwgOeg#eX>izO5rfrO5Wn1-*4GZ?@CI{wzl|{+2)&{PK0aBxW~-hfy^T* z_DffOPx4+0f_e1kc75;0BpZzsgCQ^0l+V{w;Vpip!LF%|*^#bC;j;J|yiCfk7<{(k z*5os(@h7(ux>wwfZaK{17X8L7H~4t3Ha}-Ca-y*`uDFj#!CP=bE|BNed@n*ls?(4G z3Gly=z@{ruqjQenYmJMm9?um_Q&|0Z}=4TBMG~D4m;Y z_8y%@sI+f+c+7{ID$!{TCOu2vP1bS>^2b99@^L4_LF>JyF^E4W-M`kmwr|`eL8Nz+ z_f%37L&BpdV|UYd;H6^txK4Ou#n!+^ICY#n!%gO$p0YeOv7YWM4sv&a%0BS62y?x8 zsq0sJ`f?h)_*nwa+0!>cwXaz_SuuBG6IozN1_c16>8}p8fR#}@0Wf^_xEj&r!AJ{< zSGdTaB;n*?Mi-p$w;KpMyax8Si(pZgH$A{Y2@aajG!6cB;KN414T)~Oz(o^NrIrJL z(j8hnPv1M3aVvUH^aGmm2X=td**tA!DROU=BjL;FRy_&C|4%_%-`ZA8heFu^A;@V~ zFmEw2+RPiM|EqM%Vsm(yw9NccH?=mX}<5Lz(N`i-0M^&hg)LkKi!8uhM{o&L^)y8I7U4@*L z>PX65Y^h{L?YG_2d^&zA4aO<1Md`^KLT8txtDQKfjOmrI`<+Tu@~@o8nM*XsE1hB> zVPVhX+#H>)I*JFI%3Vyyz=laBa@#})-4H_rP!U_}-nWbu&k(OHGe1IfC&{tH%?v=BBxTV81IIhFNq{f3dNmQy4kY$>FTV20S}I$_fC zxsQ&?%eq0lNQIly6s5;33(OzLNb|*3w$>D_kfT6N%CC;yT~*(aAP7KY0Af z-?aHXDHt>Sh-f{i5KmwfftouIiHhAUNND4t9LSY;nhnc8x!RP^>XWtQ>HXDe9aK0~ zKTJd;2Dv0f$fqO)!_Mhg3@BdEv1PJw1F z=W&@XwPdtM{}G6m#*CZ!@7(P5OA>IO9Q~CwvQ4h0zMg9W!82YeRisOpD;m$&cZ0Wk zWCpiUMLS!bvId_8_FVxPt^b)z*fy!Z>_M$x@KBf}sF#$xWS#$=QRHXU41Jnh8mQN6 z9*iu6_(iXWQH#MtJ1Xr#1=dI+JQsg?a{MD0x%^eG@_`ORsc*@(LEScJ-;UgmCn;== z^M$FID^E2l?dkRD=qye%zY%ZeHp|6Fkvtl1ls7G}*vdG6Gi%%B?(xtA+`}Ckp}Njp zWBh2HB-p}fXTP*671-x-05yEkG5)u{Hm4aSex9A;Vv+%O96s%r8DIM8UJOWnly3&m z?u(SI)MtEl=B{$M>$3*&x=(qyj^w4U*6SDQF6rcgtf|d(0yh3wMN4k)%QMUen~s5j zU&yv?OErOLQtjNrH9ySjcyvYfGgP+=@XS?#4-$fikNi$Ts zvUq)caGz^Oy5ef8UiIdyO}nwqCU^Pno@P)6iv#J73bbnBK7c`Z_3{o1bGn$H@{`!l za9k7^;cu>QNBI7iizD40;h3*20d`eGb{<1jH#~m5{$U!Ez1j0b$g~h-coH`5l6xDT zbW8Pm!dxUk-EP^|uV9HZ!M>=}vo1#CPgiYTp~Kr7iDI8<&l`Kk~)(R^aQWyt~ecyV2*w0usVaeT~%QHO9dZ>JSe zeK8aUa&Dt?tS!D>EJzY;{@uYbEvxa*Xhfi8Mzsf#^_z3DJEStoZdp~yrL?`Kn*=n_ za4D`_l@W?d|Ay+;>5F zFl9|09wEQ9^0kNEVAwG=2(2IRQ^WIz930m_X8Du-)n$jiXoXv~$fci6MMb_=EUp7IL)wyH;*lbWS^?L>~oEWJE?MBKG^$nGe1E z&SH=$z zdl3zZrfm{sSob!lP@0DT6I+=Q15I1E(NrTeAVxx4?;F7=lOeiU@3FT9z!~4x=&74X z>o+7tQ0cNh`pdpKMAtG4MlKu$iNLx3tX5w7@-srlG(%F^K1UQ|G*B_oayN*I4;k{u z(z~vJxfK>Y>c=?2;pc=*0kbi2RmsKbw5)`0t`6ayJyunBR{Awb4#1abZv~AA-Uw7w zaE#yV`*tGlWM9{S9_?H?Zv;q;3A|=-9q+d(N{!u>LUt1@eVUUMNy|P7X|N1I`81BK z$)3DIMNt$cHH!-8%2-<7`f5IZk$XQ-Bkz-xMGh{$tKY6r2OctAZ1W3FLRmPNY-<5b zmeTuJwitt}`4fsItr6D<#~Jl)avNIS6$^ytwSNz&L^Wz{4KuZy(K9v4IE)n1Awj-F zhn+tO)c3|z@5Lv$x1G4C!pOfWi1)`>z+oDLy|y^A6QZ9R8+GHr12L~QipTYOYtsig z;;Ga6Cc>v6wIwSt()_xXfg=&-!Kcqx7)`jAE7rM0fMQmsWCTkLXcX+iLUMqD7-*pdf!fcqEI#hC+I6G_tSH&47>c?Gtn-{vCP1JE!x zzM&FbWdAK+&OlS)iMt>!3aEos2y}IwQo&1m}|uNx$XAPuk2PqOeDW> zmP(W*zhLW*XOtJI+k5Qgt~WLQIB#%()C>-idYnR}1VfaI8H;yr_ZIq0%#>+yZgaa2 z_U$Q$*en_4@wJ_jE~LYXmNi<4Sm_JlILLO3@sHr(?6;7GO)Vs+>2`1)YQ~Bt{dy0x z3PUJh9JT%0hx$F5)jte6fv4IBT=8R;!rq-=%Vji7O-mc^JC)otn~^rIo?~&AcV^nC z(WuIy>7)6Dz?*I01`TBgP0X&bO*XKdy_fc#Uj~vKBj(#byYfYDJ^?&S_(GvAaG}fj zhI7Qbu;NUfWsptG%jJ9;QoG{Zp#u78?|1NQQr`61p9|32n&%Mll6bl=@I{*}} zZY1-)q*i=#8nh0nB0I=pBwLozUKs|$)a2x6&Ms(24#&5 zcRd8>qihsClX_oRDB((22Rq1qAn*)g;g<5Bvc0>4;iB4Y7d=vr55sVYQNZlZy=&%5 z%F3|Zl!9OTr6VmT-i}-E1BANV1^#E(qVjr>U)x(_>N6p$Hk?t`zaJu`CFMAJgi}FE zf7}U2o`i1cYn4``?9Hd`K@G`S!d0*D<`liL?3V9ajn`L6`5~OS8}YUl%#Od=Ba>~P zst9P-1>PmQEcP6+Q>#MLrq{y|C41m9yzxi)Xr>=*ey#W2r0ZG=wi`wsh}|i@@TV{4h?t&MXmFtS zSH7tqC7!*RIqcwmPG#)t37#*YmJs78BBHV@lp!M+^#IrUJ@gk~jz5U+3ibBw12dtG_2r3tBO2APxpCQNLs?i5=7^UxxdfP?(9i$6lD_- zQ~P0Q?R*uu#TQqBF$>0r2Fia=yal(*z0)M%4%WXe;zRGuEy}$gjlrkQPo6Sp2d8$A zw6Q+2(k-EaI(c-lQcP+)B84RVF%|4RAj% zxtD3~H-GQCZEm&Ksr>r1r6eN{f>!CxijDfb=*??aE(4rzej-?1#=*$&Vb z%H2E5-7-*$J@81^HM5a|gvMi{E6BK-;=)cw=rlcw%ng z+&eS1=l?U&37ijg#Cv1mxSQyaEY^uWa26O;ydmTV&gc>bq`^e)iu8@1esz`=u7Yvg z$MCuW1FLMWZExg#w`U5e=^(N5y;jwB3t8|9U2+;XizcKQh7tqcO^XY|x*I(2w+Bg9 zAovrd>MZN8A7Wx-KF6459^dw<@I|o7W-u3AGZ>Kh#bf+z>y}!FN^b;JuTFFur_AeI zRdUT3A_fh-zLb|*sTk~9R{~1JlWtIb-UkKUoWItYS6h2`ZXP@pPzC>a=MXDUVe%*? zK|sIsH?IB*+KG6{(xUa^7s2BdpE9=mdyORDs4M-Zr1@|Mia`N@CS-zJKG9P}1tI6p z0}4)c8ASodwjb|=-h51HND^G6u=9%yO)(Bgg4D)Kc^NSYgczK>H9d0Vw%!L3vW>!x zY#wOzja{K-jQk|Nou4|G5{WWLE+>`vsbCJh@BoL)3{J^n$FJ49Mxa|lHVEy_>=#d! z5sai|ye+x{zX9+VPWdn%VeG^tVSgBqpvsykKoF^%eWS~-j)26bCiti!+r(z-VYl5x zXw5{!j|3-2t6E{zyA<*7Yiwn%jJTxzO;oS#SUTY&ma%ZimrW^Bvr*~*5(%!0-8?d%m^11Zn@XTTOqS%?KWTysNlZ4`#4T4^r(cu0Ww z|K>OTJF1Yt;qF9q7x6rp#6CV-zqpS<51KKZ;+#vS)Vg8Ch}cpyaW~@0-*V$bMi+6` zcQ-sIu-DY&&|o-B^Bjl;nuoy8IK|t~PqrZpj=kTz0j8{DzvnJz9d=jd(2!ljUshke zHi?@&q%81^@dc-$Ng@tOIV+(X0tNe>$lv4N!)2fCLHS}ZkF4}hxef(}sc(nUtuE;? zK7sPSLLIi7K{7_(VALFkKZXpQk+Ppe`1)%Xi)xI4*Lv-yl%+^zZb+Nk#nss~;gqRhrrS!8~eUcHug&@8Kwnc=cX)!*yB5231c*OOu9 zoynp-Jv}UPWJg)cpTT-9bGT1@Nqk~sN%9dl`R8$UeW+uZXP56z$(ET#y862STB!`x zYQR@VhsccLpnbq|meY)qc%K2Odbi+sr6B~8M=|5@5(+)fd0_$)(Atr-QoMZkgh`iu zykmge+R48A$&dKNXitotezm3#VaPO7kepB~o9c88W3a&WzYA<|wfrOYEfH#o6*}2T zdotSV#a1Ajxk#~@dsB6iF1KSesb~l*Y|Lykz9T$qxWiGrHpLUeIF>hixB~a6f~YwS zp0&7hfB{_b0xSU1=P?-V)$fT;avh1xZxwR8u?z2>RDO)eV#T3uLC?*RYk2~qoPKGk za3%$*=9jP8watN}yNM6vd3NWjB4%*KIol|0|mXKX>yg|%n-Mn6MSmWYfAPSqfWaI`aZ&y@n*b(hk7e`u=X`IOfIR88T#V=0vSa${izg>ylaV3C%^1Vvv zf7ubhk|CS-31&c?edZHceKWWeSZtYTrPOz2-uxN zaApVbW96KImvQB)Zk&PT2|Y?+6TtTyV6K69A&PxCvH4ifEe2jA=5)<1rcZJChgcs+ z9dx-z*O(3()c>zRmEDRqKP4`4E7n1YE1y2H)IqJN9qOeP^30SD6M0h_s9v4G^)zS-nvt zt5zcpIv($C^6~VJ)wXXaURQHeUF&Ya#7y0&{&P?1n7r<)L->4j%SdJ#82ze$qn{1< zC1#$r^(7|9JPaWco%GoZ*B4&@<|x^b&oIwh5M|<-_*Ds10G2%2G4_~g#ZB5{e!WU& ziUE&!Xn9f*Yow}3TaQAv!Hk+)(d31QU>DH$2Yy3q7>JmSr4RpaUjp>e$=}Thvxu@= zjQzlc749Gf!eP9Ydl_@DSuLL!N7faSrB^o#PSw?<=JRZ!L2!{i`v+XWFQ>MDGMa{L z`c?^{l1Cl(eJl@v-r>3FUO0Iv(M%R(0!-vKJwewx{ibaWzu#2{l4tqdu(e-aTHu#H zejL8zwC-Pg<>jbFtM!F)7iNlMp~o8e9LST!{)T!D@bj$-IO9C*!q^n(py%&|ihCDV z?z*3^N2fO4b|kO?Jr)xS?O+1hafIdpAPK7+SZB`kJPQUJdop=;D?*FmRMR=9S(YfD@AghB5#lrL-DU7<#% z+lq@hJv5cYq44gNACH&cV#2$}96o$#Cl0_9ChUEO^~LIhr@MjP-5oUg|H0my$3xlv{lg`NT=pwUmh6=+y6kI~kfKs!nV2G!eG-#pNE;$V zkq}bJzJ;;ONVe=`88KrS`!>op7|U~oCw)-lEi+2ZK%~X6t@4ePo^YTH`SOx$C&H@W7g^)ww3^8|6gIWK%e@q7O z=4I!@xStnaFpAX>%f-D5*i%WI$0ftWK>Me-E&D+cAcoPi#6-JIi@#r;0+{}cqY|4H zkuzPH&lxROwCH$9p%_8Nz+n*=6Lp8wc!`^^|(5J!-EKK;Cya5S|kk;4qp@iAb~#cSYv26V1Feg`s(TL)bj zGI5_wB`H^9<3{}zB^<9!3NQ?VNGLRL`#c+?LXJN0CR*k1?k{{$`aVzApDcbJ)P_ca z#D+!pr!U&GY~WLp}(58)0ObYXLVL&!!8Gprb` z7p9`mx={*CXWg%WtR|KWL*xg_i2SWjlmouL50x9^lA?Wbo0_fdO86wygs)u*x&Kz4 zfteQ7`1rzM5s=R8q_8rrpvd#U6u?1_c=oB+wcx*WgH*_)H?f|7RH`xS*6MGI!`uMP z0J8xpngBlJHN;LpaO5kReX@S$Kh}y~ig4@U_Cls|<)}q~wN$_th(n$9o*31`XYca}MkMzI`-;`7f#O0J2TfcCwxQrR1;$ zxbY(<`f@H_v=ZbALLux3CdLCKStDt95Tc0;0B6>P&X-$8u-{^efb6?VexbS)b#B|jSkH#qnae-%nstVK+!$a!<`4-f1bF`#s- zoyfL5FjHvoO$5`2eQXT z_qMaa!SdDrA;arCxEY@?@9&Oe?Ub;KK4EhRA9MdL9ip&lR?ZxO56kf=2!*G~@ySke z8p1mTBSdmXLNiRZ61=eQwzitK;{0+rGa7m8c%!K5pDDPbi&lB%f%_#LiOIE9dX1Lu z_k2q3@g#x+6h5lrHmm&NhTrl+9b8;d!_Mg0|ReOi_Ik7l!cSdP|{rpM2zIzK?gL{>E!=~cE}@=TV%6sMR8gM zpbCNtFHzX^1Oz~Fxy}0}fHS6_|H>dBV8TSluIz8#o)9v(Q&s`^lQ zzDCC|l6(`eh69|3D1{GeD#?RGl58Up(G(<^c6F@tNQ|n?IY6fje==o|UGt50fZboc z$r8QNI~NMiE@-H*iS#<%=yBT3c#Qi|?*YIdZ|%4e(DmqwZ;}CzksJG9zT#U}2J?p` z@p~%O>ctOPxdJNqa)$^2x^BMtNets&r?#;F+^41UtEjWGG<2U>ew_tqz2U{rL5M1O zlI8H-&*C4Z!$2k=$HRO^ z2U1?1wCJy+MLjtx_vf+9?TR-V(>xDahXe?8)_pFypKheS7|dpSdm1`Bp#Qfya{#1# z?jP>ZvOPt)*PS<@z5Qc5u=^Y#--uWNpaD>3e(pl^A;~$Ldu6Aeac4o{`J=@Lh480w z!Jq&dH`UUWz&g&l;e#%w{CUU%qS>Iul7RXp22f-hD)i*+Ka>N~!hONv&%^AXB^jlq z91-I&>xdpCJ35TgKErr}`nq=R69#!`G|UG1t!vNl7}r673O87fLc7vQ+q=>~_?~S6 zts&y1f!3}MKW-^32OoFkT*E-kQ*J5LJpDL22DM~SSf}W#la8X%M?lX62*H4G=yYQf zX)uGXb~H={uelCo)v+CKU6+m%F+3+NCZf`$?ZPEgDCb1K0i^wg=!AJ?zR7AUSv1t3d?TjW?P zfOoE)`5}}54)L|ojv*Rn7x`e<4P@?O+ykVe7tyy_auHn2x3(Wv3}*dld*|W@Y5Fmh zYW4>JB!66wjy31;UHS-k`8anF^hViTnm=#~4BV)vd8t9AZ015oDoA)#3eD^XKuH-{^zP<_c7pS!Z=Fh$)4VTpmj&?cm!mUX+-V=X+suJC0|04l7)k$d&a1rBs1rLaHr@s&{G*; z*d0=#;UsrDNf6@y8t%WPF^`I2sPVZX`oiC($1qM3%+c-^O}-FKFwFw_dtbVsZLRRd z>dQC~d!^4i<|53C&3a#R21r2U{@xC`e>DRjh@tVUL~<3RqoBJ$FQm32%b0}-iOnf7 z_s-3PD?yEPhSone(#FgA=Jy`hibl5$K@twm0rU}kzOfIxb84FK1kfLLXT<}-^6v_a zm`|+4sZX*Th7;eq?gQFU2sjE0u1RYeZ$0Md0wQ-BW>f))Li%|iB@98d_inAgDRA5| z3VSyC$hFvDk=rX%m0xmlEC2}7A?%Wu9Ay^d6hPp6h^GPqYnlo+zeDh zV#6fV_#XHN$-pN+rk-6`;$+?fX27b}mM%L2U>aK&YzzFZ>Ahk~#YE?X+3K+CHHrcDnG7z6`wte&jh{W_{P^=9>i=QBJ z6*N|%(~d5<+NIG!$16aMK}rHS@UXWekQ@J2On`{V{wegwO>nQ(`IZStY8`Esf0ZKX z9x@(!EmX^m#U>%LR|HTJ*}qZ}1NxKEcbzBkvM!^ejVdXSq78(IJ7GmllCMralG$IW z7V2@vAqKFeh8?*Guq6?JCNrST0!it4jPws}7T)hPtkf%8e5d7L?NM6FiSA=*mY@oV z{}lVT8AkMj6E+MzpuZW0S>ei0t^tkO#jD`S_>D#aNy+A53M-+?EcJ~ic>f~vx1CD) zuf_eN=bmv8&_+F`yFSn|0F*k{r_pD2H%Z+&^j; zR(HNa$QD6A`+9y9$Q9afMg%?w!be*|TMj6NwuNc`02Z_qMd?mq&@G#NSUj9+B2s(i z9)OvUU|S-=0a{p#O<(!VzjG)f3`Ane~tku5-t^Yz_(95Gxwc_}zAau6Vrb>xHTrZ3W! zSRTo+|A(vjPaP$F3*YahX&80cwoLTnu02gjf@{^|Sogg+WH*R#T}U)MckVLYFT;VNc6q7OLIPKU z!jsXj&}+*bsm7O8@+UgWgi}pM(gW_B+^?#hj5E0(g-_!`CTfScvr!n_cOesZJu!iu z+J)33#KgZ3Te;3~8P+eNlptuz#1qfWBP!Q)GS#XY%`DlXOAu??U5%#s!^nK^?rL5n z;@0c#BbUM?l$WIHCx0(-7#!>zJb_#r>l_^Mn9?PTw~?+?qd)Oz62vrzpIv6vB*ff| z5~6EVB~9il$_~4IZkUchI=Ic-)!2Ram}*Y1te@_L+k~&uh^F4J79?|w?INk~=1$GL zw~K^38<+paCU$Df7Fk_46!9E?C<`n@L0mp0$HW!JWMq}v&+QY6QQ;NlL?)h~W2G>( zDcT;OWu@5mJ`>?>yrdu#AYHlP2g;xoP=bW@i-6!S>bK*p#_Gf~jjj(dd2azaceF>8KHRdB^jwEEm zarz_V#t+8~m7~*KtT^?2d6><;655sJ6ukX64ldm35r5{tN3Eqw~G9>ct2(y3mp-J>@2@_(uMA(WV zPyW1eipSr5$cFm=+Jz_d2nAL*_O^KCHD?O->ZJDHXlcSoo^0fX!@|MR-U~Qd**$4a zqg=vj5)!gm|7M)@Q_AsA74MNoJgf^|*gna61n#Mv%;SiYlTY1ePi_Jkgeukgn-g!g zgsjLOxMR+9>bq=R%Fdkv{nw;r9i5C0v=CxVq=8i*{>`c#@m$|+kAKStW7>x4*~@^L zGA(tV*DkNJy58Lre>}bfN<-r0{=Y}6k~FFD(x5Gpwx*FMyu})9QE*RXB|o|oi2aZTj1pf$4cMWP>x64FeP{N0YHs76?l#sE?+tt@ z{`YhoD-V)32>`+KyR^Hia)^0%kHh=oN8BXdWx;~lu}ecu2}(7aQUo z4M@nNR%3T=a~5Rgb)9!*lB-sk(Sr9Gq=e| z?&o$0LiL0+amSfFJc%0X48f>a3j7TO{utNwjolAIHtA06aJzX^el1KJE7!?7m&+9k z9J>EM+o3On$mCrKT)JYEc&_u~$gmn{WvW7KCle z1=ai+CC@X@;T+zhq!vsP6XjYRCTgPV9$^yKV)JL~vj{ZpRkc8U&gU zdq*-JdGq4rs_mK!xV`g6^8~;ge2Pr=koYD`N+!2 z%PGi*srA)t=Uoh)cQA(T2wVP{ZRBZ;WKJzDtT$y^QucKWYJO=iw`kpHqfKzUv+R_* zuUf6muv%?baKvSHsARySZ!30XL2LZ0?GBTystW8?>a|4mhz zi}UuC+ zjoYyv{@ct~Bc>pqq?jO|UF-rG!cajdl9+T$hsXJLozJN%e$d)}`3qlv){*oNV#2Q)+w^&*jI&j)Oe+Ohq#3!Ldo-Y9o|?bt~3%Yf9# zw#>DdhUrDpr6yL&+tRXDRbZmI9TVY+Mci*}8s)A(X|9ix+hZ(}Y`!G@96wo;_CkPc z=z&V~0zosd6Erzx&T{&7zgD~N)fugVDa7%djt~cxS$zTc7AN$}w^pb=hlnsq ze1x35m}~cMs?Q(TbyZxlO65wmeuR@P2GUGz$@ihq2F zgu8^I#L|J-;r)Nb@DO<1P2f7Ofcm4~>sm`XK21PbN}=UY9H2T@DBt!wpqLrebbV|( z)Ym)JJoPXNWd!O_Am6wkIRH?YUuovn2dK--IsM2ee6UCDSZ5qyO>rF4ozo*(0kJ2G z3cKRE7{%t|K_OyR1JrI0V1hq0167T7B6T0}u#q#`zzP+&*mV9oW2unT<&lmNrFq<` zoBdDW<1!9c$3mT*m+Y24)vJgBG}aCWT=OGff%kfcYb6Ln#_r~n)898kG6t$LcNDLyYNt3T69(Q6!(GxrIk6p0TI zqF)IvM7H55-j{Yb135LvTjIf&vd^xiryJ%9la4QU2bEK4QIQMD0{HJQWMZS_1;g)m zKIu|iqJ$BY!!PVPY47Bip^(94)UO1JG>>YobtlRZJBb)?OB7jmGb$xSC~Un~;DYGhSq8kCU` z!{mwY0i{0z@F+W)Jn*Y<6dA1`pS!FAjv{w4Ifd>DnSKAkc?ghqYdy$(4&cAL1pv+M zmZ!?&-}B#mh4^paeZbg?xaS4QEoYB-hJudbG(D1qRq#4gKnp#Yk%-ssVVz3hF(S?X zkx2zn*%RE~w0MHY)+(*hIXL zN6WCSK>^gKFw=?21Bx{Q=uNEV%t3HXC@YEYh|XsVP;nh+c}O^&ah^1HK54_3+wF;> z;xkN0$SZ?K+Sa=XQhZJkLxEa+%f+$19c@;mBGz`HF~k3>_u5s zPVYkU3q8Ql(X-{lK-INS3B*XZE9p(e*EL4kLCpvcvyl^^ZJz;UIvLO-1FktJP_3pJ z6j8|Da}R_6*#A^mPdGkLX}cK!&C*o!V-LmaU4!31v(`{1&uk6|UaDYOcpU`1d~17p zC=2A)#Q|S;hh4G);Ky;1h&LPTt>`5HCt_*arUPGjcaPUtx$!(TJA5`!my<4aU*M0H6>FXc8d7c^ zsNM3y*9v4<&1+pXpaCf$x{VlQda$G*s$*)P(503)x2SB*v%tsNAG(_q!FqWAVLbqr zGh@y?>@`0LjDn~WjP-dQTtJ1$&`G2V^O$kK8g_W=-9rsdXOHaIa!F=g(NUeQW z+;aux>?TEfr&tdica;T&mmN>sUjX8zqE4dd0oyL}?5tI%dZVzTYG^D-2iBh%oN(C{ z)-S!K-US-B}g!j5ShW!e=$*hrA3Z~3tckza=dd-1+)`Z zsjeX2njGFMgzWJu)`o`qK^P&k>HxFq5yrQI-4fjw_>oqRxOIB2fkt8>$bFd)WM_cX zHd~nqySs2N2qQ!eqONPOk=24VE7fB!dU5Zy7(J#>Z_1Ey#8r`NUknJ(ZvrUqI0W z#=ZR`@wbg@N^rb88J6?tWr`f=f!*BZPdi?Y-xjL?0RlowQDIH?lUS3vUhh*_4-PP= z4S+k_bJDxcN-2F8D8y@&m{88l1_YNJlN=LB({OSOu{?Y-wZCxbp7*pUFy$IxeV`3d zp$YqZgzRySMNc_iUB!!65NID(saBxcm4l+sStf61gms+WyVQLSiU5F6pk+YO@;p67 zn10C@c3TncIj^h}56L-=^QsxY&H`YKn1FDTmnG@x+!XJP?`E9Jii7M7NI2-KKtc=N z$Kd7EcqZM}W=1@M0LlTCC4n1gD1ge$^uAD9%Fg2IgeySd!T&2fboL>09K9943@v`G zpJs*hGUX*@IiN@ZzSL7F<342xmwsRD0vTT8m*IncBU#}W-QHA97DV9%NZWdxhXc^t z04d9Jv>EFxE3!K8N`&IYf15FZcwswKjm@4M(huyg7t7JE1|kxmik*y8OSWWtR1?HcB_dZZvsUqSl~J);p#KUq2n%Jel_cAB-|h za-w3)=;OJdy686%Jn)0W%xXNI9fOl{rB<*nqau`%E0wjidl=bzdcsPwTimu$RY6=FbJ`mVFyl1xY z?njd>!bvl(3(!AeTT=K*s0{-;<=_b+PMW{8NIt?pYAZEnLV=;tPs^nTma;Vo3zRO zg*i|OtK{hd8;FNg@HCJwq1zo`-gfh{{p>=(zY=*k!_M1|dMN%>q!O$Y^IBBh=anI* z3*C9`^uGboW&LfIVh%>)L5{r?x+^!5P&L|90`cN}uL-(KMBaV zs$P7_7`-Et*SOm$Lfxe8(w+6E!FM$wj`#7o8YCO+&@lH`M_R6E6A;?HLnl%d>ROto zfRe%_r?|w<81SR(fk8dn_OtUipMIW-s`|v1o>|Tu_(N^V>0lJ=j)-9YOM{IJt$kDU#XV2=rvzu?J?% zZJn@seh)O}%Ec;A;MAY$C}a#$Frbb^F@4aJHC+Q&%xBNCd;vxApRtY++HID%slgVu ztF*O5ZsS4&>AZHo+){wWv=}7kg2r;2M`i0=$KJIIXiZlT&jHOnU=2;dncNV62AgC< z=e09W^7Amu0m(^P&M6QgLq8;DsJnTUi76npJXR4W*KO)|AL(fs_#?HSh*dl>cnRRa zF6m#aN&40{JB9c(*-G@F8x8RBt^+?qm6d`oB3v8F>{RkfV3^ouI?d_HkGu;M6@S=p zp%TpEpPHi{8v4f^{@H_3FhW^We=JAKnH$|v;u*kRKuKwsZVKdvw&Y}l=F}lB^h2QB zHt)alJW45yP+U$k(NZ`O)qX7+v~n;=)QeLQQV)Up;arjNBFJHW+j2H|B2{a;{BCK< z$wB)rxOV?&tCF<**AQH`Hx^5HeZ{>ep}1VrDhK;7t-Ov$aR-sIejp5cCQ8yEL^tIBD;pUKg$$xmufQkKn+%D_)lO6 z0?9=*m}?2nQok9|hrjLVyihsW2OF0fPEUkzfNdx68d^eL|A^O-<_VakQi1oCs-uVC zOqNNH)Sy0(_^Se&X5*LdB?1Abc)?5H;|~G#C9+S?#W{{ z1N&KgNk4YYRA|T{^U;Xkc{%0WfDHGt$DHSfF2`%TPH$|{NBmyS2`{gf3 z;f}hl>b76Iu!jyPK*0(B8t8wB>CB}Y07L$KiYc@J=%Uk2{E*_oF}$P+Wy#|D-+5c<)Sv(DGOfMGn@%@Hf5|kBqih-g%?bL0~|owcj7GIu072} z1)H6}HpWzF;C^|nPVa=++mz|GJR%uI6!2TkeA0Fik-qwETL+5wHWp_YrJ)fnSr_e&Y6DBEVJ!%i#7Q zgn*r~SoOgk58bzI#xK!26?D+Ua~h(*5E3ZJ+B2CA@{v`a{IZ!?Cy0=$r2Cx?FPnOBeV( zTx_>m=GKiCI9S>gJ3#$5myunXUQ&O~Mb=Zf zB5)Ti_^~p|$J+qRBIpA&M`?vw5;rFk``7FI*Y(`PeF!ODD^>jcSKKQ1+F#xpbKSIC z$QoG73f%IWk4hw{ybM&Exz>!>=tA&TW2;XGZg6OP=XkQ2I&7n{Y!es0IT21)TPkTq zyDXR|E|~`oV86y;%@>>pmYv%JF{cP9e+o*WV!g4#cV!jtH;b1?l{8w4jw^eT#1X3v zV~|0$N)TI1h>F!JjrFS2uCL>VJ&VgM7XtWK0#Z`n*KIAkZgmqTPAfRPTB#NbtCi+W zQn(TBx0n)VuQqMoY#U?Q?YkP|@8w>n(vO@BR2uPVaevjJy7@gEAwj{;q36c1{nJ>^ zMpJf6pV`7@^qkK?l|OEyuL8aB9I^Vm+2qyyvoQwaJ6a0|5Z@1AUEgp?-+RE?Ll5;U z`akN=C8NQc#y-8L-rp`Cpkfh12CJ{7*Irlo`_EDQ=1vLskv1V)G`DWfwDQ5~pe1dx zqAIAw9Itvqt1pCUD~bc(d_>>_z03-M#P#|_Lix9c(yH+%Mcc#gR13Y-*yzwut3E>z z`DPMkL)x6N*C<=1R%}s^-DDB3ccm7&`goExHitFj6DeMaTn$T-1M`wk)Js<0BG%rX z_hs-7r`!o2ThQ2A&}&t4Mf!hjwCYx!e;Te@UfF>xh$GIN$dOf_T>*iCEhtgiFUtB& zhJQik$tGUSZ*v^{z$_Hm?Z1I%A$rSOrpsHHNTsCglGnzC+2!*MRbp3_=bZb;oKG)s zfWrI-6F8)@_eb%2|Ke`-kcq~U2@A=$Bd&ij*H(8+N^oUkU~7Y}-65n*y%+y@5RUDF z%MGkN8MxEq_Z`VkK?YuMU_8Bwy53`f?rL})rr@2T;2_*t$Lt){ekO|-(>#c!Gn*esn1M&K3yO z`y%9C@y)hC^~DosvR*lMVbND^)r$mnb>Xzx#p=HZ3~fMJbeh@|&fDwvTD#9n3b#V4 zY{R38c(ro2Nfje-Ki_-@aK63B4c(2>>1>ru9BxiN z#08~9j^5g#ASz%`4^3n0Ol_k4bX!0zn&0c%t7ctIA>4o033wi$E z==|MQX!YwNFd4-a%Z?SELG956`I2WA=9jGImRk7cr*Y(I?&dmjyBPU#d$ed)(RB0u z?9sMmIc9pzK}C=NC$zV>9Y}shph)F%dj|HvIDq*e|_-q`WdbQ zKa!ZAwUF0S!WIO3r_O&MVX6g?Ysvg;$=b(f2e$A4d9K2{jYozT4}LWo z3MzYWnWXG_!+&`X|H{X_+{5cuuKU%g1sY}T%ZDr_GMtUy(s|~;;^-wweNV`@&Kj?j z-Xux$<+B9)P?MJ)O+$dmqVdHHx1dt7FoXB5dM9Z{)XvWd@$eu_!HONo-uSXt4=2vu zg{=>)CJdB#1)}Z*7BHH|3~DpeJ3WQug5!`}Ku>#IIB8ZIv22UzzOiJHaeu8?Dy(DZ zaoNbqxF30ZpPrI1S!3a(M#uUDz{w$1;yG=>RLQ+ttBG4B^m*|9`DlCBgSRitvaEk( z2_5UI33ym_^hmrm_kqFknq7D|`< z=y&^XQT(^Q*j&-+Gv zJKybLqry7u2BxkWyTQ=T)!ZO&!n=`So_|=KP}2D99=>mPt7Z=u5=R(ugN!dx zKE)`NjC@IQ>7HQN-a_mtJ!(ZGAF7^wGI6!(G)niFcw8yR6RF$|`Nu39Z7f(TztMK} zwbu2fR@FYvc_;@{itokW&)bKT?z{Kl)A}$#wm`@IYhlZ{6@hCVaXjdINyhpO4*n{p z)N@+T;>NVT<`6kiXp1lXk#CHd8!Yd>%f`gK{Dac)@S_cvs9s$%rU>ImZGQ$AchJC% z<%X4rlfmI~HEV$rPk^HaA8j`P0s9$zzjOJ2A99gNoIjR$3lmV+ zI}?KewcLN{nrk^MxQ{(mVIO2zJYj2-;;K#(QYY=Z`FJJ|zC_`IV;!P#amliv-t!TH|$!imDl+(4+NQ`cft=$hG3&QHG(gRC~^ zWB6-Rap?LOUjy3J8%=4;m|{kyB2u>GDKuVcfYx_+u`3Z9=3i0D$J^aw07&T&3ez*YY&^T2}IK*8r(0Z>u}_~!+h>CkO-U)>E71<`mE+Vs8seq~XzHjDagq(d-% zVQ{`ty4Z1?mVRbCC6#|I?nqk4-elzuqgs6ei@u%}ng(14rUXrSZI`UXDWh>p9&nW` z>lke=%PIA)(p|TWN!eL4Pyz8en6Lh@P3-Wj_O0c2u50fC`3osIsDZW#$}HTPug_#4 z!E-9uJB9jAV@xJoiCReJ&7M_CWz_Yh`k4OTGFuoU`7L6AH`f^2%C{Mt@whGa$Y^&lMBx6Fa>*H`}+jNO$W6W*QZ_ek^b~jKA#M zn_I7L)mVz>m7aTtOLCaF18jybfICb*XB$Nj3QcVQNV54FDtTyMZ)4WXAJr;^A?9Y3ZyoB7 zS!!wV+@05IUoF%+A7+RaHvn$Oa@GBmu!lOgIm%aTE~xKq^LpyQ_rp52Ar72y~)wE3WQG`a0KLWdwSP?~$bPgQucbeA#ea67NOe+wfgj*>c2g~=p&3GYDZ=<`g;(*hoPKY{1)Q)H#Zm(zcBH@@D}Ps1$^+%o>f-)#cM`Ia_g@(oU(fB~RU+MONcp8A-`X5A6*B&+9`W=a1DD}B! zGr%f5z|Q6D)pq7JOmXQc4wg}9arBtaj19jJ*2DwPZt!wMM?7m>fZY2oSyB~^IPu{F z#<6_VFlj$ML%P(9PJv{OaB%9n=+1;^RH@Cm0Re-qI8k1rwEuCq`{L=$nX1;`Da#vu zh3uA6S}q8>Bn5%eClj41SZlS#G##o%0AS*jn~UH~Hd~3}SU1kGuKDrRH$uJ^3P{;;!H!UFT1iYp4Jtg z9>pz=`Z7F&y)InKrnWmW>QHEUljR-!J)h<+KQ7x;Kcfj%GRn6!ZLcO9l2rRvTH;#T zx6r4Hj+YUE%UOBn^F!e&YQ2axi|^o`!v>Mp^_4u@Q>4szmBWImUp_Cs(-Oy`17TkaOZ#x9B?~Kc zE_BCae0n=xd77C^Q|rQm%N8(?ySPMe!h@NKRIE?&%Shvj4%{u^b&=%=H2PL-;_tMfF zV2^n3823=hPXaFTsM?atq<_)~rJT9XA>>II*^_bOtWQa!g6r`dSX?-7UG>Hgi~pUJ zvhuS^y(qn7Yi0rkI z8hWl1BW-74K@gc{@#(2*=?^V*4epsm`oP1-o=UyoE5q8?Crj_?IDCwkyMWJr;&cCS zwsGd7Z=0BucgLN$KEl}fGgP><$J1M1+21|)C3>X!?oW38Luw#hdDQEp5^K+4sNF~Y zYWI_j35iRY_Hw0ni~@_gCbAD(Fi8k@ijimCKbT&DpNM8fF@(bqbNgHod_SCsHkpVdW#bv1 zWYDMg9|_uObI6y9?~d=_Vx0`?=evlY6|Kh$Ft2sK^Q|}IZ#;{{^K>dUcrbZaWq4&O zSWKC(=AT3#idvaIih6kr@$s!vj&T*#u!NmM`>}uy{Tw(_`fm46E+p0_MqZ_~ zlDm`D7P);WZB|qE>I<=f{Nny42W*4IQAIcX9m!xBDvWBA2FA*&@oVoHU(Wea%J_NT zy|J8^TJ2&hIbU$4_yTFE{tVkHj?RR9iy~~Ps#+weixT|88BI=}JzqLu?s}!Lv|s(1 zsZG~1zhy|0iuC09t=q(aY;g=wZhThE*-&Wickp{t%50Xj*&$(NTyVWHdYOOXFOfs+ zcD#}945#4)4;5`^gErkKr4N%v-p(Mpl_Eun2i~~u#SCf^a{N{FiGl^?=G<|z5S3?6 z40`5@<@GLjy*bhGbeYS^^3Kph`^hDRE8-i{_rK+*>IMMDXwrc97tMwAf`IRnxnwzz2G}j@_Sn1L3*bIsK-3< z7biw)WgRJUW$WRj$O=3Aw4Hea^J{dlUbUoz_>~@9K29!`F2*Su)I}w(k5qX+zQvD1 zr9ZoD=UjCZ&;P;w!`$J1%CVq+x5;c!mSr=2UQNFM9M8Ee205wKGp{_z7OaLrv4PZrT5bY3u*v@ ztK<_tN2hv9Ioq~-_0n7(v7}ry#g){t`w*6D^${;c%(*Jm^I&sJOf}Xy&W5A7rF4IN z!jj4oMS#WY^AJEA!nis`i3%f@*o^!BLuW14b^tY-AVuME9r&u1ks9v5KFpHR&U@p7 z$Eo5=o$|XFf*zO`3qLDAnksb%{0LDN&oc3g^l`Ej4%CMM$@-*zw}~76!|$st`;^}C zrLv>C(wt>2u64JgWZdF%vh~_5EqeoDeO+a@q`xme_JtF|^Clis7l7NUj+em78j}?c zkkpC-eJ7BM`*ac|ZuEsS*H;uBn2fqdKD--!(~Qzs_-Oud!dM0~7FoKrF~{#v2HTXb zSfAIm6``nwR z(ZhcIk|iV3ZovRcY)m$WH5|??!c2b}Sr+5tb}gy%GX-*Eh*W1M@@#I3lVLoqscUXS zLz&-Z5|AT5mZ5JjN4si!@C$M0NS=I2F=1KK{FLLDPRAX`jAnR2P8(2LtUcrUs84*! zggWd!YybryEF~ri}p{1xc2Imq!!^` z7;sH_gulm38@dVxy3idx#%qCeZFH)~7qjzvWqLN{NnbsV)TSWuVh1e*x|aosw+iOk ztdJWyb8YgR_3o%bGcNn+t~<`fZE=#kvms%9tEIBa9^qxM-lbY&56Qx}Z#&Zz4wbf+ zuf2B->WVq(G55M)AJUXqK`pEBx$imkV>vD-LDwH?objeCS>TK=B7HOX{gpys7@&H)Fs z#cTCwZP^_3SBsLEE7Rv*ys>h<+Pl%ATVQ+H(zU!7araGG@r03VXW# z%W_=fBRoE5{a6D2^0EoEbMj&7^~{C2sN~G~LS?B#UCFb5%qlSS@*|3n6R>AbwPh`q z74%wzM=Tvf&4Q8_M6YGpymf*0!*<>s{nzgDkD9i#pZ@>Tz58xK^6|pXi>jz%1Du+-6HW*)Lh0L64xY(qyf_Gr_CZKs4`~Ab=eJ73fu!_`e>{IL!K)oq`K7 zFB6}C`vS)Z3$!M3zM|Ho*~(#mV@IOk`l^4P+PF)9{jtv?vuqqYMR00%va4U}q%NUP z>FaO{2iMLij+v;B5f~ID7iHR3w~Wzq-RtFKi`Jh=FnXJfp%|=PLAvI&@s}NaPQ|En z+%?-VLarYTctgA18~!PUF?54} zJtdM0?f%{Uu3C+SbBdD#PXFa^@@K(YWVS0#&nIK)(H93cV%?EbJ;(h}1Jo?33a6@$ zsN_?>POI#u|DA@NqAtK+Ce-wzx}Xj#SNHC*&p>giAX(K*;MX)hsxyQJ`}FbFUALs| zD;A$?*y@Q3aG~==BOL<15(PV~p5o`skOSi!F&DIdJ~>x|#u>?P;WbB1@UGcP(vkJ~dcl!csw_ zblIq%ow(4S4^$ET_L{3=#A#9Y0~NfwZ(J6cd-F_J4+b4XxE`gJWC_c6}7j zLCIq@351RJY0Q*Am-nirN&Au0%f!i}OqD$ewqV!r z_1IX9RcSSAJt)Gz(iQB;i6fTt$9f%e4j-TdW#aGX#ok}fu_QFAyuhh6JWE6dIJ|8= z(Qt3a&5W#{OHUMH&l98y=9p#b$lL|hyte5a6v3gr7k7^k%-^A<`fNGxh(^%ttooAC z)du<4A{6sR**o|tmqYoS=P3_X%--!<)k$ zP3C%3A6tRcS>iJY9=w9e_)Oq-Qsv)1skI_&nRmsjQ0))7o`8uZMk&Z3@Tr5P;(6*igbci&+^ z__0$V+eBRdNaR?p0#1g;->OxI?@zG(jWwdx(!E)(SsXlUCOX_QfUkF1Rv2&7cl~k7 z>E&XUdS7aD)x~Bte3q;>sej)I;Q^w1oePtfgmFNcN(*6v6cQIARr)QL&T0!jwX9|+ z7pl55$*UQ(BaXcl*;|$jaTAT*s&VGQTL4tktS7_rTE?Wu<H#S~7dU+xzgFBDNLr5WHWD&zKV5IYvW_h+zz@}L773imP|ST%w5j-T;a zT*q}S;}p1UQgebHynxDuN1rq+b+ zcpwaIC)~&1p_Pp%2+g2nXY{|tV`uAx``IWmRyGwVpGyxhlzMlH&EuddKrF12D**JB^H0i_J`UbE}q zo7}I7&zD_n!m>UU+tf|H=yL-<7yJtcFVNJ%LQ`B$7F`w<#MHs?jb^2cMeVbSmiW-l zKEEa#i3|i%`V-oTRU2-zb{(ZpC<{L++)WhaQ*k}}GNClEcA%N<=7gDLyBI(6&j<_p z_`4XgsOUF_wVvVAZ?Gd_nJ@U^lQ~{-P1iyV2bgcAV<-uDI{zC>>n|dt3vJ(>T@<0+ zG|SFoXiDVy6--AW&Ly>?1jE=Z^Ik=Y=jULl`-Wb;r%1Bu6@Lru(paV*+sLpWgoL(Q zdd%S%RhC5s=4*$|n%br?1rFqDqnpa(e*&8fP1qggnoQjxRR%Q|!CIh@jj z&E&Vk)4HqZ$AF{c2o<}-h2ueE#T*snqinCG~$9fw&B<^ zrs~I<8)FasIgv=O#EFU(<6RSLbwlvl*p2HIInsexFV_X$ zXy3$0OSbm%RpuvIL^8~Brlu8kdK;?xt0{n`;rQnp|D74rIVR4I*M8gs%?56?Wq!V_ zyYgpEcs9Jvyp!2Mmvn8Qi+ z?|(_ZB>yf(rIbDXR4g;!^NGEA8yHn7T$0CI+)`LQy;7!kac=s9p*jFJltZDF*G>Bl zZYkguqwD#wUt?abak=YjlU3^{uWo&qnVax0(vD*phH#^BgxkV|?XpWXaqGFezNczE}qvEY%`F68(Mtvjxjc9{pTJJ~a z9@LuRRwj4x3nb5q?O1u-MtooL1#DLgv7t`qhUdNQ`=j?#X}??1xmC zcPW59$Y6b=ufoH$nL(_1K7o0hk(I5!3|lj(_}uEb(37b}IVPCXvu%rj!hH3;NUdCx zT)K`m>PA|v&*GlH-Ht6bjsPxe2m5zcy@s(RlKPq^_+;(jgZen+l*i2%PcIWCm0sTn94Tv`ro=Vrnx4Ar_e+t}RHmgSweccnLjcska3`EV^@`jqCB!Ao=_st=OD?qR zCmG^y)G81DfOW)`=~5%A_N~^3IS9F}p%h$avQS`&siAaQGBN8s*%2kewPEFuNAgqM zjv9DVqXsADzvb3N$`JW5mU>4-UWFU}HDcZzJ%_b#&$rC^$zZ!8A6%DBXC>slgeKgl zXpN7~OT+L2dolnpE&Pbso}^OuzB}D0;)$Whq|&*{4DrugVYyIa2>-{inqXpwl;~!f;*5l z3Z0{Dxy0Nk+>n^yrWmpn<7ku75vnuBDhknIJx?#M&(+-^a>gkg+pTJ0NAYKbcDoeMh9vsME!;0m6B2JaD!4m`L#cyEC3SijUXT}To4UK_PkJciW= z*#iDR-ePYRTAkRZ{fME`dZQVFt&V+xk`E9}*>3PR2FK+49;iQ((+1SfHl&j!Do5xWcIt~VL1pkO zmWE6Wg?^*YlT8r!CZEcs8RB43PqsG2^yA()6pr^GdpuFUgF8As64z4bad$CcpRDCYq8?2=|=;^_Gf(oq-1GJ>1ILj~MsEk7y||GHVF`q^1;t+?_B u`+n99N9>N{SFiZz^B;(b?Zl_OUtl1`%jemzqi9CDZ;u}lSMPh^{Qm*&s`@Yh From f9c302b415ce583c07666aaffe6f3e259228f5fe Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Tue, 23 Jan 2024 14:17:34 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- README.zh-CN.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cf35b4ffc..d523e5a25 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Looking forward to your use! Discord Link: [https://discord.gg/MpdBSBnFTu](https://discord.gg/MpdBSBnFTu) -## LF CLUB Community +## 👑LF CLUB Community LF CLUB is a premium paid community founded by the author of LiteFlow. diff --git a/README.zh-CN.md b/README.zh-CN.md index 9376a260b..fa636c339 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,7 +57,7 @@ LiteFlow拥有极其详细易懂的文档体系,能帮助你解决在使用框 LiteFlow期待你的了解! -## LF CLUB社区 +## 👑LF CLUB社区 LF CLUB是由LiteFlow作者创办的高级付费社区 From 6f63864b65c59bb9761aa0afd8d19b540c7c6aa9 Mon Sep 17 00:00:00 2001 From: gezuao <673843192@qq.com> Date: Thu, 25 Jan 2024 20:03:53 +0800 Subject: [PATCH 10/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E8=84=9A=E6=9C=AC=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/script/ScriptExecutor.java | 9 ++ .../script/jsr223/JSR223ScriptExecutor.java | 11 ++- .../script/validator/ScriptValidator.java | 82 +++++++++++++++++++ .../graaljs/GraalJavaScriptExecutor.java | 11 ++- .../liteflow/script/java/JavaExecutor.java | 17 ++-- .../qlexpress/QLExpressScriptExecutor.java | 10 ++- .../ValidateAviatorScriptComponentTest.java | 41 ++++++++++ .../ValidateGraaljsScriptComponentTest.java | 51 ++++++++++++ .../ValidateGroovyScriptComponentTest.java | 79 ++++++++++++++++++ .../ValidateJavaScriptComponentTest.java | 63 ++++++++++++++ ...ValidateJavaScriptScriptComponentTest.java | 51 ++++++++++++ .../ValidateLuaScriptComponentTest.java | 37 +++++++++ ...idateMultiLanguageScriptComponentTest.java | 62 ++++++++++++++ .../ValidatePythonScriptComponentTest.java | 53 ++++++++++++ .../ValidateQLExpressScriptComponentTest.java | 31 +++++++ 15 files changed, 596 insertions(+), 12 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java index 0f4be12dd..64d47c0b8 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java @@ -10,6 +10,7 @@ import com.yomahub.liteflow.exception.LiteFlowException; import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.Slot; +import javax.script.ScriptException; import java.util.Map; import java.util.function.BiConsumer; @@ -86,4 +87,12 @@ public abstract class ScriptExecutor { ScriptBeanManager.getScriptBeanMap().forEach(putIfAbsentConsumer); } + /** + * 利用相应框架编译脚本 + * + * @param script 脚本 + * @return boolean + * @throws Exception 例外 + */ + public abstract Object compile(String script) throws Exception; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java index 0bb20d921..4d7882015 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java @@ -38,8 +38,7 @@ public abstract class JSR223ScriptExecutor extends ScriptExecutor { @Override public void load(String nodeId, String script) { try { - CompiledScript compiledScript = ((Compilable) scriptEngine).compile(convertScript(script)); - compiledScriptMap.put(nodeId, compiledScript); + compiledScriptMap.put(nodeId, (CompiledScript) compile(script)); } catch (Exception e) { String errorMsg = StrUtil.format("script loading error for node[{}], error msg:{}", nodeId, e.getMessage()); @@ -68,4 +67,12 @@ public abstract class JSR223ScriptExecutor extends ScriptExecutor { compiledScriptMap.clear(); } + @Override + public Object compile(String script) throws ScriptException { + if(scriptEngine == null) { + LOG.error("script engine has not init"); + } + return ((Compilable) scriptEngine).compile(convertScript(script)); + } + } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java new file mode 100644 index 000000000..dd1c7b72c --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java @@ -0,0 +1,82 @@ +package com.yomahub.liteflow.script.validator; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; +import com.yomahub.liteflow.enums.ScriptTypeEnum; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.script.ScriptExecutor; + +import java.util.*; + +/** + * 脚本验证类 + * + * @author Ge_Zuao + * @since 2.12.0 + */ +public class ScriptValidator { + + private static final LFLog LOG = LFLoggerManager.getLogger(ScriptValidator.class); + + private static Map scriptExecutors; + + static { + List scriptExecutorList = new ArrayList<>(); + scriptExecutors = new HashMap<>(); + ServiceLoader.load(ScriptExecutor.class).forEach(scriptExecutorList::add); + scriptExecutorList.stream() + .peek(ScriptExecutor::init) + .forEach(scriptExecutor -> scriptExecutors.put(scriptExecutor.scriptType(), scriptExecutor)); + } + + /** + * 只引入一种脚本语言时,使用该语言验证 + * + * @param script 脚本 + * @return boolean + */ + public static boolean validate(String script){ + if(scriptExecutors.isEmpty()){ + LOG.error("The loaded script modules not found."); + return false; + } + // 使用多脚本语言需要指定验证语言 + if(scriptExecutors.size() > 1){ + LOG.error("The loaded script modules more than 1. Please specify the script language."); + return false; + } + + ScriptExecutor scriptExecutor = scriptExecutors.values().iterator().next(); + try { + scriptExecutor.compile(script); + } catch (Exception e) { + LOG.error("script component validate failure. " + e.getMessage()); + return false; + } + return true; + } + + /** + * 多语言脚本验证 + * + * @param scripts 脚本 + * @return boolean + */ + public static boolean validate(Map scripts){ + for(Map.Entry script : scripts.entrySet()){ + ScriptExecutor scriptExecutor = scriptExecutors.getOrDefault(script.getValue(), null); + if(scriptExecutor == null){ + LOG.error(StrUtil.format("Specified script language {} was not found.", script.getValue())); + return false; + } + try { + scriptExecutor.compile(script.getKey()); + } catch (Exception e) { + LOG.error(StrUtil.format("{} script component validate failure. ", script.getValue()) + e.getMessage()); + return false; + } + } + return true; + } +} diff --git a/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java b/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java index e6b194a9d..936e10464 100644 --- a/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java +++ b/liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java @@ -33,8 +33,7 @@ public class GraalJavaScriptExecutor extends ScriptExecutor { @Override public void load(String nodeId, String script) { try { - String wrapScript = StrUtil.format("function process(){{}} process();", script); - scriptMap.put(nodeId, Source.create("js", wrapScript)); + scriptMap.put(nodeId, Source.create("js", (CharSequence) compile(script))); } catch (Exception e) { String errorMsg = StrUtil.format("script loading error for node[{}], error msg:{}", nodeId, e.getMessage()); @@ -84,4 +83,12 @@ public class GraalJavaScriptExecutor extends ScriptExecutor { return ScriptTypeEnum.JS; } + @Override + public Object compile(String script) throws Exception { + String wrapScript = StrUtil.format("function process(){{}} process();", script); + Context context = Context.newBuilder().allowAllAccess(true).engine(engine).build(); + context.parse(Source.create("js", wrapScript)); + return wrapScript; + } + } diff --git a/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java b/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java index b0693fda4..900db68f3 100644 --- a/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java +++ b/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java @@ -19,12 +19,7 @@ public class JavaExecutor extends ScriptExecutor { @Override public void load(String nodeId, String script) { try{ - IScriptEvaluator se = CompilerFactoryFactory.getDefaultCompilerFactory(this.getClass().getClassLoader()).newScriptEvaluator(); - se.setTargetVersion(8); - se.setReturnType(Object.class); - se.setParameters(new String[] {"_meta"}, new Class[] {ScriptExecuteWrap.class}); - se.cook(convertScript(script)); - compiledScriptMap.put(nodeId, se); + compiledScriptMap.put(nodeId, (IScriptEvaluator) compile(script)); }catch (Exception e){ String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage()); throw new ScriptLoadException(errorMsg); @@ -52,6 +47,16 @@ public class JavaExecutor extends ScriptExecutor { return ScriptTypeEnum.JAVA; } + @Override + public Object compile(String script) throws Exception { + IScriptEvaluator se = CompilerFactoryFactory.getDefaultCompilerFactory(this.getClass().getClassLoader()).newScriptEvaluator(); + se.setTargetVersion(8); + se.setReturnType(Object.class); + se.setParameters(new String[] {"_meta"}, new Class[] {ScriptExecuteWrap.class}); + se.cook(convertScript(script)); + return se; + } + private String convertScript(String script){ //替换掉public,private,protected等修饰词 String script1 = script.replaceAll("public class", "class") diff --git a/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java b/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java index f83a4833e..a2a7604a3 100644 --- a/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java +++ b/liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java @@ -13,6 +13,8 @@ import com.yomahub.liteflow.script.exception.ScriptLoadException; import com.yomahub.liteflow.util.CopyOnWriteHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; + +import javax.script.ScriptException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -40,8 +42,7 @@ public class QLExpressScriptExecutor extends ScriptExecutor { @Override public void load(String nodeId, String script) { try { - InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(script); - compiledScriptMap.put(nodeId, instructionSet); + compiledScriptMap.put(nodeId, (InstructionSet) compile(script)); } catch (Exception e) { String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage()); @@ -85,4 +86,9 @@ public class QLExpressScriptExecutor extends ScriptExecutor { return ScriptTypeEnum.QLEXPRESS; } + @Override + public Object compile(String script) throws Exception { + return expressRunner.getInstructionSetFromLocalCache(script); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java new file mode 100644 index 000000000..9a6b5553e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java @@ -0,0 +1,41 @@ +package com.yomahub.liteflow.test.script.aviator.validate; + +import com.yomahub.liteflow.script.aviator.AviatorScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@SpringBootTest(classes = ValidateAviatorScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateAviatorScriptComponentTest { + + @Test + public void testAviatorScriptComponentValidateFunction(){ + String correctScript = " use java.util.Date;\n" + + " use cn.hutool.core.date.DateUtil;\n" + + " let d = DateUtil.formatDateTime(new Date());\n" + + " println(d);\n" + + "\n" + + " a = 2;\n" + + " b = 3;\n" + + "\n" + + " setData(defaultContext, \"s1\", a*b);"; + // 语法错误 + String wrongScript = " use java.util.Date;\n" + + " use cn.hutool.core.date.DateUtil;\n" + + " lt d = DateUtil.formatDateTime(new Date());\n" + + " println(d);\n" + + "\n" + + " a = 2;\n" + + " b = 3;\n" + + "\n" + + " setData(defaultContext, \"s1\", a*b);"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java new file mode 100644 index 000000000..7a01031f6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java @@ -0,0 +1,51 @@ +package com.yomahub.liteflow.test.script.graaljs.validate; + +import com.yomahub.liteflow.script.graaljs.GraalJavaScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateGraaljsScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateGraaljsScriptComponentTest { + @Test + public void testGraaljsScriptComponentValidateFunction(){ + String correctScript = " var a=3;\n" + + " var b=2;\n" + + " var c=1;\n" + + " var d=5;\n" + + "\n" + + " function addByArray(values) {\n" + + " var sum = 0;\n" + + " for (var i = 0; i < values.length; i++) {\n" + + " sum += values[i];\n" + + " }\n" + + " return sum;\n" + + " }\n" + + "\n" + + " var result = addByArray([a,b,c,d]);\n" + + "\n" + + " defaultContext.setData(\"s1\",parseInt(result));"; + // 语法错误 + String wrongScript = " var a=3;\n" + + " var b=2;\n" + + " var c=1;\n" + + " var d=5;\n" + + "\n" + + " fn addByArray(values) {\n" + + " var sum = 0;\n" + + " for (var i = 0; i < values.length; i++) {\n" + + " sum += values[i];\n" + + " }\n" + + " return sum;\n" + + " }\n" + + "\n" + + " var result = addByArray([a,b,c,d]);\n" + + "\n" + + " defaultContext.setData(\"s1\",parseInt(result));"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java new file mode 100644 index 000000000..912590029 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java @@ -0,0 +1,79 @@ +package com.yomahub.liteflow.test.script.groovy.validate; + +import com.yomahub.liteflow.script.groovy.GroovyScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateGroovyScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateGroovyScriptComponentTest { + @Test + public void testGroovyScriptComponentValidateFunction(){ + String correctScript = " import cn.hutool.core.collection.ListUtil\n" + + " import cn.hutool.core.date.DateUtil\n" + + "\n" + + " import java.util.function.Consumer\n" + + " import java.util.function.Function\n" + + " import java.util.stream.Collectors\n" + + "\n" + + " def date = DateUtil.parse(\"2022-10-17 13:31:43\")\n" + + " println(date)\n" + + " defaultContext.setData(\"demoDate\", date)\n" + + "\n" + + " List list = ListUtil.toList(\"a\", \"b\", \"c\")\n" + + "\n" + + " List resultList = list.stream().map(s -> \"hello,\" + s).collect(Collectors.toList())\n" + + "\n" + + " defaultContext.setData(\"resultList\", resultList)\n" + + "\n" + + " class Student {\n" + + " int studentID\n" + + " String studentName\n" + + " }\n" + + "\n" + + " Student student = new Student()\n" + + " student.studentID = 100301\n" + + " student.studentName = \"张三\"\n" + + " defaultContext.setData(\"student\", student)\n" + + "\n" + + " def a = 3\n" + + " def b = 2\n" + + " defaultContext.setData(\"s1\", a * b)"; + // 语法错误 + String wrongScript = " import cn.hutool.core.collection.ListUtil\n" + + " import cn.hutool.core.date.DateUtil\n" + + "\n" + + " import java.util.function.Consumer\n" + + " import java.util.function.Function\n" + + " import java.util.stream.Collectors\n" + + "\n" + + " d date = DateUtil.parse(\"2022-10-17 13:31:43\")\n" + + " println(date)\n" + + " defaultContext.setData(\"demoDate\", date)\n" + + "\n" + + " List list = ListUtil.toList(\"a\", \"b\", \"c\")\n" + + "\n" + + " List resultList = list.stream().map(s -> \"hello,\" + s).collect(Collectors.toList())\n" + + "\n" + + " defaultContext.setData(\"resultList\", resultList)\n" + + "\n" + + " class Student {\n" + + " int studentID\n" + + " String studentName\n" + + " }\n" + + "\n" + + " Student student = new Student()\n" + + " student.studentID = 100301\n" + + " student.studentName = \"张三\"\n" + + " defaultContext.setData(\"student\", student)\n" + + "\n" + + " def a = 3\n" + + " def b = 2\n" + + " defaultContext.setData(\"s1\", a * b)"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java new file mode 100644 index 000000000..548d6d023 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java @@ -0,0 +1,63 @@ +package com.yomahub.liteflow.test.script.java.validate; + +import com.yomahub.liteflow.script.java.JavaExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateJavaScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateJavaScriptComponentTest { + @Test + public void testJavaScriptComponentValidateFunction(){ + String correctScript = "import com.alibaba.fastjson2.JSON;\n" + + " import com.yomahub.liteflow.slot.DefaultContext;\n" + + " import com.yomahub.liteflow.spi.holder.ContextAwareHolder;\n" + + " import com.yomahub.liteflow.test.script.java.common.cmp.TestDomain;\n" + + " import com.yomahub.liteflow.script.body.JaninoCommonScriptBody;\n" + + " import com.yomahub.liteflow.script.ScriptExecuteWrap;\n" + + "\n" + + " public class Demo implements JaninoCommonScriptBody {\n" + + " public Void body(ScriptExecuteWrap wrap) {\n" + + " int v1 = 2;\n" + + " int v2 = 3;\n" + + " DefaultContext ctx = (DefaultContext) wrap.getCmp().getFirstContextBean();\n" + + " ctx.setData(\"s1\", v1 * v2);\n" + + "\n" + + " TestDomain domain = (TestDomain) ContextAwareHolder.loadContextAware().getBean(TestDomain.class);\n" + + " System.out.println(JSON.toJSONString(domain));\n" + + " String str = domain.sayHello(\"jack\");\n" + + " ctx.setData(\"hi\", str);\n" + + "\n" + + " return null;\n" + + " }\n" + + " }"; + // 未指定类型名错误 + String wrongScript = "import com.alibaba.fastjson2.JSON;\n" + + " import com.yomahub.liteflow.slot.DefaultContext;\n" + + " import com.yomahub.liteflow.spi.holder.ContextAwareHolder;\n" + + " import com.yomahub.liteflow.test.script.java.common.cmp.TestDomain;\n" + + " import com.yomahub.liteflow.script.body.JaninoCommonScriptBody;\n" + + " import com.yomahub.liteflow.script.ScriptExecuteWrap;\n" + + "\n" + + " public class Demo implements JaninoCommonScriptBody {\n" + + " public Void body(ScriptExecuteWrap wrap) {\n" + + " v1 = 2;\n" + + " int v2 = 3;\n" + + " DefaultContext ctx = (DefaultContext) wrap.getCmp().getFirstContextBean();\n" + + " ctx.setData(\"s1\", v1 * v2);\n" + + "\n" + + " TestDomain domain = (TestDomain) ContextAwareHolder.loadContextAware().getBean(TestDomain.class);\n" + + " System.out.println(JSON.toJSONString(domain));\n" + + " String str = domain.sayHello(\"jack\");\n" + + " ctx.setData(\"hi\", str);\n" + + "\n" + + " return null;\n" + + " }\n" + + " }"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java new file mode 100644 index 000000000..6c923ad34 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java @@ -0,0 +1,51 @@ +package com.yomahub.liteflow.test.script.javascript.validate; + +import com.yomahub.liteflow.script.javascript.JavaScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateJavaScriptScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateJavaScriptScriptComponentTest { + @Test + public void testJavaScriptScriptComponentValidateFunction(){ + String correctScript = "var a=3;\n" + + " var b=2;\n" + + " var c=1;\n" + + " var d=5;\n" + + "\n" + + " function addByArray(values) {\n" + + " var sum = 0;\n" + + " for (var i = 0; i < values.length; i++) {\n" + + " sum += values[i];\n" + + " }\n" + + " return sum;\n" + + " }\n" + + "\n" + + " var result = addByArray([a,b,c,d]);\n" + + "\n" + + " defaultContext.setData(\"s1\",parseInt(result));"; + // 语法错误 + String wrongScript = "var a=3;\n" + + " var b=2;\n" + + " var c=1;\n" + + " var d=5;\n" + + "\n" + + " fon addByArray(values) {\n" + + " var sum = 0;\n" + + " for (var i = 0; i < values.length; i++) {\n" + + " sum += values[i];\n" + + " }\n" + + " return sum;\n" + + " }\n" + + "\n" + + " var result = addByArray([a,b,c,d]);\n" + + "\n" + + " defaultContext.setData(\"s1\",parseInt(result));"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java new file mode 100644 index 000000000..f5983e785 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java @@ -0,0 +1,37 @@ +package com.yomahub.liteflow.test.script.lua.validate; + +import com.yomahub.liteflow.script.lua.LuaScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateLuaScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateLuaScriptComponentTest { + @Test + public void testLuaScriptComponentValidateFunction(){ + String correctScript = " local a=6\n" + + " local b=10\n" + + " if(a>5) then\n" + + " b=5\n" + + " else\n" + + " b=2\n" + + " end\n" + + " defaultContext:setData(\"s1\",a*b)\n" + + " defaultContext:setData(\"s2\",_meta:get(\"nodeId\"))"; + // 语法错误 + String wrongScript = " local a=6\n" + + " local b=10\n" + + " if(a>5) tn\n" + + " b=5\n" + + " else\n" + + " b=2\n" + + " end\n" + + " defaultContext:setData(\"s1\",a*b)\n" + + " defaultContext:setData(\"s2\",_meta:get(\"nodeId\"))"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java new file mode 100644 index 000000000..31a9792ba --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java @@ -0,0 +1,62 @@ +package com.yomahub.liteflow.test.script.multi.language.validate; + +import com.yomahub.liteflow.enums.ScriptTypeEnum; +import com.yomahub.liteflow.script.ScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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 java.util.HashMap; +import java.util.Map; + +@SpringBootTest(classes = ValidateMultiLanguageScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateMultiLanguageScriptComponentTest { + @Test + public void testMultiLanguageScriptComponentValidateFunction(){ + String correctGroovyScript = " class Student {\n" + + " int studentID;\n" + + " String studentName;\n" + + "\n" + + " public void setStudentID(int id){\n" + + " this.studentID = id;\n" + + " }\n" + + " }\n" + + "\n" + + " Student student = new Student()\n" + + " student.studentID = 100301\n" + + " student.studentName = \"张三\"\n" + + " defaultContext.setData(\"student\", student)\n" + + "\n" + + " def a = 3\n" + + " def b = 2\n" + + " defaultContext.setData(\"s1\", a * b)"; + String correctJavascriptScript = " var student = defaultContext.getData(\"student\");\n" + + " student.setStudentID(10032);"; + String correctPythonScript = " a = 3\n" + + " s1 = defaultContext.getData(\"s1\")\n" + + " defaultContext.setData(\"s1\",s1*a)"; + // 语法错误 缩进 + String wrongPythonScript = " a = 3\n" + + " s1 = defaultContext.getData(\"s1\")\n" + + " defaultContext.setData(\"s1\",s1*a)"; + // 在加载多脚本时使用默认验证方法会错误 + Assertions.assertFalse(ScriptValidator.validate(correctGroovyScript)); + + // 多语言脚本验证 正确样例 + Map correctData = new HashMap<>(); + correctData.put(correctGroovyScript, ScriptTypeEnum.GROOVY); + correctData.put(correctJavascriptScript, ScriptTypeEnum.JS); + correctData.put(correctPythonScript, ScriptTypeEnum.PYTHON); + Assertions.assertTrue(ScriptValidator.validate(correctData)); + + // 多语言脚本验证 错误样例 + Map wrongData = new HashMap<>(); + wrongData.put(correctGroovyScript, ScriptTypeEnum.GROOVY); + wrongData.put(correctJavascriptScript, ScriptTypeEnum.JS); + wrongData.put(wrongPythonScript, ScriptTypeEnum.PYTHON); + Assertions.assertFalse(ScriptValidator.validate(wrongData)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java new file mode 100644 index 000000000..27eb00eb2 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java @@ -0,0 +1,53 @@ +package com.yomahub.liteflow.test.script.python.validate; + +import com.yomahub.liteflow.script.python.PythonScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidatePythonScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidatePythonScriptComponentTest { + @Test + public void testPythonScriptComponentValidateFunction(){ + String correctScript = " import json\n" + + "\n" + + " x='{\"name\": \"杰克\", \"age\": 75, \"nationality\": \"China\"}'\n" + + " jsonData=json.loads(x)\n" + + " name=jsonData['name']\n" + + " defaultContext.setData(\"name\", name.decode('utf-8'))\n" + + "\n" + + "\n" + + " a=6\n" + + " b=10\n" + + " if a>5:\n" + + " b=5\n" + + " print '你好'.decode('UTF-8')\n" + + " else:\n" + + " print 'hi'\n" + + " defaultContext.setData(\"s1\",a*b)\n" + + " defaultContext.setData(\"td\", td.sayHi(\"jack\"))"; + // 语法错误 缩进 + String wrongScript = " import json\n" + + "\n" + + " x='{\"name\": \"杰克\", \"age\": 75, \"nationality\": \"China\"}'\n" + + " jsonData=json.loads(x)\n" + + " name=jsonData['name']\n" + + " defaultContext.setData(\"name\", name.decode('utf-8'))\n" + + "\n" + + "\n" + + " a=6\n" + + " b=10\n" + + " if a>5:\n" + + " b=5\n" + + " print '你好'.decode('UTF-8')\n" + + " else:\n" + + " print 'hi'\n" + + " defaultContext.setData(\"s1\",a*b)\n" + + " defaultContext.setData(\"td\", td.sayHi(\"jack\"))"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java new file mode 100644 index 000000000..44a5f63ab --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.test.script.qlexpress.validate; + +import com.yomahub.liteflow.script.qlexpress.QLExpressScriptExecutor; +import com.yomahub.liteflow.script.validator.ScriptValidator; +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; + +@SpringBootTest(classes = ValidateQLExpressScriptComponentTest.class) +@EnableAutoConfiguration +public class ValidateQLExpressScriptComponentTest { + @Test + public void testQLExpressScriptComponentValidateFunction(){ + String correctScript = " count = defaultContext.getData(\"count\");\n" + + " if(count > 100){\n" + + " return \"a\";\n" + + " }else{\n" + + " return \"b\";\n" + + " }"; + // 语法错误 + String wrongScript = " count = defaultContext.getData(\"count\");\n" + + " if(count > 100){\n" + + " return \"a\";\n" + + " }el{\n" + + " return \"b\";\n" + + " }"; + Assertions.assertTrue(ScriptValidator.validate(correctScript)); + Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + } +} From 82348cb926f69f916beb01bf0dfe09b857c5bf00 Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Fri, 26 Jan 2024 14:31:36 +0800 Subject: [PATCH 11/20] =?UTF-8?q?=E5=AF=B9EL=E8=AF=AD=E5=8F=A5=E9=87=8C?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E9=87=8D=E8=AF=95=E6=AC=A1=E6=95=B0=E7=9A=84?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=BF=9B=E8=A1=8C=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/annotation/LiteflowRetry.java | 1 + .../builder/el/LiteFlowChainELBuilder.java | 2 +- ...yTimesOperator.java => RetryOperator.java} | 13 ++-- .../liteflow/common/ChainConstant.java | 2 +- .../element/condition/RetryCondition.java | 19 +++++- .../RetryELDeclMultiSpringbootTest.java} | 26 +++++--- .../{retryTimes => retry}/cmp/CmpConfig.java | 15 +++-- .../resources/retry/application.properties | 1 + .../src/test/resources/retry/flow.el.xml | 51 ++++++++++++++++ .../retryTimes/application.properties | 1 - .../src/test/resources/retryTimes/flow.el.xml | 43 ------------- .../retry/RetryELDeclSpringbootTest.java} | 26 +++++--- .../test/{retryTimes => retry}/cmp/ACmp.java | 2 +- .../test/{retryTimes => retry}/cmp/BCmp.java | 2 +- .../liteflow/test/retry}/cmp/CCmp.java | 2 +- .../liteflow/test/retry}/cmp/DCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/FCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/ICmp.java | 2 +- .../yomahub/liteflow/test/retry/cmp/MCmp.java | 18 ++++++ .../test/{retryTimes => retry}/cmp/NCmp.java | 2 +- .../resources/retry/application.properties | 1 + .../src/test/resources/retry/flow.el.xml | 51 ++++++++++++++++ .../retryTimes/application.properties | 1 - .../src/test/resources/retryTimes/flow.el.xml | 43 ------------- .../RetryTest.java} | 21 +++++-- .../test/{retryTimes => retry}/cmp/ACmp.java | 2 +- .../test/{retryTimes => retry}/cmp/BCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/CCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/DCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/FCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/ICmp.java | 2 +- .../yomahub/liteflow/test/retry/cmp/MCmp.java | 16 +++++ .../test/{retryTimes => retry}/cmp/NCmp.java | 2 +- .../src/test/resources/retry/flow.el.xml | 61 +++++++++++++++++++ .../src/test/resources/retryTimes/flow.el.xml | 52 ---------------- .../RetrySpringbootTest.java} | 21 +++++-- .../test/{retryTimes => retry}/cmp/ACmp.java | 2 +- .../test/{retryTimes => retry}/cmp/BCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/CCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/DCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/FCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/ICmp.java | 2 +- .../yomahub/liteflow/test/retry/cmp/MCmp.java | 18 ++++++ .../test/{retryTimes => retry}/cmp/NCmp.java | 2 +- .../resources/retry/application.properties | 1 + .../src/test/resources/retry/flow.el.xml | 51 ++++++++++++++++ .../retryTimes/application.properties | 1 - .../src/test/resources/retryTimes/flow.el.xml | 43 ------------- .../test/retry/RetrySpringbootTest.java} | 25 ++++++-- .../test/{retryTimes => retry}/cmp/ACmp.java | 2 +- .../test/{retryTimes => retry}/cmp/BCmp.java | 3 +- .../liteflow/test/retry}/cmp/CCmp.java | 2 +- .../liteflow/test/retry}/cmp/DCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/FCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/ICmp.java | 2 +- .../yomahub/liteflow/test/retry/cmp/MCmp.java | 18 ++++++ .../test/{retryTimes => retry}/cmp/NCmp.java | 2 +- .../resources/retry/application.properties | 1 + .../src/test/resources/retry/flow.el.xml | 50 +++++++++++++++ .../retryTimes/application.properties | 1 - .../src/test/resources/retryTimes/flow.el.xml | 42 ------------- .../RetrySpringTest.java} | 21 +++++-- .../test/{retryTimes => retry}/cmp/ACmp.java | 2 +- .../test/{retryTimes => retry}/cmp/BCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/CCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/DCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/FCmp.java | 2 +- .../test/{retryTimes => retry}/cmp/ICmp.java | 2 +- .../yomahub/liteflow/test/retry/cmp/MCmp.java | 18 ++++++ .../test/{retryTimes => retry}/cmp/NCmp.java | 2 +- .../{retryTimes => retry}/application.xml | 4 +- .../src/test/resources/retry/flow.el.xml | 51 ++++++++++++++++ .../src/test/resources/retryTimes/flow.el.xml | 43 ------------- 73 files changed, 592 insertions(+), 352 deletions(-) rename liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/{RetryTimesOperator.java => RetryOperator.java} (66%) rename liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes/RetryTimesELDeclMultiSpringbootTest.java => retry/RetryELDeclMultiSpringbootTest.java} (82%) rename liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/CmpConfig.java (86%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml rename liteflow-testcase-el/{liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java => liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java} (81%) rename liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ACmp.java (84%) rename liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/BCmp.java (88%) rename liteflow-testcase-el/{liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes => liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry}/cmp/CCmp.java (89%) rename liteflow-testcase-el/{liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes => liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry}/cmp/DCmp.java (89%) rename liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/FCmp.java (89%) rename liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ICmp.java (91%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java rename liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/NCmp.java (89%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes/RetryTimesTest.java => retry/RetryTest.java} (84%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ACmp.java (79%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/BCmp.java (85%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/CCmp.java (86%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/DCmp.java (87%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/FCmp.java (86%) rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ICmp.java (90%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java rename liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/NCmp.java (87%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes/RetryTimesSpringbootTest.java => retry/RetrySpringbootTest.java} (84%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ACmp.java (83%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/BCmp.java (87%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/CCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/DCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/FCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ICmp.java (91%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java rename liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/NCmp.java (89%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml rename liteflow-testcase-el/{liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java => liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java} (81%) rename liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ACmp.java (84%) rename liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/BCmp.java (78%) rename liteflow-testcase-el/{liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes => liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry}/cmp/CCmp.java (89%) rename liteflow-testcase-el/{liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes => liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry}/cmp/DCmp.java (89%) rename liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/FCmp.java (89%) rename liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ICmp.java (91%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java rename liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/NCmp.java (89%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes/RetryTimesSpringTest.java => retry/RetrySpringTest.java} (84%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ACmp.java (83%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/BCmp.java (87%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/CCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/DCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/FCmp.java (88%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/ICmp.java (91%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/{retryTimes => retry}/cmp/NCmp.java (89%) rename liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/{retryTimes => retry}/application.xml (91%) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/flow.el.xml delete mode 100644 liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java index 2af9f5dae..34ad6ac8f 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java @@ -12,6 +12,7 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited +@Deprecated public @interface LiteflowRetry { @LFAliasFor("retry") 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 f749aee94..6de02fb00 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 @@ -92,7 +92,7 @@ public class LiteFlowChainELBuilder { EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PARALLEL, Object.class, new ParallelOperator()); - EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY_TIMES, Object.class, new RetryTimesOperator()); + EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY, Object.class, new RetryOperator()); } public static LiteFlowChainELBuilder createChain() { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java similarity index 66% rename from liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java rename to liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java index 72ac3dded..2e8b5a4a1 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryTimesOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java @@ -2,12 +2,9 @@ package com.yomahub.liteflow.builder.el.operator; import com.yomahub.liteflow.builder.el.operator.base.BaseOperator; import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; -import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.flow.element.Condition; import com.yomahub.liteflow.flow.element.Executable; import com.yomahub.liteflow.flow.element.condition.RetryCondition; -import com.yomahub.liteflow.flow.element.condition.ThenCondition; -import com.yomahub.liteflow.flow.element.condition.WhileCondition; /** * @@ -15,7 +12,7 @@ import com.yomahub.liteflow.flow.element.condition.WhileCondition; * @since 2.11.5 * */ -public class RetryTimesOperator extends BaseOperator { +public class RetryOperator extends BaseOperator { @Override public Condition build(Object[] objects) throws Exception { OperatorHelper.checkObjectSizeGtTwo(objects); @@ -24,6 +21,14 @@ public class RetryTimesOperator extends BaseOperator { RetryCondition retryCondition = new RetryCondition(); retryCondition.addExecutable(executable); retryCondition.setRetryTimes(retryTimes); + if(objects.length > 2) { + Class[] forExceptions = new Class[objects.length - 2]; + for(int i = 2; i < objects.length; i ++) { + String className = OperatorHelper.convert(objects[i], String.class); + forExceptions[i - 2] = Class.forName(className); + } + retryCondition.setRetryForExceptions(forExceptions); + } return retryCondition; } 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 977a9371a..c90600165 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 @@ -94,6 +94,6 @@ public interface ChainConstant { String EXTENDS = "extends"; - String RETRY_TIMES = "retryTimes"; + String RETRY = "retry"; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java index 6f4bbaf99..91c1d14f3 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java @@ -3,7 +3,6 @@ package com.yomahub.liteflow.flow.element.condition; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.ObjectUtil; import com.yomahub.liteflow.exception.ChainEndException; -import com.yomahub.liteflow.exception.ELParseException; import com.yomahub.liteflow.flow.element.Chain; import com.yomahub.liteflow.flow.element.Condition; import com.yomahub.liteflow.flow.element.Executable; @@ -12,12 +11,25 @@ import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.slot.DataBus; +import java.util.Arrays; +import java.util.List; + public class RetryCondition extends ThenCondition{ private final LFLog LOG = LFLoggerManager.getLogger(this.getClass()); private Integer retryTimes; + private Class[] retryForExceptions = new Class[] { Exception.class }; + + public Class[] getRetryForExceptions() { + return retryForExceptions; + } + + public void setRetryForExceptions(Class[] retryForExceptions) { + this.retryForExceptions = retryForExceptions; + } + public Integer getRetryTimes() { return retryTimes; } @@ -29,6 +41,7 @@ public class RetryCondition extends ThenCondition{ @Override public void executeCondition(Integer slotIndex) throws Exception { int retryTimes = this.getRetryTimes() < 0 ? 0 : this.getRetryTimes(); + List> forExceptions = Arrays.asList(this.getRetryForExceptions()); for (int i = 0; i <= retryTimes; i ++) { try { if(i == 0) { @@ -40,7 +53,9 @@ public class RetryCondition extends ThenCondition{ } catch (ChainEndException e) { throw e; } catch (Exception e) { - if(i >= retryTimes) { + // 判断抛出的异常是不是指定异常的子类 + boolean flag = forExceptions.stream().anyMatch(clazz -> clazz.isAssignableFrom(e.getClass())); + if(!flag || i >= retryTimes) { if(retryTimes > 0) { String retryFailMsg = StrFormatter.format("retry fail when executing the chain[{}] because {} occurs {}.", this.getCurrChainId(), this.getCurrentExecutableId(), e); diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclMultiSpringbootTest.java similarity index 82% rename from liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclMultiSpringbootTest.java index 2ed4fe38e..664816a7d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclMultiSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclMultiSpringbootTest.java @@ -1,9 +1,8 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.test.BaseTest; -import com.yomahub.liteflow.test.rollback.RollbackELDeclMultiSpringbootTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -17,11 +16,11 @@ import javax.annotation.Resource; @ExtendWith(SpringExtension.class) -@TestPropertySource(value = "classpath:/retryTimes/application.properties") -@SpringBootTest(classes = RetryTimesELDeclMultiSpringbootTest.class) +@TestPropertySource(value = "classpath:/retry/application.properties") +@SpringBootTest(classes = RetryELDeclMultiSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) -public class RetryTimesELDeclMultiSpringbootTest extends BaseTest { +@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"}) +public class RetryELDeclMultiSpringbootTest extends BaseTest { @Resource private FlowExecutor flowExecutor; @@ -46,7 +45,6 @@ public class RetryTimesELDeclMultiSpringbootTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -105,4 +103,18 @@ public class RetryTimesELDeclMultiSpringbootTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CmpConfig.java similarity index 86% rename from liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CmpConfig.java index da8c2cb99..996ace9ee 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CmpConfig.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CmpConfig.java @@ -1,13 +1,12 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.annotation.LiteflowMethod; -import com.yomahub.liteflow.annotation.LiteflowRetry; 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.exception.ELParseException; import java.util.Iterator; import java.util.List; @@ -21,7 +20,7 @@ public class CmpConfig { int flagf = 0; int flagi = 0; int flagn = 0; - int flagw = 0; + int flagm = 0; @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a") public void processA(NodeComponent bindCmp) { @@ -70,6 +69,14 @@ public class CmpConfig { } } + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "m") + public void processM(NodeComponent bindCmp) { + flagm ++; + System.out.println("MCmp executed!"); + if(flagm < 4) throw new ELParseException("MCmp error!"); + else flagm = 0; + } + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE, nodeId = "n", nodeType = NodeTypeEnum.WHILE) public boolean processN(NodeComponent bindCmp) { flagn ++; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/application.properties new file mode 100644 index 000000000..4323c74b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retry/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..c567d84d5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml @@ -0,0 +1,51 @@ + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties deleted file mode 100644 index ecdb08bea..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index 775e7a742..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java similarity index 81% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java index 24d30a669..486aaddf6 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java @@ -1,8 +1,7 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; 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; @@ -14,11 +13,11 @@ import org.springframework.test.context.TestPropertySource; import javax.annotation.Resource; -@TestPropertySource(value = "classpath:/retryTimes/application.properties") -@SpringBootTest(classes = RetryTimesSpringbootTest.class) +@TestPropertySource(value = "classpath:/retry/application.properties") +@SpringBootTest(classes = RetryELDeclSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) -public class RetryTimesSpringbootTest extends BaseTest { +@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"}) +public class RetryELDeclSpringbootTest extends BaseTest { @Resource private FlowExecutor flowExecutor; @@ -43,7 +42,6 @@ public class RetryTimesSpringbootTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -102,4 +100,18 @@ public class RetryTimesSpringbootTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java similarity index 84% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java index 46e99dc1f..98bcc2379 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java index 5a02501b3..991f5e68a 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java index f6a1014ee..ecf7f1a25 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeForComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java index 319c98cd5..34e9bb5b7 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeSwitchComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java index f9764757e..1bc95f542 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeIfComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java similarity index 91% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java index 3341869d8..9ccd8457e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java new file mode 100644 index 000000000..0498cee17 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.retry.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.exception.ELParseException; + +@LiteflowComponent("m") +public class MCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() throws Exception { + flag ++; + System.out.println("MCmp executed!"); + if(flag < 4) throw new ELParseException("MCmp error!"); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java index d4e371647..ac0434c95 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeWhileComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties new file mode 100644 index 000000000..4323c74b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retry/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..c567d84d5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml @@ -0,0 +1,51 @@ + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties deleted file mode 100644 index ecdb08bea..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index 775e7a742..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/RetryTest.java similarity index 84% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/RetryTest.java index b3dec4514..19cde5ca8 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/RetryTest.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; @@ -10,14 +10,14 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -public class RetryTimesTest extends BaseTest { +public class RetryTest extends BaseTest { private static FlowExecutor flowExecutor; @BeforeAll public static void init() { LiteflowConfig config = new LiteflowConfig(); - config.setRuleSource("retryTimes/flow.el.xml"); + config.setRuleSource("retry/flow.el.xml"); flowExecutor = FlowExecutorHolder.loadInstance(config); } @@ -41,7 +41,6 @@ public class RetryTimesTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -100,4 +99,18 @@ public class RetryTimesTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java similarity index 79% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java index 23ed69a7a..689fe29f6 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java similarity index 85% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java index d52cfdb2d..1c5ecbf14 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java similarity index 86% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java index 9a39cb349..cad5acb41 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeForComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java similarity index 87% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java index e88942178..b845c6008 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeSwitchComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java similarity index 86% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java index bd5601bbb..6135b9444 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeIfComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java similarity index 90% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java index 058bb3c4e..8fbc1c767 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java new file mode 100644 index 000000000..9dd76d1bd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.retry.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.exception.ELParseException; + +public class MCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() throws Exception { + flag ++; + System.out.println("MCmp executed!"); + if(flag < 4) throw new ELParseException("MCmp error!"); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java similarity index 87% rename from liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java index 68409ab83..709dce3a0 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeWhileComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..175044312 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index 685f6ef27..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java similarity index 84% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java index 79b92c2b3..ed5371230 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; @@ -12,8 +12,8 @@ import org.noear.solon.test.SolonJUnit5Extension; import org.noear.solon.test.annotation.TestPropertySource; @ExtendWith(SolonJUnit5Extension.class) -@TestPropertySource("classpath:/retryTimes/application.properties") -public class RetryTimesSpringbootTest extends BaseTest { +@TestPropertySource("classpath:/retry/application.properties") +public class RetrySpringbootTest extends BaseTest { @Inject private FlowExecutor flowExecutor; @@ -38,7 +38,6 @@ public class RetryTimesSpringbootTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -97,4 +96,18 @@ public class RetryTimesSpringbootTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java similarity index 83% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java index 9c5531a86..795d8247e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java similarity index 87% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java index b4890b4ef..0eb4b1573 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java index 069acb496..9d135e74d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeForComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java index ccbd72a3e..7848456db 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeSwitchComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java index 5d13709bb..54df152d5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeIfComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java similarity index 91% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java index 922ace500..a711d5b4f 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java new file mode 100644 index 000000000..37e1d9f35 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.retry.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.exception.ELParseException; +import org.noear.solon.annotation.Component; + +@Component("m") +public class MCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() throws Exception { + flag ++; + System.out.println("MCmp executed!"); + if(flag < 4) throw new ELParseException("MCmp error!"); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java index e6b27d11f..5605c729f 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeWhileComponent; import org.noear.solon.annotation.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties new file mode 100644 index 000000000..4323c74b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retry/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..c567d84d5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml @@ -0,0 +1,51 @@ + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties deleted file mode 100644 index ecdb08bea..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index 775e7a742..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java similarity index 81% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java index 9a65758dc..da8fa7f5e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesELDeclSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; @@ -13,11 +13,11 @@ import org.springframework.test.context.TestPropertySource; import javax.annotation.Resource; -@TestPropertySource(value = "classpath:/retryTimes/application.properties") -@SpringBootTest(classes = RetryTimesELDeclSpringbootTest.class) +@TestPropertySource(value = "classpath:/retry/application.properties") +@SpringBootTest(classes = RetrySpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.retryTimes.cmp" }) -public class RetryTimesELDeclSpringbootTest extends BaseTest { +@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"}) +public class RetrySpringbootTest extends BaseTest { @Resource private FlowExecutor flowExecutor; @@ -42,7 +42,6 @@ public class RetryTimesELDeclSpringbootTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -101,4 +100,18 @@ public class RetryTimesELDeclSpringbootTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java similarity index 84% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java index 46e99dc1f..98bcc2379 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java similarity index 78% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java index e5ab69d42..d622bed92 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java @@ -1,7 +1,6 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; -import com.yomahub.liteflow.annotation.LiteflowRetry; import com.yomahub.liteflow.core.NodeComponent; @LiteflowComponent("b") diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java index f6a1014ee..ecf7f1a25 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeForComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java index 319c98cd5..34e9bb5b7 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeSwitchComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java index f9764757e..1bc95f542 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeIfComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java similarity index 91% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java index 3341869d8..9ccd8457e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java new file mode 100644 index 000000000..0498cee17 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.retry.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.exception.ELParseException; + +@LiteflowComponent("m") +public class MCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() throws Exception { + flag ++; + System.out.println("MCmp executed!"); + if(flag < 4) throw new ELParseException("MCmp error!"); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java index d4e371647..ac0434c95 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.annotation.LiteflowComponent; import com.yomahub.liteflow.core.NodeWhileComponent; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties new file mode 100644 index 000000000..4323c74b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=retry/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..6e3410c57 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml @@ -0,0 +1,50 @@ + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties deleted file mode 100644 index ecdb08bea..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=retryTimes/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index b12f5eced..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringTest.java similarity index 84% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringTest.java index 7026672e5..be03cb224 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/RetryTimesSpringTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringTest.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes; +package com.yomahub.liteflow.test.retry; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; @@ -13,8 +13,8 @@ import javax.annotation.Resource; @ExtendWith(SpringExtension.class) -@ContextConfiguration("classpath:/retryTimes/application.xml") -public class RetryTimesSpringTest extends BaseTest { +@ContextConfiguration("classpath:/retry/application.xml") +public class RetrySpringTest extends BaseTest { @Resource private FlowExecutor flowExecutor; @@ -39,7 +39,6 @@ public class RetryTimesSpringTest extends BaseTest { public void testNode() throws Exception { LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>b==>b==>b", response.getExecuteStepStr()); } // FOR测试 @@ -98,4 +97,18 @@ public class RetryTimesSpringTest extends BaseTest { Assertions.assertEquals("a==>b", response.getExecuteStepStr()); } + // 指定异常重试测试1 + @Test + public void testException1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg"); + Assertions.assertTrue(response.isSuccess()); + } + + // 指定异常重试测试2 + @Test + public void testException2() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg"); + Assertions.assertFalse(response.isSuccess()); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java similarity index 83% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java index 0dd4114a0..e56306412 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java similarity index 87% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java index a50952f53..85898c803 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/BCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java index 51949af98..c3ed52c34 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/CCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeForComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java index ee453a2ab..236546f45 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/DCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeSwitchComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java similarity index 88% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java index d41ed62c9..ee942ea6c 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/FCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeIfComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java similarity index 91% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java index e936cc096..5c29b8d15 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/ICmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import cn.hutool.core.collection.ListUtil; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java new file mode 100644 index 000000000..cf7755b06 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.retry.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.exception.ELParseException; +import org.springframework.stereotype.Component; + +@Component("m") +public class MCmp extends NodeComponent { + int flag = 0; + + @Override + public void process() throws Exception { + flag ++; + System.out.println("MCmp executed!"); + if(flag < 4) throw new ELParseException("MCmp error!"); + else flag = 0; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java similarity index 89% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java index 5bb3b5865..ff24b004d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retryTimes/cmp/NCmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java @@ -1,4 +1,4 @@ -package com.yomahub.liteflow.test.retryTimes.cmp; +package com.yomahub.liteflow.test.retry.cmp; import com.yomahub.liteflow.core.NodeWhileComponent; import org.springframework.stereotype.Component; diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/application.xml similarity index 91% rename from liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml rename to liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/application.xml index c1d4af03e..441929a5c 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/application.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/application.xml @@ -7,14 +7,14 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> - + - + diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/flow.el.xml new file mode 100644 index 000000000..c567d84d5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retry/flow.el.xml @@ -0,0 +1,51 @@ + + + + THEN( a, b ).retry(3); + + + + WHEN( a, b ).retry(3); + + + + THEN( a, b.retry(3) ); + + + + FOR(c).DO(a).retry(3); + + + + SWITCH(d).TO(a).retry(3); + + + + IF(f, a).retry(3); + + + + WHILE(n).DO(a).retry(3); + + + + ITERATOR(i).DO(a).retry(3); + + + + THEN( a, b ).retry(1); + + + + THEN( a, FINALLY(b, a).retry(3) ); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException"); + + + + THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException"); + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml deleted file mode 100644 index 775e7a742..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/retryTimes/flow.el.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - THEN( a, b ).retryTimes(3); - - - - WHEN( a, b ).retryTimes(3); - - - - THEN( a, b.retryTimes(3) ); - - - - FOR(c).DO(a).retryTimes(3); - - - - SWITCH(d).TO(a).retryTimes(3); - - - - IF(f, a).retryTimes(3); - - - - WHILE(n).DO(a).retryTimes(3); - - - - ITERATOR(i).DO(a).retryTimes(3); - - - - THEN( a, b ).retryTimes(1); - - - - THEN( a, FINALLY(b, a).retryTimes(3) ); - - - \ No newline at end of file From bed132b25a50db3d5fce637bcd5f027911347c9c Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Tue, 30 Jan 2024 15:51:35 +0800 Subject: [PATCH 12/20] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/yomahub/liteflow/annotation/LiteflowRetry.java | 5 +++++ .../yomahub/liteflow/builder/el/operator/RetryOperator.java | 2 +- .../liteflow/flow/element/condition/RetryCondition.java | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java index 34ad6ac8f..15bdd48ef 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java @@ -12,6 +12,11 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited +/** + * This class has been deprecated due to its only component retry function. Please use the retry method in the EL expression. + * @Deprecated + * @see # retry(int retryTimes) e.g. THEN( a, b.retry(3) ); WHEN( a, b ).retry(3); + */ @Deprecated public @interface LiteflowRetry { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java index 2e8b5a4a1..a6c756b17 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java @@ -9,7 +9,7 @@ import com.yomahub.liteflow.flow.element.condition.RetryCondition; /** * * @author Rain - * @since 2.11.5 + * @since 2.12.0 * */ public class RetryOperator extends BaseOperator { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java index 91c1d14f3..6d5b42906 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java @@ -14,6 +14,12 @@ import com.yomahub.liteflow.slot.DataBus; import java.util.Arrays; import java.util.List; +/** + * + * @author Rain + * @since 2.12.0 + * + */ public class RetryCondition extends ThenCondition{ private final LFLog LOG = LFLoggerManager.getLogger(this.getClass()); From 3565045d54beecf11fbfcc8c3c1c5055888a71ec Mon Sep 17 00:00:00 2001 From: rain <672378783@qq.com> Date: Tue, 30 Jan 2024 15:59:57 +0800 Subject: [PATCH 13/20] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/yomahub/liteflow/annotation/LiteflowRetry.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java index 15bdd48ef..f436c6554 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java @@ -12,12 +12,12 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited +@Deprecated /** * This class has been deprecated due to its only component retry function. Please use the retry method in the EL expression. * @Deprecated * @see # retry(int retryTimes) e.g. THEN( a, b.retry(3) ); WHEN( a, b ).retry(3); */ -@Deprecated public @interface LiteflowRetry { @LFAliasFor("retry") From 13af5f704af809e5aeeefa78d5c0a9e6c7bc762d Mon Sep 17 00:00:00 2001 From: gezuao <673843192@qq.com> Date: Tue, 30 Jan 2024 16:08:45 +0800 Subject: [PATCH 14/20] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E8=84=9A=E6=9C=AC=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../script/validator/ScriptValidator.java | 60 +++++++++++++------ .../ValidateAviatorScriptComponentTest.java | 6 ++ .../ValidateGraaljsScriptComponentTest.java | 4 ++ .../ValidateGroovyScriptComponentTest.java | 4 ++ .../ValidateJavaScriptComponentTest.java | 4 ++ ...ValidateJavaScriptScriptComponentTest.java | 4 ++ .../ValidateLuaScriptComponentTest.java | 4 ++ ...idateMultiLanguageScriptComponentTest.java | 16 ++--- .../ValidatePythonScriptComponentTest.java | 4 ++ .../ValidateQLExpressScriptComponentTest.java | 4 ++ 10 files changed, 83 insertions(+), 27 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java index dd1c7b72c..a05ce0606 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java @@ -31,49 +31,71 @@ public class ScriptValidator { } /** - * 只引入一种脚本语言时,使用该语言验证 + * 验证脚本逻辑的公共部分 * - * @param script 脚本 + * @param script 脚本 + * @param scriptType 脚本类型 * @return boolean */ - public static boolean validate(String script){ + private static boolean validateScript(String script, ScriptTypeEnum scriptType){ + // 未加载任何脚本模块 if(scriptExecutors.isEmpty()){ LOG.error("The loaded script modules not found."); return false; } - // 使用多脚本语言需要指定验证语言 - if(scriptExecutors.size() > 1){ + + // 指定脚本语言未加载 + if (scriptType != null && !scriptExecutors.containsKey(scriptType)) { + LOG.error(StrUtil.format("Specified script language {} was not found.", scriptType)); + return false; + } + + // 加载多个脚本语言需要指定语言验证 + if (scriptExecutors.size() > 1 && scriptType == null) { LOG.error("The loaded script modules more than 1. Please specify the script language."); return false; } - ScriptExecutor scriptExecutor = scriptExecutors.values().iterator().next(); + ScriptExecutor scriptExecutor = (scriptType != null) ? scriptExecutors.get(scriptType) : scriptExecutors.values().iterator().next(); try { scriptExecutor.compile(script); } catch (Exception e) { - LOG.error("script component validate failure. " + e.getMessage()); + LOG.error(StrUtil.format("{} Script component validate failure. ", scriptExecutor.scriptType()) + e.getMessage()); return false; } return true; } /** - * 多语言脚本验证 + * 只引入一种脚本语言时,可以不指定语言验证 + * + * @param script 脚本 + * @return boolean + */ + public static boolean validate(String script){ + return validateScript(script, null); + } + + /** + * 指定脚本语言验证 + * + * @param script 脚本 + * @param scriptType 脚本类型 + * @return boolean + */ + public static boolean validate(String script, ScriptTypeEnum scriptType){ + return validateScript(script, scriptType); + } + + /** + * 多语言脚本批量验证 * * @param scripts 脚本 * @return boolean */ - public static boolean validate(Map scripts){ - for(Map.Entry script : scripts.entrySet()){ - ScriptExecutor scriptExecutor = scriptExecutors.getOrDefault(script.getValue(), null); - if(scriptExecutor == null){ - LOG.error(StrUtil.format("Specified script language {} was not found.", script.getValue())); - return false; - } - try { - scriptExecutor.compile(script.getKey()); - } catch (Exception e) { - LOG.error(StrUtil.format("{} script component validate failure. ", script.getValue()) + e.getMessage()); + public static boolean validate(Map scripts){ + for(Map.Entry script : scripts.entrySet()){ + if(!validate(script.getValue(), script.getKey())){ return false; } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java index 9a6b5553e..6f43ed8f5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.aviator.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.aviator.AviatorScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -37,5 +38,10 @@ public class ValidateAviatorScriptComponentTest { " setData(defaultContext, \"s1\", a*b);"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON)); + + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java index 7a01031f6..7cea4b72b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.graaljs.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.graaljs.GraalJavaScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -47,5 +48,8 @@ public class ValidateGraaljsScriptComponentTest { " defaultContext.setData(\"s1\",parseInt(result));"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java index 912590029..ff3a705f5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.groovy.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.groovy.GroovyScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -75,5 +76,8 @@ public class ValidateGroovyScriptComponentTest { " defaultContext.setData(\"s1\", a * b)"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java index 548d6d023..033ac15fa 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.java.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.java.JavaExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -59,5 +60,8 @@ public class ValidateJavaScriptComponentTest { " }"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java index 6c923ad34..29ef33af4 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.javascript.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.javascript.JavaScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -47,5 +48,8 @@ public class ValidateJavaScriptScriptComponentTest { " defaultContext.setData(\"s1\",parseInt(result));"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java index f5983e785..01bd73487 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.lua.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.lua.LuaScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -33,5 +34,8 @@ public class ValidateLuaScriptComponentTest { " defaultContext:setData(\"s2\",_meta:get(\"nodeId\"))"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java index 31a9792ba..2bbee5dc0 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java @@ -46,17 +46,17 @@ public class ValidateMultiLanguageScriptComponentTest { Assertions.assertFalse(ScriptValidator.validate(correctGroovyScript)); // 多语言脚本验证 正确样例 - Map correctData = new HashMap<>(); - correctData.put(correctGroovyScript, ScriptTypeEnum.GROOVY); - correctData.put(correctJavascriptScript, ScriptTypeEnum.JS); - correctData.put(correctPythonScript, ScriptTypeEnum.PYTHON); + Map correctData = new HashMap<>(); + correctData.put(ScriptTypeEnum.GROOVY, correctGroovyScript); + correctData.put(ScriptTypeEnum.JS, correctJavascriptScript); + correctData.put(ScriptTypeEnum.PYTHON, correctPythonScript); Assertions.assertTrue(ScriptValidator.validate(correctData)); // 多语言脚本验证 错误样例 - Map wrongData = new HashMap<>(); - wrongData.put(correctGroovyScript, ScriptTypeEnum.GROOVY); - wrongData.put(correctJavascriptScript, ScriptTypeEnum.JS); - wrongData.put(wrongPythonScript, ScriptTypeEnum.PYTHON); + Map wrongData = new HashMap<>(); + wrongData.put(ScriptTypeEnum.GROOVY, correctGroovyScript); + wrongData.put(ScriptTypeEnum.JS, correctJavascriptScript); + wrongData.put(ScriptTypeEnum.PYTHON, wrongPythonScript); Assertions.assertFalse(ScriptValidator.validate(wrongData)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java index 27eb00eb2..77601e4f3 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.python.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.python.PythonScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -49,5 +50,8 @@ public class ValidatePythonScriptComponentTest { " defaultContext.setData(\"td\", td.sayHi(\"jack\"))"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java index 44a5f63ab..34f449357 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.test.script.qlexpress.validate; +import com.yomahub.liteflow.enums.ScriptTypeEnum; import com.yomahub.liteflow.script.qlexpress.QLExpressScriptExecutor; import com.yomahub.liteflow.script.validator.ScriptValidator; import org.junit.jupiter.api.Assertions; @@ -27,5 +28,8 @@ public class ValidateQLExpressScriptComponentTest { " }"; Assertions.assertTrue(ScriptValidator.validate(correctScript)); Assertions.assertFalse(ScriptValidator.validate(wrongScript)); + + Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.QLEXPRESS)); + Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON)); } } From 69b7fdf99176c3369ebda320e05c26e01c722f37 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 31 Jan 2024 13:57:37 +0800 Subject: [PATCH 15/20] =?UTF-8?q?future=20#I9050W=20=E4=B8=BA=E6=AF=8F?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E4=B8=8A=E4=B8=8B=E6=96=87=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=90=8D=E5=AD=97=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=97=B6=E5=8F=AF=E4=BB=A5=E6=A0=B9=E6=8D=AE=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E6=9D=A5=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/core/NodeComponent.java | 4 ++ .../liteflow/flow/LiteflowResponse.java | 4 ++ .../liteflow/script/ScriptExecutor.java | 12 +----- .../com/yomahub/liteflow/slot/DataBus.java | 23 ++++++++--- .../java/com/yomahub/liteflow/slot/Slot.java | 25 ++++++++---- .../ContextBeanSpringbootTest.java | 38 +++++++++++++++++++ .../liteflow/test/contextBean/cmp/ACmp.java | 23 +++++++++++ .../liteflow/test/contextBean/cmp/BCmp.java | 21 ++++++++++ .../test/contextBean/context/TestContext.java | 32 ++++++++++++++++ .../contextBean/application.properties | 1 + .../src/test/resources/contextBean/flow.xml | 7 ++++ 11 files changed, 167 insertions(+), 23 deletions(-) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/ContextBeanSpringbootTest.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/BCmp.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/context/TestContext.java create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.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 283de4216..2ed37eeee 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 @@ -280,6 +280,10 @@ public abstract class NodeComponent { return this.getSlot().getContextBean(contextBeanClazz); } + public T getContextBean(String contextName) { + return this.getSlot().getContextBean(contextName); + } + public String getNodeId() { return nodeId; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java index c2645a299..51e1ddc28 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java @@ -101,6 +101,10 @@ public class LiteflowResponse { return this.getSlot().getContextBean(contextBeanClazz); } + public T getContextBean(String contextName) { + return this.getSlot().getContextBean(contextName); + } + public Map> getExecuteSteps() { Map> map = new LinkedHashMap<>(); this.getSlot().getExecuteSteps().forEach(cmpStep -> { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java index 0f4be12dd..713a0e39e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java @@ -54,17 +54,7 @@ public abstract class ScriptExecutor { // key的规则为自定义上下文的simpleName // 比如你的自定义上下文为AbcContext,那么key就为:abcContext // 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法,而不是参数,所以脚本语言的绑定表里也是放多个上下文 - DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> { - ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class); - String key; - if (contextBean != null && contextBean.value().trim().length() > 0) { - key = contextBean.value(); - } - else { - key = StrUtil.lowerFirst(o.getClass().getSimpleName()); - } - putConsumer.accept(key, o); - }); + DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(tuple -> putConsumer.accept(tuple.get(0), tuple.get(1))); // 把wrap对象转换成元数据map Map metaMap = BeanUtil.beanToMap(wrap); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/DataBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/DataBus.java index 3b2d6b1dd..00e3032e6 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/DataBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/DataBus.java @@ -7,10 +7,14 @@ */ package com.yomahub.liteflow.slot; +import cn.hutool.core.lang.Tuple; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.annotation.util.AnnoUtil; +import com.yomahub.liteflow.context.ContextBean; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.property.LiteflowConfig; @@ -74,13 +78,22 @@ public class DataBus { .map((Function, Object>) ReflectUtil::newInstanceIfPossible) .collect(Collectors.toList()); - Slot slot = new Slot(contextBeanList); - - return offerIndex(slot); + return offerSlotByBean(contextBeanList); } public static int offerSlotByBean(List contextList) { - Slot slot = new Slot(contextList); + List contextBeanList = contextList.stream().map(object -> { + ContextBean contextBean = AnnoUtil.getAnnotation(object.getClass(), ContextBean.class); + String contextKey; + if (contextBean != null && StrUtil.isNotBlank(contextBean.value())){ + contextKey = contextBean.value(); + }else{ + contextKey = StrUtil.lowerFirst(object.getClass().getSimpleName()); + } + return new Tuple(contextKey, object); + }).collect(Collectors.toList()); + + Slot slot = new Slot(contextBeanList); return offerIndex(slot); } @@ -128,7 +141,7 @@ public class DataBus { return SLOTS.get(slotIndex); } - public static List getContextBeanList(int slotIndex) { + public static List getContextBeanList(int slotIndex) { Slot slot = getSlot(slotIndex); return slot.getContextBeanList(); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java index e8cbb8cba..8bb39dafe 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java @@ -9,6 +9,7 @@ package com.yomahub.liteflow.slot; import cn.hutool.core.collection.ConcurrentHashSet; import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Tuple; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.ttl.TransmittableThreadLocal; @@ -30,6 +31,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Predicate; /** * Slot的抽象类实现 @@ -91,14 +93,14 @@ public class Slot { protected ConcurrentHashMap metaDataMap = new ConcurrentHashMap<>(); - private List contextBeanList; + private List contextBeanList; private static final TransmittableThreadLocal> conditionStack = TransmittableThreadLocal.withInitial(ConcurrentLinkedDeque::new); public Slot() { } - public Slot(List contextBeanList) { + public Slot(List contextBeanList) { this.contextBeanList = contextBeanList; } @@ -449,21 +451,30 @@ public class Slot { metaDataMap.remove(SUB_EXCEPTION_PREFIX + chainId); } - public List getContextBeanList() { + public List getContextBeanList() { return this.contextBeanList; } public T getContextBean(Class contextBeanClazz) { - T t = (T) contextBeanList.stream().filter(o -> o.getClass().getName().equals(contextBeanClazz.getName())).findFirst().orElse(null); - if (t == null) { + Tuple contextTuple = contextBeanList.stream().filter(tuple -> tuple.get(1).getClass().getName().equals(contextBeanClazz.getName())).findFirst().orElse(null); + if (contextTuple == null) { contextBeanList.forEach(o -> LOG.info("ChainId[{}], Context class:{},Request class:{}", this.getChainId(), o.getClass().getName(), contextBeanClazz.getName())); throw new NoSuchContextBeanException("this type is not in the context type passed in"); } - return t; + return contextTuple.get(1); + } + + public T getContextBean(String contextBeanKey) { + Tuple contextTuple = contextBeanList.stream().filter(tuple -> tuple.get(0).equals(contextBeanKey)).findFirst().orElse(null); + if (contextTuple == null) { + contextBeanList.forEach(o -> LOG.info("ChainId[{}], Context class:{},Request contextBeanKey:{}", this.getChainId(), o.getClass().getName(), contextBeanKey)); + throw new NoSuchContextBeanException("this context key is not defined"); + } + return contextTuple.get(1); } public T getFirstContextBean() { - Class firstContextBeanClazz = (Class) this.getContextBeanList().get(0).getClass(); + Class firstContextBeanClazz = (Class) this.getContextBeanList().get(0).get(1).getClass(); return this.getContextBean(firstContextBeanClazz); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/ContextBeanSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/ContextBeanSpringbootTest.java new file mode 100644 index 000000000..55567ea7e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/ContextBeanSpringbootTest.java @@ -0,0 +1,38 @@ +package com.yomahub.liteflow.test.contextBean; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.test.contextBean.context.TestContext; +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; + +/** + * ContextBean测试 + * + * @author Bryan.Zhang + */ +@TestPropertySource(value = "classpath:/contextBean/application.properties") +@SpringBootTest(classes = ContextBeanSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.contextBean.cmp" }) +public class ContextBeanSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 最简单的情况 + @Test + public void testContextBean1() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", TestContext.class); + Assertions.assertTrue(response.isSuccess()); + TestContext context = response.getContextBean("skuContext"); + Assertions.assertEquals("J001", context.getSkuCode()); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java new file mode 100644 index 000000000..45119e9d9 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java @@ -0,0 +1,23 @@ +/** + *

Title: liteflow

+ *

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

+ * @author Bryan.Zhang + * @email weenyc31@163.com + * @Date 2020/4/1 + */ +package com.yomahub.liteflow.test.contextBean.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.test.contextBean.context.TestContext; +import org.springframework.stereotype.Component; + +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + TestContext context = this.getContextBean("skuContext"); + context.setSkuCode("J001"); + System.out.println("ACmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/BCmp.java new file mode 100644 index 000000000..1e8c6d1b1 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/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.contextBean.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/contextBean/context/TestContext.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/context/TestContext.java new file mode 100644 index 000000000..9f83e3b39 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/context/TestContext.java @@ -0,0 +1,32 @@ +package com.yomahub.liteflow.test.contextBean.context; + +import com.yomahub.liteflow.context.ContextBean; + +@ContextBean("skuContext") +public class TestContext { + + private String skuCode; + + private String skuName; + + public TestContext(String skuCode, String skuName) { + this.skuCode = skuCode; + this.skuName = skuName; + } + + public String getSkuCode() { + return skuCode; + } + + public void setSkuCode(String skuCode) { + this.skuCode = skuCode; + } + + public String getSkuName() { + return skuName; + } + + public void setSkuName(String skuName) { + this.skuName = skuName; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties new file mode 100644 index 000000000..5b68813b3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties @@ -0,0 +1 @@ +liteflow.rule-source=contextBean/flow.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.xml new file mode 100644 index 000000000..84d650db4 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.xml @@ -0,0 +1,7 @@ + + + + + THEN(a,b); + + \ No newline at end of file From f65d896a1e2cdd1d019c283e20998808a8e3b5e7 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 31 Jan 2024 13:59:01 +0800 Subject: [PATCH 16/20] =?UTF-8?q?enhancement=20#I905AD=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B3=A8=E8=A7=A3=E7=9A=84=E8=8E=B7=E5=8F=96=E9=80=9F?= =?UTF-8?q?=E5=BA=A6=EF=BC=8C=E4=BC=98=E5=8C=96=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yomahub/liteflow/annotation/util/AnnoUtil.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java index ab86d02ea..3074c4bb2 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java @@ -3,6 +3,7 @@ package com.yomahub.liteflow.annotation.util; import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.annotation.LFAliasFor; import java.lang.annotation.Annotation; @@ -10,15 +11,25 @@ import java.lang.reflect.AnnotatedElement; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 注解工具类 + * 此工具类带缓存 * * @author Bryan.Zhang */ public class AnnoUtil { + private static Map annoCacheMap = new ConcurrentHashMap<>(); + public static A getAnnotation(AnnotatedElement annotatedElement, Class annotationType) { + String cacheKey = StrUtil.format("{}-{}", annotatedElement, annotationType.getSimpleName()); + + if (annoCacheMap.containsKey(cacheKey)){ + return (A)annoCacheMap.get(cacheKey); + } + A annotation = AnnotationUtil.getAnnotation(annotatedElement, annotationType); if (ObjectUtil.isNull(annotation)) { return null; @@ -42,6 +53,8 @@ public class AnnoUtil { } }); + annoCacheMap.put(cacheKey, annotation); + return annotation; } @@ -53,5 +66,4 @@ public class AnnoUtil { return null; } } - } From a5ad3801dc7d9582da6e341fd4afa542d0fafe5d Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 31 Jan 2024 17:16:28 +0800 Subject: [PATCH 17/20] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yomahub/liteflow/builder/el/operator/RetryOperator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java index a6c756b17..38f8c6b6c 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java @@ -15,7 +15,7 @@ import com.yomahub.liteflow.flow.element.condition.RetryCondition; public class RetryOperator extends BaseOperator { @Override public Condition build(Object[] objects) throws Exception { - OperatorHelper.checkObjectSizeGtTwo(objects); + OperatorHelper.checkObjectSizeGteTwo(objects); Executable executable = OperatorHelper.convert(objects[0], Executable.class); Integer retryTimes = OperatorHelper.convert(objects[1], Integer.class); RetryCondition retryCondition = new RetryCondition(); From 366241a4ef37a84c6829ecf3bb0412640c9af38f Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Wed, 31 Jan 2024 22:06:02 +0800 Subject: [PATCH 18/20] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7=E4=B8=BA2.12.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3e880236f..94426b9fa 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ - 2.11.4.2 + 2.12.0 UTF-8 UTF-8 8 From a79af2ba298894c6daa10e441cedb6e6ec2ae237 Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 1 Feb 2024 19:11:22 +0800 Subject: [PATCH 19/20] =?UTF-8?q?enhancement=20#I90IRR=20=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E8=B6=85=E6=97=B6maxWaitSenconds=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E8=B6=85=E6=97=B6=E7=9A=84=E7=BB=84=E4=BB=B6=E4=BB=8D?= =?UTF-8?q?=E6=97=A7=E6=89=A7=E8=A1=8C=E4=BC=9A=E6=8A=A5=E5=87=BANPE?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../flow/element/condition/ThenCondition.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ThenCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ThenCondition.java index 5f41b9aec..7a7b99d5d 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ThenCondition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ThenCondition.java @@ -51,13 +51,17 @@ public class ThenCondition extends Condition { } catch (Exception e) { Slot slot = DataBus.getSlot(slotIndex); - String chainId = this.getCurrChainId(); - // 这里事先取到exception set到slot里,为了方便finally取到exception - if (slot.isSubChain(chainId)) { - slot.setSubException(chainId, e); - } - else { - slot.setException(e); + //正常情况下slot不可能为null + //当设置了超时后,还在运行的组件就有可能因为主流程已经结束释放slot而导致slot为null + if (slot != null){ + String chainId = this.getCurrChainId(); + // 这里事先取到exception set到slot里,为了方便finally取到exception + if (slot.isSubChain(chainId)) { + slot.setSubException(chainId, e); + } + else { + slot.setException(e); + } } throw e; } From 2d9c357d8feefa2c0a38fe1a1845377337656ffb Mon Sep 17 00:00:00 2001 From: "everywhere.z" Date: Thu, 1 Feb 2024 19:22:37 +0800 Subject: [PATCH 20/20] =?UTF-8?q?enhancement=20#I90IRR=20=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E8=B6=85=E6=97=B6maxWaitSenconds=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=EF=BC=8C=E8=B6=85=E6=97=B6=E7=9A=84=E7=BB=84=E4=BB=B6=E4=BB=8D?= =?UTF-8?q?=E6=97=A7=E6=89=A7=E8=A1=8C=E4=BC=9A=E6=8A=A5=E5=87=BANPE?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaxWaitSecondsELSpringbootTest.java | 10 ++++++++++ .../test/maxWaitSeconds/cmp/ECmp.java | 20 +++++++++++++++++++ .../test/resources/maxWaitSeconds/flow.el.xml | 4 ++++ 3 files changed, 34 insertions(+) create mode 100644 liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/cmp/ECmp.java diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java index a71ce42e4..611e39c17 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java @@ -182,6 +182,16 @@ public class MaxWaitSecondsELSpringbootTest extends BaseTest { assertNotTimeout("chain2"); } + // 测试超时情况下组件还在运行的场景是否会报错 + @Test + public void testChain3() { + DefaultContext context = new DefaultContext(); + context.setData("test", "123"); + LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg", context); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals(TimeoutException.class, response.getCause().getClass()); + } + private void assertTimeout(String chainId) { LiteflowResponse response = flowExecutor.execute2Resp(chainId, "arg"); Assertions.assertFalse(response.isSuccess()); diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/cmp/ECmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/cmp/ECmp.java new file mode 100644 index 000000000..a87f87d0f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/cmp/ECmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.maxWaitSeconds.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; + +@LiteflowComponent("e") +public class ECmp extends NodeComponent { + @Override + public void process() throws Exception{ + DefaultContext context = this.getFirstContextBean(); + for (int i = 0; i < 10; i++) { + String str = context.getData("test"); + System.out.println(str); + Thread.sleep(1000); + } + + System.out.println("ECmp executed!"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml index 91869ae50..8fd19db05 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml @@ -107,4 +107,8 @@ testChain.maxWaitSeconds(3); + + + THEN(a, b, e).maxWaitSeconds(8); + \ No newline at end of file