Merge branch 'dev' of https://gitee.com/dromara/liteFlow into issues/I5BR5M

This commit is contained in:
tangkc
2022-06-13 13:57:30 +08:00
65 changed files with 1091 additions and 546 deletions

View File

@@ -3,8 +3,8 @@ package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollectionUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.condition.*;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.condition.*;
import java.util.ArrayList;
import java.util.List;
@@ -18,11 +18,18 @@ public class LiteFlowChainBuilder {
private Chain chain;
//这是主体的Condition不包含前置和后置
//声明这个变量而不是用chain.getConditionList的目的是为了辅助平滑加载
//虽然FlowBus里面的map都是CopyOnWrite类型的但是在buildCondition的时候为了平滑加载所以不能事先把chain.getConditionList给设为空List
//所以在这里做一个缓存等conditionList全部build完毕后再去一次性替换chain里面的conditionList
private final List<Condition> conditionList;
//前置处理Condition用来区别主体的Condition
private final List<Condition> preConditionList;
//后置处理Condition用来区别主体的Condition
private final List<Condition> finallyConditionList;
public static LiteFlowChainBuilder createChain(){
return new LiteFlowChainBuilder();
}
@@ -30,6 +37,8 @@ public class LiteFlowChainBuilder {
public LiteFlowChainBuilder(){
chain = new Chain();
conditionList = new ArrayList<>();
preConditionList = new ArrayList<>();
finallyConditionList = new ArrayList<>();
}
//在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
@@ -52,6 +61,8 @@ public class LiteFlowChainBuilder {
public void build(){
this.chain.setConditionList(this.conditionList);
this.chain.setPreConditionList(this.preConditionList);
this.chain.setFinallyConditionList(this.finallyConditionList);
FlowBus.addChain(this.chain);
}
@@ -60,35 +71,24 @@ public class LiteFlowChainBuilder {
//对于then来说相邻的2个then会合并成一个condition
//对于when来说相同组的when会合并成一个condition不同组的when还是会拆开
if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE)) {
this.conditionList.add(new PreCondition(condition));
this.preConditionList.add(condition);
}else if(condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)) {
this.finallyConditionList.add(condition);
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN)) {
if (this.conditionList.size() >= 1 &&
CollectionUtil.getLast(this.conditionList) instanceof ThenCondition) {
CollectionUtil.getLast(this.conditionList).getNodeList().addAll(condition.getNodeList());
} else {
this.conditionList.add(new ThenCondition(condition));
this.conditionList.add(condition);
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_WHEN)) {
if (this.conditionList.size() > 1 &&
if (this.conditionList.size() >= 1 &&
CollectionUtil.getLast(this.conditionList) instanceof WhenCondition &&
CollectionUtil.getLast(this.conditionList).getGroup().equals(condition.getGroup())) {
CollectionUtil.getLast(this.conditionList).getNodeList().addAll(condition.getNodeList());
} else {
this.conditionList.add(new WhenCondition(condition));
this.conditionList.add(condition);
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)) {
this.conditionList.add(new FinallyCondition(condition));
}
//每一次build之后对conditionList进行排序pre最前面finally最后
//这里为什么要排序因为在声明的时候哪怕有人不把pre放最前finally放最后但最终也要确保是正确的顺序
CollectionUtil.sort(this.conditionList, (o1, o2) -> {
if (o1.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o2.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){
return -1;
} else if (o2.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o1.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){
return 1;
}
return 0;
});
}
}

View File

