Quellcode durchsuchen

Merge branch 'dev' of https://gitee.com/dromara/liteFlow into issue/#I8MW6Q-2

 Conflicts:
	liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java
DaleLee vor 1 Jahr
Ursprung
Commit
d6b0e684cf
100 geänderte Dateien mit 2815 neuen und 120 gelöschten Zeilen
  1. 25 0
      README.md
  2. 24 0
      README.zh-CN.md
  3. 6 0
      liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java
  4. 13 1
      liteflow-core/src/main/java/com/yomahub/liteflow/annotation/util/AnnoUtil.java
  5. 1 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java
  6. 35 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java
  7. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java
  8. 4 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java
  9. 4 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java
  10. 21 8
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java
  11. 108 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java
  12. 11 7
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ThenCondition.java
  13. 2 1
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java
  14. 0 16
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java
  15. 17 11
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java
  16. 0 17
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java
  17. 10 19
      liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java
  18. 9 2
      liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java
  19. 104 0
      liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java
  20. 18 5
      liteflow-core/src/main/java/com/yomahub/liteflow/slot/DataBus.java
  21. 20 8
      liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java
  22. 9 2
      liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java
  23. 11 6
      liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java
  24. 8 2
      liteflow-script-plugin/liteflow-script-qlexpress/src/main/java/com/yomahub/liteflow/script/qlexpress/QLExpressScriptExecutor.java
  25. 34 12
      liteflow-spring/src/main/java/com/yomahub/liteflow/spring/DeclBeanDefinition.java
  26. 120 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclMultiSpringbootTest.java
  27. 89 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CmpConfig.java
  28. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/application.properties
  29. 51 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml
  30. 117 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java
  31. 12 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java
  32. 17 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java
  33. 17 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java
  34. 17 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java
  35. 16 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java
  36. 23 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java
  37. 18 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java
  38. 17 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java
  39. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties
  40. 51 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml
  41. 116 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/RetryTest.java
  42. 11 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java
  43. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java
  44. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java
  45. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java
  46. 15 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java
  47. 21 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java
  48. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java
  49. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java
  50. 61 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml
  51. 47 0
      liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java
  52. 55 0
      liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java
  53. 83 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java
  54. 67 0
      liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java
  55. 55 0
      liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java
  56. 41 0
      liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java
  57. 62 0
      liteflow-testcase-el/liteflow-testcase-el-script-multi-language-springboot/src/test/java/com/yomahub/liteflow/test/script/multi/language/validate/ValidateMultiLanguageScriptComponentTest.java
  58. 57 0
      liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java
  59. 35 0
      liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java
  60. 0 1
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/cmpRetry/LiteflowRetryELSpringbootTest.java
  61. 1 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/execute2Future/Executor2FutureELSpringbootTest.java
  62. 113 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java
  63. 12 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java
  64. 17 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java
  65. 17 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java
  66. 17 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java
  67. 16 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java
  68. 23 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java
  69. 18 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java
  70. 17 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java
  71. 0 1
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java
  72. 1 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties
  73. 51 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml
  74. 0 1
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/ACmp.java
  75. 5 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/base/cmp/CCmp.java
  76. 38 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/ContextBeanSpringbootTest.java
  77. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java
  78. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/BCmp.java
  79. 32 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/context/TestContext.java
  80. 10 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/MaxWaitSecondsELSpringbootTest.java
  81. 20 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/maxWaitSeconds/cmp/ECmp.java
  82. 117 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java
  83. 12 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java
  84. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java
  85. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java
  86. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java
  87. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java
  88. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java
  89. 18 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/MCmp.java
  90. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java
  91. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties
  92. 7 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.xml
  93. 4 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml
  94. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties
  95. 50 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml
  96. 114 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringTest.java
  97. 12 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java
  98. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java
  99. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java
  100. 17 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

+ 25 - 0
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.
+
+<a href="https://t.zsxq.com/16VxfATen"><img src="static/img/zsxq-github.png"></a>
+
+
 ## 🦾Sponsor
 
 **MISBoot低代码开发平台**

+ 24 - 0
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,请扫描以下二维码,或者直接点击图片也可以直达:
+
+<a href="https://t.zsxq.com/16jLy4Bj6"><img src="static/img/zsxq-gitee.png"></a>
+
 ## 🦾赞助商
 
 **MISBoot低代码开发平台**

