瀏覽代碼

!208 实现组件的回滚机制
Merge pull request !208 from Rain/dev

铂赛东 1 年之前
父節點
當前提交
9ae5751305
共有 82 個文件被更改,包括 2374 次插入3 次删除
  1. 18 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java
  2. 56 0
      liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java
  3. 3 1
      liteflow-core/src/main/java/com/yomahub/liteflow/enums/LiteFlowMethodEnum.java
  4. 28 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java
  5. 23 1
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java
  6. 13 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Rollbackable.java
  7. 47 0
      liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java
  8. 45 0
      liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java
  9. 93 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java
  10. 136 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java
  11. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/application.properties
  12. 34 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml
  13. 95 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java
  14. 25 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java
  15. 31 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.java
  16. 21 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CCmp.java
  17. 26 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.java
  18. 26 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java
  19. 19 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java
  20. 19 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java
  21. 19 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java
  22. 23 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ICmp.java
  23. 19 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java
  24. 18 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java
  25. 1 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/application.properties
  26. 34 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml
  27. 92 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java
  28. 23 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java
  29. 29 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.java
  30. 19 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CCmp.java
  31. 24 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.java
  32. 24 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java
  33. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java
  34. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java
  35. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java
  36. 21 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ICmp.java
  37. 17 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java
  38. 16 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java
  39. 47 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml
  40. 1 1
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/parallelLoop/ParallelLoopELSpringbootTest.java
  41. 87 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java
  42. 26 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java
  43. 32 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.java
  44. 22 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CCmp.java
  45. 27 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.java
  46. 27 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java
  47. 19 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java
  48. 19 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java
  49. 19 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java
  50. 23 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ICmp.java
  51. 19 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java
  52. 18 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java
  53. 1 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/application.properties
  54. 34 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml
  55. 90 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java
  56. 25 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java
  57. 32 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.java
  58. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CCmp.java
  59. 26 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.java
  60. 26 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java
  61. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java
  62. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java
  63. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java
  64. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ICmp.java
  65. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java
  66. 18 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java
  67. 1 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/application.properties
  68. 34 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml
  69. 89 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java
  70. 25 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java
  71. 31 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.java
  72. 21 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CCmp.java
  73. 26 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.java
  74. 26 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java
  75. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java
  76. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java
  77. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java
  78. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ICmp.java
  79. 19 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java
  80. 18 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java
  81. 23 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/application.xml
  82. 34 0
      liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml

+ 18 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java

@@ -17,6 +17,8 @@ import com.yomahub.liteflow.flow.FlowBus;
 import com.yomahub.liteflow.flow.LiteflowResponse;
 import com.yomahub.liteflow.flow.element.Chain;
 import com.yomahub.liteflow.flow.element.Node;
+import com.yomahub.liteflow.flow.element.Rollbackable;
+import com.yomahub.liteflow.flow.entity.CmpStep;
 import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
 import com.yomahub.liteflow.log.LFLog;
 import com.yomahub.liteflow.log.LFLoggerManager;
