!208 实现组件的回滚机制

Merge pull request !208 from Rain/dev
This commit is contained in:
铂赛东 2023-08-20 08:01:12 +00:00 committed by Gitee
commit 9ae5751305
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
82 changed files with 2374 additions and 3 deletions

View File

@ -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)) {

View File

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

View File

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

View File

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

View File

@ -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的异步组件然后再异步执行

View File

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

View File

@ -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)) {

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

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

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

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

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

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

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

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

View File

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

View File

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

View File

@ -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!");
}
}

View File

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

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

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

View File

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