+ 6 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/annotation/LiteflowRetry.java

@@ -12,6 +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);
+ */
 public @interface LiteflowRetry {
 
 	@LFAliasFor("retry")

+ 13 - 1
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<String, Annotation> annoCacheMap = new ConcurrentHashMap<>();
+
 	public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> 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;
 		}
 	}
-
 }

+ 1 - 0
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, Object.class, new RetryOperator());
 	}
 
 	public static LiteFlowChainELBuilder createChain() {

+ 35 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/RetryOperator.java

@@ -0,0 +1,35 @@
+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.flow.element.Condition;
+import com.yomahub.liteflow.flow.element.Executable;
+import com.yomahub.liteflow.flow.element.condition.RetryCondition;
+
+/**
+ *
+ * @author Rain
+ * @since 2.12.0
+ *
+ */
+public class RetryOperator extends BaseOperator<Condition> {
+    @Override
+    public Condition build(Object[] objects) throws Exception {
+        OperatorHelper.checkObjectSizeGteTwo(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);
+        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;
+    }
+
+}

+ 2 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java

@@ -94,4 +94,6 @@ public interface ChainConstant {
 
 	String EXTENDS = "extends";
 
+	String RETRY = "retry";
+
 }

+ 4 - 0
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> T getContextBean(String contextName) {
+		return this.getSlot().getContextBean(contextName);
+	}
+
 	public String getNodeId() {
 		return nodeId;
 	}

+ 4 - 0
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> T getContextBean(String contextName) {
+		return this.getSlot().getContextBean(contextName);
+	}
+
 	public Map<String, List<CmpStep>> getExecuteSteps() {
 		Map<String, List<CmpStep>> map = new LinkedHashMap<>();
 		this.getSlot().getExecuteSteps().forEach(cmpStep -> {

+ 21 - 8
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 TransmittableThreadLocal<Boolean> accessResult = new TransmittableThreadLocal<>();
+
 	private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
 
 	private TransmittableThreadLocal<Object> 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 (getAccessResult() || 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)的话,流程就会立马结束
@@ -178,6 +176,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 			instance.removeIsEnd();
 			instance.removeRefNode();
 			removeLoopIndex();
+			removeAccessResult();
 		}
 	}
 
@@ -253,6 +252,19 @@ public class Node implements Executable, Cloneable, Rollbackable{
 		return currChainId;
 	}
 
+	public boolean getAccessResult() {
+		Boolean result = this.accessResult.get();
+		return result == null ? false : result;
+	}
+
+	public void setAccessResult(boolean accessResult) {
+		this.accessResult.set(accessResult);
+	}
+
+	public void removeAccessResult() {
+		this.accessResult.remove();
+	}
+
 	public void setLoopIndex(int index) {
 		this.loopIndexTL.set(index);
 	}
@@ -299,6 +311,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
 		Node node = (Node)this.clone();
 		node.loopIndexTL = new TransmittableThreadLocal<>();
 		node.currLoopObject = new TransmittableThreadLocal<>();
+		node.accessResult = new TransmittableThreadLocal<>();
 		return node;
 	}
 }

+ 108 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/RetryCondition.java

@@ -0,0 +1,108 @@
+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.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;
+
+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());
+
+    private Integer retryTimes;
+
+    private Class<? extends Exception>[] retryForExceptions = new Class[] { Exception.class };
+
+    public Class<? extends Exception>[] getRetryForExceptions() {
+        return retryForExceptions;
+    }
+
+    public void setRetryForExceptions(Class<? extends Exception>[] retryForExceptions) {
+        this.retryForExceptions = retryForExceptions;
+    }
+
+    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();
+        List<Class<? extends Exception>> forExceptions = Arrays.asList(this.getRetryForExceptions());
+        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) {
+                // 判断抛出的异常是不是指定异常的子类
+                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);
+                        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";
+        }
+    }
+}

+ 11 - 7
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;
 		}

+ 2 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java

@@ -31,9 +31,10 @@ public class AllOfParallelExecutor extends ParallelStrategyExecutor {
 
     }
 
-    //在allOf这个场景中,不需要过滤
+    //  allOf 这个场景中,不需要过滤
     @Override
     protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
         return stream;
     }