@@ -5,7 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.*;
import com.yomahub.liteflow.builder.entity.ExecutableEntity;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
@@ -28,29 +28,38 @@ public class LiteFlowConditionBuilder {
protected Condition condition;
public static LiteFlowConditionBuilder createCondition(ConditionTypeEnum conditionType){
return new LiteFlowConditionBuilder(conditionType);
switch (conditionType){
case TYPE_THEN:
return createThenCondition();
case TYPE_WHEN:
return createWhenCondition();
case TYPE_PRE:
return createPreCondition();
case TYPE_FINALLY:
return createFinallyCondition();
default:
return null;
}
}
public static LiteFlowConditionBuilder createThenCondition(){
return new LiteFlowConditionBuilder(ConditionTypeEnum.TYPE_THEN);
return new LiteFlowConditionBuilder(new ThenCondition());
}
public static LiteFlowWhenConditionBuilder createWhenCondition(){
return new LiteFlowWhenConditionBuilder(ConditionTypeEnum.TYPE_WHEN);
return new LiteFlowWhenConditionBuilder(new WhenCondition());
}
public static LiteFlowConditionBuilder createPreCondition(){
return new LiteFlowConditionBuilder(ConditionTypeEnum.TYPE_PRE);
return new LiteFlowConditionBuilder(new PreCondition());
}
public static LiteFlowConditionBuilder createFinallyCondition(){
return new LiteFlowConditionBuilder(ConditionTypeEnum.TYPE_FINALLY);
return new LiteFlowConditionBuilder(new FinallyCondition());
}
public LiteFlowConditionBuilder(ConditionTypeEnum conditionType){
this.condition = new Condition();
this.condition.setConditionType(conditionType);
this.condition.setNodeList(new ArrayList<>());
public LiteFlowConditionBuilder(Condition condition){
this.condition = condition;
}
public LiteFlowConditionBuilder setValue(String value){

View File

@@ -3,6 +3,7 @@ package com.yomahub.liteflow.builder;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.common.LocalDefaultFlowConstant;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.element.condition.Condition;
/**
* WhenCondition基于代码形式的组装器
@@ -12,8 +13,8 @@ import com.yomahub.liteflow.enums.ConditionTypeEnum;
*/
public class LiteFlowWhenConditionBuilder extends LiteFlowConditionBuilder{
public LiteFlowWhenConditionBuilder(ConditionTypeEnum conditionType) {
super(conditionType);
public LiteFlowWhenConditionBuilder(Condition condition) {
super(condition);
}
public LiteFlowWhenConditionBuilder setErrorResume(boolean errorResume){

View File

@@ -0,0 +1,93 @@
package com.yomahub.liteflow.builder.prop;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
/**
* 构建 chain 的中间属性
*/
public class ChainPropBean {
/**
* 执行规则
*/
String condValueStr;
/**
* 分组
*/
String group;
/**
* 是否抛出异常
*/
String errorResume;
/**
* 满足任意条件,执行完成
*/
String any;
/**
* 指定线程池
*/
String threadExecutorClass;
/**
* chain 类型
*/
ConditionTypeEnum conditionType;
public String getCondValueStr() {
return condValueStr;
}
public ChainPropBean setCondValueStr(String condValueStr) {
this.condValueStr = condValueStr;
return this;
}
public String getGroup() {
return group;
}
public ChainPropBean setGroup(String group) {
this.group = group;
return this;
}
public String getErrorResume() {
return errorResume;
}
public ChainPropBean setErrorResume(String errorResume) {
this.errorResume = errorResume;
return this;
}
public String getAny() {
return any;
}
public ChainPropBean setAny(String any) {
this.any = any;
return this;
}
public String getThreadExecutorClass() {
return threadExecutorClass;
}
public ChainPropBean setThreadExecutorClass(String threadExecutorClass) {
this.threadExecutorClass = threadExecutorClass;
return this;
}
public ConditionTypeEnum getConditionType() {
return conditionType;
}
public ChainPropBean setConditionType(ConditionTypeEnum conditionType) {
this.conditionType = conditionType;
return this;
}
}

View File

@@ -0,0 +1,91 @@
package com.yomahub.liteflow.builder.prop;
/**
* 构建 node 的中间属性
*/
public class NodePropBean {
/**
* id
*/
String id;
/**
* 名称
*/
String name;
/**
* 类
*/
String clazz;
/**
* 脚本
*/
String script;
/**
* 类型
*/
String type;
/**
* 脚本存放位置
*/
String file;
public String getId() {
return id;
}
public NodePropBean setId(String id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public NodePropBean setName(String name) {
this.name = name;
return this;
}
public String getClazz() {
return clazz;
}
public NodePropBean setClazz(String clazz) {
this.clazz = clazz;
return this;
}
public String getScript() {
return script;
}
public NodePropBean setScript(String script) {
this.script = script;
return this;
}
public String getType() {
return type;
}
public NodePropBean setType(String type) {
this.type = type;
return this;
}
public String getFile() {
return file;
}
public NodePropBean setFile(String file) {
this.file = file;
return this;
}
}

View File

@@ -0,0 +1,39 @@
package com.yomahub.liteflow.common;
/**
* Chain 常量
*
* @author tangkc
*/
public interface ChainConstant {
String CHAIN = "chain";
String FLOW = "flow";
String NODES = "nodes";
String NODE = "node";
String ID = "id";
String _CLASS = "class";
String FILE = "file";
String NAME = "name";
String VALUE = "value";
String ERROR_RESUME = "errorResume";
String GROUP = "group";
String ANY = "any";
String THREAD_EXECUTOR_CLASS = "threadExecutorClass";
String CONDITION = "condition";
String TYPE = "type";
}

View File

@@ -385,8 +385,6 @@ public class FlowExecutor {
String errorMsg = StrUtil.format("[{}]:couldn't find chain with the id[{}]", slot.getRequestId(), chainId);
throw new ChainNotFoundException(errorMsg);
}
// 执行前置
chain.executePre(slotIndex);
// 执行chain
chain.execute(slotIndex);
} catch (ChainEndException e) {
@@ -401,15 +399,6 @@ public class FlowExecutor {
}
slot.setException(e);
} finally {
try{
if (ObjectUtil.isNotNull(chain)){
chain.executeFinally(slotIndex);
}
}catch (Exception e){
String errMsg = StrUtil.format("[{}]:an exception occurred during the finally Component execution in chain[{}]", slot.getRequestId(), chain.getChainName());
LOG.error(errMsg, e);
}
if (!isInnerChain) {
slot.printStep();
DataBus.releaseSlot(slotIndex);

View File

@@ -18,6 +18,7 @@ import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.spi.holder.CmpAroundAspectHolder;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -303,4 +304,12 @@ public abstract class NodeComponent{
public String getChainName(){
return getSlot().getChainName();
}
public String getDisplayName(){
if(StringUtils.isEmpty(this.name)){
return this.nodeId;
}else {
return this.nodeId + "(" + this.name + ")";
}
}
}

View File

@@ -19,7 +19,7 @@ public class ScriptComponent extends NodeComponent{
}
public void loadScript(String script) {
log.info("load script for component[{}]", getNodeId());
log.info("load script for component[{}][{}]", getNodeId(),getDisplayName());
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@@ -12,5 +12,5 @@ package com.yomahub.liteflow.enums;
* @author Bryan.Zhang
*/
public enum ExecuteTypeEnum {
CHAIN,NODE
CHAIN,CONDITION,NODE
}

View File

@@ -33,6 +33,7 @@ import com.yomahub.liteflow.spi.holder.ContextAwareHolder;
import com.yomahub.liteflow.spi.local.LocalContextAware;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -160,7 +161,7 @@ public class FlowBus {
nodeMap.put(nodeId, node);
} catch (Exception e) {
String error = StrUtil.format("component[{}] register error", cmpClazz.getName());
String error = StrUtil.format("component[{}][{}] register error", cmpClazz.getName(), StringUtils.isEmpty(name)?nodeId:nodeId+"("+name+")");
LOG.error(error, e);
throw new ComponentCannotRegisterException(error);
}

View File

@@ -1,21 +0,0 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.condition;
/**
* 前置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

@@ -1,21 +0,0 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.condition;
/**
* 前置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

@@ -1,20 +0,0 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.condition;
/**
* 串行器
* @author Bryan.Zhang
*/
public class ThenCondition extends Condition {
public ThenCondition(Condition condition){
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());
}
}

View File

@@ -1,25 +0,0 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.condition;
/**
* 并行器
* @author Bryan.Zhang
*/
public class WhenCondition extends Condition {
public WhenCondition(Condition condition) {
super(condition.getNodeList());
super.setConditionType(condition.getConditionType());
super.setGroup(condition.getGroup());
super.setErrorResume(condition.isErrorResume());
super.setAny(condition.isAny());
super.setThreadExecutorClass(condition.getThreadExecutorClass());
}
}

View File

@@ -9,33 +9,16 @@
package com.yomahub.liteflow.flow.element;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.flow.parallel.CompletableFutureTimeout;
import com.yomahub.liteflow.flow.parallel.ParallelSupplier;
import com.yomahub.liteflow.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;
import com.yomahub.liteflow.flow.condition.Condition;
import com.yomahub.liteflow.flow.condition.ThenCondition;
import com.yomahub.liteflow.flow.condition.WhenCondition;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.thread.ExecutorHelper;
import com.yomahub.liteflow.flow.element.condition.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* chain对象实现可执行器
@@ -48,8 +31,15 @@ public class Chain implements Executable {
private String chainName;
//主体Condition
private List<Condition> conditionList = new ArrayList<>();
//前置处理Condition用来区别主体的Condition
private List<Condition> preConditionList = new ArrayList<>();
//后置处理Condition用来区别主体的Condition
private List<Condition> finallyConditionList = new ArrayList<>();
public Chain(String chainName){
this.chainName = chainName;
}
@@ -61,6 +51,57 @@ public class Chain implements Executable {
this.conditionList = conditionList;
}
//执行chain的主方法
@Override
public void execute(Integer slotIndex) throws Exception {
if (CollUtil.isEmpty(conditionList)) {
throw new FlowSystemException("no conditionList in this chain[" + chainName + "]");
}
try {
//执行前置
this.executePre(slotIndex);
//执行主体Condition
for (Condition condition : conditionList) {
condition.execute(slotIndex);
}
}catch (ChainEndException e){
//这里单独catch ChainEndException是因为ChainEndException是用户自己setIsEnd抛出的异常
//是属于正常逻辑所以会在FlowExecutor中判断。这里不作为异常处理
throw e;
}catch (Exception e){
//这里事先取到exception set到slot里为了方便finally取到exception
Slot<?> slot = DataBus.getSlot(slotIndex);
slot.setException(e);
throw e;
}finally {
//执行后置
this.executeFinally(slotIndex);
}
}
// 执行pre节点
private void executePre(Integer slotIndex) throws Exception {
for (Condition condition : this.preConditionList){
condition.execute(slotIndex);
}
}
private void executeFinally(Integer slotIndex) throws Exception {
for (Condition condition : this.finallyConditionList){
condition.execute(slotIndex);
}
}
@Override
public ExecuteTypeEnum getExecuteType() {
return ExecuteTypeEnum.CHAIN;
}
@Override
public String getExecuteName() {
return chainName;
}
public List<Condition> getConditionList() {
return conditionList;
}
@@ -77,160 +118,19 @@ public class Chain implements Executable {
this.chainName = chainName;
}
//执行chain的主方法
@Override
public void execute(Integer slotIndex) throws Exception {
if (CollUtil.isEmpty(conditionList)) {
throw new FlowSystemException("no conditionList in this chain[" + chainName + "]");
}
for (Condition condition : conditionList) {
if (condition instanceof ThenCondition) {
for (Executable executableItem : condition.getNodeList()) {
executableItem.execute(slotIndex);
}
} else if (condition instanceof WhenCondition) {
executeAsyncCondition((WhenCondition) condition, slotIndex);
}
}
public List<Condition> getPreConditionList() {
return preConditionList;
}
// 执行pre节点
public void executePre(Integer slotIndex) throws Exception {
doExecuteForPointConditionType(slotIndex, ConditionTypeEnum.TYPE_PRE);
public void setPreConditionList(List<Condition> preConditionList) {
this.preConditionList = preConditionList;
}
public void executeFinally(Integer slotIndex) throws Exception {
doExecuteForPointConditionType(slotIndex, ConditionTypeEnum.TYPE_FINALLY);
public List<Condition> getFinallyConditionList() {
return finallyConditionList;
}
// 执行指定的conditionType的节点
private void doExecuteForPointConditionType(Integer slotIndex, ConditionTypeEnum conditionTypeEnum) throws Exception {
//先把指定condition类型的节点过滤出来
List<Condition> conditions =filterCondition(conditionTypeEnum);
for (Condition condition : conditions){
for(Executable executableItem : condition.getNodeList()){
executableItem.execute(slotIndex);
}
}
}
// 根据节点condition类型过去出节点列表
private List<Condition> filterCondition(ConditionTypeEnum conditionTypeEnum) {
assert conditionTypeEnum != null;
return conditionList.stream().filter(condition ->
condition.getConditionType().equals(conditionTypeEnum)).collect(Collectors.toList());
}
@Override
public ExecuteTypeEnum getExecuteType() {
return ExecuteTypeEnum.CHAIN;
}
@Override
public String getExecuteName() {
return chainName;
}
//使用线程池执行when并发流程
//这块涉及到挺多的多线程逻辑,所以注释比较详细,看到这里的童鞋可以仔细阅读
private void executeAsyncCondition(WhenCondition condition, Integer slotIndex) throws Exception{
Slot slot = DataBus.getSlot(slotIndex);
//此方法其实只会初始化一次Executor不会每次都会初始化。Executor是唯一的
ExecutorService parallelExecutor = ExecutorHelper.loadInstance().buildWhenExecutor(condition.getThreadExecutorClass());
//获得liteflow的参数
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
//定义是否中断参数
//这里为什么要定义成数组呢因为后面lambda要用到根据final不能修改引用的原则这里用了数组对象
final boolean[] interrupted = {false};
//这里主要是做了封装CompletableFuture对象用lumbda表达式做了很多事情这句代码要仔细理清
//1.根据condition.getNodeList()的集合进行流处理用map进行把executable对象转换成List<CompletableFuture<WhenFutureObj>>
//2.在转的过程中套入CompletableFutureTimeout方法进行超时判断如果超时则用WhenFutureObj.timeOut返回超时的对象
//3.第2个参数是主要的本体CompletableFuture传入了ParallelSupplier和线程池对象
List<CompletableFuture<WhenFutureObj>> completableFutureList = condition.getNodeList().stream().filter(executable -> {
try {
return executable.isAccess(slotIndex);
}catch (Exception e){
LOG.error("there was an error when executing the when component isAccess",e);
return false;
}
}).map(executable -> CompletableFutureTimeout.completeOnTimeout(
WhenFutureObj.timeOut(executable.getExecuteName()),
CompletableFuture.supplyAsync(new ParallelSupplier(executable, slotIndex), parallelExecutor),
liteflowConfig.getWhenMaxWaitSeconds(),
TimeUnit.SECONDS
)).collect(Collectors.toList());
CompletableFuture<?> resultCompletableFuture;
//这里判断执行方式
//如果any为false说明这些异步任务全部执行好或者超时才返回
//如果any为true说明这些异步任务只要任意一个执行完成就返回
if(condition.isAny()){
//把这些CompletableFuture通过anyOf合成一个CompletableFuture
resultCompletableFuture = CompletableFuture.anyOf(completableFutureList.toArray(new CompletableFuture[]{}));
}else{
//把这些CompletableFuture通过allOf合成一个CompletableFuture
resultCompletableFuture = CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{}));
}
try {
//进行执行,这句执行完后,就意味着所有的任务要么执行完毕,要么超时返回
resultCompletableFuture.get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("there was an error when executing the CompletableFuture",e);
interrupted[0] = true;
}
//拿到已经完成的CompletableFuture
//如果any为false那么所有任务都已经完成
//如果any为true那么这里拿到的是第一个完成的任务
//这里过滤和转换一起用lumbda做了
List<WhenFutureObj> allCompletableWhenFutureObjList = completableFutureList.stream().filter(f -> {
//过滤出已经完成的,没完成的就直接终止
if (f.isDone()){
return true;
}else{
f.cancel(true);
return false;
}
}).map(f -> {
try {
return f.get();
} catch (InterruptedException | ExecutionException e) {
interrupted[0] = true;
return null;
}
}).collect(Collectors.toList());
//判断超时上面已经拿到了所有已经完成的CompletableFuture
//那我们只要过滤出超时的CompletableFuture
List<WhenFutureObj> timeOutWhenFutureObjList = allCompletableWhenFutureObjList.stream().filter(WhenFutureObj::isTimeout).collect(Collectors.toList());
//输出超时信息
timeOutWhenFutureObjList.forEach(whenFutureObj ->
LOG.warn("requestId [{}] executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", slot.getRequestId(), whenFutureObj.getExecutorName()));
//当配置了errorResume = false出现interrupted或者!f.get()的情况将抛出WhenExecuteException
if (!condition.isErrorResume()) {
if (interrupted[0]) {
throw new WhenExecuteException(StrUtil.format("requestId [{}] when execute interrupted. errorResume [false].", slot.getRequestId()));
}
//循环判断CompletableFuture的返回值如果异步执行失败则抛出相应的业务异常
for(WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList){
if (!whenFutureObj.isSuccess()){
LOG.info(StrUtil.format("requestId [{}] when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName(), slot.getRequestId()));
throw whenFutureObj.getEx();
}
}
} else if (interrupted[0]) {
// 这里由于配置了errorResume所以只打印warn日志
LOG.warn("requestId [{}] executing when condition timeout , but ignore with errorResume.", slot.getRequestId());
}
public void setFinallyConditionList(List<Condition> finallyConditionList) {
this.finallyConditionList = finallyConditionList;
}
}

View File

@@ -126,7 +126,7 @@ public class Node implements Executable,Cloneable{
if (instance.isAccess()) {
//根据配置判断是否打印执行中的日志
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
LOG.info("[{}]:[O]start component[{}] execution",slot.getRequestId(),instance.getClass().getSimpleName());
LOG.info("[{}]:[O]start component[{}][{}] execution",slot.getRequestId(),instance.getClass().getSimpleName(),instance.getDisplayName());
}
//这里开始进行重试的逻辑和主逻辑的运行
@@ -135,12 +135,12 @@ public class Node implements Executable,Cloneable{
nodeExecutor.execute(instance);
//如果组件覆盖了isEnd方法或者在在逻辑中主要调用了setEnd(true)的话,流程就会立马结束
if (instance.isEnd()) {
String errorInfo = StrUtil.format("[{}]:component[{}] lead the chain to end", slot.getRequestId(), instance.getClass().getSimpleName());
String errorInfo = StrUtil.format("[{}]:[{}] lead the chain to end", slot.getRequestId(), instance.getClass().getSimpleName(),instance.getDisplayName());
throw new ChainEndException(errorInfo);
}
} else {
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
LOG.info("[{}]:[X]skip component[{}] execution", slot.getRequestId(), instance.getClass().getSimpleName());
LOG.info("[{}]:[X]skip component[{}][{}] execution", slot.getRequestId(), instance.getClass().getSimpleName(),instance.getDisplayName());
}
}
} catch (ChainEndException e){

View File

@@ -5,24 +5,25 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.condition;
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.collection.CollUtil;
import com.yomahub.liteflow.common.LocalDefaultFlowConstant;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import java.util.ArrayList;
import java.util.List;
/**
* 里面包含了when或者then
* @author Bryan.Zhang
*/
public class Condition {
public abstract class Condition implements Executable{
//condition 类型 参数:ConditionTypeEnum 包含:then when
private ConditionTypeEnum conditionType;
private List<Executable> nodeList;
private List<Executable> nodeList = new ArrayList<>();
//只在when类型下有效以区分当when调用链调用失败时是否继续往下执行 默认false不继续执行
private boolean errorResume = false;
@@ -32,13 +33,18 @@ public class Condition {
//只在when类型下有效为true的话说明在多个并行节点下任意一个成功整个when就成功
private boolean any = false;
// when单独的线程池名称
private String threadExecutorClass;
public Condition(List<Executable> nodeList) {
this.nodeList = nodeList;
@Override
public ExecuteTypeEnum getExecuteType() {
return ExecuteTypeEnum.CONDITION;
}
public Condition() {
@Override
public String getExecuteName() {
return this.getExecuteType().name();
}
public List<Executable> getNodeList() {
@@ -65,13 +71,7 @@ public class Condition {
this.group = group;
}
public ConditionTypeEnum getConditionType() {
return conditionType;
}
public void setConditionType(ConditionTypeEnum conditionType) {
this.conditionType = conditionType;
}
public abstract ConditionTypeEnum getConditionType();
public boolean isAny() {
return any;

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.flow.element.condition;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.element.Executable;
/**
* 前置Condition
* @author Bryan.Zhang
* @since 2.6.4
*/
public class FinallyCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getNodeList()){
executableItem.execute(slotIndex);
}
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_FINALLY;
}
}

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.flow.element.condition;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.element.Executable;
/**
* 前置Condition
* @author Bryan.Zhang
* @since 2.6.4
*/
public class PreCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getNodeList()){
executableItem.execute(slotIndex);
}
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_PRE;
}
}

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.flow.element.condition;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.element.Executable;
/**
* 串行器
* @author Bryan.Zhang
*/
public class ThenCondition extends Condition {
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_THEN;
}
@Override
public void execute(Integer slotIndex) throws Exception {
for (Executable executableItem : this.getNodeList()) {
executableItem.execute(slotIndex);
}
}
}

View File

@@ -0,0 +1,151 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.WhenExecuteException;
import com.yomahub.liteflow.flow.parallel.CompletableFutureTimeout;
import com.yomahub.liteflow.flow.parallel.ParallelSupplier;
import com.yomahub.liteflow.flow.parallel.WhenFutureObj;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* 并行器
* @author Bryan.Zhang
*/
public class WhenCondition extends Condition {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Override
public void execute(Integer slotIndex) throws Exception {
executeAsyncCondition(slotIndex);
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_WHEN;
}
//使用线程池执行when并发流程
//这块涉及到挺多的多线程逻辑,所以注释比较详细,看到这里的童鞋可以仔细阅读
private void executeAsyncCondition(Integer slotIndex) throws Exception{
Slot<?> slot = DataBus.getSlot(slotIndex);
//此方法其实只会初始化一次Executor不会每次都会初始化。Executor是唯一的
ExecutorService parallelExecutor = ExecutorHelper.loadInstance().buildWhenExecutor(this.getThreadExecutorClass());
//获得liteflow的参数
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
//定义是否中断参数
//这里为什么要定义成数组呢因为后面lambda要用到根据final不能修改引用的原则这里用了数组对象
final boolean[] interrupted = {false};
//这里主要是做了封装CompletableFuture对象用lumbda表达式做了很多事情这句代码要仔细理清
//1.根据condition.getNodeList()的集合进行流处理用map进行把executable对象转换成List<CompletableFuture<WhenFutureObj>>
//2.在转的过程中套入CompletableFutureTimeout方法进行超时判断如果超时则用WhenFutureObj.timeOut返回超时的对象
//3.第2个参数是主要的本体CompletableFuture传入了ParallelSupplier和线程池对象
List<CompletableFuture<WhenFutureObj>> completableFutureList = this.getNodeList().stream().filter(executable -> {
try {
return executable.isAccess(slotIndex);
}catch (Exception e){
LOG.error("there was an error when executing the when component isAccess",e);
return false;
}
}).map(executable -> CompletableFutureTimeout.completeOnTimeout(
WhenFutureObj.timeOut(executable.getExecuteName()),
CompletableFuture.supplyAsync(new ParallelSupplier(executable, slotIndex), parallelExecutor),
liteflowConfig.getWhenMaxWaitSeconds(),
TimeUnit.SECONDS
)).collect(Collectors.toList());
CompletableFuture<?> resultCompletableFuture;
//这里判断执行方式
//如果any为false说明这些异步任务全部执行好或者超时才返回
//如果any为true说明这些异步任务只要任意一个执行完成就返回
if(this.isAny()){
//把这些CompletableFuture通过anyOf合成一个CompletableFuture
resultCompletableFuture = CompletableFuture.anyOf(completableFutureList.toArray(new CompletableFuture[]{}));
}else{
//把这些CompletableFuture通过allOf合成一个CompletableFuture
resultCompletableFuture = CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{}));
}
try {
//进行执行,这句执行完后,就意味着所有的任务要么执行完毕,要么超时返回
resultCompletableFuture.get();
} catch (InterruptedException | ExecutionException e) {
LOG.error("there was an error when executing the CompletableFuture",e);
interrupted[0] = true;
}
//拿到已经完成的CompletableFuture
//如果any为false那么所有任务都已经完成
//如果any为true那么这里拿到的是第一个完成的任务
//这里过滤和转换一起用lumbda做了
List<WhenFutureObj> allCompletableWhenFutureObjList = completableFutureList.stream().filter(f -> {
//过滤出已经完成的,没完成的就直接终止
if (f.isDone()){
return true;
}else{
f.cancel(true);
return false;
}
}).map(f -> {
try {
return f.get();
} catch (InterruptedException | ExecutionException e) {
interrupted[0] = true;
return null;
}
}).collect(Collectors.toList());
//判断超时上面已经拿到了所有已经完成的CompletableFuture
//那我们只要过滤出超时的CompletableFuture
List<WhenFutureObj> timeOutWhenFutureObjList = allCompletableWhenFutureObjList.stream().filter(WhenFutureObj::isTimeout).collect(Collectors.toList());
//输出超时信息
timeOutWhenFutureObjList.forEach(whenFutureObj ->
LOG.warn("requestId [{}] executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", slot.getRequestId(), whenFutureObj.getExecutorName()));
//当配置了errorResume = false出现interrupted或者!f.get()的情况将抛出WhenExecuteException
if (!this.isErrorResume()) {
if (interrupted[0]) {
throw new WhenExecuteException(StrUtil.format("requestId [{}] when execute interrupted. errorResume [false].", slot.getRequestId()));
}
//循环判断CompletableFuture的返回值如果异步执行失败则抛出相应的业务异常
for(WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList){
if (!whenFutureObj.isSuccess()){
LOG.info(StrUtil.format("requestId [{}] when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName(), slot.getRequestId()));
throw whenFutureObj.getEx();
}
}
} else if (interrupted[0]) {
// 这里由于配置了errorResume所以只打印warn日志
LOG.warn("requestId [{}] executing when condition timeout , but ignore with errorResume.", slot.getRequestId());
}
}
}

View File

@@ -50,7 +50,7 @@ public abstract class NodeExecutor {
//执行重试逻辑 - 子类通过实现该方法进行重试逻辑的控制
protected void retry(NodeComponent instance, int currentRetryCount) throws Exception {
Slot slot = DataBus.getSlot(instance.getSlotIndex());
LOG.info("[{}]:component[{}] performs {} retry", slot.getRequestId(), instance.getNodeId(), currentRetryCount + 1);
LOG.info("[{}]:component[{}][{}] performs {} retry", slot.getRequestId(), instance.getNodeId(),instance.getDisplayName(), currentRetryCount + 1);
//执行业务逻辑的主要入口
instance.execute();
}

View File

@@ -0,0 +1,94 @@
package com.yomahub.liteflow.parser;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.prop.ChainPropBean;
import com.yomahub.liteflow.builder.prop.NodePropBean;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.EmptyConditionValueException;
import com.yomahub.liteflow.exception.NodeTypeNotSupportException;
import com.yomahub.liteflow.exception.NotSupportConditionException;
/**
* 基类,用于存放通用方法
*
* @author tangkc
*/
public abstract class BaseFlowParser implements FlowParser {
/**
* 构建 node
*
* @param nodePropBean 构建 node 的中间属性
*/
public void buildNode(NodePropBean nodePropBean) {
String id = nodePropBean.getId();
String name = nodePropBean.getName();
String clazz = nodePropBean.getClazz();
String script = nodePropBean.getScript();
String type = nodePropBean.getType();
String file = nodePropBean.getFile();
//初始化type
if (StrUtil.isBlank(type)) {
type = NodeTypeEnum.COMMON.getCode();
}
//检查nodeType是不是规定的类型
NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type);
if (ObjectUtil.isNull(nodeTypeEnum)) {
throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type));
}
//进行node的build过程
LiteFlowNodeBuilder.createNode().setId(id).setName(name).setClazz(clazz).setType(nodeTypeEnum).setScript(script).setFile(file).build();
}
/**
* 构建 chain
*
* @param chainPropBean 构建 chain 的中间属性
* @param chainBuilder chainBuilder
*/
public void buildChain(ChainPropBean chainPropBean
, LiteFlowChainBuilder chainBuilder) {
String condValueStr = chainPropBean.getCondValueStr();
String group = chainPropBean.getGroup();
String errorResume = chainPropBean.getErrorResume();
String any = chainPropBean.getAny();
String threadExecutorClass = chainPropBean.getThreadExecutorClass();
ConditionTypeEnum conditionType = chainPropBean.getConditionType();
if (ObjectUtil.isNull(conditionType)) {
throw new NotSupportConditionException("ConditionType is not supported");
}
if (StrUtil.isBlank(condValueStr)) {
throw new EmptyConditionValueException("Condition value cannot be empty");
}
//如果是when类型的话有特殊化参数要设置只针对于when的
if (conditionType.equals(ConditionTypeEnum.TYPE_WHEN)) {
chainBuilder.setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setErrorResume(errorResume)
.setGroup(group)
.setAny(any)
.setThreadExecutorClass(threadExecutorClass)
.setValue(condValueStr)
.build()
).build();
} else {
chainBuilder.setCondition(
LiteFlowConditionBuilder.createCondition(conditionType)
.setValue(condValueStr)
.build()
).build();
}
}
}

View File

@@ -2,24 +2,35 @@ package com.yomahub.liteflow.parser;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.prop.ChainPropBean;
import com.yomahub.liteflow.builder.prop.NodePropBean;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.EmptyConditionValueException;
import com.yomahub.liteflow.exception.NodeTypeNotSupportException;
import com.yomahub.liteflow.exception.NotSupportConditionException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.List;
import static com.yomahub.liteflow.common.ChainConstant.ANY;
import static com.yomahub.liteflow.common.ChainConstant.CHAIN;
import static com.yomahub.liteflow.common.ChainConstant.CONDITION;
import static com.yomahub.liteflow.common.ChainConstant.ERROR_RESUME;
import static com.yomahub.liteflow.common.ChainConstant.FILE;
import static com.yomahub.liteflow.common.ChainConstant.FLOW;
import static com.yomahub.liteflow.common.ChainConstant.GROUP;
import static com.yomahub.liteflow.common.ChainConstant.ID;
import static com.yomahub.liteflow.common.ChainConstant.NAME;
import static com.yomahub.liteflow.common.ChainConstant.NODE;
import static com.yomahub.liteflow.common.ChainConstant.NODES;
import static com.yomahub.liteflow.common.ChainConstant.THREAD_EXECUTOR_CLASS;
import static com.yomahub.liteflow.common.ChainConstant.TYPE;
import static com.yomahub.liteflow.common.ChainConstant.VALUE;
import static com.yomahub.liteflow.common.ChainConstant._CLASS;
/**
* Json格式解析器
@@ -27,148 +38,123 @@ import java.util.*;
* @author guodongqing
* @since 2.5.0
*/
public abstract class JsonFlowParser implements FlowParser {
public abstract class JsonFlowParser extends BaseFlowParser {
private final Logger LOG = LoggerFactory.getLogger(JsonFlowParser.class);
private final Logger LOG = LoggerFactory.getLogger(JsonFlowParser.class);
public void parse(String content) throws Exception {
parse(ListUtil.toList(content));
}
public void parse(String content) throws Exception {
parse(ListUtil.toList(content));
}
@Override
public void parse(List<String> contentList) throws Exception {
if (CollectionUtil.isEmpty(contentList)) {
return;
}
@Override
public void parse(List<String> contentList) throws Exception {
if (CollectionUtil.isEmpty(contentList)) {
return;
}
List<JSONObject> jsonObjectList = ListUtil.toList();
for (String content : contentList) {
//把字符串原生转换为json对象如果不加第二个参数OrderedField会无序
JSONObject flowJsonObject = JSONObject.parseObject(content, Feature.OrderedField);
jsonObjectList.add(flowJsonObject);
}
List<JSONObject> jsonObjectList = ListUtil.toList();
for (String content : contentList) {
//把字符串原生转换为json对象如果不加第二个参数OrderedField会无序
JSONObject flowJsonObject = JSONObject.parseObject(content, Feature.OrderedField);
jsonObjectList.add(flowJsonObject);
}
parseJsonObject(jsonObjectList);
}
parseJsonObject(jsonObjectList);
}
//json格式解析过程
public void parseJsonObject(List<JSONObject> flowJsonObjectList) throws Exception {
//在相应的环境下进行节点的初始化工作
//在spring体系下会获得spring扫描后的节点接入元数据
//在非spring体系下是一个空实现等于不做此步骤
ContextCmpInitHolder.loadContextCmpInit().initCmp();
//json格式解析过程
public void parseJsonObject(List<JSONObject> flowJsonObjectList) throws Exception {
//在相应的环境下进行节点的初始化工作
//在spring体系下会获得spring扫描后的节点接入元数据
//在非spring体系下是一个空实现等于不做此步骤
ContextCmpInitHolder.loadContextCmpInit().initCmp();
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
JSONArray chainArray = jsonObject.getJSONObject("flow").getJSONArray("chain");
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
JSONArray chainArray = jsonObject.getJSONObject(FLOW).getJSONArray(CHAIN);
//先在元数据里放上chain
chainArray.forEach(o -> {
JSONObject innerJsonObject = (JSONObject)o;
FlowBus.addChain(innerJsonObject.getString("name"));
});
});
//先在元数据里放上chain
chainArray.forEach(o -> {
JSONObject innerJsonObject = (JSONObject) o;
FlowBus.addChain(innerJsonObject.getString(NAME));
});
});
for (JSONObject flowJsonObject : flowJsonObjectList) {
// 当存在<nodes>节点定义时解析node节点
if (flowJsonObject.getJSONObject("flow").containsKey("nodes")){
JSONArray nodeArrayList = flowJsonObject.getJSONObject("flow").getJSONObject("nodes").getJSONArray("node");
String id, name, clazz, script, type, file;
for (int i = 0; i < nodeArrayList.size(); i++) {
JSONObject nodeObject = nodeArrayList.getJSONObject(i);
id = nodeObject.getString("id");
name = nodeObject.getString("name");
clazz = nodeObject.getString("class");
type = nodeObject.getString("type");
script = nodeObject.getString("value");
file = nodeObject.getString("file");
for (JSONObject flowJsonObject : flowJsonObjectList) {
// 当存在<nodes>节点定义时解析node节点
if (flowJsonObject.getJSONObject(FLOW).containsKey(NODES)) {
JSONArray nodeArrayList = flowJsonObject.getJSONObject(FLOW).getJSONObject(NODES).getJSONArray(NODE);
String id, name, clazz, script, type, file;
for (int i = 0; i < nodeArrayList.size(); i++) {
JSONObject nodeObject = nodeArrayList.getJSONObject(i);
id = nodeObject.getString(ID);
name = nodeObject.getString(NAME);
clazz = nodeObject.getString(_CLASS);
type = nodeObject.getString(TYPE);
script = nodeObject.getString(VALUE);
file = nodeObject.getString(FILE);
//初始化type
if (StrUtil.isBlank(type)){
type = NodeTypeEnum.COMMON.getCode();
}
// 构建 node
NodePropBean nodePropBean = new NodePropBean()
.setId(id)
.setName(name)
.setClazz(clazz)
.setScript(script)
.setType(type)
.setFile(file);
//检查nodeType是不是规定的类型
NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type);
if (ObjectUtil.isNull(nodeTypeEnum)){
throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type));
}
buildNode(nodePropBean);
}
}
//进行node的build过程
LiteFlowNodeBuilder.createNode().setId(id)
.setName(name)
.setClazz(clazz)
.setType(nodeTypeEnum)
.setScript(script)
.setFile(file)
.build();
}
}
//解析每一个chain
JSONArray chainArray = flowJsonObject.getJSONObject("flow").getJSONArray("chain");
chainArray.forEach(o -> {
JSONObject jsonObject = (JSONObject)o;
parseOneChain(jsonObject);
});
}
}
/**
* 解析一个chain的过程
*/
private void parseOneChain(JSONObject chainObject){
String condValueStr;
ConditionTypeEnum conditionType;
String group;
String errorResume;
String any;
String threadExecutorClass;
//构建chainBuilder
String chainName = chainObject.getString("name");
LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName);
for (Object o : chainObject.getJSONArray("condition")) {
JSONObject condObject = (JSONObject) o;
conditionType = ConditionTypeEnum.getEnumByCode(condObject.getString("type"));
condValueStr = condObject.getString("value");
errorResume = condObject.getString("errorResume");
group = condObject.getString("group");
any = condObject.getString("any");
threadExecutorClass = condObject.getString("threadExecutorClass");
if (ObjectUtil.isNull(conditionType)){
throw new NotSupportConditionException("ConditionType is not supported");
}
if (StrUtil.isBlank(condValueStr)) {
throw new EmptyConditionValueException("Condition value cannot be empty");
}
//解析每一个chain
JSONArray chainArray = flowJsonObject.getJSONObject(FLOW).getJSONArray(CHAIN);
chainArray.forEach(o -> {
JSONObject jsonObject = (JSONObject) o;
parseOneChain(jsonObject);
});
}
}
//如果是when类型的话有特殊化参数要设置只针对于when的
if (conditionType.equals(ConditionTypeEnum.TYPE_WHEN)){
chainBuilder.setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setErrorResume(errorResume)
.setGroup(group)
.setAny(any)
.setThreadExecutorClass(threadExecutorClass)
.setValue(condValueStr)
.build()
).build();
}else{
chainBuilder.setCondition(
LiteFlowConditionBuilder.createCondition(conditionType)
.setValue(condValueStr)
.build()
).build();
}
}
}
/**
* 解析一个chain的过程
*/
private void parseOneChain(JSONObject chainObject) {
String condValueStr;
ConditionTypeEnum conditionType;
String group;
String errorResume;
String any;
String threadExecutorClass;
//构建chainBuilder
String chainName = chainObject.getString(NAME);
LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName);
for (Object o : chainObject.getJSONArray(CONDITION)) {
JSONObject condObject = (JSONObject) o;
conditionType = ConditionTypeEnum.getEnumByCode(condObject.getString(TYPE));
condValueStr = condObject.getString(VALUE);
errorResume = condObject.getString(ERROR_RESUME);
group = condObject.getString(GROUP);
any = condObject.getString(ANY);
threadExecutorClass = condObject.getString(THREAD_EXECUTOR_CLASS);
ChainPropBean chainPropBean = new ChainPropBean()
.setCondValueStr(condValueStr)
.setGroup(group)
.setErrorResume(errorResume)
.setAny(any)
.setThreadExecutorClass(threadExecutorClass)
.setConditionType(conditionType);
// 构建 chain
buildChain(chainPropBean, chainBuilder);
}
}
}

