Browse Source

Merge branch 'dev' of https://gitee.com/noear_admin/liteFlow into dev

noear 2 years ago
parent
commit
c3b4572ba2
65 changed files with 1151 additions and 128 deletions
  1. 15 8
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java
  2. 74 11
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java
  3. 1 1
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IgnoreErrorOperator.java
  4. 26 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java
  5. 2 2
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java
  6. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java
  7. 15 9
      liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java
  8. 10 6
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java
  9. 24 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeIteratorComponent.java
  10. 9 18
      liteflow-core/src/main/java/com/yomahub/liteflow/core/proxy/ComponentProxy.java
  11. 3 1
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java
  12. 3 0
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/LiteFlowMethodEnum.java
  13. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java
  14. 4 4
      liteflow-core/src/main/java/com/yomahub/liteflow/exception/DataNotFoundException.java
  15. 29 0
      liteflow-core/src/main/java/com/yomahub/liteflow/exception/NoIteratorNodeException.java
  16. 1 1
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java
  17. 14 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java
  18. 1 4
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ForCondition.java
  19. 78 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/IteratorCondition.java
  20. 14 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java
  21. 19 19
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/SwitchCondition.java
  22. 8 8
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java
  23. 4 6
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhileCondition.java
  24. 31 10
      liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java
  25. 6 2
      liteflow-core/src/main/resources/dtd/liteflow.dtd
  26. 2 2
      liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/NodeComponentOfMethod.java
  27. 0 5
      liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java
  28. 2 2
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/customMethodName/cmp/CmpConfig.java
  29. 57 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclMultiSpringbootTest.java
  30. 42 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/CmpConfig.java
  31. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/iterator/application.properties
  32. 11 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/iterator/flow.xml
  33. 2 2
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/customMethodName/cmp/ACmp.java
  34. 57 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclSpringbootTest.java
  35. 34 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  36. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  37. 22 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  38. 1 1
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/subflow/SubflowInDifferentConfigELDeclSpringbootTest.java
  39. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/iterator/application.properties
  40. 11 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/iterator/flow.xml
  41. 48 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/IteratorTest.java
  42. 27 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  43. 12 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  44. 14 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  45. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/iterator/flow.xml
  46. 39 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/common/LiteFlowXmlScriptBuilderGroovyELTest.java
  47. 14 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java
  48. 11 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml
  49. 9 1
      liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/resources/common/flow.xml
  50. 55 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java
  51. 29 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  52. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  53. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  54. 8 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/switchcase/SwitchELSpringbootTest.java
  55. 20 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/switchcase/cmp/JSwitchCmp.java
  56. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/application.properties
  57. 11 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml
  58. 6 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/switchcase/flow.el.xml
  59. 45 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringTest.java
  60. 29 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  61. 14 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  62. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  63. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/application.xml
  64. 11 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/flow.xml
  65. 3 5
      pom.xml

+ 15 - 8
liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java

@@ -1,23 +1,19 @@
 package com.yomahub.liteflow.builder;
 
 import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.util.StrUtil;
 import com.yomahub.liteflow.enums.NodeTypeEnum;
 import com.yomahub.liteflow.exception.NodeBuildException;
 import com.yomahub.liteflow.flow.FlowBus;
 import com.yomahub.liteflow.flow.element.Node;
+import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.Objects;
-import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
 
 public class LiteFlowNodeBuilder {
 
@@ -53,6 +49,10 @@ public class LiteFlowNodeBuilder {
         return new LiteFlowNodeBuilder(NodeTypeEnum.BREAK);
     }
 
+    public static LiteFlowNodeBuilder createIteratorNode() {
+        return new LiteFlowNodeBuilder(NodeTypeEnum.ITERATOR);
+    }
+
     public static LiteFlowNodeBuilder createScriptNode() {
         return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
     }
@@ -130,8 +130,15 @@ public class LiteFlowNodeBuilder {
         if (StrUtil.isBlank(filePath)) {
             return this;
         }
-        String script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", filePath.trim()));
-        return setScript(script);
+        try {
+            List<String> scriptList = PathContentParserHolder.loadContextAware().parseContent(ListUtil.toList(filePath));
+            String script = CollUtil.getFirst(scriptList);
+            setScript(script);
+        } catch (Exception e) {
+            String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(), e.getMessage());
+            throw new NodeBuildException(errMsg);
+        }
+        return this;
     }
 
     public void build() {

+ 74 - 11
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java

@@ -1,18 +1,22 @@
 package com.yomahub.liteflow.builder.el;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.CharUtil;
 import cn.hutool.core.util.StrUtil;
 import com.ql.util.express.DefaultContext;
 import com.ql.util.express.ExpressRunner;
+import com.ql.util.express.InstructionSet;
 import com.ql.util.express.exception.QLException;
 import com.yomahub.liteflow.builder.el.operator.*;
 import com.yomahub.liteflow.common.ChainConstant;
-import com.yomahub.liteflow.exception.DataNofFoundException;
+import com.yomahub.liteflow.exception.DataNotFoundException;
 import com.yomahub.liteflow.exception.ELParseException;
 import com.yomahub.liteflow.exception.FlowSystemException;
 import com.yomahub.liteflow.flow.FlowBus;
 import com.yomahub.liteflow.flow.element.Chain;
-import com.yomahub.liteflow.flow.element.condition.*;
+import com.yomahub.liteflow.flow.element.Node;
+import com.yomahub.liteflow.flow.element.condition.Condition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -69,6 +73,7 @@ public class LiteFlowChainELBuilder {
         EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
         EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
         EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
+        EXPRESS_RUNNER.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
         EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DO, Object.class, new DoOperator());
         EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BREAK, Object.class, new BreakOperator());
         EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DATA, Object.class, new DataOperator());