+
 }

+ 0 - 16
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<Executable> filterAccess(Stream<Executable> 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;
-            }
-        });
-    }
 }

+ 17 - 11
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;
@@ -93,20 +94,25 @@ public abstract class ParallelStrategyExecutor {
         // 1.先进行过滤,前置和后置组件过滤掉,因为在 EL Chain 处理的时候已经提出来了
         // 2.过滤 isAccess 为 false 的情况,因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了
         Stream<Executable> 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);
     }
 
-    //过滤isAccess的抽象接口方法
-    protected abstract Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex);
+    // 过滤 isAccess 的方法,默认实现,同时为避免同一个 node 的 isAccess 方法重复执行,给 node 设置 isAccess 方法执行结果
+    protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
+        return stream.filter(executable -> {
+            try {
+                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;
+            }
+        });
+    }
 
     /**
      * 获取 WHEN 所需线程池

+ 0 - 17
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<Executable> filterAccess(Stream<Executable> 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;
-            }
-        });
-    }
-
 }

+ 10 - 19
liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java

@@ -7,11 +7,10 @@ import com.yomahub.liteflow.annotation.util.AnnoUtil;
 import com.yomahub.liteflow.context.ContextBean;
 import com.yomahub.liteflow.enums.ScriptTypeEnum;
 import com.yomahub.liteflow.exception.LiteFlowException;
-import com.yomahub.liteflow.flow.FlowBus;
 import com.yomahub.liteflow.slot.DataBus;
 import com.yomahub.liteflow.slot.Slot;
 
-import java.util.List;
+import javax.script.ScriptException;
 import java.util.Map;
 import java.util.function.BiConsumer;
 
@@ -29,12 +28,6 @@ public abstract class ScriptExecutor {
 
 	public abstract void load(String nodeId, String script);
 
-	// 卸载脚本(不包含 node)
-	public abstract void unLoad(String nodeId);
-
-	// 获取该执行器下的所有 nodeId
-	public abstract List<String> getNodeIds();
-
 	public Object execute(ScriptExecuteWrap wrap) throws Exception{
 		try {
 			return executeScript(wrap);
@@ -62,17 +55,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<String, Object> metaMap = BeanUtil.beanToMap(wrap);
@@ -94,4 +77,12 @@ public abstract class ScriptExecutor {
 		ScriptBeanManager.getScriptBeanMap().forEach(putIfAbsentConsumer);
 	}
 
+	/**
+	 * 利用相应框架编译脚本
+	 *
+	 * @param script 脚本
+	 * @return boolean
+	 * @throws Exception 例外
+	 */
+	public abstract Object compile(String script) throws Exception;
 }

+ 9 - 2
liteflow-core/src/main/java/com/yomahub/liteflow/script/jsr223/JSR223ScriptExecutor.java

@@ -46,8 +46,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());
@@ -85,4 +84,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));
+	}
+
 }

+ 104 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/script/validator/ScriptValidator.java

@@ -0,0 +1,104 @@
+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<ScriptTypeEnum, ScriptExecutor> scriptExecutors;
+
+    static {
+        List<ScriptExecutor> 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     脚本
+     * @param scriptType 脚本类型
+     * @return boolean
+     */
+    private static boolean validateScript(String script, ScriptTypeEnum scriptType){
+        // 未加载任何脚本模块
+        if(scriptExecutors.isEmpty()){
+            LOG.error("The loaded script modules not found.");
+            return false;
+        }
+
+        // 指定脚本语言未加载
+        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 = (scriptType != null) ? scriptExecutors.get(scriptType) : scriptExecutors.values().iterator().next();
+        try {
+            scriptExecutor.compile(script);
+        } catch (Exception e) {
+            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<ScriptTypeEnum, String> scripts){
+        for(Map.Entry<ScriptTypeEnum, String> script : scripts.entrySet()){
+            if(!validate(script.getValue(), script.getKey())){
+                return false;
+            }
+        }
+        return true;
+    }
+}

+ 18 - 5
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<Class<?>, Object>) ReflectUtil::newInstanceIfPossible)
 			.collect(Collectors.toList());
 
