This commit is contained in:
noear 2022-12-19 11:07:17 +08:00
commit 7483baadd3
517 changed files with 9163 additions and 1922 deletions

View File

@ -21,8 +21,6 @@ You can find out how to join the community on the official website!
## Documents url: [Click here to enter the documentation to learn](https://liteflow.yomahub.com/en/pages/5816c5/)
## Demo projects: [DEMO1](https://github.com/bryan31/message-demo) | [DEMO2](https://gitee.com/bryan31/liteflow-example)
LiteFlow has more than 1000 test cases in the source code, including most scenarios, you can see the test cases in the source code.
## Feature
* **Component definition unified:** All logic is a component, for all logic to provide a unified component implementation, small size, large energy.
* **Rules lightweight:** based on the rules file to arrange the process, learning the rules entry only takes 5 minutes, a read both understand.
@ -55,7 +53,7 @@ These processes can be easily solved with LiteFlow! The framework has a very low
LiteFlow has an extremely detailed and easy-to-understand documentation it can help you solve more than 95% of your problems when using the framework.
Right now LiteFlow has 658 test cases and more. Complete documentation and comprehensive test case coverage guarantee the stability of LiteFlow framework!
LiteFlow has 658 test cases and more. Complete documentation and comprehensive test case coverage guarantee the stability of LiteFlow framework!
Looking forward to your use
@ -67,7 +65,7 @@ Discord Link: [https://discord.gg/MpdBSBnFTu](https://discord.gg/MpdBSBnFTu)
Since the community group is over 200 people, you need to be invited to join the group. Follow the WECHAT OFFICIAL ACCOUNT and click `Personal WeChat` to add me, I can invite you into the group
![offIical-wx](static/img/offical-wx.jpg)
![official-wx](static/img/offical-wx.jpg)
Open source is not easy, please sponsor LiteFlow if you support it

View File

@ -6,12 +6,12 @@
<h3>您的star是我继续前进的动力如果喜欢LiteFlow请右上角帮忙点个star</h3>
## 概述
## 🌈概述
LiteFlow是一个轻量且强大的国产规则引擎框架可用于复杂的组件化业务的编排领域独有的DSL规则驱动整个复杂业务并可实现平滑刷新热部署支持多种脚本语言规则的嵌入。帮助系统变得更加丝滑且灵活。
LiteFlow于2020年正式开源2021年获得开源中国年度最受欢迎开源软件殊荣。于2022年获得Gitee最有价值开源项目(GVP)荣誉。是一个正处在高速发展中的开源项目。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个800多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个1800多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
你在官网中可以找到加入社区的方式!
@ -19,18 +19,17 @@ LiteFlow是一个由社区驱动的项目我们非常重视社区建设
## 文档链接:[点这里进入文档进行学习](https://liteflow.yomahub.com/pages/5816c5/)
## 示例工程:[DEMO1](https://github.com/bryan31/message-demo) | [DEMO2](https://gitee.com/bryan31/liteflow-example)
**同时LiteFlow的源码中拥有超过1000个的测试用例包含了大部分的场景在源码中可以看到测试用例**
## 特性
## 🍬特性
* **组件定义统一:** 所有的逻辑都是组件,为所有的逻辑提供统一化的组件实现方式,小身材,大能量。
* **规则轻量:** 基于规则文件来编排流程学习规则入门只需要5分钟一看既懂。
* **规则多样化:** 规则支持xml、json、yml三种规则文件写法方式喜欢哪种用哪个。
* **任意编排:** 再复杂的逻辑过程利用LiteFlow的规则都是很容易做到的看规则文件就能知道逻辑是如何运转的。
* **规则持久化:** 框架原生支持把规则存储在标准结构化数据库NacosEtcdZookeeper。您也可以自己扩展把规则存储在任何地方。
* **规则持久化:** 框架原生支持把规则存储在标准结构化数据库NacosEtcdZookeeperApollo。您也可以自己扩展,把规则存储在任何地方。
* **优雅热刷新机制:** 规则变化,无需重启您的应用,即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。
* **支持广泛:** 不管你的项目是不是基于SpringbootSpring还是任何其他java框架构建LiteFlow都能游刃有余。
* **JDK支持** 从JDK8到JDK17统统支持。无需担心JDK版本。
* **脚本语言支持:** 可以定义脚本语言节点支持QLExpress和Groovy两种脚本。未来还会支持更多的脚本语言。
* **脚本语言支持:** 可以定义脚本语言节点支持GroovyJavascript,QLExpressPythonLua。未来还会支持更多的脚本语言。
* **脚本和Java全打通** 所有脚本语言均可调用Java方法甚至于可以引用任意的实例在脚本中调用RPC也是支持的。
* **规则嵌套支持:** 只要你想的出,你可以利用简单的表达式完成多重嵌套的复杂逻辑编排。
* **组件重试支持:** 组件可以支持重试,每个组件均可自定义重试配置和指定异常。
* **上下文隔离机制:** 可靠的上下文隔离机制,你无需担心高并发情况下的数据串流。
@ -40,7 +39,7 @@ LiteFlow是一个由社区驱动的项目我们非常重视社区建设
* **性能卓越:** 框架本身几乎不消耗额外性能,性能取决你的组件执行效率。
* **自带简单监控:** 框架内自带一个命令行的监控,能够知道每个组件的运行耗时排行。
## 什么场景适用
## ☘️什么场景适用
LiteFlow是一款编排式的规则引擎最擅长去解耦你的系统如果你的系统业务复杂并且代码臃肿不堪那LiteFlow框架会是一个非常好的解决方案。
@ -53,7 +52,7 @@ LiteFlow利用规则表达式为驱动引擎去驱动你定义的组件。你
LiteFlow拥有极其详细易懂的文档体系能帮助你解决在使用框架的时候95%以上的问题。
目前为止LiteFlow拥有658个测试用例并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
目前为止LiteFlow拥有800多个测试用例,并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
LiteFlow期待你的了解

View File

@ -46,5 +46,9 @@
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,7 +1,9 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented

View File

@ -3,7 +3,9 @@ package com.yomahub.liteflow.annotation;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented

View File

@ -5,6 +5,9 @@ import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ -13,8 +16,11 @@ public @interface LiteflowMethod {
LiteFlowMethodEnum value();
// 节点ID用于区分节点
// 默认为空 则按照Spring模式下BeanName为准
/**
* 节点ID用于区分节点
* 默认为空 则按照Spring模式下BeanName为准
* @return
*/
String nodeId() default "";
/**

View File

@ -11,7 +11,10 @@ import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* 注解工具类
* @author Bryan.Zhang
*/
public class AnnoUtil {
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
@ -41,7 +44,7 @@ public class AnnoUtil {
return annotation;
}
public static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property){
private static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property){
try{
return annotationType.getMethod(property).getDefaultValue();
}catch (Exception e){

View File

@ -1,6 +1,7 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
*
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/10/22
@ -16,7 +17,17 @@ import com.yomahub.liteflow.slot.Slot;
*/
public interface ICmpAroundAspect {
/**
* 前置处理
* @param nodeId 节点ID
* @param slot
*/
void beforeProcess(String nodeId, Slot slot);
/**
* 后置处理
* @param nodeId 节点ID
* @param slot
*/
void afterProcess(String nodeId, Slot slot);
}

View File

@ -1,115 +0,0 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import java.util.ArrayList;
import java.util.List;
/**
* Chain基于代码形式的组装器
*
* @author Bryan.Zhang
* @since 2.6.8
*/
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();
}
public LiteFlowChainBuilder() {
chain = new Chain();
conditionList = new ArrayList<>();
preConditionList = new ArrayList<>();
finallyConditionList = new ArrayList<>();
}
//在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
//2.6.8之后取消了递归的模式两段式组装先把带有chainName的chain对象放进去第二段再组装chain里面的condition
//所以这里setChainName的时候需要判断下
public LiteFlowChainBuilder setChainName(String chainName) {
if (FlowBus.containChain(chainName)) {
this.chain = FlowBus.getChain(chainName);
} else {
this.chain.setChainName(chainName);
}
return this;
}
public LiteFlowChainBuilder setCondition(Condition condition) {
//这里把condition组装进conditionList
buildConditions(condition);
return this;
}
public void build() {
this.chain.setConditionList(this.conditionList);
this.chain.setPreConditionList(this.preConditionList);
this.chain.setFinallyConditionList(this.finallyConditionList);
checkBuild();
FlowBus.addChain(this.chain);
}
/**
* build 前简单校验
*/
private void checkBuild() {
List<String> errorList = new ArrayList<>();
if (StrUtil.isBlank(this.chain.getChainName())) {
errorList.add("name is blank");
}
if (CollUtil.isNotEmpty(errorList)) {
throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
}
}
private void buildConditions(Condition condition) {
//这里进行合并逻辑
//对于then来说相邻的2个then会合并成一个condition
//对于when来说相同组的when会合并成一个condition不同组的when还是会拆开
if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE)) {
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).getExecutableList().addAll(condition.getExecutableList());
} else {
this.conditionList.add(condition);
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_WHEN)) {
if (this.conditionList.size() >= 1 &&
CollectionUtil.getLast(this.conditionList) instanceof WhenCondition &&
((WhenCondition)CollectionUtil.getLast(this.conditionList)).getGroup().equals(((WhenCondition)condition).getGroup())) {
CollectionUtil.getLast(this.conditionList).getExecutableList().addAll(condition.getExecutableList());
} else {
this.conditionList.add(condition);
}
}
}
}

View File

@ -11,8 +11,13 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class LiteFlowNodeBuilder {
@ -132,33 +137,16 @@ public class LiteFlowNodeBuilder {
public void build() {
checkBuild();
try {
if (this.node.getType().equals(NodeTypeEnum.COMMON)) {
FlowBus.addCommonNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.SWITCH)) {
FlowBus.addSwitchNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.IF)) {
FlowBus.addIfNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.FOR)) {
FlowBus.addForNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.WHILE)) {
FlowBus.addWhileNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.BREAK)) {
FlowBus.addBreakNode(this.node.getId(), this.node.getName(), this.node.getClazz());
} else if (this.node.getType().equals(NodeTypeEnum.SCRIPT)) {
FlowBus.addCommonScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.SWITCH_SCRIPT)) {
FlowBus.addSwitchScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.IF_SCRIPT)) {
FlowBus.addIfScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.FOR_SCRIPT)) {
FlowBus.addForScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.WHILE_SCRIPT)) {
FlowBus.addWhileScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
} else if (this.node.getType().equals(NodeTypeEnum.BREAK_SCRIPT)) {
FlowBus.addBreakScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
}
// 用于处理脚本 node
if (this.node.getType().isScript()){
FlowBus.addScriptNode(this.node.getId(), this.node.getName(), this.node.getType(), this.node.getScript());
}
// 用于处理普通 node
else{
FlowBus.addNode(this.node.getId(), this.node.getName(), this.node.getType(), this.node.getClazz());
}
} catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(),e.getMessage());
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(), e.getMessage());
LOG.error(errMsg, e);
throw new NodeBuildException(errMsg);
}