@@ -151,8 +156,10 @@ public class LiteFlowChainELBuilder {
             return this;
         } catch (QLException e) {
             // EL 底层会包装异常,这里是曲线处理
-            if (Objects.equals(e.getCause().getMessage(), DataNofFoundException.MSG)) {
-                throw new ELParseException(String.format("[node/chain is not exist or node/chain not register]elStr=%s", elStr));
+            if (Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
+                // 构建错误信息
+                String msg = buildDataNotFoundExceptionMsg(elStr);
+                throw new ELParseException(msg);
             }
             throw new ELParseException(e.getCause().getMessage());
         } catch (Exception e) {
@@ -162,17 +169,18 @@ public class LiteFlowChainELBuilder {
 
     /**
      * EL表达式校验
+     *
      * @param elStr EL表达式
      * @return true 校验成功 false 校验失败
      */
     public static boolean validate(String elStr) {
-       try {
-           LiteFlowChainELBuilder.createChain().setEL(elStr);
-           return Boolean.TRUE;
-       } catch (ELParseException e) {
-           LOG.error(e.getMessage());
-       }
-       return Boolean.FALSE;
+        try {
+            LiteFlowChainELBuilder.createChain().setEL(elStr);
+            return Boolean.TRUE;
+        } catch (ELParseException e) {
+            LOG.error(e.getMessage());
+        }
+        return Boolean.FALSE;
     }
 
     public void build() {
@@ -183,6 +191,8 @@ public class LiteFlowChainELBuilder {
         FlowBus.addChain(this.chain);
     }
 
+    //#region private method
+
     /**
      * build 前简单校验
      */
@@ -195,4 +205,57 @@ public class LiteFlowChainELBuilder {
             throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
         }
     }
+
+    /**
+     * 解析 EL 表达式,查找未定义的 id 并构建错误信息
+     *
+     * @param elStr el 表达式
+     */
+    private String buildDataNotFoundExceptionMsg(String elStr) {
+        String msg = String.format("[node/chain is not exist or node/chain not register]\n EL: %s", StrUtil.trim(elStr));
+        try {
+            InstructionSet parseResult = EXPRESS_RUNNER.getInstructionSetFromLocalCache(elStr);
+            if (parseResult == null) {
+                return msg;
+            }
+
+            String[] outAttrNames = parseResult.getOutAttrNames();
+            if (ArrayUtil.isEmpty(outAttrNames)) {
+                return msg;
+            }
+
+            List<String> chainIds = CollUtil.map(FlowBus.getChainMap().values(), Chain::getChainId, true);
+            List<String> nodeIds = CollUtil.map(FlowBus.getNodeMap().values(), Node::getId, true);
+            for (String attrName : outAttrNames) {
+                if (!chainIds.contains(attrName) && !nodeIds.contains(attrName)) {
+                    msg = String.format("[%s] is not exist or [%s] is not registered, you need to define a node or chain with id [%s] and register it \n EL: ", attrName, attrName, attrName);
+
+                    // 去除 EL 表达式中的空格和换行符
+                    String sourceEl = StrUtil.removeAll(elStr, CharUtil.SPACE, CharUtil.LF, CharUtil.CR);
+                    // 这里需要注意的是,nodeId 和 chainId 可能是关键字的一部分,如果直接 indexOf(attrName) 会出现误判
+                    // 所以需要判断 attrName 前后是否有 ","
+                    int commaRightIndex = sourceEl.indexOf(attrName + StrUtil.COMMA);
+                    if (commaRightIndex != -1) {
+                        // 需要加上 "EL: " 的长度 4,再加上 "^" 的长度 1,indexOf 从 0 开始,所以还需要加 1
+                        return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaRightIndex + 6, true);
+                    }
+                    int commaLeftIndex = sourceEl.indexOf(StrUtil.COMMA + attrName);
+                    if (commaLeftIndex != -1) {
+                        // 需要加上 "EL: " 的长度 4,再加上 "^" 的长度 1,再加上 "," 的长度 1,indexOf 从 0 开始,所以还需要加 1
+                        return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 7, true);
+                    }
+                    // 还有一种特殊情况,就是 EL 表达式中的节点使用 node("a")
+                    int nodeIndex = sourceEl.indexOf(String.format("node(\"%s\")", attrName));
+                    if (nodeIndex != -1) {
+                        // 需要加上 "EL: " 的长度 4,再加上 “node("” 长度 6,再加上 "^" 的长度 1,indexOf 从 0 开始,所以还需要加 1
+                        return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 12, true);
+                    }
+                }
+            }
+        } catch (Exception ex) {
+            // ignore
+        }
+        return msg;
+    }
+    //#endregion
 }

+ 1 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IgnoreErrorOperator.java

@@ -20,7 +20,7 @@ public class IgnoreErrorOperator extends BaseOperator<WhenCondition> {
 		WhenCondition condition = OperatorHelper.convert(objects[0], WhenCondition.class);
 
 		Boolean ignoreError = OperatorHelper.convert(objects[1], Boolean.class);
-		condition.setErrorResume(ignoreError);
+		condition.setIgnoreError(ignoreError);
 
 		return condition;
 	}

+ 26 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java

@@ -0,0 +1,26 @@
+package com.yomahub.liteflow.builder.el.operator;
+
+import cn.hutool.core.collection.ListUtil;
+import com.ql.util.express.exception.QLException;
+import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
+import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import com.yomahub.liteflow.flow.element.Node;
+import com.yomahub.liteflow.flow.element.condition.IteratorCondition;
+
+public class IteratorOperator extends BaseOperator<IteratorCondition> {
+    @Override
+    public IteratorCondition build(Object[] objects) throws Exception {
+        OperatorHelper.checkObjectSizeEq(objects, 1);
+
+        Node node = OperatorHelper.convert(objects[0], Node.class);
+        if (!ListUtil.toList(NodeTypeEnum.ITERATOR).contains(node.getType())) {
+            throw new QLException("The parameter must be iterator-node item");
+        }
+
+        IteratorCondition iteratorCondition = new IteratorCondition();
+        iteratorCondition.setIteratorNode(node);
+
+        return iteratorCondition;
+    }
+}

+ 2 - 2
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java

@@ -2,7 +2,7 @@ package com.yomahub.liteflow.builder.el.operator.base;
 
 import cn.hutool.core.util.StrUtil;
 import com.ql.util.express.exception.QLException;
-import com.yomahub.liteflow.exception.DataNofFoundException;
+import com.yomahub.liteflow.exception.DataNotFoundException;
 import com.yomahub.liteflow.flow.element.Node;
 
 import java.util.Objects;