-		Slot slot = new Slot(contextBeanList);
-
-		return offerIndex(slot);
+		return offerSlotByBean(contextBeanList);
 	}
 
 	public static int offerSlotByBean(List<Object> contextList) {
-		Slot slot = new Slot(contextList);
+		List<Tuple> 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<Object> getContextBeanList(int slotIndex) {
+	public static List<Tuple> getContextBeanList(int slotIndex) {
 		Slot slot = getSlot(slotIndex);
 		return slot.getContextBeanList();
 	}

+ 20 - 8
liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java

@@ -9,8 +9,10 @@ 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;
 import com.yomahub.liteflow.exception.NoSuchContextBeanException;
 import com.yomahub.liteflow.exception.NullParamException;
 import com.yomahub.liteflow.flow.element.Condition;
@@ -29,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的抽象类实现
@@ -90,14 +93,14 @@ public class Slot {
 
 	protected ConcurrentHashMap<String, Object> metaDataMap = new ConcurrentHashMap<>();
 
-	private List<Object> contextBeanList;
+	private List<Tuple> contextBeanList;
 	
-	private static final ThreadLocal<Deque<Condition>> conditionStack = ThreadLocal.withInitial(LinkedList::new);
+	private static final TransmittableThreadLocal<Deque<Condition>> conditionStack = TransmittableThreadLocal.withInitial(ConcurrentLinkedDeque::new);
 
 	public Slot() {
 	}
 
-	public Slot(List<Object> contextBeanList) {
+	public Slot(List<Tuple> contextBeanList) {
 		this.contextBeanList = contextBeanList;
 	}
 
@@ -448,21 +451,30 @@ public class Slot {
 		metaDataMap.remove(SUB_EXCEPTION_PREFIX + chainId);
 	}
 
-	public List<Object> getContextBeanList() {
+	public List<Tuple> getContextBeanList() {
 		return this.contextBeanList;
 	}
 
 	public <T> T getContextBean(Class<T> 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> 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> T getFirstContextBean() {
-		Class<T> firstContextBeanClazz = (Class<T>) this.getContextBeanList().get(0).getClass();
+		Class<T> firstContextBeanClazz = (Class<T>) this.getContextBeanList().get(0).get(1).getClass();
 		return this.getContextBean(firstContextBeanClazz);
 	}
 

+ 9 - 2
liteflow-script-plugin/liteflow-script-graaljs/src/main/java/com/yomahub/liteflow/script/graaljs/GraalJavaScriptExecutor.java

@@ -38,8 +38,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());
@@ -99,4 +98,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;
+	}
+
 }

+ 11 - 6
liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java

@@ -23,12 +23,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);
@@ -66,6 +61,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")

+ 8 - 2
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.Collections;
 import java.util.List;
@@ -41,8 +43,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());
@@ -96,4 +97,9 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
 		return ScriptTypeEnum.QLEXPRESS;
 	}
 
+	@Override
+	public Object compile(String script) throws Exception {
+		return expressRunner.getInstructionSetFromLocalCache(script);
+	}
+
 }

+ 34 - 12
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<String, BeanDefinition> beanDefinitionHolderMap = (Map<String, BeanDefinition>)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<DeclWarpBean> 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;
+        }
+    }
 }

+ 120 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclMultiSpringbootTest.java

@@ -0,0 +1,120 @@
+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 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:/retry/application.properties")
+@SpringBootTest(classes = RetryELDeclMultiSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
+public class RetryELDeclMultiSpringbootTest 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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 89 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CmpConfig.java

@@ -0,0 +1,89 @@
+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.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import com.yomahub.liteflow.exception.ELParseException;
+
+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 flagm = 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<String> list = ListUtil.toList("jack");
+            return list.iterator();
+        }
+    }
+
+    @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 ++;
+        System.out.println("NCmp executed!");
+        if(flagn < 4) throw new RuntimeException();
+        else return flagn == 4 ? true : false;
+    }
+
+
+}

+ 1 - 0
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

