feature #I4HGOW 支持链路的前置和后置节点

This commit is contained in:
bryan31
2021-11-10 16:47:52 +08:00
parent 448fa82023
commit ecfebfdc19
20 changed files with 346 additions and 49 deletions

View File

@@ -16,6 +16,7 @@ import com.yomahub.liteflow.entity.data.Slot;
import com.yomahub.liteflow.entity.flow.parallel.CompletableFutureTimeout;
import com.yomahub.liteflow.entity.flow.parallel.ParallelSupplier;
import com.yomahub.liteflow.entity.flow.parallel.WhenFutureObj;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.exception.WhenExecuteException;
@@ -72,15 +73,31 @@ public class Chain implements Executable {
Slot slot = DataBus.getSlot(slotIndex);
//循环chain里包含的condition每一个condition有可能是then也有可能是when
//when的话为异步用闭锁进行等待所有when结束后才能进入下一个condition
for (Condition condition : conditionList) {
if (condition instanceof ThenCondition) {
for (Executable executableItem : condition.getNodeList()) {
//先把finally的节点过滤出来
List<Condition> finallyConditionList = conditionList.stream().filter(condition ->
condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())).collect(Collectors.toList());
//循环chain里包含的condition每一个condition分四种类型pre,then,when,finally
//这里conditionList其实已经是有序的pre一定在最前面finally一定在最后面
try{
for (Condition condition : conditionList) {
if (condition instanceof PreCondition){
for (Executable executableItem : condition.getNodeList()) {
executableItem.execute(slotIndex);
}
} else if (condition instanceof ThenCondition) {
for (Executable executableItem : condition.getNodeList()) {
executableItem.execute(slotIndex);
}
} else if (condition instanceof WhenCondition) {
executeAsyncCondition((WhenCondition) condition, slotIndex, slot.getRequestId());
}
}
}finally {
for (Condition finallyCondition : finallyConditionList){
for(Executable executableItem : finallyCondition.getNodeList()){
executableItem.execute(slotIndex);
}
} else if (condition instanceof WhenCondition) {
executeAsyncCondition((WhenCondition) condition, slotIndex, slot.getRequestId());
}
}
}

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.entity.flow;
/**
* 前置Condition
* @author Bryan.Zhang
* @since 2.6.4
*/
public class FinallyCondition extends Condition {
public FinallyCondition(Condition condition){
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());
}
}

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.entity.flow;
/**
* 前置Condition
* @author Bryan.Zhang
* @since 2.6.4
*/
public class PreCondition extends Condition {
public PreCondition(Condition condition){
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());
}
}

View File