View File

@ -6,19 +6,19 @@ import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.exception.DataNofFoundException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.*;
import com.yomahub.liteflow.script.ScriptBeanManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
/**
* Chain基于代码形式的组装器
@ -29,47 +29,49 @@ import java.util.function.BiConsumer;
*/
public class LiteFlowChainELBuilder {
private static final Logger LOG = LoggerFactory.getLogger(LiteFlowChainELBuilder.class);
private Chain chain;
//这是主体的Condition不包含前置和后置
//声明这个变量而不是用chain.getConditionList的目的是为了辅助平滑加载
//虽然FlowBus里面的map都是CopyOnWrite类型的但是在buildCondition的时候为了平滑加载所以不能事先把chain.getConditionList给设为空List
//所以在这里做一个缓存等conditionList全部build完毕后再去一次性替换chain里面的conditionList
/**
* //这是主体的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;
//EL解析引擎
private final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
/**
* EL解析引擎
*/
public final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
static {
//初始化QLExpress的Runner
EXPRESS_RUNNER.addFunction("THEN", new ThenOperator());
EXPRESS_RUNNER.addFunction("WHEN", new WhenOperator());
EXPRESS_RUNNER.addFunction("SWITCH", new SwitchOperator());
EXPRESS_RUNNER.addFunction("PRE", new PreOperator());
EXPRESS_RUNNER.addFunction("FINALLY", new FinallyOperator());
EXPRESS_RUNNER.addFunction("IF", new IfOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("ELSE", Object.class, new ElseOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("ELIF", Object.class, new ElifOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("TO", Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("to", Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("tag", Object.class, new TagOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("any", Object.class, new AnyOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("id", Object.class, new IdOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("ignoreError", Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("threadPool", Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addFunction("NODE", new NodeOperator());
EXPRESS_RUNNER.addFunction("node", new NodeOperator());
EXPRESS_RUNNER.addFunction("FOR", new ForOperator());
EXPRESS_RUNNER.addFunction("WHILE", new WhileOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("DO", Object.class, new DoOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("BREAK", Object.class, new BreakOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod("data", Object.class, new DataOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.THEN, new ThenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHEN, new WhenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.SWITCH, new SwitchOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.PRE, new PreOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FINALLY, new FinallyOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.IF, new IfOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELSE, Object.class, new ElseOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELIF, Object.class, new ElifOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TO, Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TO.toLowerCase(), Object.class, new ToOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DEFAULT, Object.class, new DefaultOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TAG, Object.class, new TagOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ANY, Object.class, new AnyOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ID, Object.class, new IdOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.IGNORE_ERROR, Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.THREAD_POOL, Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE.toUpperCase(), new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DO, Object.class, new DoOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BREAK, Object.class, new BreakOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DATA, Object.class, new DataOperator());
}
public static LiteFlowChainELBuilder createChain() {
@ -79,13 +81,16 @@ public class LiteFlowChainELBuilder {
public LiteFlowChainELBuilder() {
chain = new Chain();
conditionList = new ArrayList<>();
preConditionList = new ArrayList<>();
finallyConditionList = new ArrayList<>();
}
//在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
//2.6.8之后取消了递归的模式两段式组装先把带有chainName的chain对象放进去第二段再组装chain里面的condition
//所以这里setChainName的时候需要判断下
/**
* @return LiteFlowChainELBuilder
* @deprecated 请使用 {@link #setChainId(String)}
*/
public LiteFlowChainELBuilder setChainName(String chainName) {
if (FlowBus.containChain(chainName)) {
this.chain = FlowBus.getChain(chainName);
@ -95,9 +100,18 @@ public class LiteFlowChainELBuilder {
return this;
}
public LiteFlowChainELBuilder setChainId(String chainId) {
if (FlowBus.containChain(chainId)) {
this.chain = FlowBus.getChain(chainId);
} else {
this.chain.setChainId(chainId);
}
return this;
}
public LiteFlowChainELBuilder setEL(String elStr) {
if (StrUtil.isBlank(elStr)) {
String errMsg = StrUtil.format("no conditionList in this chain[{}]", chain.getChainName());
String errMsg = StrUtil.format("no content in this chain[{}]", chain.getChainId());
throw new FlowSystemException(errMsg);
}
@ -107,11 +121,14 @@ public class LiteFlowChainELBuilder {
//这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
//往上下文里放入所有的chain是的el表达式可以直接引用到chain
FlowBus.getChainMap().values().forEach(chain -> context.put(chain.getChainName(), chain));
FlowBus.getChainMap().values().forEach(chain -> context.put(chain.getChainId(), chain));
//往上下文里放入所有的node使得el表达式可以直接引用到nodeId
FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.getNode(nodeId)));
//放入当前主chain的ID
context.put(ChainConstant.CURR_CHAIN_ID, this.chain.getChainId());
//解析el成为一个Condition
//为什么这里只是一个Condition而不是一个List<Condition>
//这里无论多复杂的外面必定有一个最外层的Condition所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
@ -121,13 +138,13 @@ public class LiteFlowChainELBuilder {
//为什么只寻找第一层而不往下寻找了呢
//因为这是一个规范如果在后面的层级中出现pre和finally语义上也不好理解所以pre和finally只能定义在第一层
//如果硬是要在后面定义则执行的时候会忽略相关代码已做了判断
for (Executable executable : condition.getExecutableList()) {
/*for (Executable executable : condition.getExecutableList()) {
if (executable instanceof PreCondition) {
this.preConditionList.add((PreCondition) executable);
} else if (executable instanceof FinallyCondition) {
this.finallyConditionList.add((FinallyCondition) executable);
}
}
}*/
//把主要的condition加入
this.conditionList.add(condition);
@ -143,10 +160,23 @@ public class LiteFlowChainELBuilder {
}
}
/**
* EL表达式校验
* @param elStr EL表达式
* @return true 校验成功 false 校验失败
*/
public static boolean validate(String elStr) {
try {
LiteFlowChainELBuilder.createChain().setEL(elStr);
return Boolean.TRUE;
} catch (ELParseException e) {
LOG.error(e.getMessage());
}
return Boolean.FALSE;
}
public void build() {
this.chain.setConditionList(this.conditionList);
this.chain.setPreConditionList(this.preConditionList);
this.chain.setFinallyConditionList(this.finallyConditionList);
checkBuild();
@ -158,7 +188,7 @@ public class LiteFlowChainELBuilder {
*/
private void checkBuild() {
List<String> errorList = new ArrayList<>();
if (StrUtil.isBlank(this.chain.getChainName())) {
if (StrUtil.isBlank(this.chain.getChainId())) {
errorList.add("name is blank");
}
if (CollUtil.isNotEmpty(errorList)) {

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
@ -13,14 +12,14 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
*/
public class AnyOperator extends BaseOperator<WhenCondition> {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
Boolean any = OperatorHelper.convert(objects[1], Boolean.class);
whenCondition.setAny(any);
return whenCondition;
}
Boolean any = OperatorHelper.convert(objects[1], Boolean.class);
whenCondition.setAny(any);
return whenCondition;
}
}

View File

@ -6,10 +6,7 @@ import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
import com.yomahub.liteflow.flow.element.condition.WhileCondition;
/**
* EL规则中的BREAK的操作符
@ -31,9 +28,9 @@ public class BreakOperator extends BaseOperator<LoopCondition> {
//获得需要执行的可执行表达式
Node breakNode = OperatorHelper.convert(objects[1], Node.class);
if (ListUtil.toList(NodeTypeEnum.BREAK, NodeTypeEnum.BREAK_SCRIPT).contains(breakNode.getType())){
if (ListUtil.toList(NodeTypeEnum.BREAK, NodeTypeEnum.BREAK_SCRIPT).contains(breakNode.getType())) {
condition.setBreakNode(breakNode);
}else{
} else {
throw new QLException("The parameter must be node-break item");
}
return condition;

View File

@ -12,16 +12,16 @@ import com.yomahub.liteflow.flow.element.Node;
*/
public class DataOperator extends BaseOperator<Node> {
@Override
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
@Override
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Node node = OperatorHelper.convert(objects[0], Node.class);
Node node = OperatorHelper.convert(objects[0], Node.class);
String cmpData = OperatorHelper.convert(objects[1], String.class);
String cmpData = OperatorHelper.convert(objects[1], String.class);
node.setCmpData(cmpData);
node.setCmpData(cmpData);
return node;
}
return node;
}
}

View File

@ -0,0 +1,27 @@
package com.yomahub.liteflow.builder.el.operator;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
/**
* EL规则中的default的操作符用法须和SWITCH联合使用
*
* @author Tingliang Wang
* @since 2.9.5
*/
public class DefaultOperator extends BaseOperator<SwitchCondition> {
@Override
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class);
Executable target = OperatorHelper.convert(objects[1], Executable.class);
switchCondition.setDefaultExecutor(target);
return switchCondition;
}
}

View File

@ -1,14 +1,10 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
import com.yomahub.liteflow.flow.element.condition.WhileCondition;
/**
* EL规则中的DO的操作符

View File

@ -34,7 +34,7 @@ public class ForOperator extends BaseOperator<ForCondition> {
node = new Node();
NodeForComponent nodeForComponent = new NodeForComponent() {
@Override
public int processFor() throws Exception {
public int processFor() {
return forCount;
}
};

View File

@ -1,11 +1,8 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.condition.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的id的操作符,只有condition可加id
@ -15,18 +12,16 @@ import org.slf4j.LoggerFactory;
*/
public class IdOperator extends BaseOperator<Condition> {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Condition condition = OperatorHelper.convert(objects[0], Condition.class);
Condition condition = OperatorHelper.convert(objects[0], Condition.class);
String id = OperatorHelper.convert(objects[1], String.class);
String id = OperatorHelper.convert(objects[1], String.class);
condition.setId(id);
condition.setId(id);
return condition;
}
return condition;
}
}

View File

@ -17,29 +17,29 @@ import com.yomahub.liteflow.flow.element.condition.IfCondition;
*/
public class IfOperator extends BaseOperator<IfCondition> {
@Override
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 2, 3);
@Override
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 2, 3);
//解析第一个参数
Node ifNode = OperatorHelper.convert(objects[0], Node.class);
if (!ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(ifNode.getType())) {
throw new QLException("The first parameter must be If item");
}
//解析第一个参数
Node ifNode = OperatorHelper.convert(objects[0], Node.class);
if (!ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(ifNode.getType())) {
throw new QLException("The first parameter must be If item");
}
//解析第二个参数
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
//解析第二个参数
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
//解析第三个参数如果有的话
Executable falseCaseExecutableItem = null;
if (objects.length == 3) {
falseCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
}
//解析第三个参数如果有的话
Executable falseCaseExecutableItem = null;
if (objects.length == 3) {
falseCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
}
IfCondition ifCondition = new IfCondition();
ifCondition.setExecutableList(ListUtil.toList(ifNode));
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
return ifCondition;
}
IfCondition ifCondition = new IfCondition();
ifCondition.setExecutableList(ListUtil.toList(ifNode));
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
return ifCondition;
}
}

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
@ -14,14 +13,14 @@ import com.yomahub.liteflow.flow.element.condition.PreCondition;
*/
public class PreOperator extends BaseOperator<PreCondition> {
@Override
public PreCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
@Override
public PreCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
PreCondition preCondition = new PreCondition();
for (Object obj : objects) {
preCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return preCondition;
}
PreCondition preCondition = new PreCondition();
for (Object obj : objects) {
preCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return preCondition;
}
}

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
@ -14,14 +13,14 @@ import com.yomahub.liteflow.flow.element.condition.ThenCondition;
*/
public class ThenOperator extends BaseOperator<ThenCondition> {
@Override
public ThenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
@Override
public ThenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
ThenCondition thenCondition = new ThenCondition();
for (Object obj : objects) {
thenCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return thenCondition;
}
ThenCondition thenCondition = new ThenCondition();
for (Object obj : objects) {
thenCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return thenCondition;
}
}

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
@ -13,14 +12,14 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
*/
public class ThreadPoolOperator extends BaseOperator<WhenCondition> {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
whenCondition.setThreadExecutorClass(OperatorHelper.convert(objects[1], String.class));
whenCondition.setThreadExecutorClass(OperatorHelper.convert(objects[1], String.class));
return whenCondition;
}
return whenCondition;
}
}

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
@ -14,16 +13,16 @@ import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
*/
public class ToOperator extends BaseOperator<SwitchCondition> {
@Override
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtTwo(objects);
@Override
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtTwo(objects);
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class);
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class);
for (int i = 1; i < objects.length; i++) {
Executable target = OperatorHelper.convert(objects[i], Executable.class);
switchCondition.addTargetItem(target);
}
return switchCondition;
}
for (int i = 1; i < objects.length; i++) {
Executable target = OperatorHelper.convert(objects[i], Executable.class);
switchCondition.addTargetItem(target);
}
return switchCondition;
}
}

View File

@ -1,6 +1,5 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;

View File

@ -3,13 +3,13 @@ package com.yomahub.liteflow.builder.el.operator.base;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.DataNofFoundException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Node;
import java.util.Objects;
/**
* Operator 常用工具类
*
* @author gaibu
* @since 2.8.6
*/
@ -131,17 +131,16 @@ public class OperatorHelper {
* 如果是Node类型的则进行copy
*/
public static <T> T convert(Object object, Class<T> clazz, String errorMsg) throws QLException {
try{
try {
if (clazz.isAssignableFrom(object.getClass())) {
if(clazz.equals(Node.class)){
if (clazz.equals(Node.class)) {
Node node = (Node) object;
return (T) node.copy();
}else{
} else {
return (T) object;
}
}
}catch (Exception e){
} catch (Exception e) {
throw new QLException("An error occurred while copying an object");
}

View File

@ -1,65 +0,0 @@
package com.yomahub.liteflow.builder.entity;
import cn.hutool.core.collection.CollUtil;
import java.util.ArrayList;
import java.util.List;
/**
* <pre>
* 执行器的实体类
* </pre>
*
* @author sikadai
* @version 2.6.11
* @since 2022/3/13 15:28
*/
public class ExecutableEntity {
private String id;
private String tag;
private List<ExecutableEntity> nodeCondComponents;
public ExecutableEntity() {
}
public ExecutableEntity(String id, String tag) {
this.id = id;
this.tag = tag;
}
public String getId() {
return id;
}
public ExecutableEntity setId(String id) {
this.id = id;
return this;
}
public String getTag() {
return tag;
}
public ExecutableEntity setTag(String tag) {
this.tag = tag;
return this;
}
public List<ExecutableEntity> getNodeCondComponents() {
return nodeCondComponents;
}
public ExecutableEntity setNodeCondComponents(List<ExecutableEntity> nodeCondComponents) {
this.nodeCondComponents = nodeCondComponents;
return this;
}
public ExecutableEntity addNodeCondComponent(ExecutableEntity conditionNodeEntity) {
if (CollUtil.isEmpty(this.nodeCondComponents)) {
this.nodeCondComponents = new ArrayList<>();
}
this.nodeCondComponents.add(conditionNodeEntity);
return this;
}
}

View File

@ -25,15 +25,47 @@ public interface ChainConstant {
String VALUE = "value";
String ERROR_RESUME = "errorResume";
String GROUP = "group";
String ANY = "any";
String THREAD_EXECUTOR_CLASS = "threadExecutorClass";
String CONDITION = "condition";
String TYPE = "type";
String THEN = "THEN";
String WHEN = "WHEN";
String SWITCH = "SWITCH";
String PRE = "PRE";
String FINALLY = "FINALLY";
String IF = "IF";
String ELSE = "ELSE";
String ELIF = "ELIF";
String TO = "TO";
String TAG = "tag";
String IGNORE_ERROR = "ignoreError";
String THREAD_POOL = "threadPool";
String WHILE = "WHILE";
String FOR = "FOR";
String DO = "DO";
String BREAK = "BREAK";
String DATA = "data";
String MONITOR_BUS = "monitorBus";
String CURR_CHAIN_ID = "currChainId";
String DEFAULT = "DEFAULT";
}

View File

@ -1,5 +1,8 @@
package com.yomahub.liteflow.common;
/**
* @author Yun
*/
public class LocalDefaultFlowConstant {
public static final String DEFAULT="default";
}

View File

@ -1,12 +1,12 @@
package com.yomahub.liteflow.core;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.monitor.MonitorBus;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
@ -15,6 +15,7 @@ import com.yomahub.liteflow.spi.holder.LiteflowComponentSupportHolder;
/**
* 组件初始化器
*
* @author Bryan.Zhang
* @since 2.6.0
*/
@ -22,38 +23,40 @@ public class ComponentInitializer {
private static ComponentInitializer instance;
public static ComponentInitializer loadInstance(){
if (ObjectUtil.isNull(instance)){
public static ComponentInitializer loadInstance() {
if (ObjectUtil.isNull(instance)) {
instance = new ComponentInitializer();
}
return instance;
}
public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String name, String nodeId){
public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String name, String nodeId) {
nodeComponent.setNodeId(nodeId);
nodeComponent.setSelf(nodeComponent);
nodeComponent.setType(type);
//设置MonitorBus如果没有就不注入
MonitorBus monitorBus = ContextAwareHolder.loadContextAware().getBean(MonitorBus.class);
if(ObjectUtil.isNotNull(monitorBus)){
nodeComponent.setMonitorBus(monitorBus);
if (ContextAwareHolder.loadContextAware().hasBean(ChainConstant.MONITOR_BUS)) {
MonitorBus monitorBus = ContextAwareHolder.loadContextAware().getBean(MonitorBus.class);
if (ObjectUtil.isNotNull(monitorBus)) {
nodeComponent.setMonitorBus(monitorBus);
}
}
//先取传进来的name值(配置文件中配置的)再看有没有配置@LiteflowComponent标注
//@LiteflowComponent标注只在spring体系下生效这里用了spi机制取到相应环境下的实现类
nodeComponent.setName(name);
if (!type.isScript() && StrUtil.isBlank(nodeComponent.getName())){
if (!type.isScript() && StrUtil.isBlank(nodeComponent.getName())) {
nodeComponent.setName(LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent));
}
//先从组件上取@RetryCount标注如果没有则看全局配置全局配置如果不配置的话默认是0
//默认retryForExceptions为Exception.class
LiteflowRetry liteflowRetryAnnotation = AnnoUtil.getAnnotation(nodeComponent.getClass(), LiteflowRetry.class);
LiteflowRetry liteFlowRetryAnnotation = AnnoUtil.getAnnotation(nodeComponent.getClass(), LiteflowRetry.class);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (liteflowRetryAnnotation != null) {
nodeComponent.setRetryCount(liteflowRetryAnnotation.retry());
nodeComponent.setRetryForExceptions(liteflowRetryAnnotation.forExceptions());
if (liteFlowRetryAnnotation != null) {
nodeComponent.setRetryCount(liteFlowRetryAnnotation.retry());
nodeComponent.setRetryForExceptions(liteFlowRetryAnnotation.forExceptions());
} else {
nodeComponent.setRetryCount(liteflowConfig.getRetryCount());
}

View File

@ -60,7 +60,7 @@ public class FlowExecutor {
//设置FlowExecutor的Holder虽然大部分地方都可以通过Spring上下文获取到但放入Holder还是为了某些地方能方便的取到
FlowExecutorHolder.setHolder(this);
if (BooleanUtil.isTrue(liteflowConfig.isParseOnStart())) {
this.init();
this.init(true);
}
//初始化DataBus
DataBus.init();
@ -69,7 +69,7 @@ public class FlowExecutor {
/**
* FlowExecutor的初始化化方式主要用于parse规则文件
*/
public void init() {
public void init(boolean hook) {
if (ObjectUtil.isNull(liteflowConfig)) {
throw new ConfigErrorException("config error, please check liteflow config property");
}
@ -161,12 +161,23 @@ public class FlowExecutor {
throw new FlowExecutorNotInitException(errorMsg);
}
}
//如果是ruleSource方式的最后判断下有没有解析出来,如果没有解析出来则报错
if (FlowBus.getChainMap().isEmpty()){
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
throw new ConfigErrorException(errMsg);
}
//执行钩子
if(hook){
FlowInitHook.executeHook();
}
}
//此方法就是从原有的配置源主动拉取新的进行刷新
//和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {
init();
init(false);
}
//隐式流程的调用方法
@ -265,7 +276,7 @@ public class FlowExecutor {
Integer slotIndex,
InnerChainTypeEnum innerChainType) {
if (FlowBus.needInit()) {
init();
init(true);
}
//如果不是隐式流程那么需要分配Slot

View File

@ -4,6 +4,9 @@ import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.exception.FlowExecutorNotInitException;
import com.yomahub.liteflow.property.LiteflowConfig;
/**
* @author Bryan.Zhang
*/
public class FlowExecutorHolder {
private static FlowExecutor flowExecutor;

View File

@ -0,0 +1,33 @@
package com.yomahub.liteflow.core;
import cn.hutool.core.collection.CollUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BooleanSupplier;
/**
* 流程初始化的钩子类所有的钩子都放在这里
* 目前钩子主要是放一些第三方中间件的规则监听
* 放的钩子要求都是无入参无返回的所以这里是BooleanSupplier
* @author Bryan.Zhang
* @since 2.9.4
*/
public class FlowInitHook {
private static final List<BooleanSupplier> supplierList = new ArrayList<>();
public static void executeHook(){
if (CollUtil.isNotEmpty(supplierList)){
supplierList.forEach(BooleanSupplier::getAsBoolean);
}
}
public static void addHook(BooleanSupplier hookSupplier){
supplierList.add(hookSupplier);
}
public static void cleanHook(){
supplierList.clear();
}
}

View File

@ -12,6 +12,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
import com.yomahub.liteflow.enums.NodeTypeEnum;
@ -58,7 +59,15 @@ public abstract class NodeComponent{
/** 节点执行器的类全名 */
private Class<? extends NodeExecutor> nodeExecutorClass = DefaultNodeExecutor.class;
/********************以下的属性为线程附加属性,并非不变属性********************/
/**当前对象为单例注册进spring上下文但是node实例不是单例这里通过对node实例的引用来获得一些链路属性**/
private final TransmittableThreadLocal<Node> refNodeTL = new TransmittableThreadLocal<>();
/**
*******************以下的属性为线程附加属性********************
* 线程属性是指每一个request的值都是不一样的
* 这里NodeComponent是单例所以要用ThreadLocal来修饰
*/
//当前slot的index
private final TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>();
@ -66,15 +75,6 @@ public abstract class NodeComponent{
//是否结束整个流程这个只对串行流程有效并行流程无效
private final TransmittableThreadLocal<Boolean> isEndTL = new TransmittableThreadLocal<>();
//tag标签
private final TransmittableThreadLocal<String> tagTL = new TransmittableThreadLocal<>();
//当前流程名字
private final TransmittableThreadLocal<String> currChainNameTL = new TransmittableThreadLocal<>();
//组件外部参数
private final TransmittableThreadLocal<String> cmpDataTL = new TransmittableThreadLocal<>();
public NodeComponent() {
}
@ -83,7 +83,7 @@ public abstract class NodeComponent{
//在元数据里加入step信息
CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE);
cmpStep.setTag(tagTL.get());
cmpStep.setTag(this.getTag());
slot.addStep(cmpStep);
StopWatch stopWatch = new StopWatch();
@ -273,16 +273,8 @@ public abstract class NodeComponent{
this.nodeExecutorClass = nodeExecutorClass;
}
public void setTag(String tag){
this.tagTL.set(tag);
}
public String getTag(){
return this.tagTL.get();
}
public void removeTag(){
this.tagTL.remove();
return this.refNodeTL.get().getTag();
}
public MonitorBus getMonitorBus() {
@ -298,16 +290,25 @@ public abstract class NodeComponent{
}
public <T> T getSubChainReqData(){
return getSlot().getChainReqData(this.getCurrChainName());
return getSlot().getChainReqData(this.getCurrChainId());
}
public <T> T getSubChainReqDataInAsync(){
return getSlot().getChainReqDataFromQueue(this.getCurrChainName());
return getSlot().getChainReqDataFromQueue(this.getCurrChainId());
}
/**
* @deprecated 请使用 {@link #getChainId()}
* @return String
*/
@Deprecated
public String getChainName(){
return getSlot().getChainName();
}
public String getChainId(){
return getSlot().getChainId();
}
public String getDisplayName(){
if(StrUtil.isEmpty(this.name)){
@ -317,33 +318,38 @@ public abstract class NodeComponent{
}
}
public void setCurrChainName(String currChainName){
this.currChainNameTL.set(currChainName);
public String getCurrChainId(){
return getRefNode().getCurrChainId();
}
public String getCurrChainName(){
return this.currChainNameTL.get();
public Node getRefNode(){
return this.refNodeTL.get();
}
public void removeCurrChainName(){
this.currChainNameTL.remove();
public void setRefNode(Node refNode){
this.refNodeTL.set(refNode);
}
public void setCmpData(String cmpData){
this.cmpDataTL.set(cmpData);
public void removeRefNode(){
this.refNodeTL.remove();
}
public <T> T getCmpData(Class<T> clazz){
if (StrUtil.isBlank(this.cmpDataTL.get())){
String cmpData = getRefNode().getCmpData();
if (StrUtil.isBlank(cmpData)){
return null;
}
return JsonUtil.parseObject(this.cmpDataTL.get(), clazz);
if (clazz.equals(String.class) || clazz.equals(Object.class)){
return (T) cmpData;
}
return JsonUtil.parseObject(cmpData, clazz);
}
public void removeCmpData(){
this.cmpDataTL.remove();
public Integer getLoopIndex(){
return this.refNodeTL.get().getLoopIndex();
}
@Deprecated
public void invoke(String chainId, Object param) throws Exception {
FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());
}
@ -352,6 +358,7 @@ public abstract class NodeComponent{
return FlowExecutorHolder.loadInstance().invoke2Resp(chainId, param, this.getSlotIndex());
}
@Deprecated
public void invokeInAsync(String chainId, Object param) throws Exception {
FlowExecutorHolder.loadInstance().invokeInAsync(chainId, param, this.getSlotIndex());
}

View File

@ -12,7 +12,7 @@ public class ScriptBreakComponent extends NodeBreakComponent implements ScriptCo
@Override
public boolean processBreak() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -17,7 +17,7 @@ public class ScriptCommonComponent extends NodeComponent implements ScriptCompon
@Override
public void process() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -1,11 +1,32 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.util.HashMap;
import java.util.Map;
/**
* 脚本接口
* @author Bryan.Zhang
* @since 2.9.0
*/
public interface ScriptComponent {
/**
* 用于维护脚本类型和脚本 cmp 的映射关系
*/
Map<NodeTypeEnum, Class<?>> ScriptComponentClassMap = new HashMap<NodeTypeEnum, Class<?>>() {{
put(NodeTypeEnum.SCRIPT, ScriptCommonComponent.class);
put(NodeTypeEnum.SWITCH_SCRIPT, ScriptSwitchComponent.class);
put(NodeTypeEnum.IF_SCRIPT, ScriptIfComponent.class);
put(NodeTypeEnum.FOR_SCRIPT, ScriptForComponent.class);
put(NodeTypeEnum.WHILE_SCRIPT, ScriptWhileComponent.class);
put(NodeTypeEnum.BREAK_SCRIPT, ScriptBreakComponent.class);
}};
/**
* 加载脚本
* @param script
*/
void loadScript(String script);
}

View File

@ -12,7 +12,7 @@ public class ScriptForComponent extends NodeForComponent implements ScriptCompon
@Override
public int processFor() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -12,7 +12,7 @@ public class ScriptIfComponent extends NodeIfComponent implements ScriptComponen
@Override
public boolean processIf() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -13,7 +13,7 @@ public class ScriptSwitchComponent extends NodeSwitchComponent implements Script
@Override
public String processSwitch() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -13,7 +13,7 @@ public class ScriptWhileComponent extends NodeWhileComponent implements ScriptCo
@Override
public boolean processWhile() throws Exception {
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainName(this.getCurrChainName());
wrap.setCurrChainId(this.getCurrChainId());
wrap.setNodeId(this.getNodeId());
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());

View File

@ -62,7 +62,7 @@ public class ComponentProxy {
beanClazz = bean.getClass();
}
//得到当前bean里所覆盖的LiteflowMethod(一定是被@LiteFlowMethod修饰的)自己定义的不算
Map<String, List<Method>> methodListMap = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
Map<String, List<Method>> methodListMap = Arrays.stream(beanClazz.getMethods()).filter(
m -> m.getAnnotation(LiteflowMethod.class) != null
).collect(Collectors.groupingBy(
m -> m.getAnnotation(LiteflowMethod.class).nodeId()
@ -70,7 +70,7 @@ public class ComponentProxy {
return methodListMap.entrySet().stream().map(entry -> {
// 获取当前节点的原有注解LiteFlowRetry 之类的规则注解
Annotation[] beanClassAnnotation = bean.getClass().getAnnotations();
Annotation[] beanClassAnnotation = beanClazz.getAnnotations();
// 如果entry的key为空字符串则是为了兼容老版本的写法没有指定nodeId的情况
// 判断是否是方法级创造节点
boolean isMethodCreate = !StrUtil.isEmpty(entry.getKey());
@ -139,7 +139,7 @@ public class ComponentProxy {
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
Object instance = new ByteBuddy().subclass(cmpClazz)
.name(StrUtil.format("{}.ByteBuddy${}${}",
ClassUtil.getPackage(bean.getClass()),
ClassUtil.getPackage(beanClazz),
activeNodeId,
SerialsUtil.generateShortUUID()))
.method(ElementMatchers.namedOneOf(methodList.stream().map(m -> m.value().getMethodName()).toArray(String[]::new)))
@ -163,8 +163,11 @@ public class ComponentProxy {
private final Object bean;
private final Class<?> clazz;
public AopInvocationHandler(Object bean) {
this.bean = bean;
this.clazz = LiteFlowProxyUtil.getUserClass(bean.getClass());
}
@Override
@ -172,7 +175,7 @@ public class ComponentProxy {
//这里做了2件事情
//先是从普通的bean里过滤出含有@LiteFlowMethod这个标注的方法
//然后进行转换成LiteFlowMethodBean对象List,形成<methodName,Method>键值对的对象
List<LiteFlowMethodBean> liteFlowMethodBeanList = Arrays.stream(ReflectUtil.getMethods(bean.getClass())).filter(m -> {
List<LiteFlowMethodBean> liteFlowMethodBeanList = Arrays.stream(ReflectUtil.getMethods(clazz)).filter(m -> {
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return ObjectUtil.isNotNull(liteFlowMethod);
}).filter(m -> {

View File

@ -2,6 +2,9 @@ package com.yomahub.liteflow.core.proxy;
import java.lang.reflect.Method;
/**
* @author Bryan.Zhang
*/
public class LiteFlowMethodBean {
private String methodName;

View File

@ -1,5 +1,8 @@
package com.yomahub.liteflow.enums;
/**
* @author Yun
*/
public enum ConditionTypeEnum {
TYPE_THEN("then","then"),
TYPE_WHEN("when","when"),

View File

@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.*;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -16,170 +17,175 @@ import java.util.function.Predicate;
/**
* 节点类型枚举
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public enum NodeTypeEnum {
COMMON("common","普通", false, NodeComponent.class),
COMMON("common", "普通", false, NodeComponent.class),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
IF("if", "条件", false, NodeIfComponent.class),
IF("if", "条件", false, NodeIfComponent.class),
FOR("for","循环次数", false, NodeForComponent.class),
FOR("for", "循环次数", false, NodeForComponent.class),
WHILE("while", "循环条件", false, NodeWhileComponent.class),
WHILE("while", "循环条件", false, NodeWhileComponent.class),
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
SCRIPT("script","脚本", true, ScriptCommonComponent.class),
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class)
;
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class);
private String code;
private String name;
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
private boolean isScript;
private String code;
private String name;
private Class<? extends NodeComponent> mappingClazz;
private boolean isScript;
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
private Class<? extends NodeComponent> mappingClazz;
public String getCode() {
return code;
}
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
public void setCode(String code) {
this.code = code;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public boolean isScript() {
return isScript;
}
public void setName(String name) {
this.name = name;
}
public void setScript(boolean script) {
isScript = script;
}
public boolean isScript() {
return isScript;
}
public Class<? extends NodeComponent> getMappingClazz() {
return mappingClazz;
}
public void setScript(boolean script) {
isScript = script;
}
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public Class<? extends NodeComponent> getMappingClazz() {
return mappingClazz;
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
return e;
}
}
return null;
}
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz){
Class<?> superClazz = clazz;
while(true){
superClazz = superClazz.getSuperclass();
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")){
break;
}
if(superClazz.equals(Object.class)){
return null;
}
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
return e;
}
}
return null;
}
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz) {
Class<?> superClazz = clazz;
while (true) {
superClazz = superClazz.getSuperclass();
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")) {
break;
}
if (superClazz.equals(Object.class)) {
return null;
}
}
public static NodeTypeEnum guessType(Class<?> clazz){
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
if (nodeType == null){
//尝试从类声明处进行推断
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
if (liteflowCmpDefine != null){
//类声明方式中@LiteflowMethod是无需设置nodeId的
//但是如果设置了那么核心逻辑其实是取类上定义的id的
//这种可以运行但是理解起来不大好理解所以给出提示建议不要这么做
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null){
return StrUtil.isNotBlank(liteflowMethod.nodeId());
}else{
return false;
}
});
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
if (mixDefined){
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
clazz.getName());
}
public static NodeTypeEnum guessType(Class<?> clazz) {
if(LiteFlowProxyUtil.isCglibProxyClass(clazz)){
clazz = LiteFlowProxyUtil.getUserClass(clazz);
}
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
if (nodeType == null) {
//尝试从类声明处进行推断
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
if (liteflowCmpDefine != null) {
//类声明方式中@LiteflowMethod是无需设置nodeId的
//但是如果设置了那么核心逻辑其实是取类上定义的id的
//这种可以运行但是理解起来不大好理解所以给出提示建议不要这么做
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null) {
return StrUtil.isNotBlank(liteflowMethod.nodeId());
} else {
return false;
}
});
if (mixDefined) {
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
clazz.getName());
}
//在返回之前还要对方法级别的@LiteflowMethod进行检查如果存在方法上的类型与类上的不一致时给予警告信息
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null){
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())){
differenceTypeMethod.set(method);
return true;
}else{
return false;
}
}else{
return false;
}
});
//在返回之前还要对方法级别的@LiteflowMethod进行检查如果存在方法上的类型与类上的不一致时给予警告信息
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
if (liteflowMethod != null) {
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())) {
differenceTypeMethod.set(method);
return true;
} else {
return false;
}
} else {
return false;
}
});
//表示存在不一样的类型
if (hasDifferenceNodeType){
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
clazz.getName(), differenceTypeMethod.get().getName());
}
//表示存在不一样的类型
if (hasDifferenceNodeType) {
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
clazz.getName(), differenceTypeMethod.get().getName());
}
return liteflowCmpDefine.value();
}
return liteflowCmpDefine.value();
}
//再尝试声明式组件这部分的推断
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
//再尝试声明式组件这部分的推断
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
if (liteflowMethod != null) {
nodeType = liteflowMethod.nodeType();
}
}
return nodeType;
}
if (liteflowMethod != null) {
nodeType = liteflowMethod.nodeType();
}
}
return nodeType;
}
}

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 链端异常
* @author Bryan.Zhang
*/
public class ChainEndException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 链端不存在
* @author Bryan.Zhang
*/
public class ChainNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 组件方法定义错误异常
* @author Bryan.Zhang
*/
public class ComponentMethodDefineErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 组件不可访问异常
* @author Bryan.Zhang
*/
public class ComponentNotAccessException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 组件代理错误异常
* @author Yun
*/
public class ComponentProxyErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 配置错误异常
* @author Yun
*/
public class ConfigErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 循环依赖异常
* @author Yun
*/
public class CyclicDependencyException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,7 @@
package com.yomahub.liteflow.exception;
/**
* 未找到数据异常
* @author tangkc
*/
public class DataNofFoundException extends RuntimeException {

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* EL 解析异常
* @author Yun
*/
public class ELParseException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 空条件值异常
* @author Yun
*/
public class EmptyConditionValueException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 错误支持路径异常
* @author Yun
*/
public class ErrorSupportPathException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 流程执行者未初始化
* @author Yun
*/
public class FlowExecutorNotInitException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 流程系统异常
* @author Yun
*/
public class FlowSystemException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 如果目标不能是 Pre Finally 异常
* @author Yun
*/
public class IfTargetCannotBePreOrFinallyException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 类型错误异常
* @author Yun
*/
public class IfTypeErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* Json 进程异常
* @author Yun
*/
public class JsonProcessException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 重复解析器异常
* @author Yun
*/
public class MultipleParsersException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 无可用插槽异常
* @author Yun
*/
public class NoAvailableSlotException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 没有节点异常
* @author Yun
*/
public class NoForNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 节点不为真异常
* @author Yun
*/
public class NoIfTrueNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 无切换目标节点异常
* @author Yun
*/
public class NoSwitchTargetNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 没有 While 节点异常
* @author Yun
*/
public class NoWhileNodeException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 节点构建异常
* @author Yun
*/
public class NodeBuildException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 找不到节点类异常
* @author Yun
*/
public class NodeClassNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 节点类型无法猜测异常
* @author Yun
*/
public class NodeTypeCanNotGuessException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 不支持条件异常
* @author Yun
*/
public class NotSupportConditionException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 空节点异常
* @author Yun
*/
public class NullNodeTypeException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 解析异常
* @author Yun
*/
public class ParseException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 解析器找不到异常
* @author Yun
*/
public class ParserCannotFindException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,29 @@
package com.yomahub.liteflow.exception;
/**
* ScriptBean的方法无法被调用异常
* @author Bryan.Zhang
*/
public class ScriptBeanMethodInvokeException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public ScriptBeanMethodInvokeException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 切换目标不能是 Pre Finally 异常
* @author Yun
*/
public class SwitchTargetCannotBePreOrFinallyException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,5 +1,9 @@
package com.yomahub.liteflow.exception;
/**
* 开关类型错误异常
* @author Yun
*/
public class SwitchTypeErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 执行异常时
* @author Yun
*/
public class WhenExecuteException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -1,6 +1,10 @@
package com.yomahub.liteflow.exception;
/**
* 当超时异常
* @author Yun
*/
public class WhenTimeoutException extends RuntimeException {
private static final long serialVersionUID = 1L;

View File

@ -35,12 +35,16 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* 流程元数据类
*
* @author Bryan.Zhang
*/
public class FlowBus {
@ -54,20 +58,20 @@ public class FlowBus {
private FlowBus() {
}
public static Chain getChain(String id){
public static Chain getChain(String id) {
return chainMap.get(id);
}
//这一方法主要用于第一阶段chain的预装载
public static void addChain(String chainName){
if (!chainMap.containsKey(chainName)){
public static void addChain(String chainName) {
if (!chainMap.containsKey(chainName)) {
chainMap.put(chainName, new Chain(chainName));
}
}
//这个方法主要用于第二阶段的替换chain
public static void addChain(Chain chain) {
chainMap.put(chain.getChainName(), chain);
chainMap.put(chain.getChainId(), chain);
}
public static boolean containChain(String chainId) {
@ -86,119 +90,53 @@ public class FlowBus {
//根据class来猜测类型
NodeTypeEnum type = NodeTypeEnum.guessType(nodeComponent.getClass());
if (type == null){
if (type == null) {
throw new NullNodeTypeException(StrUtil.format("node type is null for node[{}]", nodeId));
}
nodeMap.put(nodeId, new Node(ComponentInitializer.loadInstance().initComponent(nodeComponent, type, null, nodeId)));
}
public static void addCommonNode(String nodeId, String name, String cmpClazzStr){
/**
* 添加 node
*
* @param nodeId 节点id
* @param name 节点名称
* @param type 节点类型
* @param cmpClazz 节点组件类
*/
public static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz) {
addNode(nodeId, name, type, cmpClazz, null);
}
/**
* 添加 node
*
* @param nodeId 节点id
* @param name 节点名称
* @param nodeType 节点类型
* @param cmpClazzStr 节点组件类路径
*/
public static void addNode(String nodeId, String name, NodeTypeEnum nodeType, String cmpClazzStr) {
Class<?> cmpClazz;
try{
try {
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
} catch (Exception e) {
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.COMMON, cmpClazz, null);
addNode(nodeId, name, nodeType, cmpClazz, null);
}
public static void addCommonNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.COMMON, cmpClazz, null);
}
public static void addSwitchNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.SWITCH, cmpClazz, null);
}
public static void addSwitchNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.SWITCH, cmpClazz, null);
}
public static void addIfNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.IF, cmpClazz, null);
}
public static void addIfNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.IF, cmpClazz, null);
}
public static void addForNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.FOR, cmpClazz, null);
}
public static void addForNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.FOR, cmpClazz, null);
}
public static void addWhileNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.WHILE, cmpClazz, null);
}
public static void addWhileNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.WHILE, cmpClazz, null);
}
public static void addBreakNode(String nodeId, String name, String cmpClazzStr){
Class<?> cmpClazz;
try{
cmpClazz = Class.forName(cmpClazzStr);
}catch (Exception e){
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, NodeTypeEnum.BREAK, cmpClazz, null);
}
public static void addBreakNode(String nodeId, String name, Class<?> cmpClazz){
addNode(nodeId, name, NodeTypeEnum.BREAK, cmpClazz, null);
}
public static void addCommonScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.SCRIPT, ScriptCommonComponent.class, script);
}
public static void addSwitchScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.SWITCH_SCRIPT, ScriptSwitchComponent.class, script);
}
public static void addIfScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.IF_SCRIPT, ScriptIfComponent.class, script);
}
public static void addForScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.FOR_SCRIPT, ScriptForComponent.class, script);
}
public static void addWhileScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.WHILE_SCRIPT, ScriptWhileComponent.class, script);
}
public static void addBreakScriptNode(String nodeId, String name, String script){
addNode(nodeId, name, NodeTypeEnum.BREAK_SCRIPT, ScriptBreakComponent.class, script);
/**
* 添加脚本 node
*
* @param nodeId 节点id
* @param name 节点名称
* @param nodeType 节点类型
* @param script 脚本
*/
public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script) {
addNode(nodeId, name, nodeType, ScriptComponent.ScriptComponentClassMap.get(nodeType), script);
}
private static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz, String script) {
@ -206,7 +144,7 @@ public class FlowBus {
//判断此类是否是声明式的组件如果是声明式的组件就用动态代理生成实例
//如果不是声明式的就用传统的方式进行判断
List<NodeComponent> cmpInstances = new ArrayList<>();
if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)){
if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)) {
//这里的逻辑要仔细看下
//如果是spring体系把原始的类往spring上下文中进行注册那么会走到ComponentScanner中
//由于ComponentScanner中已经对原始类进行了动态代理出来的对象已经变成了动态代理类所以这时候的bean已经是NodeComponent的子类了
@ -215,16 +153,16 @@ public class FlowBus {
//这里用ContextAware的spi机制来判断是否spring体系
ContextAware contextAware = ContextAwareHolder.loadContextAware();
Object bean = ContextAwareHolder.loadContextAware().registerBean(nodeId, cmpClazz);
if (LocalContextAware.class.isAssignableFrom(contextAware.getClass())){
if (LocalContextAware.class.isAssignableFrom(contextAware.getClass())) {
cmpInstances = LiteFlowProxyUtil.proxy2NodeComponent(bean, nodeId);
}else {
} else {
cmpInstances = ListUtil.toList((NodeComponent) bean);
}
}else{
} else {
//以node方式配置本质上是为了适配无spring的环境如果有spring环境其实不用这么配置
//这里的逻辑是判断是否能从spring上下文中取到如果没有spring则就是new instance了
//如果是script类型的节点因为class只有一个所以也不能注册进spring上下文注册的时候需要new Instance
if (!type.isScript()){
if (!type.isScript()) {
cmpInstances = ListUtil.toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz));
}
// 去除null元素
@ -235,7 +173,7 @@ public class FlowBus {
cmpInstances.add(cmpInstance);
}
}
//进行初始化
//进行初始化component
cmpInstances = cmpInstances.stream()
.map(cmpInstance -> ComponentInitializer.loadInstance().initComponent(
cmpInstance,
@ -244,7 +182,7 @@ public class FlowBus {
cmpInstance.getNodeId() == null ? nodeId : cmpInstance.getNodeId())
).collect(Collectors.toList());
//初始化Node
//初始化Node把component放到Node里去
List<Node> nodes = cmpInstances.stream().map(Node::new).collect(Collectors.toList());
@ -252,11 +190,11 @@ public class FlowBus {
Node node = nodes.get(i);
NodeComponent cmpInstance = cmpInstances.get(i);
//如果是脚本节点则还要加载script脚本
if (type.isScript()){
if (StrUtil.isNotBlank(script)){
if (type.isScript()) {
if (StrUtil.isNotBlank(script)) {
node.setScript(script);
((ScriptComponent)cmpInstance).loadScript(script);
}else{
((ScriptComponent) cmpInstance).loadScript(script);
} else {
String errorMsg = StrUtil.format("script for node[{}] is empty", nodeId);
throw new ScriptLoadException(errorMsg);
}
@ -267,8 +205,8 @@ public class FlowBus {
}
} catch (Exception e) {
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name)?nodeId:StrUtil.format("{}({})",nodeId,name));
LOG.error(error);
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name));
LOG.error(e.getMessage());
throw new ComponentCannotRegisterException(error);
}
}
@ -281,19 +219,19 @@ public class FlowBus {
//那condNodeMap共用有关系么原则上没有关系但是从设计理念上以后应该要分开
//tag和condNodeMap这2个属性不属于全局概念属于每个chain范围的属性
public static Node copyNode(String nodeId) {
try{
try {
Node node = nodeMap.get(nodeId);
return node.copy();
}catch (Exception e){
} catch (Exception e) {
return null;
}
}
public static Map<String, Node> getNodeMap(){
public static Map<String, Node> getNodeMap() {
return nodeMap;
}
public static Map<String, Chain> getChainMap(){
public static Map<String, Chain> getChainMap() {
return chainMap;
}
@ -305,12 +243,13 @@ public class FlowBus {
public static void cleanScriptCache() {
//如果引入了脚本组件SPI则还需要清理脚本的缓存
try{
try {
ScriptExecutor scriptExecutor = ScriptExecutorFactory.loadInstance().getScriptExecutor();
if (ObjectUtil.isNotNull(scriptExecutor)){
if (ObjectUtil.isNotNull(scriptExecutor)) {
scriptExecutor.cleanCache();
}
}catch (ScriptSpiException ignored){}
} catch (ScriptSpiException ignored) {
}
}
public static void refreshFlowMetaData(FlowParserTypeEnum type, String content) throws Exception {
@ -323,14 +262,18 @@ public class FlowBus {
}
}
public static boolean removeChain(String chainId){
if (containChain(chainId)){
public static boolean removeChain(String chainId) {
if (containChain(chainId)) {
chainMap.remove(chainId);
return true;
}else{
} else {
String errMsg = StrUtil.format("cannot find the chain[{}]", chainId);
LOG.error(errMsg);
return false;
}
}
public static void removeChain(String... chainIds) {
Arrays.stream(chainIds).forEach(FlowBus::removeChain);
}
}

View File

@ -29,24 +29,18 @@ public class Chain implements Executable {
private static final Logger LOG = LoggerFactory.getLogger(Chain.class);
private String chainName;
private String chainId;
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;
this.chainId = chainName;
}
public Chain(){}
public Chain(String chainName, List<Condition> conditionList) {
this.chainName = chainName;
this.chainId = chainName;
this.conditionList = conditionList;
}
@ -58,29 +52,44 @@ public class Chain implements Executable {
this.conditionList = conditionList;
}
/**
* @deprecated 请使用{@link #getChainId()}
*/
@Deprecated
public String getChainName() {
return chainName;
return chainId;
}
/**
*
* @param chainName
* @deprecated 请使用 {@link #setChainId(String)}
*/
public void setChainName(String chainName) {
this.chainName = chainName;
this.chainId = chainName;
}
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
//执行chain的主方法
@Override
public void execute(Integer slotIndex) throws Exception {
if (CollUtil.isEmpty(conditionList)) {
throw new FlowSystemException("no conditionList in this chain[" + chainName + "]");
throw new FlowSystemException("no conditionList in this chain[" + chainId + "]");
}
Slot slot = DataBus.getSlot(slotIndex);
try {
//设置主ChainName
slot.setChainName(chainName);
//执行前置
this.executePre(slotIndex);
slot.setChainId(chainId);
//执行主体Condition
for (Condition condition : conditionList) {
condition.setCurrChainName(chainName);
condition.setCurrChainId(chainId);
condition.execute(slotIndex);
}
}catch (ChainEndException e){
@ -89,29 +98,12 @@ public class Chain implements Executable {
throw e;
}catch (Exception e){
//这里事先取到exception set到slot里为了方便finally取到exception
if (slot.isSubChain(chainName)){
slot.setSubException(chainName, e);
if (slot.isSubChain(chainId)){
slot.setSubException(chainId, e);
}else{
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);
}
}
@ -121,22 +113,7 @@ public class Chain implements Executable {
}
@Override
public String getExecuteName() {
return chainName;
}
public List<Condition> getPreConditionList() {
return preConditionList;
}
public void setPreConditionList(List<Condition> preConditionList) {
this.preConditionList = preConditionList;
}
public List<Condition> getFinallyConditionList() {
return finallyConditionList;
}
public void setFinallyConditionList(List<Condition> finallyConditionList) {
this.finallyConditionList = finallyConditionList;
public String getExecuteId() {
return chainId;
}
}

View File

@ -18,9 +18,28 @@ public interface Executable{
ExecuteTypeEnum getExecuteType();
String getExecuteName();
/**
*
* @return
* @deprecated 请使用 {@link #getExecuteId()}
*/
@Deprecated
default String getExecuteName() {
return getExecuteId();
}
/**
*
* @param currentChainName
* @deprecated 请使用 {@link #setCurrChainId(String)}
*/
default void setCurrChainName(String currentChainName){
setCurrChainId(currentChainName);
}
String getExecuteId();
default void setCurrChainId(String currentChainId){
}
}

View File

@ -12,6 +12,7 @@ import java.text.MessageFormat;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
@ -28,6 +29,7 @@ import org.slf4j.LoggerFactory;
/**
* Node节点实现可执行器
* Node节点并不是单例的每构建一次都会copy出一个新的实例
* @author Bryan.Zhang
*/
public class Node implements Executable,Cloneable{
@ -50,6 +52,10 @@ public class Node implements Executable,Cloneable{
private String cmpData;
private String currChainId;
private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
public Node(){
}
@ -106,8 +112,7 @@ public class Node implements Executable,Cloneable{
try {
//把线程属性赋值给组件对象
instance.setSlotIndex(slotIndex);
instance.setTag(tag);
instance.setCmpData(cmpData);
instance.setRefNode(this);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
@ -148,9 +153,8 @@ public class Node implements Executable,Cloneable{
//移除threadLocal里的信息
instance.removeSlotIndex();
instance.removeIsEnd();
instance.removeTag();
instance.removeCurrChainName();
instance.removeCmpData();
instance.removeRefNode();
removeLoopIndex();
}
}
@ -160,7 +164,9 @@ public class Node implements Executable,Cloneable{
//详情见这个issue:https://gitee.com/dromara/liteFlow/issues/I4XRBA
@Override
public boolean isAccess(Integer slotIndex) throws Exception {
//把线程属性赋值给组件对象
instance.setSlotIndex(slotIndex);
instance.setRefNode(this);
return instance.isAccess();
}
@ -179,7 +185,7 @@ public class Node implements Executable,Cloneable{
}
@Override
public String getExecuteName() {
public String getExecuteId() {
return id;
}
@ -207,11 +213,6 @@ public class Node implements Executable,Cloneable{
this.clazz = clazz;
}
@Override
public void setCurrChainName(String currentChainName) {
instance.setCurrChainName(currentChainName);
}
public String getCmpData() {
return cmpData;
}
@ -219,4 +220,25 @@ public class Node implements Executable,Cloneable{
public void setCmpData(String cmpData) {
this.cmpData = cmpData;
}
@Override
public void setCurrChainId(String currentChainId) {
this.currChainId = currentChainId;
}
public String getCurrChainId() {
return currChainId;
}
public void setLoopIndex(int index){
this.loopIndexTL.set(index);
}
public Integer getLoopIndex(){
return this.loopIndexTL.get();
}
public void removeLoopIndex(){
this.loopIndexTL.remove();
}
}

View File

@ -7,7 +7,6 @@
*/
package com.yomahub.liteflow.flow.element.condition;
import com.yomahub.liteflow.common.LocalDefaultFlowConstant;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
@ -23,14 +22,17 @@ public abstract class Condition implements Executable{
private String id;
//可执行元素的集合
/**
* 可执行元素的集合
*/
private List<Executable> executableList = new ArrayList<>();
//当前所在的ChainName
//如果对于子流程来说那这个就是子流程所在的Chain
private String currChainName;
/**
* 当前所在的ChainName
* 如果对于子流程来说那这个就是子流程所在的Chain
*/
private String currChainId;
@Override
public ExecuteTypeEnum getExecuteType() {
@ -38,7 +40,7 @@ public abstract class Condition implements Executable{
}
@Override
public String getExecuteName() {
public String getExecuteId() {
return this.id;
}
@ -64,12 +66,22 @@ public abstract class Condition implements Executable{
this.id = id;
}
/**
*
* @return
* @deprecated 请使用 {@link #setCurrChainId(String)}
*/
@Deprecated
public String getCurrChainName() {
return currChainName;
return currChainId;
}
public String getCurrChainId() {
return currChainId;
}
@Override
public void setCurrChainName(String currChainName) {
this.currChainName = currChainName;
public void setCurrChainId(String currChainId) {
this.currChainId = currChainId;
}
}

View File

@ -20,7 +20,7 @@ public class FinallyCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getExecutableList()){
executableItem.setCurrChainName(this.getCurrChainName());
executableItem.setCurrChainId(this.getCurrChainId());
executableItem.execute(slotIndex);
}
}

View File

@ -29,7 +29,7 @@ public class ForCondition extends LoopCondition{
}
//执行forCount组件
forNode.setCurrChainName(this.getCurrChainName());
forNode.setCurrChainId(this.getCurrChainId());
forNode.execute(slotIndex);
//这里可能会有spring代理过的bean所以拿到user原始的class
@ -42,10 +42,13 @@ public class ForCondition extends LoopCondition{
//循环执行
for (int i = 0; i < forCount; i++) {
executableItem.setCurrChainId(this.getCurrChainId());
//设置循环index
setLoopIndex(executableItem, i);
executableItem.execute(slotIndex);
//如果break组件不为空则去执行
if (ObjectUtil.isNotNull(breakNode)){
breakNode.setCurrChainName(this.getCurrChainName());
breakNode.setCurrChainId(this.getCurrChainId());
breakNode.execute(slotIndex);
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
@ -54,7 +57,6 @@ public class ForCondition extends LoopCondition{
}
}
}
}
@Override

View File

@ -28,7 +28,7 @@ public class IfCondition extends Condition {
public void execute(Integer slotIndex) throws Exception {
if (ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(getIfNode().getType())){
//先执行IF节点
this.getIfNode().setCurrChainName(this.getCurrChainName());
this.getIfNode().setCurrChainId(this.getCurrChainId());
this.getIfNode().execute(slotIndex);
Slot slot = DataBus.getSlot(slotIndex);
@ -63,7 +63,7 @@ public class IfCondition extends Condition {
}
//执行falseCaseExecutableItem
falseCaseExecutableItem.setCurrChainName(this.getCurrChainName());
falseCaseExecutableItem.setCurrChainId(this.getCurrChainId());
falseCaseExecutableItem.execute(slotIndex);
}
}

View File

@ -1,5 +1,7 @@
package com.yomahub.liteflow.flow.element.condition;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
/**
@ -20,4 +22,14 @@ public abstract class LoopCondition extends Condition {
public void setBreakNode(Node breakNode) {
this.breakNode = breakNode;
}
protected void setLoopIndex(Executable executableItem, int index){
if (executableItem instanceof Chain){
((Chain)executableItem).getConditionList().forEach(condition -> setLoopIndex(condition, index));
}else if(executableItem instanceof Condition){
((Condition)executableItem).getExecutableList().forEach(executable -> setLoopIndex(executable, index));
}else if(executableItem instanceof Node){
((Node)executableItem).setLoopIndex(index);
}
}
}

View File

@ -20,7 +20,7 @@ public class PreCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getExecutableList()){
executableItem.setCurrChainName(this.getCurrChainName());
executableItem.setCurrChainId(this.getCurrChainId());
executableItem.execute(slotIndex);
}
}

View File

@ -3,7 +3,6 @@ package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.proxy.ComponentProxy;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.NoSwitchTargetNodeException;
@ -15,10 +14,8 @@ import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.ArrayList;
import java.util.List;
/**
* 选择Condition
@ -27,15 +24,20 @@ import java.util.function.Predicate;
*/
public class SwitchCondition extends Condition{
private final Map<String, Executable> targetMap = new HashMap<>();
private final String TAG_PREFIX = "tag:";
private final List<Executable> targetList = new ArrayList<>();
private final String TAG_PREFIX = "tag";
private final String TAG_FLAG = ":";
private Executable defaultExecutor;
@Override
public void execute(Integer slotIndex) throws Exception {
if (ListUtil.toList(NodeTypeEnum.SWITCH, NodeTypeEnum.SWITCH_SCRIPT).contains(this.getSwitchNode().getType())){
//先执行switch节点
this.getSwitchNode().setCurrChainName(this.getCurrChainName());
this.getSwitchNode().setCurrChainId(this.getCurrChainId());
this.getSwitchNode().execute(slotIndex);
//根据switch节点执行出来的结果选择
@ -46,31 +48,42 @@ public class SwitchCondition extends Condition{
if (StrUtil.isNotBlank(targetId)) {
Executable targetExecutor;
//这里要判断是否跳转到tag
if (targetId.startsWith(TAG_PREFIX)){
String targetTag = targetId.replaceAll(TAG_PREFIX, "");
targetExecutor = targetMap.values().stream().filter(executable -> {
//这里要判断是否使用tag模式跳转
if (targetId.contains(TAG_FLAG)){
String[] target = targetId.split(TAG_FLAG, 2);
String _targetId = target[0];
String _targetTag = target[1];
targetExecutor = targetList.stream().filter(executable -> {
if (executable instanceof Node){
Node node = (Node) executable;
return targetTag.equals(node.getTag());
return (StrUtil.startWith(_targetId, TAG_PREFIX) && _targetTag.equals(node.getTag())) || ((StrUtil.isEmpty(_targetId) || _targetId.equals(node.getId())) && (StrUtil.isEmpty(_targetTag) || _targetTag.equals(node.getTag())));
}else{
return false;
}
}).findFirst().orElse(null);
}else{
targetExecutor = targetMap.get(targetId);
targetExecutor = targetList.stream().filter(
executable -> executable.getExecuteId().equals(targetId)
).findFirst().orElse(null);
}
if (ObjectUtil.isNull(targetExecutor)) {
//没有匹配到执行节点则走默认的执行节点
targetExecutor = defaultExecutor;
}
if (ObjectUtil.isNotNull(targetExecutor)) {
//switch的目标不能是Pre节点或者Finally节点
if (targetExecutor instanceof PreCondition || targetExecutor instanceof FinallyCondition){
String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally", slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally",
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
throw new SwitchTargetCannotBePreOrFinallyException(errorInfo);
}
targetExecutor.setCurrChainName(this.getCurrChainName());
targetExecutor.setCurrChainId(this.getCurrChainId());
targetExecutor.execute(slotIndex);
}else{
String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}]", slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}],target str is [{}]",
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName(), targetId);
throw new NoSwitchTargetNodeException(errorInfo);
}
}
@ -85,18 +98,26 @@ public class SwitchCondition extends Condition{
}
public void addTargetItem(Executable executable){
this.targetMap.put(executable.getExecuteName(), executable);
this.targetList.add(executable);
}
public void setSwitchNode(Node switchNode) {
this.getExecutableList().add(switchNode);
}
public Map<String, Executable> getTargetMap() {
return targetMap;
public List<Executable> getTargetList(){
return targetList;
}
public Node getSwitchNode(){
return (Node) this.getExecutableList().get(0);
}
public Executable getDefaultExecutor() {
return defaultExecutor;
}
public void setDefaultExecutor(Executable defaultExecutor) {
this.defaultExecutor = defaultExecutor;
}
}

View File

@ -8,13 +8,30 @@
package com.yomahub.liteflow.flow.element.condition;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import java.util.ArrayList;
import java.util.List;
/**
* 串行器
* @author Bryan.Zhang
*/
public class ThenCondition extends Condition {
/**
* 前置处理Condition
*/
private final List<PreCondition> preConditionList = new ArrayList<>();
/**
* 后置处理Condition
*/
private final List<FinallyCondition> finallyConditionList = new ArrayList<>();
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_THEN;
@ -22,13 +39,46 @@ public class ThenCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
for (Executable executableItem : this.getExecutableList()) {
//前置和后置组不执行因为在build的时候会抽出来放在chain里面
if (executableItem instanceof PreCondition || executableItem instanceof FinallyCondition){
continue;
try{
for (PreCondition preCondition : preConditionList){
preCondition.setCurrChainId(this.getCurrChainId());
preCondition.execute(slotIndex);
}
executableItem.setCurrChainName(this.getCurrChainName());
executableItem.execute(slotIndex);
for (Executable executableItem : this.getExecutableList()) {
executableItem.setCurrChainId(this.getCurrChainId());
executableItem.execute(slotIndex);
}
}catch (ChainEndException e){
//这里单独catch ChainEndException是因为ChainEndException是用户自己setIsEnd抛出的异常
//是属于正常逻辑所以会在FlowExecutor中判断这里不作为异常处理
throw e;
}catch (Exception e){
Slot slot = DataBus.getSlot(slotIndex);
String chainId = this.getCurrChainId();
//这里事先取到exception set到slot里为了方便finally取到exception
if (slot.isSubChain(chainId)){
slot.setSubException(chainId, e);
}else{
slot.setException(e);
}
throw e;
}finally {
for (FinallyCondition finallyCondition : finallyConditionList){
finallyCondition.setCurrChainId(this.getCurrChainId());
finallyCondition.execute(slotIndex);
}
}
}
@Override
public void addExecutable(Executable executable) {
if (executable instanceof PreCondition){
preConditionList.add((PreCondition) executable);
}else if (executable instanceof FinallyCondition){
finallyConditionList.add((FinallyCondition) executable);
}else{
super.addExecutable(executable);
}
}
}

View File

@ -40,6 +40,7 @@ public class WhenCondition extends Condition {
private boolean errorResume = false;
//只在when类型下有效用于不同node进行同组合并相同的组会进行合并不同的组不会进行合并
//此属性已弃用
private String group = LocalDefaultFlowConstant.DEFAULT;
//只在when类型下有效为true的话说明在多个并行节点下任意一个成功整个when就成功
@ -64,7 +65,7 @@ public class WhenCondition extends Condition {
private void executeAsyncCondition(Integer slotIndex) throws Exception{
Slot slot = DataBus.getSlot(slotIndex);
String currChainName = this.getCurrChainName();
String currChainName = this.getCurrChainId();
//此方法其实只会初始化一次Executor不会每次都会初始化Executor是唯一的
ExecutorService parallelExecutor = ExecutorHelper.loadInstance().buildWhenExecutor(this.getThreadExecutorClass());
@ -92,7 +93,7 @@ public class WhenCondition extends Condition {
return false;
}
}).map(executable -> CompletableFutureTimeout.completeOnTimeout(
WhenFutureObj.timeOut(executable.getExecuteName()),
WhenFutureObj.timeOut(executable.getExecuteId()),
CompletableFuture.supplyAsync(new ParallelSupplier(executable, currChainName, slotIndex), parallelExecutor),
liteflowConfig.getWhenMaxWaitSeconds(),
TimeUnit.SECONDS

View File

@ -32,11 +32,14 @@ public class WhileCondition extends LoopCondition{
Executable executableItem = this.getDoExecutor();
//循环执行
int index = 0;
while(getWhileResult(slotIndex)){
executableItem.setCurrChainId(this.getCurrChainId());
setLoopIndex(executableItem, index++);
executableItem.execute(slotIndex);
//如果break组件不为空则去执行
if (ObjectUtil.isNotNull(breakNode)){
breakNode.setCurrChainName(this.getCurrChainName());
breakNode.setCurrChainId(this.getCurrChainId());
breakNode.execute(slotIndex);
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());

View File

@ -19,7 +19,11 @@ import java.util.List;
public abstract class NodeExecutor {
protected final Logger LOG = LoggerFactory.getLogger(this.getClass());
//执行器执行入口-若需要更大维度的执行方式可以重写该方法
/**
* 执行器执行入口-若需要更大维度的执行方式可以重写该方法
* @param instance
* @throws Exception
*/
public void execute(NodeComponent instance) throws Exception {
int retryCount = instance.getRetryCount();
List<Class<? extends Exception>> forExceptions = Arrays.asList(instance.getRetryForExceptions());
@ -47,7 +51,12 @@ public abstract class NodeExecutor {
}
}
//执行重试逻辑 - 子类通过实现该方法进行重试逻辑的控制
/**
* 执行重试逻辑 - 子类通过实现该方法进行重试逻辑的控制
* @param instance
* @param currentRetryCount
* @throws Exception
*/
protected void retry(NodeComponent instance, int currentRetryCount) throws Exception {
Slot slot = DataBus.getSlot(instance.getSlotIndex());
LOG.info("[{}]:component[{}] performs {} retry", slot.getRequestId(),instance.getDisplayName(), currentRetryCount + 1);

View File

@ -13,19 +13,26 @@ import java.util.Map;
* @since 2.6.9
*/
public class NodeExecutorHelper {
//此处使用Map缓存线程池信息
/**
* 此处使用Map缓存线程池信息
*/
private final Map<Class<? extends NodeExecutor>, NodeExecutor> nodeExecutorMap;
private NodeExecutorHelper() {
nodeExecutorMap = MapUtil.newConcurrentHashMap();
}
//使用静态内部类实现单例模式
/**
* 使用静态内部类实现单例模式
*/
private static class Holder {
static final NodeExecutorHelper INSTANCE = new NodeExecutorHelper();
}
//获取帮助者的实例
/**
* 获取帮助者的实例
* @return
*/
public static NodeExecutorHelper loadInstance() {
// 外围类能直接访问内部类不管是否是静态的的私有变量
return Holder.INSTANCE;

View File

@ -29,11 +29,11 @@ public class ParallelSupplier implements Supplier<WhenFutureObj> {
@Override
public WhenFutureObj get() {
try {
executableItem.setCurrChainName(currChainName);
executableItem.setCurrChainId(currChainName);
executableItem.execute(slotIndex);
return WhenFutureObj.success(executableItem.getExecuteName());
return WhenFutureObj.success(executableItem.getExecuteId());
} catch (Exception e){
return WhenFutureObj.fail(executableItem.getExecuteName(), e);
return WhenFutureObj.fail(executableItem.getExecuteId(), e);
}
}
}

View File

@ -35,7 +35,8 @@ public abstract class BaseJsonFlowParser implements FlowParser {
JsonNode flowJsonNode = JsonUtil.parseObject(content);
jsonObjectList.add(flowJsonNode);
}
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
ParserHelper.parseNodeJson(jsonObjectList);
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
}
/**

View File

@ -36,8 +36,8 @@ public abstract class BaseXmlFlowParser implements FlowParser {
documentList.add(document);
}
Consumer<Element> parseOneChainConsumer = this::parseOneChain;
ParserHelper.parseDocument(documentList, CHAIN_NAME_SET, parseOneChainConsumer);
ParserHelper.parseNodeDocument(documentList);
ParserHelper.parseChainDocument(documentList, CHAIN_NAME_SET, this::parseOneChain);
}
/**

View File

@ -39,7 +39,8 @@ public abstract class BaseYmlFlowParser implements FlowParser {
}
Consumer<JsonNode> parseOneChainConsumer = this::parseOneChain;
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET,parseOneChainConsumer);
ParserHelper.parseNodeJson(jsonObjectList);
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, parseOneChainConsumer);
}
protected JsonNode convertToJson(String yamlString) {

View File

@ -133,12 +133,12 @@ public class FlowParserProvider {
* 统一管理类的常量
*/
protected static class ConfigRegexConstant {
public static final String LOCAL_XML_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.xml$";
public static final String LOCAL_JSON_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.json$";
public static final String LOCAL_YML_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.yml$";
public static final String LOCAL_EL_XML_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.el\\.xml$";
public static final String LOCAL_EL_JSON_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.el\\.json$";
public static final String LOCAL_EL_YML_CONFIG_REGEX = "^[\\w\\:\\-\\@\\/\\\\\\*]+\\.el\\.yml$";
public static final String LOCAL_XML_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.xml$";
public static final String LOCAL_JSON_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.json$";
public static final String LOCAL_YML_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.yml$";
public static final String LOCAL_EL_XML_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.el\\.xml$";
public static final String LOCAL_EL_JSON_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.el\\.json$";
public static final String LOCAL_EL_YML_CONFIG_REGEX = "^[\\w\\:\\-\\.\\@\\/\\\\\\*]+\\.el\\.yml$";
public static final String PREFIX_FORMAT_CONFIG_REGEX = "xml:|json:|yml:|el_xml:|el_json:|el_yml:";
public static final String CLASS_CONFIG_REGEX = "^(xml:|json:|yml:|el_xml:|el_json:|el_yml:)?\\w+(\\.\\w+)*$";
}

View File

@ -1,11 +1,9 @@
package com.yomahub.liteflow.parser.helper;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.yomahub.liteflow.annotation.*;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.builder.prop.NodePropBean;
@ -87,35 +85,13 @@ public class ParserHelper {
/**
* xml 形式的主要解析过程
*
* @param documentList documentList
* @param chainNameSet 用于去重
* @param parseOneChainConsumer parseOneChain 函数
*/
public static void parseDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer) {
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
documentList.forEach(document -> {
// 解析chain节点
List<Element> chainList = document.getRootElement().elements(CHAIN);
//先在元数据里放上chain
chainList.forEach(e -> {
//校验加载的 chainName 是否有重复的
//TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = e.attributeValue(NAME);
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
});
});
// 清空
chainNameSet.clear();
/**
* xml 形式的主要解析过程
* @param documentList documentList
*/
public static void parseNodeDocument(List<Document> documentList) {
for (Document document : documentList) {
Element rootElement = document.getRootElement();
Element nodesElement = rootElement.element(NODES);
@ -128,7 +104,7 @@ public class ParserHelper {
name = e.attributeValue(NAME);
clazz = e.attributeValue(_CLASS);
type = e.attributeValue(TYPE);
script = e.getTextTrim();
script = e.getText();
file = e.attributeValue(FILE);
// 构建 node
@ -143,37 +119,42 @@ public class ParserHelper {
ParserHelper.buildNode(nodePropBean);
}
}
}
}
public static void parseChainDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer){
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
documentList.forEach(document -> {
// 解析chain节点
List<Element> chainList = document.getRootElement().elements(CHAIN);
//解析每一个chain
//先在元数据里放上chain
chainList.forEach(e -> {
//校验加载的 chainName 是否有重复的
//TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
});
});
// 清空
chainNameSet.clear();
//解析每一个chain
for (Document document : documentList) {
Element rootElement = document.getRootElement();
List<Element> chainList = rootElement.elements(CHAIN);
chainList.forEach(parseOneChainConsumer);
}
}
public static void parseJsonNode(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer) {
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
//先在元数据里放上chain
while (iterator.hasNext()) {
JsonNode innerJsonObject = iterator.next();
//校验加载的 chainName 是否有重复的
// TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = innerJsonObject.get(NAME).textValue();
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(innerJsonObject.get(NAME).textValue());
}
});
// 清空
chainNameSet.clear();
public static void parseNodeJson(List<JsonNode> flowJsonObjectList) {
for (JsonNode flowJsonNode : flowJsonObjectList) {
// 当存在<nodes>节点定义时解析node节点
if (flowJsonNode.get(FLOW).has(NODES)) {
@ -200,7 +181,34 @@ public class ParserHelper {
ParserHelper.buildNode(nodePropBean);
}
}
}
}
public static void parseChainJson(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer){
//先在元数据里放上chain
//先放有一个好处可以在parse的时候先映射到FlowBus的chainMap然后再去解析
//这样就不用去像之前的版本那样回归调用
//同时也解决了不能循环依赖的问题
flowJsonObjectList.forEach(jsonObject -> {
// 解析chain节点
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
//先在元数据里放上chain
while (iterator.hasNext()) {
JsonNode innerJsonObject = iterator.next();
//校验加载的 chainName 是否有重复的
// TODO 这里是否有个问题当混合格式加载的时候2个同名的Chain在不同的文件里就不行了
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
if (!chainNameSet.add(chainName)) {
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
}
FlowBus.addChain(chainName);
}
});
// 清空
chainNameSet.clear();
for (JsonNode flowJsonNode : flowJsonObjectList) {
//解析每一个chain
Iterator<JsonNode> chainIterator = flowJsonNode.get(FLOW).get(CHAIN).elements();
while (chainIterator.hasNext()) {
@ -217,9 +225,9 @@ public class ParserHelper {
*/
public static void parseOneChainEl(JsonNode chainNode) {
//构建chainBuilder
String chainName = chainNode.get(NAME).textValue();
String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
String el = chainNode.get(VALUE).textValue();
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
chainELBuilder.setEL(el).build();
}
@ -230,10 +238,10 @@ public class ParserHelper {
*/
public static void parseOneChainEl(Element e) {
//构建chainBuilder
String chainName = e.attributeValue(NAME);
String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
String text = e.getText();
String el = RegexUtil.removeComments(text);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
chainELBuilder.setEL(el).build();
}
@ -253,15 +261,10 @@ public class ParserHelper {
return elStr;
}
String text = Pattern.compile(REGEX_COMMENT)
return Pattern.compile(REGEX_COMMENT)
.matcher(elStr)
//移除注释
.replaceAll(CharSequenceUtil.EMPTY)
//移除字符串中的空格
.replaceAll(CharSequenceUtil.SPACE, CharSequenceUtil.EMPTY);
// 移除所有换行符
return StrUtil.removeAllLineBreaks(text);
.replaceAll(CharSequenceUtil.EMPTY);
}
}
}

Some files were not shown because too many files have changed in this diff Show More