View File

@@ -3,13 +3,10 @@ package com.yomahub.liteflow.parser;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.prop.ChainPropBean;
import com.yomahub.liteflow.builder.prop.NodePropBean;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.*;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
import org.dom4j.Document;
@@ -17,14 +14,32 @@ import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Iterator;
import java.util.List;
import static com.yomahub.liteflow.common.ChainConstant.ANY;
import static com.yomahub.liteflow.common.ChainConstant.CHAIN;
import static com.yomahub.liteflow.common.ChainConstant.ERROR_RESUME;
import static com.yomahub.liteflow.common.ChainConstant.FILE;
import static com.yomahub.liteflow.common.ChainConstant.GROUP;
import static com.yomahub.liteflow.common.ChainConstant.ID;
import static com.yomahub.liteflow.common.ChainConstant.NAME;
import static com.yomahub.liteflow.common.ChainConstant.NODE;
import static com.yomahub.liteflow.common.ChainConstant.NODES;
import static com.yomahub.liteflow.common.ChainConstant.THREAD_EXECUTOR_CLASS;
import static com.yomahub.liteflow.common.ChainConstant.TYPE;
import static com.yomahub.liteflow.common.ChainConstant.VALUE;
import static com.yomahub.liteflow.common.ChainConstant._CLASS;
;
/**
* xml形式的解析器
*
* @author Bryan.Zhang
*/
public abstract class XmlFlowParser implements FlowParser {
public abstract class XmlFlowParser extends BaseFlowParser {
private final Logger LOG = LoggerFactory.getLogger(XmlFlowParser.class);
@@ -32,6 +47,7 @@ public abstract class XmlFlowParser implements FlowParser {
parse(ListUtil.toList(content));
}
@Override
public void parse(List<String> contentList) throws Exception {
if (CollectionUtil.isEmpty(contentList)) {
return;
@@ -57,51 +73,42 @@ public abstract class XmlFlowParser implements FlowParser {
//同时也解决了不能循环依赖的问题
documentList.forEach(document -> {
// 解析chain节点
List<Element> chainList = document.getRootElement().elements("chain");
List<Element> chainList = document.getRootElement().elements(CHAIN);
//先在元数据里放上chain
chainList.forEach(e -> FlowBus.addChain(e.attributeValue("name")));
chainList.forEach(e -> FlowBus.addChain(e.attributeValue(NAME)));
});
for (Document document : documentList) {
Element rootElement = document.getRootElement();
Element nodesElement = rootElement.element("nodes");
Element nodesElement = rootElement.element(NODES);
// 当存在<nodes>节点定义时解析node节点
if (ObjectUtil.isNotNull(nodesElement)){
List<Element> nodeList = nodesElement.elements("node");
if (ObjectUtil.isNotNull(nodesElement)) {
List<Element> nodeList = nodesElement.elements(NODE);
String id, name, clazz, type, script, file;
for (Element e : nodeList) {
id = e.attributeValue("id");
name = e.attributeValue("name");
clazz = e.attributeValue("class");
type = e.attributeValue("type");
id = e.attributeValue(ID);
name = e.attributeValue(NAME);
clazz = e.attributeValue(_CLASS);
type = e.attributeValue(TYPE);
script = e.getTextTrim();
file = e.attributeValue("file");
file = e.attributeValue(FILE);
//初始化type
if (StrUtil.isBlank(type)){
type = NodeTypeEnum.COMMON.getCode();
}
//检查nodeType是不是规定的类型
NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type);
if (ObjectUtil.isNull(nodeTypeEnum)){
throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type));
}
//进行node的build过程
LiteFlowNodeBuilder.createNode().setId(id)
// 构建 node
NodePropBean nodePropBean = new NodePropBean()
.setId(id)
.setName(name)
.setClazz(clazz)
.setType(nodeTypeEnum)
.setScript(script)
.setFile(file)
.build();
.setType(type)
.setFile(file);
buildNode(nodePropBean);
}
}
//解析每一个chain
List<Element> chainList = rootElement.elements("chain");
List<Element> chainList = rootElement.elements(CHAIN);
chainList.forEach(this::parseOneChain);
}
}
@@ -118,44 +125,29 @@ public abstract class XmlFlowParser implements FlowParser {
ConditionTypeEnum conditionType;
//构建chainBuilder
String chainName = e.attributeValue("name");
String chainName = e.attributeValue(NAME);
LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName);
for (Iterator<Element> it = e.elementIterator(); it.hasNext(); ) {
Element condE = it.next();
conditionType = ConditionTypeEnum.getEnumByCode(condE.getName());
condValueStr = condE.attributeValue("value");
errorResume = condE.attributeValue("errorResume");
group = condE.attributeValue("group");
any = condE.attributeValue("any");
threadExecutorClass = condE.attributeValue("threadExecutorClass");
condValueStr = condE.attributeValue(VALUE);
errorResume = condE.attributeValue(ERROR_RESUME);
group = condE.attributeValue(GROUP);
any = condE.attributeValue(ANY);
threadExecutorClass = condE.attributeValue(THREAD_EXECUTOR_CLASS);
if (ObjectUtil.isNull(conditionType)){
throw new NotSupportConditionException("ConditionType is not supported");
}
ChainPropBean chainPropBean = new ChainPropBean()
.setCondValueStr(condValueStr)
.setGroup(group)
.setErrorResume(errorResume)
.setAny(any)
.setThreadExecutorClass(threadExecutorClass)
.setConditionType(conditionType);
if (StrUtil.isBlank(condValueStr)) {
throw new EmptyConditionValueException("Condition value cannot be empty");
}
//如果是when类型的话有特殊化参数要设置只针对于when的
if (conditionType.equals(ConditionTypeEnum.TYPE_WHEN)){
chainBuilder.setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setErrorResume(errorResume)
.setGroup(group)
.setAny(any)
.setThreadExecutorClass(threadExecutorClass)
.setValue(condValueStr)
.build()
).build();
}else{
chainBuilder.setCondition(
LiteFlowConditionBuilder.createCondition(conditionType)
.setValue(condValueStr)
.build()
).build();
}
// 构建 chain
buildChain(chainPropBean, chainBuilder);
}
}
}