@@ -433,6 +435,22 @@ public class FlowExecutor {
 			else {
 				slot.setSubException(chainId, e);
 			}
+			Deque<CmpStep> executeSteps = slot.getExecuteSteps();
+			try {
+				Iterator<CmpStep> cmpStepIterator = executeSteps.descendingIterator();
+				while(cmpStepIterator.hasNext()) {
+					CmpStep cmpStep = cmpStepIterator.next();
+					if(cmpStep.getInstance().isRollback()) {
+						Rollbackable rollbackItem = new Node(cmpStep.getInstance());
+						rollbackItem.rollback(slotIndex);
+					}
+				}
+			} catch (Exception exception) {
+				LOG.error(exception.getMessage());
+			}
+			finally {
+				slot.printRollbackStep();
+			}
 		}
 		finally {
 			if (innerChainType.equals(InnerChainTypeEnum.NONE)) {

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

@@ -31,6 +31,7 @@ import com.yomahub.liteflow.flow.element.Executable;
 import com.yomahub.liteflow.monitor.CompStatistics;
 import com.yomahub.liteflow.monitor.MonitorBus;
 
+import java.lang.reflect.Method;
 import java.util.Map;
 
 /**
@@ -57,6 +58,9 @@ public abstract class NodeComponent {
 	// 重试次数
 	private int retryCount = 0;
 
+	// 是否重写了rollback方法
+	private boolean isRollback = false;
+
 	// 在目标异常抛出时才重试
 	private Class<? extends Exception>[] retryForExceptions = new Class[] { Exception.class };
 
@@ -79,6 +83,14 @@ public abstract class NodeComponent {
 	private final TransmittableThreadLocal<Boolean> isEndTL = new TransmittableThreadLocal<>();
 
 	public NodeComponent() {
+		// 反射判断是否重写了rollback方法
+		Class<?> clazz = this.getClass();
+		try {
+			Method method = clazz.getDeclaredMethod("rollback");
+			if(ObjectUtil.isNotNull(method))
+				this.setRollback(true);
+		} catch (Exception e) {
+		}
 	}
 
 	public void execute() throws Exception {
@@ -87,6 +99,7 @@ public abstract class NodeComponent {
 		// 在元数据里加入step信息
 		CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE);
 		cmpStep.setTag(this.getTag());
+		cmpStep.setInstance(this);
 		slot.addStep(cmpStep);
 
 		StopWatch stopWatch = new StopWatch();
@@ -140,6 +153,37 @@ public abstract class NodeComponent {
 		}
 	}
 
+	public void doRollback() throws Exception {
+		Slot slot = this.getSlot();
+
+		CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE);
+		cmpStep.setTag(this.getTag());
+		slot.addRollbackStep(cmpStep);
+
+		StopWatch stopWatch = new StopWatch();
+		stopWatch.start();
+
+		try {
+			self.rollback();
+		}
+		catch (Exception e) {
+			throw e;
+		}
+		finally {
+			stopWatch.stop();
+			final long timeSpent = stopWatch.getTotalTimeMillis();
+			LOG.info("component[{}] rollback in {} milliseconds", this.getDisplayName(), timeSpent);
+
+			// 往CmpStep中放入时间消耗信息
+			cmpStep.setRollbackTimeSpent(timeSpent);
+			// 性能统计
+			if (ObjectUtil.isNotNull(monitorBus)) {
+				CompStatistics statistics = new CompStatistics(this.getClass().getSimpleName(), timeSpent);
+				monitorBus.addStatistics(statistics);
+			}
+		}
+	}
+
 	public void beforeProcess() {
 		// 全局切面只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
 		// 非spring环境下,全局切面为空实现
@@ -148,6 +192,10 @@ public abstract class NodeComponent {
 
 	public abstract void process() throws Exception;
 
+	public void rollback() throws Exception{
+		// 如果需要失败后回滚某个方法,请覆盖这个方法
+	};
+
 	public void onSuccess() throws Exception {
 		// 如果需要在成功后回调某一个方法,请覆盖这个方法
 		// 全局切面只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
@@ -309,6 +357,14 @@ public abstract class NodeComponent {
 		return getSlot().getChainReqDataFromQueue(this.getCurrChainId());
 	}
 
+	public boolean isRollback() {
+		return isRollback;
+	}
+
+	public void setRollback(boolean rollback) {
+		isRollback = rollback;
+	}
+
 	/**
 	 * @deprecated 请使用 {@link #getChainId()}
 	 * @return String

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

@@ -26,7 +26,9 @@ public enum LiteFlowMethodEnum {
 
 	AFTER_PROCESS("afterProcess", false),
 
-	GET_DISPLAY_NAME("getDisplayName", false)
+	GET_DISPLAY_NAME("getDisplayName", false),
+
+	ROLLBACK("rollback", false)
 	;
 
 	private String methodName;

+ 28 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/LiteflowResponse.java

@@ -113,6 +113,34 @@ public class LiteflowResponse {
 		return map;
 	}
 
+	public Queue<CmpStep> getRollbackStepQueue() {
+		return this.getSlot().getRollbackSteps();
+	}
+
+	public String getRollbackStepStr() {
+		return getRollbackStepStrWithoutTime();
+	}
+
+	public String getRollbackStepStrWithTime() {
+		return this.getSlot().getRollbackStepStr(true);
+	}
+
+	public String getRollbackStepStrWithoutTime() {
+		return this.getSlot().getRollbackStepStr(false);
+	}
+
+	public Map<String, List<CmpStep>> getRollbackSteps() {
+		Map<String, List<CmpStep>> map = new LinkedHashMap<>();
+		this.getSlot().getRollbackSteps().forEach(cmpStep -> {
+			if (map.containsKey(cmpStep.getNodeId())){
+				map.get(cmpStep.getNodeId()).add(cmpStep);
+			}else{
+				map.put(cmpStep.getNodeId(), ListUtil.toList(cmpStep));
+			}
+		});
+		return map;
+	}
+
 	public Queue<CmpStep> getExecuteStepQueue() {
 		return this.getSlot().getExecuteSteps();
 	}

+ 23 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java

@@ -31,7 +31,7 @@ import com.yomahub.liteflow.exception.FlowSystemException;
  *
  * @author Bryan.Zhang
  */
-public class Node implements Executable, Cloneable{
+public class Node implements Executable, Cloneable, Rollbackable{
 
 	private static final LFLog LOG = LFLoggerManager.getLogger(Node.class);
 
@@ -174,6 +174,28 @@ public class Node implements Executable, Cloneable{
 		}
 	}
 
+	// 回滚的主要逻辑
+	@Override
+	public void rollback(Integer slotIndex) throws Exception {
+
+		Slot slot = DataBus.getSlot(slotIndex);
+		try {
+			// 把线程属性赋值给组件对象
+			instance.setSlotIndex(slotIndex);
+			instance.setRefNode(this);
+			instance.doRollback();
+		}
+		catch (Exception e) {
+			String errorMsg = StrUtil.format("component[{}] rollback error,error:{}", id, e.getMessage());
+			LOG.error(errorMsg);
+		}
+		finally {
+			// 移除threadLocal里的信息
+			instance.removeSlotIndex();
+			instance.removeRefNode();
+		}
+	}
+
 	// 在同步场景并不会单独执行这方法,同步场景会在execute里面去判断isAccess。
 	// 但是在异步场景的any=true情况下,如果isAccess返回了false,那么异步的any有可能会认为这个组件先执行完。就会导致不正常
 	// 增加这个方法是为了在异步的时候,先去过滤掉isAccess为false的异步组件。然后再异步执行。

+ 13 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Rollbackable.java

@@ -0,0 +1,13 @@
+package com.yomahub.liteflow.flow.element;
+
+
+/**
+ * 回滚接口 目前实现这个接口的只有Node
+ *
+ * @author RainZs
+ */
+public interface Rollbackable {
+
+    void rollback(Integer slotIndex) throws Exception;
+
+}

+ 47 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java

@@ -10,6 +10,7 @@ package com.yomahub.liteflow.flow.entity;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import com.yomahub.liteflow.core.NodeComponent;
 import com.yomahub.liteflow.enums.CmpStepTypeEnum;
 
 /**
@@ -37,6 +38,11 @@ public class CmpStep {
 	// 但是success为false,不一定有exception,因为有可能没执行到,或者没执行结束(any)
 	private Exception exception;
 
+	private NodeComponent instance;
+
+	// 回滚消耗的时间
+	private Long rollbackTimeSpent;
+
 	public CmpStep(String nodeId, String nodeName, CmpStepTypeEnum stepType) {
 		this.nodeId = nodeId;
 		this.nodeName = nodeName;
@@ -91,6 +97,22 @@ public class CmpStep {
 		this.exception = exception;
 	}
 
+	public NodeComponent getInstance() {
+		return instance;
+	}
+
+	public void setInstance(NodeComponent instance) {
+		this.instance = instance;
+	}
+
+	public Long getRollbackTimeSpent() {
+		return rollbackTimeSpent;
+	}
+
+	public void setRollbackTimeSpent(Long rollbackTimeSpent) {
+		this.rollbackTimeSpent = rollbackTimeSpent;
+	}
+
 	public String buildString() {
 		if (stepType.equals(CmpStepTypeEnum.SINGLE)) {
 			if (StrUtil.isBlank(nodeName)) {
@@ -131,6 +153,31 @@ public class CmpStep {
 		}
 	}
 
+	public String buildRollbackStringWithTime() {
+		if (stepType.equals(CmpStepTypeEnum.SINGLE)) {
+			if (StrUtil.isBlank(nodeName)) {
+				if (rollbackTimeSpent != null) {
+					return StrUtil.format("{}<{}>", nodeId, rollbackTimeSpent);
+				}
+				else {
+					return StrUtil.format("{}", nodeId);
+				}
+			}
+			else {
+				if (rollbackTimeSpent != null) {
+					return StrUtil.format("{}[{}]<{}>", nodeId, nodeName, rollbackTimeSpent);
+				}
+				else {
+					return StrUtil.format("{}[{}]", nodeId, nodeName);
+				}
+			}
+		}
+		else {
+			// 目前没有其他的类型
+			return null;
+		}
+	}
+
 	@Override
 	public boolean equals(Object obj) {
 		if (ObjectUtil.isNull(obj)) {

+ 45 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java

@@ -81,6 +81,10 @@ public class Slot {
 
 	private String executeStepsStr;
 
+	private final Deque<CmpStep> rollbackSteps = new ConcurrentLinkedDeque<>();
+
+	private String rollbackStepsStr;
+
 	protected ConcurrentHashMap<String, Object> metaDataMap = new ConcurrentHashMap<>();
 
 	private List<Object> contextBeanList;
@@ -346,6 +350,43 @@ public class Slot {
 		}
 	}
 
+	public void addRollbackStep(CmpStep step) {
+		this.rollbackSteps.add(step);
+	}
+
+	public String getRollbackStepStr(boolean withRollbackTimeSpent) {
+		StringBuilder str = new StringBuilder();
+		CmpStep cmpStep;
+		for (Iterator<CmpStep> it = rollbackSteps.iterator(); it.hasNext();) {
+			cmpStep = it.next();
+			if (withRollbackTimeSpent) {
+				str.append(cmpStep.buildRollbackStringWithTime());
+			}
+			else {
+				str.append(cmpStep.buildString());
+			}
+			if (it.hasNext()) {
+				str.append("==>");
+			}
+		}
+		this.rollbackStepsStr = str.toString();
+		return this.rollbackStepsStr;
+	}
+
+	public String getRollbackStepStr() {
+		return getRollbackStepStr(false);
+	}
+
+	public void printRollbackStep() {
+		if (ObjectUtil.isNull(this.rollbackStepsStr)) {
+			this.rollbackStepsStr = getRollbackStepStr(true);
+		}
+		if (LiteflowConfigGetter.get().getPrintExecutionLog()) {
+			LOG.info("ROLLBACK_CHAIN_NAME[{}]\n{}", this.getChainName(), this.rollbackStepsStr);
+		}
+	}
+
+
 	public void generateRequestId() {
 		metaDataMap.put(REQUEST_ID, IdGeneratorHolder.getInstance().generate());
 	}
@@ -362,6 +403,10 @@ public class Slot {
 		return executeSteps;
 	}
 
+	public Deque<CmpStep> getRollbackSteps() {
+		return rollbackSteps;
+	}
+
 	public Exception getException() {
 		return (Exception) this.metaDataMap.get(EXCEPTION);
 	}

+ 93 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java

@@ -0,0 +1,93 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.annotation.Resource;
+
+@ExtendWith(SpringExtension.class)
+@TestPropertySource(value = "classpath:/rollback/application.properties")
+@SpringBootTest(classes = RollbackELDeclMultiSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({ "com.yomahub.liteflow.test.rollback.cmp" })
+public class RollbackELDeclMultiSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+}

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

@@ -0,0 +1,136 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.annotation.LiteflowComponent;
+import com.yomahub.liteflow.annotation.LiteflowMethod;
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
+import com.yomahub.liteflow.enums.NodeTypeEnum;
+
+import java.util.Iterator;
+import java.util.List;
+
+@LiteflowComponent
+public class CmpConfig {
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a")
+    public void processA(NodeComponent bindCmp) {
+        System.out.println("ACmp executed!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "a")
+    public void rollbackA(NodeComponent bindCmp) throws Exception {
+        System.out.println("ACmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b")
+    public void processB(NodeComponent bindCmp) {
+        System.out.println("BCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.IS_CONTINUE_ON_ERROR, nodeId = "b")
+    public boolean isContinueOnErrorB(NodeComponent bindCmp) {
+        return true;
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "b")
+    public void rollbackB(NodeComponent bindCmp) throws Exception {
+        System.out.println("BCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "c")
+    public void processC(NodeComponent bindCmp) {
+        System.out.println("CCmp executed!");
+    }
+
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "d")
+    public void processD(NodeComponent bindCmp) {
+        System.out.println("DCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "d")
+    public void rollbackD(NodeComponent bindCmp) throws Exception {
+        System.out.println("DCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "e")
+    public void processE(NodeComponent bindCmp) {
+        System.out.println("ECmp executed!");
+        throw new RuntimeException();
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "e")
+    public void rollbackE() throws Exception {
+        System.out.println("ECmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH, nodeId = "f", nodeType = NodeTypeEnum.SWITCH)
+    public String processF(NodeComponent bindCmp) {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "f", nodeType = NodeTypeEnum.SWITCH)
+    public void rollbackF() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = "g", nodeType = NodeTypeEnum.FOR)
+    public int processG(NodeComponent bindCmp) {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "g", nodeType = NodeTypeEnum.FOR)
+    public void rollbackG() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK, nodeId = "h", nodeType = NodeTypeEnum.BREAK)
+    public int processH(NodeComponent bindCmp) {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "h", nodeType = NodeTypeEnum.BREAK)
+    public void rollbackH() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "i", nodeType = NodeTypeEnum.ITERATOR)
+    public Iterator<?> processI(NodeComponent bindCmp) {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "i", nodeType = NodeTypeEnum.ITERATOR)
+    public void rollbackI() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE, nodeId = "w", nodeType = NodeTypeEnum.WHILE)
+    public boolean processW(NodeComponent bindCmp) {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "w", nodeType = NodeTypeEnum.WHILE)
+    public void rollbackW() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF, nodeId = "x", nodeType = NodeTypeEnum.IF)
+    public boolean processX(NodeComponent bindCmp) {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "x", nodeType = NodeTypeEnum.IF)
+    public void rollbackX() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+
+}

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

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

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

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>

+ 95 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java

@@ -0,0 +1,95 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import com.yomahub.liteflow.test.whenTimeOut.WhenTimeOutELDeclSpringbootTest1;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.annotation.Resource;
+
+
+@ExtendWith(SpringExtension.class)
+@TestPropertySource(value = "classpath:/rollback/application.properties")
+@SpringBootTest(classes = RollbackELDeclSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({ "com.yomahub.liteflow.test.rollback.cmp" })
+public class RollbackELDeclSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+}

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

@@ -0,0 +1,25 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ACmp executed!");
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ACmp rollback!");
+	}
+}

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

@@ -0,0 +1,31 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+public class BCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("BCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("BCmp rollback!");
+	}
+
+	@Override
+	public boolean isContinueOnError() {
+		return true;
+	}
+}

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

@@ -0,0 +1,21 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("c")
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

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

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("d")
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("DCmp rollback!");
+	}
+}

+ 26 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("e")
+public class ECmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ECmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ECmp rollback!");
+	}
+}

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

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("f")
+public class FCmp extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeForComponent;
+import org.springframework.stereotype.Component;
+
+@Component("g")
+public class GCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import org.springframework.stereotype.Component;
+
+@Component("h")
+public class HCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+}

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

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("i")
+public class ICmp extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+import org.springframework.stereotype.Component;
+
+@Component("w")
+public class WCmp extends NodeWhileComponent {
+
+    @Override
+    public boolean processWhile() throws Exception {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+import org.springframework.stereotype.Component;
+
+@Component("x")
+public class XCmp extends NodeIfComponent {
+    @Override
+    public boolean processIf() throws Exception {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+}

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

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

+ 34 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);;
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>

+ 92 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java

@@ -0,0 +1,92 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.core.FlowExecutorHolder;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.property.LiteflowConfig;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+
+
+public class RollbackTest extends BaseTest {
+
+	private static FlowExecutor flowExecutor;
+
+	@BeforeAll
+	public static void init() {
+		LiteflowConfig config = new LiteflowConfig();
+		config.setRuleSource("rollback/flow.el.xml");
+		flowExecutor = FlowExecutorHolder.loadInstance(config);
+	}
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+
+}

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

@@ -0,0 +1,23 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ACmp executed!");
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ACmp rollback!");
+	}
+}

+ 29 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/BCmp.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.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class BCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("BCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("BCmp rollback!");
+	}
+
+	@Override
+	public boolean isContinueOnError() {
+		return true;
+	}
+}

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

@@ -0,0 +1,19 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

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

@@ -0,0 +1,24 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("DCmp rollback!");
+	}
+}

+ 24 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java

@@ -0,0 +1,24 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+
+public class ECmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ECmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ECmp rollback!");
+	}
+}

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

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+
+public class FCmp extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeForComponent;
+
+public class GCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+
+public class HCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+}

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

@@ -0,0 +1,21 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class ICmp extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+}

+ 17 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java

@@ -0,0 +1,17 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+
+public class WCmp extends NodeWhileComponent {
+
+    @Override
+    public boolean processWhile() throws Exception {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+}

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

@@ -0,0 +1,16 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+
+public class XCmp extends NodeIfComponent {
+    @Override
+    public boolean processIf() throws Exception {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+}

+ 47 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml

@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <nodes>
+        <node id="a" class="com.yomahub.liteflow.test.rollback.cmp.ACmp"/>
+        <node id="b" class="com.yomahub.liteflow.test.rollback.cmp.BCmp"/>
+        <node id="c" class="com.yomahub.liteflow.test.rollback.cmp.CCmp"/>
+        <node id="d" class="com.yomahub.liteflow.test.rollback.cmp.DCmp"/>
+        <node id="e" class="com.yomahub.liteflow.test.rollback.cmp.ECmp"/>
+        <node id="f" class="com.yomahub.liteflow.test.rollback.cmp.FCmp"/>
+        <node id="g" class="com.yomahub.liteflow.test.rollback.cmp.GCmp"/>
+        <node id="h" class="com.yomahub.liteflow.test.rollback.cmp.HCmp"/>
+        <node id="i" class="com.yomahub.liteflow.test.rollback.cmp.ICmp"/>
+        <node id="w" class="com.yomahub.liteflow.test.rollback.cmp.WCmp"/>
+        <node id="x" class="com.yomahub.liteflow.test.rollback.cmp.XCmp"/>
+    </nodes>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);;
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>

+ 1 - 1
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/parallelLoop/ParallelLoopELSpringbootTest.java

@@ -13,7 +13,7 @@ import org.noear.solon.annotation.Inject;
 import org.noear.solon.test.SolonJUnit5Extension;
 import org.noear.solon.test.annotation.TestPropertySource;
 
-import javax.annotation.Resource;
+//import javax.annotation.Resource;
 import java.util.List;
 import java.util.regex.Pattern;
 

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

@@ -0,0 +1,87 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.noear.solon.annotation.Inject;
+import org.noear.solon.test.SolonJUnit5Extension;
+import org.noear.solon.test.annotation.TestPropertySource;
+
+
+@ExtendWith(SolonJUnit5Extension.class)
+@TestPropertySource("classpath:/rollback/application.properties")
+public class RollbackSpringbootTest extends BaseTest {
+
+	@Inject
+	private FlowExecutor flowExecutor;
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+}

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

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.noear.solon.annotation.Component;
+
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ACmp executed!");
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ACmp rollback!");
+	}
+}

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

@@ -0,0 +1,32 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.noear.solon.annotation.Component;
+
+
+@Component("b")
+public class BCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("BCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("BCmp rollback!");
+	}
+
+	@Override
+	public boolean isContinueOnError() {
+		return true;
+	}
+}

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

@@ -0,0 +1,22 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.noear.solon.annotation.Component;
+
+
+@Component("c")
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

+ 27 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/DCmp.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.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.noear.solon.annotation.Component;
+
+
+@Component("d")
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("DCmp rollback!");
+	}
+}

+ 27 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.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.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.noear.solon.annotation.Component;
+
+
+@Component("e")
+public class ECmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ECmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ECmp rollback!");
+	}
+}

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

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.noear.solon.annotation.Component;
+
+@Component("f")
+public class FCmp extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeForComponent;
+import org.noear.solon.annotation.Component;
+
+@Component("g")
+public class GCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import org.noear.solon.annotation.Component;
+
+@Component("h")
+public class HCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+}

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

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.noear.solon.annotation.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("i")
+public class ICmp extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+import org.noear.solon.annotation.Component;
+
+@Component("w")
+public class WCmp extends NodeWhileComponent {
+
+    @Override
+    public boolean processWhile() throws Exception {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+import org.noear.solon.annotation.Component;;
+
+@Component("x")
+public class XCmp extends NodeIfComponent {
+    @Override
+    public boolean processIf() throws Exception {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+}

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

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

+ 34 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);;
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>

+ 90 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java

@@ -0,0 +1,90 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.test.context.TestPropertySource;
+
+import javax.annotation.Resource;
+
+
+@TestPropertySource(value = "classpath:/rollback/application.properties")
+@SpringBootTest(classes = RollbackSpringbootTest.class)
+@EnableAutoConfiguration
+@ComponentScan({ "com.yomahub.liteflow.test.rollback.cmp" })
+public class RollbackSpringbootTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+}

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

@@ -0,0 +1,25 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ACmp executed!");
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ACmp rollback!");
+	}
+}

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

@@ -0,0 +1,32 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import com.yomahub.liteflow.flow.element.Rollbackable;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+public class BCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("BCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("BCmp rollback!");
+	}
+
+	@Override
+	public boolean isContinueOnError() {
+		return true;
+	}
+}

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

@@ -0,0 +1,21 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("c")
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

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

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("d")
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("DCmp rollback!");
+	}
+}

+ 26 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("e")
+public class ECmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ECmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ECmp rollback!");
+	}
+}

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

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("f")
+public class FCmp extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeForComponent;
+import org.springframework.stereotype.Component;
+
+@Component("g")
+public class GCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import org.springframework.stereotype.Component;
+
+@Component("h")
+public class HCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+}

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

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("i")
+public class ICmp extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+import org.springframework.stereotype.Component;
+
+@Component("w")
+public class WCmp extends NodeWhileComponent {
+
+    @Override
+    public boolean processWhile() throws Exception {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+import org.springframework.stereotype.Component;
+
+@Component("x")
+public class XCmp extends NodeIfComponent {
+    @Override
+    public boolean processIf() throws Exception {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+}

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

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

+ 34 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);;
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>

+ 89 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java

@@ -0,0 +1,89 @@
+package com.yomahub.liteflow.test.rollback;
+
+import com.yomahub.liteflow.core.FlowExecutor;
+import com.yomahub.liteflow.flow.LiteflowResponse;
+import com.yomahub.liteflow.test.BaseTest;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.annotation.Resource;
+
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration("classpath:/rollback/application.xml")
+public class RollbackSpringTest extends BaseTest {
+
+	@Resource
+	private FlowExecutor flowExecutor;
+
+	// 在流程正常执行结束情况下的测试
+	@Test
+	public void testRollback() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+	// 对串行编排与并行编排语法的测试
+	@Test
+	public void testWhenAndThen() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a", response.getRollbackStepStr());
+	}
+
+	// 对条件编排语法的测试
+	@Test
+	public void testIf() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>x", response.getRollbackStepStr());
+	}
+
+	// 对选择编排语法的测试
+	@Test
+	public void testSwitch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>f", response.getRollbackStepStr());
+	}
+
+	// 对FOR循环编排语法的测试
+	@Test
+	public void testFor() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("h==>b==>g", response.getRollbackStepStr());
+	}
+
+	// 对WHILE循环编排语法的测试
+	@Test
+	public void testWhile() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>w", response.getRollbackStepStr());
+	}
+
+	// 对ITERATOR迭代循环编排语法的测试
+	@Test
+	public void testIterator() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
+		Assertions.assertFalse(response.isSuccess());
+		Assertions.assertEquals("d==>b==>a==>i", response.getRollbackStepStr());
+	}
+
+	@Test
+	// 对捕获异常表达式的测试
+	public void testCatch() throws Exception {
+		LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
+		Assertions.assertTrue(response.isSuccess());
+		Assertions.assertNull(response.getCause());
+		Assertions.assertEquals("", response.getRollbackStepStr());
+	}
+
+
+}

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

@@ -0,0 +1,25 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("a")
+public class ACmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ACmp executed!");
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ACmp rollback!");
+	}
+}

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

@@ -0,0 +1,31 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("b")
+public class BCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("BCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("BCmp rollback!");
+	}
+
+	@Override
+	public boolean isContinueOnError() {
+		return true;
+	}
+}

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

@@ -0,0 +1,21 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("c")
+public class CCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("CCmp executed!");
+	}
+
+}

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

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("d")
+public class DCmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("DCmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("DCmp rollback!");
+	}
+}

+ 26 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ECmp.java

@@ -0,0 +1,26 @@
+/**
+ * <p>Title: liteflow</p>
+ * <p>Description: 轻量级的组件式流程框架</p>
+ * @author Bryan.Zhang
+ * @email weenyc31@163.com
+ * @Date 2020/4/1
+ */
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeComponent;
+import org.springframework.stereotype.Component;
+
+@Component("e")
+public class ECmp extends NodeComponent {
+
+	@Override
+	public void process() {
+		System.out.println("ECmp executed!");
+		throw new RuntimeException();
+	}
+
+	@Override
+	public void rollback() throws Exception {
+		System.out.println("ECmp rollback!");
+	}
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/FCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeSwitchComponent;
+import org.springframework.stereotype.Component;
+
+@Component("f")
+public class FCmp extends NodeSwitchComponent {
+
+    @Override
+    public String processSwitch() {
+        System.out.println("FCmp executed!");
+        return "abc";
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("FCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/GCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeForComponent;
+import org.springframework.stereotype.Component;
+
+@Component("g")
+public class GCmp extends NodeForComponent {
+
+    @Override
+    public int processFor() throws Exception {
+        System.out.println("GCmp executed!");
+        return 3;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("GCmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/HCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeBreakComponent;
+import org.springframework.stereotype.Component;
+
+@Component("h")
+public class HCmp extends NodeBreakComponent {
+
+    @Override
+    public boolean processBreak() throws Exception {
+        System.out.println("HCmp executed!");
+        throw new RuntimeException();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("HCmp rollback!");
+    }
+}

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

@@ -0,0 +1,23 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import cn.hutool.core.collection.ListUtil;
+import com.yomahub.liteflow.core.NodeIteratorComponent;
+import org.springframework.stereotype.Component;
+
+import java.util.Iterator;
+import java.util.List;
+
+@Component("i")
+public class ICmp extends NodeIteratorComponent {
+
+    @Override
+    public Iterator<?> processIterator() throws Exception {
+        List<String> list = ListUtil.toList("jack", "mary", "tom");
+        return list.iterator();
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("ICmp rollback!");
+    }
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/WCmp.java

@@ -0,0 +1,19 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeWhileComponent;
+import org.springframework.stereotype.Component;
+
+@Component("w")
+public class WCmp extends NodeWhileComponent {
+
+    @Override
+    public boolean processWhile() throws Exception {
+        System.out.println("WCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("WCmp rollback!");
+    }
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/XCmp.java

@@ -0,0 +1,18 @@
+package com.yomahub.liteflow.test.rollback.cmp;
+
+import com.yomahub.liteflow.core.NodeIfComponent;
+import org.springframework.stereotype.Component;
+
+@Component("x")
+public class XCmp extends NodeIfComponent {
+    @Override
+    public boolean processIf() throws Exception {
+        System.out.println("XCmp executed!");
+        return true;
+    }
+
+    @Override
+    public void rollback() throws Exception {
+        System.out.println("XCmp rollback!");
+    }
+}

+ 23 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/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.rollback.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="rollback/flow.el.xml"/>
+    </bean>
+
+    <bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor">
+        <constructor-arg name="liteflowConfig" ref="liteflowConfig"/>
+    </bean>
+</beans>

+ 34 - 0
liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<flow>
+    <chain name="chain1">
+        THEN( a, b, WHEN(c, d).ignoreError(true), CATCH(e) );
+    </chain>
+
+    <chain name="chain2">
+        THEN( a, b, WHEN(c, d) );
+    </chain>
+
+    <chain name="chain3">
+        THEN( IF(x, d, a), CATCH(IF(x, d, a)) );
+    </chain>
+
+    <chain name="chain4">
+        SWITCH(f).TO(a, b).DEFAULT(d);
+    </chain>
+
+    <chain name="chain5">
+        FOR(g).DO(THEN(b, c)).BREAK(h);;
+    </chain>
+
+    <chain name="chain6">
+        WHILE(w).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain7">
+        ITERATOR(i).DO(THEN(a, b, d));
+    </chain>
+
+    <chain name="chain8">
+        CATCH( THEN(b, c, d) ).DO(a);
+    </chain>
+</flow>