+ 51 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/retry/flow.el.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain2">
+        WHEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain3">
+        THEN( a, b.retry(3) );
+    </chain>
+
+    <chain name="chain4">
+        FOR(c).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain5">
+        SWITCH(d).TO(a).retry(3);
+    </chain>
+
+    <chain name="chain6">
+        IF(f, a).retry(3);
+    </chain>
+
+    <chain name="chain7">
+        WHILE(n).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain8">
+        ITERATOR(i).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain9">
+        THEN( a, b ).retry(1);
+    </chain>
+
+    <chain name="chain10">
+        THEN( a, FINALLY(b, a).retry(3) );
+    </chain>
+
+    <chain name="chain11">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
+    </chain>
+
+    <chain name="chain12">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
+    </chain>
+
+</flow>

+ 117 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetryELDeclSpringbootTest.java

@@ -0,0 +1,117 @@
+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 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:/retry/application.properties")
+@SpringBootTest(classes = RetryELDeclSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
+public class RetryELDeclSpringbootTest 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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.retry.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!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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";
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.retry.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<String> list = ListUtil.toList("jack");
+            return list.iterator();
+        }
+    }
+}

+ 18 - 0
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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/application.properties

@@ -0,0 +1 @@
+liteflow.rule-source=retry/flow.el.xml

+ 51 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/retry/flow.el.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain2">
+        WHEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain3">
+        THEN( a, b.retry(3) );
+    </chain>
+
+    <chain name="chain4">
+        FOR(c).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain5">
+        SWITCH(d).TO(a).retry(3);
+    </chain>
+
+    <chain name="chain6">
+        IF(f, a).retry(3);
+    </chain>
+
+    <chain name="chain7">
+        WHILE(n).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain8">
+        ITERATOR(i).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain9">
+        THEN( a, b ).retry(1);
+    </chain>
+
+    <chain name="chain10">
+        THEN( a, FINALLY(b, a).retry(3) );
+    </chain>
+
+    <chain name="chain11">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
+    </chain>
+
+    <chain name="chain12">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
+    </chain>
+
+</flow>

+ 116 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/RetryTest.java

@@ -0,0 +1,116 @@
+package com.yomahub.liteflow.test.retry;
+
+
+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 RetryTest extends BaseTest {
+
+	private static FlowExecutor flowExecutor;
+
+	@BeforeAll
+	public static void init() {
+		LiteflowConfig config = new LiteflowConfig();
+		config.setRuleSource("retry/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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java

@@ -0,0 +1,11 @@
+package com.yomahub.liteflow.test.retry.cmp;
+
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class ACmp extends NodeComponent {
+    @Override
+    public void process() {
+        System.out.println("ACmp executed!");
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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";
+    }
+}

+ 15 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java

@@ -0,0 +1,15 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java

@@ -0,0 +1,21 @@
+package com.yomahub.liteflow.test.retry.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<String> list = ListUtil.toList("jack");
+            return list.iterator();
+        }
+    }
+}

+ 16 - 0
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;
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 61 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/retry/flow.el.xml

@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <nodes>
+        <node id="a" class="com.yomahub.liteflow.test.retry.cmp.ACmp"/>
+        <node id="b" class="com.yomahub.liteflow.test.retry.cmp.BCmp"/>
+        <node id="c" class="com.yomahub.liteflow.test.retry.cmp.CCmp"/>
+        <node id="d" class="com.yomahub.liteflow.test.retry.cmp.DCmp"/>
+        <node id="f" class="com.yomahub.liteflow.test.retry.cmp.FCmp"/>
+        <node id="i" class="com.yomahub.liteflow.test.retry.cmp.ICmp"/>
+        <node id="n" class="com.yomahub.liteflow.test.retry.cmp.NCmp"/>
+        <node id="m" class="com.yomahub.liteflow.test.retry.cmp.MCmp"/>
+    </nodes>
+    <chain name="chain1">
+        THEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain2">
+        WHEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain3">
+        THEN( a, b.retry(3) );
+    </chain>
+
+    <chain name="chain4">
+        FOR(c).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain5">
+        SWITCH(d).TO(a).retry(3);
+    </chain>
+
+    <chain name="chain6">
+        IF(f, a).retry(3);
+    </chain>
+
+    <chain name="chain7">
+        WHILE(n).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain8">
+        ITERATOR(i).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain9">
+        THEN( a, b ).retry(1);
+    </chain>
+
+    <chain name="chain10">
+        THEN( a, FINALLY(b, a).retry(3) );
+    </chain>
+
+    <chain name="chain11">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
+    </chain>
+
+    <chain name="chain12">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
+    </chain>
+
+</flow>