View File

@@ -20,6 +20,7 @@ public abstract class YmlFlowParser extends JsonFlowParser{
private final Logger LOG = LoggerFactory.getLogger(YmlFlowParser.class);
@Override
public void parse(String content) throws Exception{
parse(ListUtil.toList(content));
}

View File

@@ -0,0 +1,39 @@
package com.yomahub.liteflow.test.extend;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境测试声明式组件继承其他类的场景
* @author Bryan.Zhang
* @since 2.7.1
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/extend/application.properties")
@SpringBootTest(classes = CmpExtendSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.extend.cmp"})
public class CmpExtendSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testExtend() throws Exception{
LiteflowResponse<DefaultContext> response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}
}

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.extend.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
@Component("a")
@LiteflowCmpDefine
public class ACmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("ACmp 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.extend.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
@Component("b")
@LiteflowCmpDefine
public class BCmp extends ParentCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
System.out.println(this.sayHi("jack"));
}
}

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.extend.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
@Component("c")
@LiteflowCmpDefine
public class CCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

View File

@@ -0,0 +1,10 @@
package com.yomahub.liteflow.test.extend.cmp;
import cn.hutool.core.util.StrUtil;
public class ParentCmp {
protected String sayHi(String name){
return StrUtil.format("hi,{}",name);
}
}