@@ -156,7 +156,7 @@ public class OperatorHelper {
     public static void checkNodeAndChainExist(Object[] objects) throws QLException {
         for (Object object : objects) {
             if (Objects.isNull(object)) {
-                throw new QLException(DataNofFoundException.MSG);
+                throw new QLException(DataNotFoundException.MSG);
             }
         }
     }

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

@@ -63,6 +63,8 @@ public interface ChainConstant {
 
     String DATA = "data";
 
+    String ITERATOR = "ITERATOR";
+
     String MONITOR_BUS = "monitorBus";
 
     String CURR_CHAIN_ID = "currChainId";

+ 15 - 9
liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java

@@ -9,6 +9,7 @@
 package com.yomahub.liteflow.core;
 
 import cn.hutool.core.collection.ListUtil;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.*;
 import com.yomahub.liteflow.enums.InnerChainTypeEnum;
 import com.yomahub.liteflow.exception.*;
@@ -82,14 +83,16 @@ public class FlowExecutor {
         //进行id生成器的初始化
         IdGeneratorHolder.init();
 
-        if (StrUtil.isBlank(liteflowConfig.getRuleSource())) {
+        String ruleSource = liteflowConfig.getRuleSource();
+        if (StrUtil.isBlank(ruleSource)) {
             //查看有没有Parser的SPI实现
             //所有的Parser的SPI实现都是以custom形式放入的,且只支持xml形式
             ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
             Iterator<ParserClassNameSpi> it = loader.iterator();
             if (it.hasNext()){
                 ParserClassNameSpi parserClassNameSpi = it.next();
-                liteflowConfig.setRuleSource("el_xml:" + parserClassNameSpi.getSpiClassName());
+                ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
+                liteflowConfig.setRuleSource(ruleSource);
             }else{
                 //ruleSource为空,而且没有spi形式的扩展,那么说明真的没有ruleSource
                 //这种情况有可能是基于代码动态构建的
@@ -100,10 +103,11 @@ public class FlowExecutor {
         //如果有前缀的,则不需要再进行分割了,说明是一个整体
         //如果没有前缀,说明是本地文件,可能配置多个,所以需要分割
         List<String> sourceRulePathList;
-        if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, liteflowConfig.getRuleSource())){
-            sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource());
-        }else{
-            sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource().split(",|;"));
+        if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, ruleSource)) {
+            sourceRulePathList = ListUtil.toList(ruleSource);
+        } else {
+            String afterHandleRuleSource = ruleSource.replace(StrUtil.SPACE, StrUtil.EMPTY);
+            sourceRulePathList = ListUtil.toList(afterHandleRuleSource.split(",|;"));
         }
 
         FlowParser parser = null;
@@ -163,9 +167,11 @@ public class FlowExecutor {
         }
 
         //如果是ruleSource方式的,最后判断下有没有解析出来,如果没有解析出来则报错
-        if (FlowBus.getChainMap().isEmpty()){
-            String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
-            throw new ConfigErrorException(errMsg);
+        if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())){
+            if (FlowBus.getChainMap().isEmpty()){
+                String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
+                throw new ConfigErrorException(errMsg);
+            }
         }
 
         //执行钩子

+ 10 - 6
liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java

@@ -92,7 +92,7 @@ public abstract class NodeComponent{
 
 		try{
 			//前置处理
-			self.beforeProcess(this.getNodeId(), slot);
+			self.beforeProcess();
 
 			//主要的处理逻辑
 			self.process();
@@ -118,7 +118,7 @@ public abstract class NodeComponent{
 			throw e;
 		} finally {
 			//后置处理
-			self.afterProcess(this.getNodeId(), slot);
+			self.afterProcess();
 
 			stopWatch.stop();
 			final long timeSpent = stopWatch.getTotalTimeMillis();
@@ -135,10 +135,10 @@ public abstract class NodeComponent{
 		}
 	}
 
-	public <T> void beforeProcess(String nodeId, Slot slot){
+	public void beforeProcess(){
 		//全局切面只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
 		//非spring环境下,全局切面为空实现
-		CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(nodeId, slot);
+		CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(nodeId, this.getSlot());
 	}
 
 	public abstract void process() throws Exception;
@@ -151,8 +151,8 @@ public abstract class NodeComponent{
 		//如果需要在抛错后回调某一段逻辑,请覆盖这个方法
 	}
 
-	public <T> void afterProcess(String nodeId, Slot slot){
-		CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(nodeId, slot);
+	public void afterProcess(){
+		CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(nodeId, this.getSlot());
 	}
 
 	//是否进入该节点
@@ -349,6 +349,10 @@ public abstract class NodeComponent{
 		return this.refNodeTL.get().getLoopIndex();
 	}
 
+	public <T> T getCurrLoopObj(){
+		return this.refNodeTL.get().getCurrLoopObject();
+	}
+
 	@Deprecated
 	public void invoke(String chainId, Object param) throws Exception {
 		FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());

+ 24 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeIteratorComponent.java

@@ -0,0 +1,24 @@
+package com.yomahub.liteflow.core;
+
+import com.yomahub.liteflow.slot.Slot;
+import com.yomahub.liteflow.util.LiteFlowProxyUtil;
+
+import java.util.Iterator;
+
+/**
+ * ITERATOR迭代器循环组件抽象类
+ * @author Bryan.Zhang
+ * @since 2.9.7
+ */
+public abstract class NodeIteratorComponent extends NodeComponent{
+
+    @Override
+    public void process() throws Exception {
+        Iterator<?> it = processIterator();
+        Slot slot = this.getSlot();
+        Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
+        slot.setIteratorResult(originalClass.getName(), it);
+    }
+
+    public abstract Iterator<?> processIterator() throws Exception;
+}

+ 9 - 18
liteflow-core/src/main/java/com/yomahub/liteflow/core/proxy/ComponentProxy.java

@@ -194,26 +194,17 @@ public class ComponentProxy {
             ).findFirst().orElse(null);
 
             //如果被代理的对象里有此标注标的方法,则调用此被代理的对象里的方法,如果没有,则调用父类里的方法
-            //beforeProcess和afterProcess这2个方法除外
-            if (!ListUtil.toList("beforeProcess","afterProcess").contains(liteFlowMethodBean.getMethodName())) {
-                //进行检查,检查被代理的bean里是否有且仅有NodeComponent这个类型的参数
-                boolean checkFlag = liteFlowMethodBean.getMethod().getParameterTypes().length == 1
-                        && Arrays.asList(liteFlowMethodBean.getMethod().getParameterTypes()).contains(NodeComponent.class);
-                if (!checkFlag) {
-                    String errMsg = StrUtil.format("Method[{}.{}] must have NodeComponent parameter(and only one parameter)", bean.getClass().getName(), liteFlowMethodBean.getMethod().getName());
-                    LOG.error(errMsg);
-                    throw new ComponentMethodDefineErrorException(errMsg);
-                }
-
-                try{
-                    return liteFlowMethodBean.getMethod().invoke(bean, proxy);
-                }catch (Exception e){
-                    InvocationTargetException targetEx = (InvocationTargetException)e;
-                    throw targetEx.getTargetException();
-                }
+            //进行检查,检查被代理的bean里是否有且仅有NodeComponent这个类型的参数
+            boolean checkFlag = liteFlowMethodBean.getMethod().getParameterTypes().length == 1
+                    && Arrays.asList(liteFlowMethodBean.getMethod().getParameterTypes()).contains(NodeComponent.class);
+            if (!checkFlag) {
+                String errMsg = StrUtil.format("Method[{}.{}] must have NodeComponent parameter(and only one parameter)", bean.getClass().getName(), liteFlowMethodBean.getMethod().getName());
+                LOG.error(errMsg);
+                throw new ComponentMethodDefineErrorException(errMsg);
             }
+
             try{
-                return liteFlowMethodBean.getMethod().invoke(bean, args);
+                return liteFlowMethodBean.getMethod().invoke(bean, proxy);
             }catch (Exception e){
                 InvocationTargetException targetEx = (InvocationTargetException)e;
                 throw targetEx.getTargetException();

+ 3 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java

@@ -14,7 +14,9 @@ public enum ConditionTypeEnum {
 
     TYPE_FOR("for", "for"),
 
-    TYPE_WHILE("while", "while")
+    TYPE_WHILE("while", "while"),
+
+    TYPE_ITERATOR("iterator", "iterator")
     ;
     private String type;
     private String name;

+ 3 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/enums/LiteFlowMethodEnum.java

@@ -7,6 +7,9 @@ public enum LiteFlowMethodEnum {
     PROCESS_FOR("processFor", true),
     PROCESS_WHILE("processWhile", true),
     PROCESS_BREAK("processBreak", true),
+
+    PROCESS_ITERATOR("processIterator", true),
+
     IS_ACCESS("isAccess", false),
 
     IS_END("isEnd", false),

+ 2 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java

@@ -35,6 +35,8 @@ public enum NodeTypeEnum {
 
 	BREAK("break", "循环跳出", false, NodeBreakComponent.class),
 
+	ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class),
+
 	SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
 
 	SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),

+ 4 - 4
liteflow-core/src/main/java/com/yomahub/liteflow/exception/DataNofFoundException.java → liteflow-core/src/main/java/com/yomahub/liteflow/exception/DataNotFoundException.java

@@ -4,8 +4,8 @@ package com.yomahub.liteflow.exception;
  * 未找到数据异常
  * @author tangkc
  */
-public class DataNofFoundException extends RuntimeException {
-    public static final String MSG = "DataNofFoundException";
+public class DataNotFoundException extends RuntimeException {
+    public static final String MSG = "DataNotFoundException";
 
     private static final long serialVersionUID = 1L;
 
@@ -14,11 +14,11 @@ public class DataNofFoundException extends RuntimeException {
      */
     private String message;
 
-    public DataNofFoundException() {
+    public DataNotFoundException() {
         this.message = MSG;
     }
 
-    public DataNofFoundException(String message) {
+    public DataNotFoundException(String message) {
         this.message = message;
     }
 

+ 29 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/exception/NoIteratorNodeException.java

@@ -0,0 +1,29 @@
+package com.yomahub.liteflow.exception;
+
+/**
+ * 没有节点异常
+ * @author Yun
+ */
+public class NoIteratorNodeException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 异常信息
+     */
+    private String message;
+
+    public NoIteratorNodeException(String message) {
+        this.message = message;
+    }
+
+    @Override
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+}

+ 1 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java

@@ -207,7 +207,7 @@ public class FlowBus {
         } catch (Exception e) {
             String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name));
             LOG.error(e.getMessage());
-            throw new ComponentCannotRegisterException(error);
+            throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage()));
         }
     }
 

+ 14 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java

@@ -56,6 +56,8 @@ public class Node implements Executable,Cloneable{
 
 	private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
 
+	private TransmittableThreadLocal<Object> currLoopObject = new TransmittableThreadLocal<>();
+
 	public Node(){
 
 	}
@@ -241,4 +243,16 @@ public class Node implements Executable,Cloneable{
 	public void removeLoopIndex(){
 		this.loopIndexTL.remove();
 	}
+
+	public void setCurrLoopObject(Object obj){
+		this.currLoopObject.set(obj);
+	}
+
+	public <T> T getCurrLoopObject(){
+		return (T)this.currLoopObject.get();
+	}
+
+	public void removeCurrLoopObject(){
+		this.currLoopObject.remove();
+	}
 }

+ 1 - 4
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ForCondition.java

@@ -49,6 +49,7 @@ public class ForCondition extends LoopCondition{
             //如果break组件不为空,则去执行
             if (ObjectUtil.isNotNull(breakNode)){
                 breakNode.setCurrChainId(this.getCurrChainId());
+                setLoopIndex(breakNode, i);
                 breakNode.execute(slotIndex);
                 Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
                 boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
@@ -64,10 +65,6 @@ public class ForCondition extends LoopCondition{
         return ConditionTypeEnum.TYPE_FOR;
     }
 
-    public Executable getDoExecutor() {
-        return this.getExecutableList().get(0);
-    }
-
     public Node getForNode() {
         return forNode;
     }

+ 78 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/IteratorCondition.java

@@ -0,0 +1,78 @@
+package com.yomahub.liteflow.flow.element.condition;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.yomahub.liteflow.enums.ConditionTypeEnum;
+import com.yomahub.liteflow.exception.NoIteratorNodeException;
+import com.yomahub.liteflow.flow.element.Executable;
+import com.yomahub.liteflow.flow.element.Node;
+import com.yomahub.liteflow.slot.DataBus;
+import com.yomahub.liteflow.slot.Slot;
+import com.yomahub.liteflow.util.LiteFlowProxyUtil;
+
+import java.util.Iterator;
+
+public class IteratorCondition extends LoopCondition{
+
+    private Node iteratorNode;
+
+    @Override
+    public void execute(Integer slotIndex) throws Exception {
+        Slot slot = DataBus.getSlot(slotIndex);
+        if (ObjectUtil.isNull(iteratorNode)){
+            String errorInfo = StrUtil.format("[{}]:no iterator-node found", slot.getRequestId());
+            throw new NoIteratorNodeException(errorInfo);
+        }
+
+        //执行Iterator组件
+        iteratorNode.setCurrChainId(this.getCurrChainId());
+        iteratorNode.execute(slotIndex);
+
+        //这里可能会有spring代理过的bean,所以拿到user原始的class
+        Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(this.iteratorNode.getInstance().getClass());
+        //获得迭代器
+        Iterator<?> it = slot.getIteratorResult(originalForCountClass.getName());
+
+        //获得要循环的可执行对象
+        Executable executableItem = this.getDoExecutor();
+
+        int index = 0;
+        while(it.hasNext()){
+            Object itObj = it.next();
+
+            executableItem.setCurrChainId(this.getCurrChainId());
+            //设置循环index
+            setLoopIndex(executableItem, index);
+            //设置循环迭代器对象
+            setCurrLoopObject(executableItem, itObj);
+            //执行可执行对象
+            executableItem.execute(slotIndex);
+            //如果break组件不为空,则去执行
+            if (ObjectUtil.isNotNull(breakNode)){
+                breakNode.setCurrChainId(this.getCurrChainId());
+                setLoopIndex(breakNode, index);
+                setCurrLoopObject(breakNode, itObj);
+                breakNode.execute(slotIndex);
+                Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
+                boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
+                if (isBreak){
+                    break;
+                }
+            }
+            index++;
+        }
+    }
+
+    @Override
+    public ConditionTypeEnum getConditionType() {
+        return ConditionTypeEnum.TYPE_ITERATOR;
+    }
+
+    public Node getIteratorNode() {
+        return iteratorNode;
+    }
+
+    public void setIteratorNode(Node iteratorNode) {
+        this.iteratorNode = iteratorNode;
+    }
+}

+ 14 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java

@@ -32,4 +32,18 @@ public abstract class LoopCondition extends Condition {
             ((Node)executableItem).setLoopIndex(index);
         }
     }
+
+    protected void setCurrLoopObject(Executable executableItem, Object obj){
+        if (executableItem instanceof Chain){
+            ((Chain)executableItem).getConditionList().forEach(condition -> setCurrLoopObject(condition, obj));
+        }else if(executableItem instanceof Condition){
+            ((Condition)executableItem).getExecutableList().forEach(executable -> setCurrLoopObject(executable, obj));
+        }else if(executableItem instanceof Node){
+            ((Node)executableItem).setCurrLoopObject(obj);
+        }
+    }
+
+    protected Executable getDoExecutor() {
+        return this.getExecutableList().get(0);
+    }
 }

+ 19 - 19
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/SwitchCondition.java

@@ -45,9 +45,9 @@ public class SwitchCondition extends Condition{
             //这里可能会有spring代理过的bean,所以拿到user原始的class
             Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getSwitchNode().getInstance().getClass());
             String targetId = slot.getSwitchResult(originalClass.getName());
-            if (StrUtil.isNotBlank(targetId)) {
-                Executable targetExecutor;
 
+            Executable targetExecutor = null;
+            if (StrUtil.isNotBlank(targetId)) {
                 //这里要判断是否使用tag模式跳转
                 if (targetId.contains(TAG_FLAG)){
                     String[] target = targetId.split(TAG_FLAG, 2);
@@ -66,26 +66,26 @@ public class SwitchCondition extends Condition{
                             executable -> executable.getExecuteId().equals(targetId)
                     ).findFirst().orElse(null);
                 }
+            }
 
-                if (ObjectUtil.isNull(targetExecutor)) {
-                    //没有匹配到执行节点,则走默认的执行节点
-                    targetExecutor = defaultExecutor;
-                }
+            if (ObjectUtil.isNull(targetExecutor)) {
+                //没有匹配到执行节点,则走默认的执行节点
+                targetExecutor = defaultExecutor;
+            }
 
-                if (ObjectUtil.isNotNull(targetExecutor)) {
-                    //switch的目标不能是Pre节点或者Finally节点
-                    if (targetExecutor instanceof PreCondition || targetExecutor instanceof FinallyCondition){
-                        String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally",
-                                slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
-                        throw new SwitchTargetCannotBePreOrFinallyException(errorInfo);
-                    }
-                    targetExecutor.setCurrChainId(this.getCurrChainId());
-                    targetExecutor.execute(slotIndex);
-                }else{
-                    String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}],target str is [{}]",
-                            slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName(), targetId);
-                    throw new NoSwitchTargetNodeException(errorInfo);
+            if (ObjectUtil.isNotNull(targetExecutor)) {
+                //switch的目标不能是Pre节点或者Finally节点
+                if (targetExecutor instanceof PreCondition || targetExecutor instanceof FinallyCondition){
+                    String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally",
+                            slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
+                    throw new SwitchTargetCannotBePreOrFinallyException(errorInfo);
                 }
+                targetExecutor.setCurrChainId(this.getCurrChainId());
+                targetExecutor.execute(slotIndex);
+            }else{
+                String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}],target str is [{}]",
+                        slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName(), targetId);
+                throw new NoSwitchTargetNodeException(errorInfo);
             }
         }else{
             throw new SwitchTypeErrorException("switch instance must be NodeSwitchComponent");

+ 8 - 8
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java

@@ -37,7 +37,7 @@ public class WhenCondition extends Condition {
 	private final Logger LOG = LoggerFactory.getLogger(this.getClass());
 
 	//只在when类型下有效,以区分当when调用链调用失败时是否继续往下执行 默认false不继续执行
-	private boolean errorResume = false;
+	private boolean ignoreError = false;
 
 	//只在when类型下有效,用于不同node进行同组合并,相同的组会进行合并,不同的组不会进行合并
 	//此属性已弃用
@@ -150,8 +150,8 @@ public class WhenCondition extends Condition {
 		timeOutWhenFutureObjList.forEach(whenFutureObj ->
 				LOG.warn("requestId [{}] executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", slot.getRequestId(), whenFutureObj.getExecutorName()));
 
-		//当配置了errorResume = false,出现interrupted或者!f.get()的情况,将抛出WhenExecuteException
-		if (!this.isErrorResume()) {
+		//当配置了ignoreError = false,出现interrupted或者!f.get()的情况,将抛出WhenExecuteException
+		if (!this.isIgnoreError()) {
 			if (interrupted[0]) {
 				throw new WhenExecuteException(StrUtil.format("requestId [{}] when execute interrupted. errorResume [false].", slot.getRequestId()));
 			}
@@ -164,17 +164,17 @@ public class WhenCondition extends Condition {
 				}
 			}
 		} else if (interrupted[0]) {
-			//  这里由于配置了errorResume,所以只打印warn日志
+			//  这里由于配置了ignoreError,所以只打印warn日志
 			LOG.warn("requestId [{}] executing when condition timeout , but ignore with errorResume.", slot.getRequestId());
 		}
 	}
 
-	public boolean isErrorResume() {
-		return errorResume;
+	public boolean isIgnoreError() {
+		return ignoreError;
 	}
 
-	public void setErrorResume(boolean errorResume) {
-		this.errorResume = errorResume;
+	public void setIgnoreError(boolean ignoreError) {
+		this.ignoreError = ignoreError;
 	}
 
 	public String getGroup() {

+ 4 - 6
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhileCondition.java

@@ -35,11 +35,12 @@ public class WhileCondition extends LoopCondition{
         int index = 0;
         while(getWhileResult(slotIndex)){
             executableItem.setCurrChainId(this.getCurrChainId());
-            setLoopIndex(executableItem, index++);
+            setLoopIndex(executableItem, index);
             executableItem.execute(slotIndex);
             //如果break组件不为空,则去执行
             if (ObjectUtil.isNotNull(breakNode)){
                 breakNode.setCurrChainId(this.getCurrChainId());
+                setLoopIndex(breakNode, index);
                 breakNode.execute(slotIndex);
                 Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
                 boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
@@ -47,22 +48,19 @@ public class WhileCondition extends LoopCondition{
                     break;
                 }
             }
+            index++;
         }
     }
 
     private boolean getWhileResult(Integer slotIndex) throws Exception{
         Slot slot = DataBus.getSlot(slotIndex);
         //执行while组件
-        whileNode.setCurrChainName(this.getCurrChainName());
+        whileNode.setCurrChainId(this.getCurrChainId());
         whileNode.execute(slotIndex);
         Class<?> originalWhileClass = LiteFlowProxyUtil.getUserClass(this.whileNode.getInstance().getClass());
         return slot.getWhileResult(originalWhileClass.getName());
     }
 
-    public Executable getDoExecutor() {
-        return this.getExecutableList().get(0);
-    }
-
     @Override
     public ConditionTypeEnum getConditionType() {
         return ConditionTypeEnum.TYPE_WHILE;

+ 31 - 10
liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java

@@ -10,6 +10,7 @@ package com.yomahub.liteflow.slot;
 import cn.hutool.core.collection.ConcurrentHashSet;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import com.yomahub.liteflow.exception.NoSuchContextBeanException;
 import com.yomahub.liteflow.exception.NullParamException;
 import com.yomahub.liteflow.flow.entity.CmpStep;
@@ -52,6 +53,8 @@ public class Slot{
 
 	private static final String WHILE_PREFIX = "_while_";
 
+	private static final String ITERATOR_PREFIX = "_iterator_";
+
 	private static final String BREAK_PREFIX = "_break_";
 
 	private static final String NODE_INPUT_PREFIX = "_input_";
@@ -89,6 +92,16 @@ public class Slot{
 		return metaDataMap.containsKey(key);
 	}
 
+	private <T> void putThreadMetaDataMap(String key, T t){
+		String threadKey = StrUtil.format("{}_{}", key, Thread.currentThread().getName());
+		putMetaDataMap(threadKey, t);
+	}
+
+	private <T> T getThreadMetaData(String key){
+		String threadKey = StrUtil.format("{}_{}", key, Thread.currentThread().getName());
+		return (T)metaDataMap.get(threadKey);
+	}
+
 	private <T> void putMetaDataMap(String key, T t) {
 		if (ObjectUtil.isNull(t)) {
 			//data slot is a ConcurrentHashMap, so null value will trigger NullPointerException
@@ -197,43 +210,51 @@ public class Slot{
 	}
 
 	public <T> void setSwitchResult(String key, T t){
-		putMetaDataMap(SWITCH_NODE_PREFIX + key, t);
+		putThreadMetaDataMap(SWITCH_NODE_PREFIX + key, t);
 	}
 
 	public <T> T getSwitchResult(String key){
-		return (T) metaDataMap.get(SWITCH_NODE_PREFIX + key);
+		return getThreadMetaData(SWITCH_NODE_PREFIX + key);
 	}
 
 	public void setIfResult(String key, boolean result){
-		putMetaDataMap(IF_NODE_PREFIX + key, result);
+		putThreadMetaDataMap(IF_NODE_PREFIX + key, result);
 	}
 
 	public boolean getIfResult(String key){
-		return (boolean) metaDataMap.get(IF_NODE_PREFIX + key);
+		return getThreadMetaData(IF_NODE_PREFIX + key);
 	}
 
 	public void setForResult(String key, int forCount){
-		putMetaDataMap(FOR_PREFIX + key, forCount);
+		putThreadMetaDataMap(FOR_PREFIX + key, forCount);
 	}
 
 	public int getForResult(String key){
-		return (int) metaDataMap.get(FOR_PREFIX + key);
+		return getThreadMetaData(FOR_PREFIX + key);
 	}
 
 	public void setWhileResult(String key, boolean whileFlag){
-		putMetaDataMap(WHILE_PREFIX + key, whileFlag);
+		putThreadMetaDataMap(WHILE_PREFIX + key, whileFlag);
 	}
 
 	public boolean getWhileResult(String key){
-		return (boolean) metaDataMap.get(WHILE_PREFIX + key);
+		return getThreadMetaData(WHILE_PREFIX + key);
 	}
 
 	public void setBreakResult(String key, boolean breakFlag){
-		putMetaDataMap(BREAK_PREFIX + key, breakFlag);
+		putThreadMetaDataMap(BREAK_PREFIX + key, breakFlag);
 	}
 
 	public boolean getBreakResult(String key){
-		return (boolean) metaDataMap.get(BREAK_PREFIX + key);
+		return getThreadMetaData(BREAK_PREFIX + key);
+	}
+
+	public void setIteratorResult(String key, Iterator<?> it){
+		putThreadMetaDataMap(ITERATOR_PREFIX + key, it);
+	}
+
+	public Iterator<?> getIteratorResult(String key){
+		return getThreadMetaData(ITERATOR_PREFIX + key);
 	}
 
 	/**

+ 6 - 2
liteflow-core/src/main/resources/dtd/liteflow.dtd

@@ -1,15 +1,19 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
-<!ELEMENT flow  ((nodes) , (chain)+)>
+<!ELEMENT flow  ((nodes)? , (chain)+)>
 <!ELEMENT nodes  (node)+>
 <!ELEMENT node  (#PCDATA | EMPTY)*>
 <!ELEMENT chain (#PCDATA)>
 
 <!ATTLIST node
         id CDATA #REQUIRED
+        name CDATA #IMPLIED
         type (script|if_script|switch_script|while_script|for_script|break_script) #IMPLIED
         class CDATA #IMPLIED
+        file CDATA #IMPLIED
         language (groovy|js|python|lua) #IMPLIED
         >
 <!ATTLIST chain
-        name CDATA>
+        id CDATA #IMPLIED
+        name CDATA #IMPLIED
+        >

+ 2 - 2
liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/NodeComponentOfMethod.java

@@ -53,7 +53,7 @@ public class NodeComponentOfMethod extends NodeComponent {
 
 
     @Override
-    public <T> void beforeProcess(String nodeId, Slot slot) {
+    public void beforeProcess() {
         if(methodEnum != LiteFlowMethodEnum.BEFORE_PROCESS){
             return;
         }
@@ -68,7 +68,7 @@ public class NodeComponentOfMethod extends NodeComponent {
     }
 
     @Override
-    public <T> void afterProcess(String nodeId, Slot slot) {
+    public void afterProcess() {
         if (methodEnum != LiteFlowMethodEnum.AFTER_PROCESS) {
             return;
         }

+ 0 - 5
liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java

@@ -1,6 +1,5 @@
 package com.yomahub.liteflow.spi.spring;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.io.FileUtil;
@@ -9,19 +8,15 @@ import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.StrUtil;
 import com.yomahub.liteflow.exception.ConfigErrorException;
-import com.yomahub.liteflow.property.LiteflowConfig;
-import com.yomahub.liteflow.property.LiteflowConfigGetter;
 import com.yomahub.liteflow.spi.PathContentParser;
 import org.springframework.core.io.Resource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.core.io.support.ResourcePatternResolver;
 import org.springframework.util.ResourceUtils;
-
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 public class SpringPathContentParser implements PathContentParser {
     @Override

+ 2 - 2
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/customMethodName/cmp/CmpConfig.java

@@ -20,12 +20,12 @@ public class CmpConfig {
     }
 
     @LiteflowMethod(value = LiteFlowMethodEnum.BEFORE_PROCESS,nodeId = "a")
-    public void beforeAcmp(String nodeId, Slot slot){
+    public void beforeAcmp(NodeComponent bindCmp){
         System.out.println("before A");
     }
 
     @LiteflowMethod(value = LiteFlowMethodEnum.AFTER_PROCESS,nodeId = "a")
-    public void afterAcmp(String nodeId, Slot slot){
+    public void afterAcmp(NodeComponent bindCmp){
         System.out.println("after A");
     }
 

+ 57 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclMultiSpringbootTest.java

@@ -0,0 +1,57 @@
+package com.yomahub.liteflow.test.iterator;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * springboot环境最普通的例子测试
+ * @author Bryan.Zhang
+ * @since 2.6.4
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/iterator/application.properties")
+@SpringBootTest(classes = IteratorELDeclMultiSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
+public class IteratorELDeclMultiSpringbootTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    //最简单的情况
+    @Test
+    public void testIt1() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("123", str);
+    }
+
+    //迭代器带break
+    @Test
+    public void testIt2() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("12", str);
+    }
+
+}

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

@@ -0,0 +1,42 @@
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.base.cmp.TestDomain;
+
+import javax.annotation.Resource;
+import java.util.Iterator;
+import java.util.List;
+
+@LiteflowComponent
+public class CmpConfig {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a")
+    public void processA(NodeComponent bindCmp) {
+        String key = "test";
+        DefaultContext context = bindCmp.getFirstContextBean();
+        if (!context.hasData(key)){
+            context.setData(key, bindCmp.getCurrLoopObj());
+        }else{
+            String str = context.getData(key);
+            str += bindCmp.getCurrLoopObj();
+            context.setData(key, str);
+        }
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK, nodeId = "b", nodeType = NodeTypeEnum.BREAK)
+    public boolean processB(NodeComponent bindCmp) {
+        return bindCmp.getLoopIndex() == 1;
+    }
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "it", nodeType = NodeTypeEnum.ITERATOR)
+    public Iterator<?> processIT(NodeComponent bindCmp) {
+        List<String> list = bindCmp.getRequestData();
+        return list.iterator();
+    }
+}
+
+

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

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

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

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

+ 2 - 2
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/customMethodName/cmp/ACmp.java

@@ -27,12 +27,12 @@ public class ACmp{
 	}
 
 	@LiteflowMethod(LiteFlowMethodEnum.BEFORE_PROCESS)
-	public void beforeAcmp(String nodeId, Slot slot){
+	public void beforeAcmp(NodeComponent bindCmp){
 		System.out.println("before A");
 	}
 
 	@LiteflowMethod(LiteFlowMethodEnum.AFTER_PROCESS)
-	public void afterAcmp(String nodeId, Slot slot){
+	public void afterAcmp(NodeComponent bindCmp){
 		System.out.println("after A");
 	}
 

+ 57 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclSpringbootTest.java

@@ -0,0 +1,57 @@
+package com.yomahub.liteflow.test.iterator;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * springboot环境最普通的例子测试
+ * @author Bryan.Zhang
+ * @since 2.6.4
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/iterator/application.properties")
+@SpringBootTest(classes = IteratorELDeclSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
+public class IteratorELDeclSpringbootTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    //最简单的情况
+    @Test
+    public void testIt1() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("123", str);
+    }
+
+    //迭代器带break
+    @Test
+    public void testIt2() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("12", str);
+    }
+
+}

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

@@ -0,0 +1,34 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import com.yomahub.liteflow.slot.DefaultContext;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+@LiteflowCmpDefine(NodeTypeEnum.COMMON)
+public class ACmp{
+
+	@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
+	public void process(NodeComponent bindCmp) {
+		String key = "test";
+		DefaultContext context = bindCmp.getFirstContextBean();
+		if (!context.hasData(key)){
+			context.setData(key, bindCmp.getCurrLoopObj());
+		}else{
+			String str = context.getData(key);
+			str += bindCmp.getCurrLoopObj();
+			context.setData(key, str);
+		}
+	}
+}

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

@@ -0,0 +1,20 @@
+
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+@LiteflowCmpDefine(NodeTypeEnum.BREAK)
+public class BCmp{
+
+	@LiteflowMethod(LiteFlowMethodEnum.PROCESS_BREAK)
+	public boolean processBreak(NodeComponent bindCmp) throws Exception {
+		return bindCmp.getLoopIndex() == 1;
+	}
+}

+ 22 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java

@@ -0,0 +1,22 @@
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("it")
+@LiteflowCmpDefine(NodeTypeEnum.ITERATOR)
+public class ITCmp{
+    @LiteflowMethod(LiteFlowMethodEnum.PROCESS_ITERATOR)
+    public Iterator<?> processIterator(NodeComponent bindCmp) throws Exception {
+        List<String> list = bindCmp.getRequestData();
+        return list.iterator();
+    }
+}

+ 1 - 1
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/subflow/SubflowInDifferentConfigELDeclSpringbootTest.java

@@ -47,7 +47,7 @@ public class SubflowInDifferentConfigELDeclSpringbootTest extends BaseTest {
     @Test(expected = MultipleParsersException.class)
     public void testExplicitSubFlow2() {
         LiteflowConfig config = context.getBean(LiteflowConfig.class);
-        config.setRuleSource("subflow/flow-main.xml,subflow/flow-sub1.xml,subflow/flow-sub2.yml");
+        config.setRuleSource("subflow/flow-main.xml,   subflow/flow-sub1.xml,subflow/flow-sub2.yml");
         flowExecutor.reloadRule();
     }
 }

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

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

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/iterator/flow.xml

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

+ 48 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/IteratorTest.java

@@ -0,0 +1,48 @@
+package com.yomahub.liteflow.test.iterator;
+
+import cn.hutool.core.collection.ListUtil;
+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.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.List;
+
+public class IteratorTest extends BaseTest{
+
+    private static FlowExecutor flowExecutor;
+
+    @BeforeClass
+    public static void init(){
+        LiteflowConfig config = new LiteflowConfig();
+        config.setRuleSource("iterator/flow.xml");
+        flowExecutor = FlowExecutorHolder.loadInstance(config);
+    }
+
+    //最简单的情况
+    @Test
+    public void testIt1() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("123", str);
+    }
+
+    //迭代器带break
+    @Test
+    public void testIt2() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("12", str);
+    }
+}

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

@@ -0,0 +1,27 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		String key = "test";
+		DefaultContext context = this.getFirstContextBean();
+		if (!context.hasData(key)){
+			context.setData(key, this.getCurrLoopObj());
+		}else{
+			String str = context.getData(key);
+			str += this.getCurrLoopObj();
+			context.setData(key, str);
+		}
+	}
+}

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

@@ -0,0 +1,12 @@
+
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+
+public class BCmp extends NodeBreakComponent {
+
+	@Override
+	public boolean processBreak() throws Exception {
+		return this.getLoopIndex() == 1;
+	}
+}

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java

@@ -0,0 +1,14 @@
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class ITCmp extends NodeIteratorComponent {
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = this.getRequestData();
+        return list.iterator();
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/iterator/flow.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
+<flow>
+    <nodes>
+        <node id="a" class="com.yomahub.liteflow.test.iterator.cmp.ACmp"/>
+        <node id="b" class="com.yomahub.liteflow.test.iterator.cmp.BCmp"/>
+        <node id="it" class="com.yomahub.liteflow.test.iterator.cmp.ITCmp"/>
+    </nodes>
+
+    <chain name="chain1">
+        ITERATOR(it).DO(a);
+    </chain>
+
+    <chain name="chain2">
+        ITERATOR(it).DO(a).BREAK(b);
+    </chain>
+</flow>

+ 39 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/common/LiteFlowXmlScriptBuilderGroovyELTest.java

@@ -1,5 +1,6 @@
 package com.yomahub.liteflow.test.script.groovy.common;
 
+import cn.hutool.core.io.resource.ClassPathResource;
 import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
 import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
 import com.yomahub.liteflow.core.FlowExecutor;
@@ -25,6 +26,44 @@ public class LiteFlowXmlScriptBuilderGroovyELTest extends BaseTest {
     @Resource
     private FlowExecutor flowExecutor;
 
+    /**
+     * 测试通过builder方式运行普通script节点,以file绝对路径的方式运行
+     */
+    @Test
+    public void testAbsoluteScriptFilePath(){
+        String absolutePath = new ClassPathResource("classpath:builder/s2.groovy").getAbsolutePath();
+        LiteFlowNodeBuilder.createNode().setId("d")
+                .setName("组件D")
+                .setType(NodeTypeEnum.COMMON)
+                .setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.DCmp")
+                .build();
+        LiteFlowNodeBuilder.createNode().setId("s2")
+                .setName("条件脚本S2")
+                .setType(NodeTypeEnum.SWITCH_SCRIPT)
+                .setFile(absolutePath)
+                .build();
+        LiteFlowNodeBuilder.createNode().setId("a")
+                .setName("组件A")
+                .setType(NodeTypeEnum.COMMON)
+                .setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.ACmp")
+                .build();
+        LiteFlowNodeBuilder.createNode().setId("b")
+                .setName("组件B")
+                .setType(NodeTypeEnum.COMMON)
+                .setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.BCmp")
+                .build();
+
+        LiteFlowChainELBuilder.createChain().setChainName("chain2")
+                .setEL("THEN(d,SWITCH(s2).to(a,b))")
+                .build();
+
+
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2","arg1");
+        DefaultContext context = response.getFirstContextBean();
+        Assert.assertTrue(response.isSuccess());
+        Assert.assertEquals("d[组件D]==>s2[条件脚本S2]==>a[组件A]", response.getExecuteStepStr());
+    }
+
     //测试通过builder方式运行普通script节点,以脚本文本的方式运行
     @Test
     public void testBuilderScript1() {

+ 14 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/scriptbean/LiteFlowScriptScriptbeanGroovyELTest.java

@@ -3,6 +3,7 @@ package com.yomahub.liteflow.test.script.groovy.scriptbean;
 import com.yomahub.liteflow.core.FlowExecutor;
 import com.yomahub.liteflow.exception.ScriptBeanMethodInvokeException;
 import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.script.ScriptBeanManager;
 import com.yomahub.liteflow.slot.DefaultContext;
 import com.yomahub.liteflow.test.BaseTest;
 import org.junit.Assert;
@@ -15,6 +16,8 @@ import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
 
 import javax.annotation.Resource;
+import java.util.HashMap;
+import java.util.Map;
 
 @RunWith(SpringRunner.class)
 @TestPropertySource(value = "classpath:/scriptbean/application.properties")
@@ -75,4 +78,15 @@ public class LiteFlowScriptScriptbeanGroovyELTest extends BaseTest {
         Assert.assertFalse(response.isSuccess());
         Assert.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass());
     }
+
+    //测试在ScriptBeanManager里放入上下文,实现自定义脚本引用名称
+    @Test
+    public void testScriptBean7() throws Exception{
+        Map<String, String> map = new HashMap<>();
+        ScriptBeanManager.addScriptBean("abcCx", map);
+        LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg", map);
+        Assert.assertTrue(response.isSuccess());
+        Map<String, String> context = response.getFirstContextBean();
+        Assert.assertEquals("hello", context.get("demo"));
+    }
 }

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/scriptbean/flow.xml

@@ -43,6 +43,13 @@
             defaultContext.setData("demo", str)
             ]]>
         </node>
+
+        <node id="s5" type="script" language="groovy">
+            <![CDATA[
+            def str = demo.getDemoStr1()
+            abcCx.put("demo", str)
+            ]]>
+        </node>
     </nodes>
 
     <chain name="chain1">
@@ -68,4 +75,8 @@
     <chain name="chain6">
         THEN(a,b,c,s4);
     </chain>
+
+    <chain name="chain7">
+        THEN(a,b,c,s5);
+    </chain>
 </flow>

+ 9 - 1
liteflow-testcase-el/liteflow-testcase-el-script-python-springboot/src/test/resources/common/flow.xml

@@ -1,8 +1,16 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE flow PUBLIC  "liteflow" "liteflow.dtd">
 <flow>
     <nodes>
-        <node id="s1" name="普通脚本1" type="script">
+        <node id="s1" name="普通脚本1" type="script" language="python">
             <![CDATA[
+                import json
+                x='{"name": "Jack", "age": 75, "nationality": "China"}'
+                jsonData=json.loads(x)
+                temperature=jsonData['name']
+                print(temperature)
+
+
                 a=6
                 b=10
                 if a>5:

+ 55 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java

@@ -0,0 +1,55 @@
+package com.yomahub.liteflow.test.iterator;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * springboot环境EL常规的例子测试
+ * @author Bryan.Zhang
+ */
+@RunWith(SpringRunner.class)
+@TestPropertySource(value = "classpath:/iterator/application.properties")
+@SpringBootTest(classes = IteratorELSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
+public class IteratorELSpringbootTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    //最简单的情况
+    @Test
+    public void testIt1() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("123", str);
+    }
+
+    //迭代器带break
+    @Test
+    public void testIt2() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("12", str);
+    }
+}

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

@@ -0,0 +1,29 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		String key = "test";
+		DefaultContext context = this.getFirstContextBean();
+		if (!context.hasData(key)){
+			context.setData(key, this.getCurrLoopObj());
+		}else{
+			String str = context.getData(key);
+			str += this.getCurrLoopObj();
+			context.setData(key, str);
+		}
+	}
+}

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

@@ -0,0 +1,15 @@
+
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+public class BCmp extends NodeBreakComponent {
+
+	@Override
+	public boolean processBreak() throws Exception {
+		return this.getLoopIndex() == 1;
+	}
+}

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

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("it")
+public class ITCmp extends NodeIteratorComponent {
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = this.getRequestData();
+        return list.iterator();
+    }
+}

+ 8 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/switchcase/SwitchELSpringbootTest.java

@@ -84,4 +84,12 @@ public class SwitchELSpringbootTest extends BaseTest {
         Assert.assertEquals("a==>i==>d",response.getExecuteStepStr());
     }
 
+    //switch返回如果是空,会走default选项
+    @Test
+    public void testSwitch8() throws Exception{
+        LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+        Assert.assertTrue(response.isSuccess());
+        Assert.assertEquals("a==>j==>d",response.getExecuteStepStr());
+    }
+
 }

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/switchcase/cmp/JSwitchCmp.java

@@ -0,0 +1,20 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Tingliang Wang
+ * @email bytlwang@126.com
+ * @Date 2022/12/09
+ */
+package com.yomahub.liteflow.test.switchcase.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("j")
+public class JSwitchCmp extends NodeSwitchComponent {
+
+	@Override
+	public String processSwitch() throws Exception {
+		return "";
+	}
+}

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

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

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml

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

+ 6 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/switchcase/flow.el.xml

@@ -50,4 +50,10 @@
         );
     </chain>
 
+    <chain name="chain8">
+        THEN(
+        a,
+        SWITCH(j).to(b, c).DEFAULT(d)
+        );
+    </chain>
 </flow>

+ 45 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringTest.java

@@ -0,0 +1,45 @@
+package com.yomahub.liteflow.test.iterator;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.slot.DefaultContext;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+@RunWith(SpringRunner.class)
+@ContextConfiguration("classpath:/iterator/application.xml")
+public class IteratorELSpringTest extends BaseTest {
+
+    @Resource
+    private FlowExecutor flowExecutor;
+
+    //最简单的情况
+    @Test
+    public void testIt1() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("123", str);
+    }
+
+    //迭代器带break
+    @Test
+    public void testIt2() throws Exception{
+        List<String> list = ListUtil.toList("1","2","3");
+        LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
+        Assert.assertTrue(response.isSuccess());
+        DefaultContext context = response.getFirstContextBean();
+        String str = context.getData("test");
+        Assert.assertEquals("12", str);
+    }
+}

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

@@ -0,0 +1,29 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.slot.DefaultContext;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		String key = "test";
+		DefaultContext context = this.getFirstContextBean();
+		if (!context.hasData(key)){
+			context.setData(key, this.getCurrLoopObj());
+		}else{
+			String str = context.getData(key);
+			str += this.getCurrLoopObj();
+			context.setData(key, str);
+		}
+	}
+}

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

@@ -0,0 +1,14 @@
+
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+public class BCmp extends NodeBreakComponent {
+
+	@Override
+	public boolean processBreak() throws Exception {
+		return this.getLoopIndex() == 1;
+	}
+}

+ 16 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.iterator.cmp;
+
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("it")
+public class ITCmp extends NodeIteratorComponent {
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = this.getRequestData();
+        return list.iterator();
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/application.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns="http://www.springframework.org/schema/beans"
+       xmlns:context="http://www.springframework.org/schema/context"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
+       http://www.springframework.org/schema/context
+       http://www.springframework.org/schema/context/spring-context-4.0.xsd">
+
+    <context:component-scan base-package="com.yomahub.liteflow.test.iterator.cmp" />
+
+    <bean id="springAware" class="com.yomahub.liteflow.spi.spring.SpringAware"/>
+
+    <bean class="com.yomahub.liteflow.spring.ComponentScanner"/>
+
+    <bean id="liteflowConfig" class="com.yomahub.liteflow.property.LiteflowConfig">
+        <property name="ruleSource" value="iterator/flow.xml"/>
+    </bean>
+
+    <bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor">
+        <constructor-arg name="liteflowConfig" ref="liteflowConfig"/>
+    </bean>
+</beans>

+ 11 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/flow.xml

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

+ 3 - 5
pom.xml

@@ -39,7 +39,7 @@
 	</scm>
 
 	<properties>
-		<revision>2.9.6</revision>
+		<revision>2.9.7</revision>
 		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 		<maven.compiler.source>8</maven.compiler.source>
@@ -52,7 +52,7 @@
 		<dom4j.version>2.1.3</dom4j.version>
 		<curator.version>5.3.0</curator.version>
 		<junit.version>4.12</junit.version>
-		<hutool-core.version>5.8.8</hutool-core.version>
+		<hutool-core.version>5.8.11</hutool-core.version>
 		<transmittable-thread-local.version>2.12.3</transmittable-thread-local.version>
 		<curator-test.version>5.1.0</curator-test.version>
 		<zkclient.version>0.10</zkclient.version>
@@ -64,9 +64,7 @@
 		<bytebuddy.version>1.11.13</bytebuddy.version>
 		<aspectjweaver.version>1.8.13</aspectjweaver.version>
 		<logback-classic.version>1.2.3</logback-classic.version>
-
-		<solon.version>1.12.0</solon.version>
-
+		<solon.version>2.0.0</solon.version>
 		<netty.version>4.1.84.Final</netty.version>
 		<guava.version>31.1-jre</guava.version>
 		<httpclient.version>4.5.13</httpclient.version>