+ 47 - 0
liteflow-testcase-el/liteflow-testcase-el-script-aviator-springboot/src/test/java/com/yomahub/liteflow/test/script/aviator/validate/ValidateAviatorScriptComponentTest.java

@@ -0,0 +1,47 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
+
+
+    }
+}

+ 55 - 0
liteflow-testcase-el/liteflow-testcase-el-script-graaljs-springboot/src/test/java/com/yomahub/liteflow/test/script/graaljs/validate/ValidateGraaljsScriptComponentTest.java

@@ -0,0 +1,55 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR));
+    }
+}

+ 83 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/validate/ValidateGroovyScriptComponentTest.java

@@ -0,0 +1,83 @@
+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;
+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<String> list = ListUtil.toList(\"a\", \"b\", \"c\")\n" +
+                "\n" +
+                "            List<String> 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<String> list = ListUtil.toList(\"a\", \"b\", \"c\")\n" +
+                "\n" +
+                "            List<String> 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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
+    }
+}

+ 67 - 0
liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/java/com/yomahub/liteflow/test/script/java/validate/ValidateJavaScriptComponentTest.java

@@ -0,0 +1,67 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY));
+    }
+}

+ 55 - 0
liteflow-testcase-el/liteflow-testcase-el-script-javascript-springboot/src/test/java/com/yomahub/liteflow/test/script/javascript/validate/ValidateJavaScriptScriptComponentTest.java

@@ -0,0 +1,55 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA));
+    }
+}

+ 41 - 0
liteflow-testcase-el/liteflow-testcase-el-script-lua-springboot/src/test/java/com/yomahub/liteflow/test/script/lua/validate/ValidateLuaScriptComponentTest.java

@@ -0,0 +1,41 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
+    }
+}

+ 62 - 0
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<ScriptTypeEnum, String> correctData = new HashMap<>();
+        correctData.put(ScriptTypeEnum.GROOVY, correctGroovyScript);
+        correctData.put(ScriptTypeEnum.JS, correctJavascriptScript);
+        correctData.put(ScriptTypeEnum.PYTHON, correctPythonScript);
+        Assertions.assertTrue(ScriptValidator.validate(correctData));
+
+        // 多语言脚本验证 错误样例
+        Map<ScriptTypeEnum, String> wrongData = new HashMap<>();
+        wrongData.put(ScriptTypeEnum.GROOVY, correctGroovyScript);
+        wrongData.put(ScriptTypeEnum.JS, correctJavascriptScript);
+        wrongData.put(ScriptTypeEnum.PYTHON, wrongPythonScript);
+        Assertions.assertFalse(ScriptValidator.validate(wrongData));
+    }
+}

+ 57 - 0
liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/java/com/yomahub/liteflow/test/script/python/validate/ValidatePythonScriptComponentTest.java

@@ -0,0 +1,57 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA));
+    }
+}

+ 35 - 0
liteflow-testcase-el/liteflow-testcase-el-script-qlexpress-springboot/src/test/java/com/yomahub/liteflow/test/script/qlexpress/validate/ValidateQLExpressScriptComponentTest.java

@@ -0,0 +1,35 @@
+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;
+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));
+
+        Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.QLEXPRESS));
+        Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
+    }
+}

+ 0 - 1
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/cmpRetry/LiteflowRetryELSpringbootTest.java

@@ -6,7 +6,6 @@ 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.snack.ONode;
 import org.noear.solon.annotation.Import;
 import org.noear.solon.annotation.Inject;
 import org.noear.solon.test.SolonJUnit5Extension;

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/execute2Future/Executor2FutureELSpringbootTest.java

@@ -10,6 +10,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.noear.solon.annotation.Import;
 import org.noear.solon.annotation.Inject;
 import org.noear.solon.test.SolonJUnit5Extension;