View File

@@ -1,4 +1,4 @@
package com.yomahub.liteflow.test.liteflowcomponent;
package com.yomahub.liteflow.test.lfCmpAnno;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
@@ -21,10 +21,10 @@ import org.springframework.test.context.junit4.SpringRunner;
* @since 2.5.10
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/liteflowComponent/application.properties")
@TestPropertySource(value = "classpath:/lfCmpAnno/application.properties")
@SpringBootTest(classes = LiteflowComponentSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.liteflowcomponent.cmp"})
@ComponentScan({"com.yomahub.liteflow.test.lfCmpAnno.cmp"})
public class LiteflowComponentSpringbootTest extends BaseTest {
@Autowired

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;

View File

@@ -61,4 +61,11 @@ public class PreAndFinallySpringbootTest extends BaseTest {
Assert.assertFalse(response.isSuccess());
Assert.assertTrue(response.getContextBean().getData("hasEx"));
}
@Test
public void testPreAndFinally5() throws Exception{
LiteflowResponse<DefaultContext> response = flowExecutor.execute2Resp("chain5", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>p1==>p2==>a==>b==>c==>f1==>f2==>f1", response.getExecuteStepStrWithoutTime());
}
}

View File

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

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
<then value="a,b,c"/>
</chain>
</flow>

View File

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

View File

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

View File

@@ -23,4 +23,10 @@
<then value="a,d,c"/>
<finally value="f3"/>
</chain>
<chain name="chain5">
<pre value="p1,p2"/>
<then value="chain1"/>
<finally value="f1"/>
</chain>
</flow>

View File

@@ -57,4 +57,11 @@ public class PreAndFinallyTest extends BaseTest {
Assert.assertFalse(response.isSuccess());
Assert.assertTrue(response.getContextBean().getData("hasEx"));
}
@Test
public void testPreAndFinally5() throws Exception{
LiteflowResponse<DefaultContext> response = flowExecutor.execute2Resp("chain5", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>p1==>p2==>a==>b==>c==>f1==>f2==>f1", response.getExecuteStepStrWithoutTime());
}
}

View File

@@ -35,4 +35,10 @@
<then value="a,d,c"/>
<finally value="f3"/>
</chain>
<chain name="chain5">
<pre value="p1,p2"/>
<then value="chain1"/>
<finally value="f1"/>
</chain>
</flow>

View File

@@ -1,4 +1,4 @@
package com.yomahub.liteflow.test.liteflowcomponent;
package com.yomahub.liteflow.test.lfCmpAnno;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
@@ -21,10 +21,10 @@ import org.springframework.test.context.junit4.SpringRunner;
* @since 2.5.10
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/liteflowComponent/application.properties")
@TestPropertySource(value = "classpath:/lfCmpAnno/application.properties")
@SpringBootTest(classes = LiteflowComponentSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.liteflowcomponent.cmp"})
@ComponentScan({"com.yomahub.liteflow.test.lfCmpAnno.cmp"})
public class LiteflowComponentSpringbootTest extends BaseTest {
@Autowired

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -61,4 +61,12 @@ public class PreAndFinallySpringbootTest extends BaseTest {
Assert.assertFalse(response.isSuccess());
Assert.assertTrue(response.getContextBean().getData("hasEx"));
}
//测试嵌套结构pre和finally是否在各自的chain里打出
@Test
public void testPreAndFinally5() throws Exception{
LiteflowResponse<DefaultContext> response = flowExecutor.execute2Resp("chain5", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>p1==>p2==>a==>b==>c==>f1==>f2==>f1", response.getExecuteStepStrWithoutTime());
}
}

View File

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

View File

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

View File

@@ -23,4 +23,10 @@
<then value="a,d,c"/>
<finally value="f3"/>
</chain>
<chain name="chain5">
<pre value="p1,p2"/>
<then value="chain1"/>
<finally value="f1"/>
</chain>
</flow>

View File

@@ -1,4 +1,4 @@
package com.yomahub.liteflow.test.liteflowcomponent;
package com.yomahub.liteflow.test.lfCmpAnno;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
@@ -18,7 +18,7 @@ import org.springframework.test.context.junit4.SpringRunner;
* @since 2.5.10
*/
@RunWith(SpringRunner.class)
@ContextConfiguration("classpath:/liteflowcomponent/application.xml")
@ContextConfiguration("classpath:/lfCmpAnno/application.xml")
public class LiteflowComponentSpringTest extends BaseTest {
@Autowired

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -5,7 +5,7 @@
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.liteflowcomponent.cmp;
package com.yomahub.liteflow.test.lfCmpAnno.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;

View File

@@ -55,4 +55,12 @@ public class PreAndFinallySpringTest extends BaseTest {
Assert.assertFalse(response.isSuccess());
Assert.assertTrue(response.getContextBean().getData("hasEx"));
}
//测试嵌套结构pre和finally是否在各自的chain里打出
@Test
public void testPreAndFinally5() throws Exception{
LiteflowResponse<DefaultContext> response = flowExecutor.execute2Resp("chain5", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("p1==>p2==>p1==>p2==>a==>b==>c==>f1==>f2==>f1", response.getExecuteStepStrWithoutTime());
}
}

View File

@@ -7,14 +7,14 @@
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.liteflowcomponent.cmp" />
<context:component-scan base-package="com.yomahub.liteflow.test.lfCmpAnno.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="liteflowcomponent/flow.xml"/>
<property name="ruleSource" value="lfCmpAnno/flow.xml"/>
</bean>
<bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor">

View File

@@ -23,4 +23,10 @@
<then value="a,d,c"/>
<finally value="f3"/>
</chain>
<chain name="chain5">
<pre value="p1,p2"/>
<then value="chain1"/>
<finally value="f1"/>
</chain>
</flow>