Переглянути джерело

feature #I6A2GL 增加迭代器表达式特性,用于迭代循环中的集合

everywhere.z 2 роки тому
батько
коміт
cebfb2867e
42 змінених файлів з 848 додано та 11 видалено
  1. 4 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java
  2. 1 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java
  3. 26 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java
  4. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java
  5. 4 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java
  6. 24 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeIteratorComponent.java
  7. 3 1
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java
  8. 3 0
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/LiteFlowMethodEnum.java
  9. 2 0
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java
  10. 29 0
      liteflow-core/src/main/java/com/yomahub/liteflow/exception/NoIteratorNodeException.java
  11. 14 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java
  12. 1 4
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/ForCondition.java
  13. 78 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/IteratorCondition.java
  14. 14 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/LoopCondition.java
  15. 4 6
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhileCondition.java
  16. 57 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclMultiSpringbootTest.java
  17. 42 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/CmpConfig.java
  18. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/iterator/application.properties
  19. 11 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/iterator/flow.xml
  20. 57 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELDeclSpringbootTest.java
  21. 34 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  22. 20 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  23. 22 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  24. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/iterator/application.properties
  25. 11 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/iterator/flow.xml
  26. 48 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/IteratorTest.java
  27. 27 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  28. 12 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  29. 14 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  30. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/iterator/flow.xml
  31. 55 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringbootTest.java
  32. 29 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  33. 15 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  34. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  35. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/application.properties
  36. 11 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/iterator/flow.xml
  37. 45 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/IteratorELSpringTest.java
  38. 29 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ACmp.java
  39. 14 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/BCmp.java
  40. 16 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/iterator/cmp/ITCmp.java
  41. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/application.xml
  42. 11 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/iterator/flow.xml

+ 4 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java

@@ -53,6 +53,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);
     }

+ 1 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java

@@ -69,6 +69,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());

+ 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 - 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";

+ 4 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java

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

+ 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),

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

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

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

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

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

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

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

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