+
 import java.util.concurrent.Future;
 
 /**

+ 113 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java

@@ -0,0 +1,113 @@
+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 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:/retry/application.properties")
+public class RetrySpringbootTest 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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.retry.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!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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";
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.retry.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<String> list = ListUtil.toList("jack");
+            return list.iterator();
+        }
+    }
+}

+ 18 - 0
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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 0 - 1
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java

@@ -10,7 +10,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
 import org.noear.solon.annotation.Import;
 import org.noear.solon.annotation.Inject;
 import org.noear.solon.test.SolonJUnit5Extension;
-import org.noear.solon.test.annotation.TestPropertySource;
 
 
 @ExtendWith(SolonJUnit5Extension.class)

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/application.properties

@@ -0,0 +1 @@
+liteflow.rule-source=retry/flow.el.xml

+ 51 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/retry/flow.el.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain2">
+        WHEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain3">
+        THEN( a, b.retry(3) );
+    </chain>
+
+    <chain name="chain4">
+        FOR(c).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain5">
+        SWITCH(d).TO(a).retry(3);
+    </chain>
+
+    <chain name="chain6">
+        IF(f, a).retry(3);
+    </chain>
+
+    <chain name="chain7">
+        WHILE(n).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain8">
+        ITERATOR(i).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain9">
+        THEN( a, b ).retry(1);
+    </chain>
+
+    <chain name="chain10">
+        THEN( a, FINALLY(b, a).retry(3) );
+    </chain>
+
+    <chain name="chain11">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
+    </chain>
+
+    <chain name="chain12">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
+    </chain>
+
+</flow>

+ 0 - 1
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!");
 	}
-
 }

+ 5 - 0
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;
+	}
 }

+ 38 - 0
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());
+	}
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/ACmp.java

@@ -0,0 +1,23 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @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!");
+	}
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/contextBean/cmp/BCmp.java

@@ -0,0 +1,21 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @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!");
+	}
+
+}

+ 32 - 0
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;
+    }
+}

+ 10 - 0
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());

+ 20 - 0
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!");
+    }
+}

+ 117 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringbootTest.java

@@ -0,0 +1,117 @@
+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 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:/retry/application.properties")
+@SpringBootTest(classes = RetrySpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
+public class RetrySpringbootTest 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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.retry.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!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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";
+    }
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/FCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/ICmp.java

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.retry.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<String> list = ListUtil.toList("jack");
+            return list.iterator();
+        }
+    }
+}

+ 18 - 0
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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/retry/cmp/NCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/application.properties

@@ -0,0 +1 @@
+liteflow.rule-source=contextBean/flow.xml

+ 7 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/contextBean/flow.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
+<flow>
+    <chain name="chain1">
+        THEN(a,b);
+    </chain>
+</flow>

+ 4 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/maxWaitSeconds/flow.el.xml

@@ -107,4 +107,8 @@
         <!-- 不超时 -->
         testChain.maxWaitSeconds(3);
     </chain>
+
+    <chain name="chain3">
+        THEN(a, b, e).maxWaitSeconds(8);
+    </chain>
 </flow>

+ 1 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/application.properties

@@ -0,0 +1 @@
+liteflow.rule-source=retry/flow.el.xml

+ 50 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/retry/flow.el.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain2">
+        WHEN( a, b ).retry(3);
+    </chain>
+
+    <chain name="chain3">
+        THEN( a, b.retry(3) );
+    </chain>
+
+    <chain name="chain4">
+        FOR(c).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain5">
+        SWITCH(d).TO(a).retry(3);
+    </chain>
+
+    <chain name="chain6">
+        IF(f, a).retry(3);
+    </chain>
+
+    <chain name="chain7">
+        WHILE(n).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain8">
+        ITERATOR(i).DO(a).retry(3);
+    </chain>
+
+    <chain name="chain9">
+        THEN( a, b ).retry(1);
+    </chain>
+
+    <chain name="chain10">
+        THEN( a, FINALLY(b, a).retry(3) );
+    </chain>
+
+    <chain name="chain11">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
+    </chain>
+
+    <chain name="chain12">
+        THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
+    </chain>
+</flow>

+ 114 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/RetrySpringTest.java

@@ -0,0 +1,114 @@
+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 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:/retry/application.xml")
+public class RetrySpringTest 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());
+	}
+
+	// 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());
+	}
+
+	// 指定异常重试测试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());
+	}
+
+}

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/ACmp.java

@@ -0,0 +1,12 @@
+package com.yomahub.liteflow.test.retry.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!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/BCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/CCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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;
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/retry/cmp/DCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.retry.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";
+    }
+}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.