@@ -7,18 +7,12 @@
*/
package com.yomahub.liteflow.entity.flow;
import java.util.List;
/**
* 串行器
* @author Bryan.Zhang
*/
public class ThenCondition extends Condition {
public ThenCondition(List<Executable> nodeList) {
super(nodeList);
}
public ThenCondition(Condition condition){
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());

View File

@@ -7,25 +7,12 @@
*/
package com.yomahub.liteflow.entity.flow;
import java.util.List;
/**
* 并行器
* @author Bryan.Zhang
*/
public class WhenCondition extends Condition{
public WhenCondition(List<Executable> nodeList) {
super(nodeList);
super.setErrorResume(true);
}
public WhenCondition(List<Executable> nodeList, boolean errorResume) {
super(nodeList);
super.setErrorResume(errorResume);
}
public WhenCondition(Condition condition) {
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());

View File

@@ -2,7 +2,9 @@ package com.yomahub.liteflow.enums;
public enum ConditionTypeEnum {
TYPE_THEN("then","then"),
TYPE_WHEN("when","when")
TYPE_WHEN("when","when"),
TYPE_PRE("pre","pre"),
TYPE_FINALLY("finally","finally")
;
private String type;
private String name;

View File

@@ -4,9 +4,8 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import com.yomahub.liteflow.entity.flow.Condition;
import com.yomahub.liteflow.entity.flow.ThenCondition;
import com.yomahub.liteflow.entity.flow.WhenCondition;
import com.google.common.collect.Lists;
import com.yomahub.liteflow.entity.flow.*;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.ConfigErrorException;
import org.springframework.core.io.Resource;
@@ -15,12 +14,10 @@ import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
/**
* 虽则Parser的抽象类所有的parser需要继承这个抽象类
* @author guodongqing
* @since 2.5.0
*/
@@ -28,10 +25,15 @@ public abstract class FlowParser {
public abstract void parseMain(List<String> pathList) throws Exception;
public abstract void parse(List<String> contentList) throws Exception ;
public abstract void parse(List<String> contentList) throws Exception;
protected void buildBaseFlowConditions(List<Condition> conditionList,Condition condition){
if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN.getType())) {
protected void buildConditions(List<Condition> conditionList, Condition condition) {
//这里进行合并逻辑
//对于then来说相邻的2个then会合并成一个condition
//对于when来说相同组的when会合并成一个condition不同组的when还是会拆开
if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType())) {
conditionList.add(new PreCondition(condition));
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN.getType())) {
if (conditionList.size() >= 1 &&
CollectionUtil.getLast(conditionList) instanceof ThenCondition) {
CollectionUtil.getLast(conditionList).getNodeList().addAll(condition.getNodeList());
@@ -46,24 +48,37 @@ public abstract class FlowParser {
} else {
conditionList.add(new WhenCondition(condition));
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())) {
conditionList.add(new FinallyCondition(condition));
}
//每一次build之后对conditionList进行排序pre最前面finally最后
//这里为什么要排序因为在声明的时候哪怕有人不把pre放最前finally放最后但最终也要确保是正确的顺序
CollectionUtil.sort(conditionList, (o1, o2) -> {
if (o1.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType()) || o2.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())){
return -1;
} else if (o2.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType()) || o1.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())){
return 1;
}
return 0;
});
}
/**
* 根据配置的ruleSource查找匹配的资源
* 根据配置的ruleSource查找匹配的资源
*/
protected Resource[] matchRuleResources(final List<String> pathList) throws IOException {
protected Resource[] matchRuleResources(final List<String> pathList) throws IOException {
Assert.notEmpty(pathList, "rule source must not be null");
List<Resource> allResource = new ArrayList<>();
for (String path : pathList){
for (String path : pathList) {
String locationPattern = path;
if (!locationPattern.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
locationPattern = ResourceUtils.CLASSPATH_URL_PREFIX + locationPattern;
}
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources(locationPattern);
if(ArrayUtil.isEmpty(resources)) {
if (ArrayUtil.isEmpty(resources)) {
throw new ConfigErrorException("config error,please check rule source property");
}
allResource.addAll(ListUtil.toList(resources));
@@ -72,7 +87,7 @@ public abstract class FlowParser {
//如果有多个资源,检查资源都是同一个类型,如果出现不同类型的配置,则抛出错误提示
Set<String> fileTypeSet = new HashSet<>();
allResource.forEach(resource -> fileTypeSet.add(FileUtil.extName(resource.getFilename())));
if (fileTypeSet.size() != 1){
if (fileTypeSet.size() != 1) {
throw new ConfigErrorException("config error,please use the same type of configuration");
}

View File

@@ -131,14 +131,13 @@ public abstract class JsonFlowParser extends FlowParser {
String condArrayStr;
String[] condArray;
List<Executable> chainNodeList;
List<Condition> conditionList;
List<Condition> conditionList = new ArrayList<>();
String group;
String errorResume;
Condition condition;
String any;
String chainName = chainObject.getString("name");
JSONArray conditionArray = chainObject.getJSONArray("condition");
conditionList = new ArrayList<>();
for (Object o : conditionArray) {
JSONObject condObject = (JSONObject) o;
String condType = condObject.getString("type");
@@ -199,7 +198,7 @@ public abstract class JsonFlowParser extends FlowParser {
condition.setAny(any.equals(Boolean.TRUE.toString()));
condition.setConditionType(condType);
condition.setNodeList(chainNodeList);
super.buildBaseFlowConditions(conditionList, condition);
super.buildConditions(conditionList, condition);
}
FlowBus.addChain(chainName, new Chain(chainName, conditionList));
}

View File

@@ -135,10 +135,9 @@ public abstract class XmlFlowParser extends FlowParser {
Condition condition;
Element condE;
List<Executable> chainNodeList;
List<Condition> conditionList;
List<Condition> conditionList = new ArrayList<>();
String chainName = e.attributeValue("name");
conditionList = new ArrayList<>();
for (Iterator<Element> it = e.elementIterator(); it.hasNext(); ) {
condE = it.next();
condArrayStr = condE.attributeValue("value");
@@ -198,7 +197,7 @@ public abstract class XmlFlowParser extends FlowParser {
condition.setAny(any.equals(Boolean.TRUE.toString()));
condition.setConditionType(condE.getName());
condition.setNodeList(chainNodeList);
super.buildBaseFlowConditions(conditionList, condition);
super.buildConditions(conditionList, condition);
}
FlowBus.addChain(chainName, new Chain(chainName, conditionList));
}

View File

@@ -0,0 +1,56 @@
package com.yomahub.liteflow.test.preAndFinally;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境下pre节点和finally节点的测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/preAndFinally/application.properties")
@SpringBootTest(classes = PreAndFinallySpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.preAndFinally.cmp"})
public class PreAndFinallySpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试普通的pre和finally节点
@Test
public void testPreAndFinally1() throws Exception{
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>a==>b==>c==>f1==>f2",response.getSlot().printStep());
}
//测试pre和finally节点不放在开头和结尾的情况
@Test
public void testPreAndFinally2() throws Exception{
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain2", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>a==>b==>c==>f1==>f2",response.getSlot().printStep());
}
//测试有节点报错是否还执行finally节点的情况其中d节点会报错但依旧执行f1,f2节点
@Test
public void testPreAndFinally3() throws Exception{
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain3", "arg");
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("p1==>p2==>a==>d==>f1==>f2", response.getSlot().printStep());
}
}

View File

@@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.preAndFinally.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!");
}
}

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

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.preAndFinally.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,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.preAndFinally.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("CCmp executed!");
int i = 1/0;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
<pre value="p1,p2"/>
<then value="a,b,c"/>
<finally value="f1,f2"/>
</chain>
<chain name="chain2">
<then value="a"/>
<pre value="p1,p2"/>
<finally value="f1,f2"/>
<then value="b,c"/>
</chain>
<chain name="chain3">
<pre value="p1,p2"/>
<then value="a,d,c"/>
<finally value="f1,f2"/>
</chain>
</flow>