Compare commits

..

No commits in common. "master" and "v2.8.5" have entirely different histories.

4889 changed files with 54421 additions and 93856 deletions

View File

@ -1 +0,0 @@
java-baseline=8

View File

@ -1,6 +1,6 @@
<p align="center">
<a href="https://liteflow.cc/">
<img src="static/img/logo-main.svg" width="50%" height="auto" alt="logo">
<a href="https://liteflow.yomahub.com/">
<img src="static/img/logo-main.svg" alt="logo">
</a>
</p>
@ -11,16 +11,18 @@
## Overview
LiteFlow is a lightweight and powerful rules engine framework, which can be used in the field of complex componenzed business orchestration. DSL rules drive the whole complex business, and can achieve smooth refresh hot deployment, supporting the embedding of multiple scripting language rules. Help the system become more silky and flexible.
LiteFlow was officially open-sourced in 2020 and has since become an indispensable presence in the field of open-source rule engines in China. What's more, the most crucial aspect is that LiteFlow is an open-source project that continues to iterate at a high speed.
LiteFlow was officially open source in 2020 and won the title of "the Most Popular Open Source software" in China in 2021. Gitee-GVP award in 2022. It is an open source project that is growing rapidly.
LiteFlow is a community-driven project. We take community building very seriously. We have a community of more than 4000 users who can respond to any problems or suggestions they encounter.
LiteFlow is a community-driven project. We take community building very seriously. We have a community of more than 800 users who can respond to any problems or suggestions they encounter.
You can find out how to join the community on the official website!
## Website url: [Click here to access the official website](https://liteflow.cc)
## Documents url: [Click here to enter the documentation to learn](https://liteflow.cc/pages/5816c5/)
## Website url: [Click here to access the official website](https://liteflow.yomahub.com/en)
## 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 860 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.
@ -30,8 +32,7 @@ You can find out how to join the community on the official website!
* **Elegant hot refresh mechanism:** Rule changes, instant change of application rules without restarting your application. High concurrency does not cause any errors in executing rules due to refreshing rules.
* **Wide support:** LiteFlow works regardless of whether your project is built on Springboot, Spring, or any other Java framework.
* **JDK support:** From JDK8 to JDK17. Don't worry about JDK versions.
* **Full Springboot support:** Supports Springboot 2.X through the latest Springboot 3.X.
* **Scripting language support:** You can define script language nodes that support Groovy, Javascript, QLExpress, Python, Lua, Kotlin and Aviator. More script languages will be supported in the future.
* **Scripting language support:** Scripting language nodes can be defined, supporting both QLExpress and Groovy scripting. More scripting languages will be supported in the future.
* **Rule nesting support:** You can use simple expressions to create multiple nested complex logic layouts if you want.
* **Component retry support:** Components can support retry, and each component can customize the retry configuration and specify exceptions.
* **Context isolation mechanism:** Reliable context isolation mechanism, you do not have to worry about high concurrency data flow.
@ -54,7 +55,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.
LiteFlow has 1500 test cases and more. Complete documentation and comprehensive test case coverage guarantee the stability of LiteFlow framework!
Right now 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
@ -62,58 +63,11 @@ Looking forward to your use
Discord Link: [https://discord.gg/MpdBSBnFTu](https://discord.gg/MpdBSBnFTu)
## 👑LF CLUB Community
LF CLUB is a premium paid community founded by the author of LiteFlow.
LF CLUB can help all users of the LiteFlow framework, as well as potential developers who want to use LiteFlow.
LF CLUB provides the following services:
**1.Weekly releases of a condensed analysis series for LF. As long as users study along with the Planet series articles, they will definitely be able to fully grasp LF.**
**2.Provide a Q&A service where members can ask unlimited questions and receive detailed replies and guidance on the same day.**
**3.Each enrolled user is entitled to two remote one-on-one tutoring sessions per year as part of the remote assistance service.**
**4.Every 1 to 2 days, there will be updates on LF's current progress and the focus of the next version.**
The LF CLUB can solve all the problems you encounter when using the LiteFlow framework. It offers a series of courses to help you gain a deep understanding of the LiteFlow framework. Unlike the WeChat community, the LF CLUB prioritizes the importance of questions and provides detailed answers.
Exclusive content helps you gain a profound understanding without the need to search for answers on other platforms. The author personally teaches, providing expert guidance at your fingertips, eliminating the need to seek help from others.
To join the LF CLUB, please scan the QR code below or click on the image to go directly to the website.
<a href="https://t.zsxq.com/16VxfATen"><img src="static/img/zsxq-github.png"></a>
## 🦾Sponsor
**MISBoot低代码开发平台**
<a href="https://www.misboot.com/?from=LiteFlow"><img src="static/img/misboot-banner.png"></a>
**云程企业级低代码平台**
<a href="http://www.yunchengxc.com/"><img src="static/img/yuncheng-banner.png"></a>
**驰骋工作流引擎**
<a href="https://ccbpm.cn/?frm=liteFlow"><img src="static/img/chicheng-banner.png"></a>
**FastBee物联网平台**
<a href="https://fastbee.cn/"><img src="static/img/fb-banner.png"></a>
**JNPF低代码开发平台**
<a href="https://www.jnpfsoft.com/index.html?from=liteflow"><img src="static/img/yinmai-banner.png"></a>
**WECHAT OFFICIAL ACCOUNT**
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
![official-wx](static/img/offical-wx.jpg)
![offIical-wx](static/img/offical-wx.jpg)
Open source is not easy, please sponsor LiteFlow if you support it

View File

@ -1,36 +1,36 @@
<p align="center">
<a href="https://liteflow.cc/">
<img src="static/img/logo-main.svg" width="50%" height="auto" alt="logo">
<a href="https://liteflow.yomahub.com/">
<img src="static/img/logo-main.svg" alt="logo">
</a>
</p>
<h3>您的star是我继续前进的动力如果喜欢LiteFlow请右上角帮忙点个star</h3>
## 🌈概述
## 概述
LiteFlow是一个轻量且强大的国产规则引擎框架可用于复杂的组件化业务的编排领域独有的DSL规则驱动整个复杂业务并可实现平滑刷新热部署支持多种脚本语言规则的嵌入。帮助系统变得更加丝滑且灵活。
LiteFlow于2020年正式开源直到现在已经是国内开源规则引擎中不可忽视的存在而且最关键的是LiteFlow还是一个持续高速迭代的开源项目。
LiteFlow于2020年正式开源2021年获得开源中国年度最受欢迎开源软件殊荣。于2022年获得Gitee最有价值开源项目(GVP)荣誉。是一个正处在高速发展中的开源项目。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个5000多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个800多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
你在官网中可以找到加入社区的方式!
## 官网链接:[点这里进入官网](https://liteflow.cc/)
## 文档链接:[点这里进入文档进行学习](https://liteflow.cc/pages/5816c5/)
## 官网链接:[点这里进入官网](https://liteflow.yomahub.com/)
## 文档链接:[点这里进入文档进行学习](https://liteflow.yomahub.com/pages/5816c5/)
## 示例工程:[DEMO1](https://github.com/bryan31/message-demo) | [DEMO2](https://gitee.com/bryan31/liteflow-example)
## 🍬特性
**同时LiteFlow的源码中拥有超过860个测试用例包含了大部分的场景在源码中可以看到测试用例**
## 特性
* **组件定义统一:** 所有的逻辑都是组件,为所有的逻辑提供统一化的组件实现方式,小身材,大能量。
* **规则轻量:** 基于规则文件来编排流程学习规则入门只需要5分钟一看既懂。
* **规则多样化:** 规则支持xml、json、yml三种规则文件写法方式喜欢哪种用哪个。
* **任意编排:** 再复杂的逻辑过程利用LiteFlow的规则都是很容易做到的看规则文件就能知道逻辑是如何运转的。
* **规则持久化:** 框架原生支持把规则存储在标准结构化数据库NacosEtcdZookeeperApolloredis。您也可以自己扩展把规则存储在任何地方。
* **任意编排:** 同步异步混编,再复杂的逻辑过程利用LiteFlow的规则都是很容易做到的看规则文件就能知道逻辑是如何运转的。
* **规则能从任意地方加载:** 框架中提供本地文件配置源和zk配置源的实现也提供了扩展接口您可以把规则存储在任何地方。
* **优雅热刷新机制:** 规则变化,无需重启您的应用,即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。
* **支持广泛:** 不管你的项目是不是基于SpringbootSpring还是任何其他java框架构建LiteFlow都能游刃有余。
* **JDK支持** 从JDK8到JDK17全部支持。无需担心JDK版本。
* **Springboot支持全面** 支持Springboot 2.X到最新的Springboot 3.X。
* **脚本语言支持:** 可以定义脚本语言节点支持GroovyJavascriptQLExpressPythonLuaAviatorJavaKotlin。未来还会支持更多的脚本语言。
* **脚本和Java全打通** 所有脚本语言均可调用Java方法甚至于可以引用任意的实例在脚本中调用RPC也是支持的。
* **JDK支持** 从JDK8到JDK17统统支持。无需担心JDK版本。
* **脚本语言支持:** 可以定义脚本语言节点支持QLExpress和Groovy两种脚本。未来还会支持更多的脚本语言。
* **规则嵌套支持:** 只要你想的出,你可以利用简单的表达式完成多重嵌套的复杂逻辑编排。
* **组件重试支持:** 组件可以支持重试,每个组件均可自定义重试配置和指定异常。
* **上下文隔离机制:** 可靠的上下文隔离机制,你无需担心高并发情况下的数据串流。
@ -40,7 +40,7 @@ LiteFlow是一个由社区驱动的项目我们非常重视社区建设
* **性能卓越:** 框架本身几乎不消耗额外性能,性能取决你的组件执行效率。
* **自带简单监控:** 框架内自带一个命令行的监控,能够知道每个组件的运行耗时排行。
## ☘️什么场景适用
## 什么场景适用
LiteFlow是一款编排式的规则引擎最擅长去解耦你的系统如果你的系统业务复杂并且代码臃肿不堪那LiteFlow框架会是一个非常好的解决方案。
@ -53,62 +53,16 @@ LiteFlow利用规则表达式为驱动引擎去驱动你定义的组件。你
LiteFlow拥有极其详细易懂的文档体系能帮助你解决在使用框架的时候95%以上的问题。
目前为止LiteFlow拥有1500多个测试用例,并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
目前为止LiteFlow拥有658个测试用例,并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
LiteFlow期待你的了解
## 👑LF CLUB社区
LF CLUB是由LiteFlow作者创办的高级付费社区
LF CLUB能帮助到所有LiteFlow框架的使用者以及想使用LiteFlow的潜在开发者。
LF CLUB提供以下服务
**1.每周发布一篇LF的解析精华系列。从头开始解析LF只要跟着星球解析系列走使用者一定能完全掌握LF。**
**2.提供答疑服务,会员可以无限制提问,当天必定得到详细的回复和指导建议。**
**3.每个加入的用户每年提供2次远程一对一答疑远程协助服务。**
**4.每1到2天会分享LF目前的进度以及下一个版本的重点。**
LF CLUB里能解决你在使用LiteFlow框架时碰到的所有问题并有系列课程能帮助你深刻理解LiteFlow框架不同于微信社区LF CLUB的问题优先级程度是最高的且答疑非常详细。
独家内容帮助深刻理解,不用在其他平台去搜索问题的答案。作者亲授,相当于随时拥有专家在身边,不用再去求助其他人。
加入LF CLUB请扫描以下二维码或者直接点击图片也可以直达
<a href="https://t.zsxq.com/16jLy4Bj6"><img src="static/img/zsxq-gitee.png"></a>
## 🦾赞助商
**MISBoot低代码开发平台**
<a href="https://www.misboot.com/?from=LiteFlow"><img src="static/img/misboot-banner.png"></a>
**云程企业级低代码平台**
<a href="http://www.yunchengxc.com/"><img src="static/img/yuncheng-banner.png"></a>
**驰骋工作流引擎**
<a href="https://ccbpm.cn/?frm=liteFlow"><img src="static/img/chicheng-banner.png"></a>
**FastBee物联网平台**
<a href="https://fastbee.cn/"><img src="static/img/fb-banner.png"></a>
**JNPF低代码开发平台**
<a href="https://www.jnpfsoft.com/index.html?from=liteflow"><img src="static/img/yinmai-banner.png"></a>
**微信公众号**
社区群需要邀请入群。关注公众号后点击`个人微信`加我,我可以拉你入群
由于社区群超过200人需要邀请入群。关注公众号后点击`个人微信`加我,我可以拉你入群
![offIical-wx](static/img/offical-wx.jpg)
开源不易支持就请赞助LiteFlow,请我喝一杯咖啡吧
开源不易支持就请赞助LiteFlow
<img src="static/img/support.png" alt="support" width="258" />

View File

@ -14,6 +14,12 @@
</parent>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-common</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
@ -34,6 +40,28 @@
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<exclusions>
<exclusion>
<artifactId>log4j</artifactId>
<groupId>log4j</groupId>
</exclusion>
<exclusion>
<artifactId>slf4j-api</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
<exclusion>
<artifactId>commons-lang</artifactId>
<groupId>commons-lang</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
@ -46,13 +74,5 @@
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,11 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {
String value() default "";
}

View File

@ -1,22 +0,0 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 降级组件
*
* @author DaleLee
* @since 2.11.1
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FallbackCmp {
}

View File

@ -1,15 +0,0 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface LFAliasFor {
String value() default "";
}

View File

@ -1,18 +1,10 @@
package com.yomahub.liteflow.annotation;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Target({ ElementType.TYPE })
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowCmpDefine {
NodeTypeEnum value() default NodeTypeEnum.COMMON;
}

View File

@ -1,12 +0,0 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Documented
@Inherited
public @interface LiteflowFact {
String value();
}

View File

@ -0,0 +1,10 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowIfCmpDefine {
}

View File

@ -1,37 +1,14 @@
package com.yomahub.liteflow.annotation;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.lang.annotation.*;
/**
* @author Bryan.Zhang
*/
@Target({ ElementType.METHOD })
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowMethod {
LiteFlowMethodEnum value();
/**
* 节点ID用于区分节点 默认为空 则按照Spring模式下BeanName为准
* @return nodeId
*/
String nodeId() default "";
/**
* 节点Name
* @return nodeName
*/
String nodeName() default "";
/**
* CMP类型定义
* @return AnnotationNodeTypeEnum
*/
NodeTypeEnum nodeType() default NodeTypeEnum.COMMON;
LiteFlowMethodEnum value();
}

View File

@ -8,24 +8,17 @@ import java.lang.annotation.*;
* @author Bryan.Zhang
* @since 2.6.0
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Deprecated
/**
* This class has been deprecated due to its only component retry function. Please use the retry method in the EL expression.
* @Deprecated
* @see # retry(int retryTimes) e.g. THEN( a, b.retry(3) ); WHEN( a, b ).retry(3);
*/
public @interface LiteflowRetry {
@LFAliasFor("retry")
int value() default 0;
@AliasFor("retry")
int value() default 0;
@LFAliasFor("value")
int retry() default 0;
Class<? extends Exception>[] forExceptions() default { Exception.class };
@AliasFor("value")
int retry() default 0;
Class<? extends Exception>[] forExceptions() default {Exception.class};
}

View File

@ -0,0 +1,10 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowSwitchCmpDefine {
}

View File

@ -3,67 +3,49 @@ package com.yomahub.liteflow.annotation.util;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LFAliasFor;
import com.yomahub.liteflow.annotation.AliasFor;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 注解工具类
* 此工具类带缓存
*
* @author Bryan.Zhang
*/
public class AnnoUtil {
private static Map<String, Annotation> annoCacheMap = new ConcurrentHashMap<>();
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
A annotation = AnnotationUtil.getAnnotation(annotatedElement, annotationType);
if (ObjectUtil.isNull(annotation)){
return null;
}
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
String cacheKey = StrUtil.format("{}-{}", annotatedElement, annotationType.getSimpleName());
Map<String, String> aliasMap = new HashMap<>();
Map<String, Object> defaultValueMap = new HashMap<>();
Arrays.stream(ReflectUtil.getMethods(annotationType)).forEach(method -> {
AliasFor aliasFor = AnnotationUtil.getAnnotation(method, AliasFor.class);
if (ObjectUtil.isNotNull(aliasFor)){
aliasMap.put(method.getName(), aliasFor.value());
defaultValueMap.put(method.getName(), getDefaultValue(annotationType, method.getName()));
}
});
if (annoCacheMap.containsKey(cacheKey)){
return (A)annoCacheMap.get(cacheKey);
}
aliasMap.forEach((key, value1) -> {
Object value = ReflectUtil.invoke(annotation, key);
Object defaultValue = defaultValueMap.get(key);
if (ObjectUtil.notEqual(value, defaultValue)) {
AnnotationUtil.setValue(annotation, value1, value);
}
});
A annotation = AnnotationUtil.getAnnotation(annotatedElement, annotationType);
if (ObjectUtil.isNull(annotation)) {
return null;
}
return annotation;
}
Map<String, String> aliasMap = new HashMap<>();
Map<String, Object> defaultValueMap = new HashMap<>();
Arrays.stream(ReflectUtil.getMethods(annotationType)).forEach(method -> {
LFAliasFor aliasFor = AnnotationUtil.getAnnotation(method, LFAliasFor.class);
if (ObjectUtil.isNotNull(aliasFor)) {
aliasMap.put(method.getName(), aliasFor.value());
defaultValueMap.put(method.getName(), getDefaultValue(annotationType, method.getName()));
}
});
aliasMap.forEach((key, value1) -> {
Object value = ReflectUtil.invoke(annotation, key);
Object defaultValue = defaultValueMap.get(key);
if (ObjectUtil.notEqual(value, defaultValue)) {
AnnotationUtil.setValue(annotation, value1, value);
}
});
annoCacheMap.put(cacheKey, annotation);
return annotation;
}
private static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property) {
try {
return annotationType.getMethod(property).getDefaultValue();
}
catch (Exception e) {
return null;
}
}
public static <A extends Annotation> Object getDefaultValue(Class<A> annotationType, String property){
try{
return annotationType.getMethod(property).getDefaultValue();
}catch (Exception e){
return null;
}
}
}

View File

@ -1,29 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
*
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/10/22
*/
package com.yomahub.liteflow.aop;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.Slot;
/**
* 全局组件拦截器接口 实现这个接口并注入到spring上下文即可
*
* 全局组件拦截器接口
* 实现这个接口并注入到spring上下文即可
* @author Bryan.Zhang
*/
public interface ICmpAroundAspect {
void beforeProcess(NodeComponent cmp);
void afterProcess(NodeComponent cmp);
void onSuccess(NodeComponent cmp);
void onError(NodeComponent cmp, Exception e);
void beforeProcess(String nodeId, Slot slot);
void afterProcess(String nodeId, Slot slot);
}

View File

@ -0,0 +1,116 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
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

@ -0,0 +1,158 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.condition.*;
import com.yomahub.liteflow.builder.entity.ExecutableEntity;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.ExecutableItemNotFoundException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.parser.RegexEntity;
import com.yomahub.liteflow.parser.RegexNodeEntity;
import com.yomahub.liteflow.spi.holder.ContextAwareHolder;
import java.util.List;
/**
* Condition基于代码形式的组装器
* @author Bryan.Zhang
* @since 2.6.8
*/
public class LiteFlowConditionBuilder {
protected Condition condition;
public static LiteFlowConditionBuilder createCondition(ConditionTypeEnum conditionType){
switch (conditionType){
case TYPE_THEN:
return createThenCondition();
case TYPE_WHEN:
return createWhenCondition();
case TYPE_PRE:
return createPreCondition();
case TYPE_FINALLY:
return createFinallyCondition();
default:
return null;
}
}
public static LiteFlowConditionBuilder createThenCondition(){
return new LiteFlowConditionBuilder(new ThenCondition());
}
public static LiteFlowWhenConditionBuilder createWhenCondition(){
return new LiteFlowWhenConditionBuilder(new WhenCondition());
}
public static LiteFlowConditionBuilder createPreCondition(){
return new LiteFlowConditionBuilder(new PreCondition());
}
public static LiteFlowConditionBuilder createFinallyCondition(){
return new LiteFlowConditionBuilder(new FinallyCondition());
}
public LiteFlowConditionBuilder(Condition condition){
this.condition = condition;
}
public LiteFlowConditionBuilder setValue(String value){
if (StrUtil.isBlank(value)){
return this;
}
String[] condArray = value.split(",");
RegexEntity regexEntity;
String itemExpression;
for (String s : condArray) {
itemExpression = s.trim();
regexEntity = RegexEntity.parse(itemExpression);
// 先转化为执行实体对象
ExecutableEntity executableEntity = convertExecutableEntity(regexEntity);
// 构建节点或流程
setExecutable(executableEntity);
}
return this;
}
// 将正则表达式实体转化为执行实体
private ExecutableEntity convertExecutableEntity(RegexEntity regexEntity) {
RegexNodeEntity item = regexEntity.getItem();
ExecutableEntity executableEntity = new ExecutableEntity(item.getId(), item.getTag());
if (ObjectUtil.isNotNull(regexEntity.getRealItemArray())) {
for (RegexNodeEntity realItem : regexEntity.getRealItemArray()) {
executableEntity.addNodeCondComponent(new ExecutableEntity(realItem.getId(), realItem.getTag()));
}
}
return executableEntity;
}
// 设置执行节点或者流程
public LiteFlowConditionBuilder setExecutable(ExecutableEntity executableEntity) {
if (FlowBus.containNode(executableEntity.getId())) {
Node node = FlowBus.copyNode(executableEntity.getId());
node.setTag(executableEntity.getTag());
//如果没有条件节点说明是普通组件如果有条件节点就去构建SwitchCondition
if (CollUtil.isEmpty(executableEntity.getNodeCondComponents())) {
this.condition.getExecutableList().add(node);
}else{
buildSwitchNode(node, executableEntity.getNodeCondComponents());
}
} else if (hasChain(executableEntity.getId())) {
Chain chain = FlowBus.getChain(executableEntity.getId());
this.condition.getExecutableList().add(chain);
} else {
//元数据没有的话从spring上下文再取一遍
//这部分有2个目的
//一是为了防止标有@Lazy懒加载的组件二是spring负责扫描而用动态代码的形式加载组件这种情况
NodeComponent nodeComponent = ContextAwareHolder.loadContextAware().getBean(executableEntity.getId());
if (ObjectUtil.isNotNull(nodeComponent)){
FlowBus.addSpringScanNode(executableEntity.getId(), nodeComponent);
setExecutable(executableEntity);
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", executableEntity.getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
return this;
}
// 构建条件节点
private void buildSwitchNode(Node node, List<ExecutableEntity> executableEntities) {
if (CollUtil.isEmpty(executableEntities)) {
return;
}
SwitchCondition switchCondition = new SwitchCondition();
switchCondition.setSwitchNode(node);
for (ExecutableEntity realItem : executableEntities) {
if (FlowBus.containNode(realItem.getId())) {
Node targetNode = FlowBus.copyNode(realItem.getId());
targetNode.setTag(realItem.getTag());
switchCondition.addTargetItem(targetNode);
} else if (hasChain(realItem.getId())) {
Chain chain = FlowBus.getChain(realItem.getId());
switchCondition.addTargetItem(chain);
} else{
String errorMsg = StrUtil.format("executable node[{}] is not found!", realItem.getId());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
this.condition.getExecutableList().add(switchCondition);
}
public Condition build(){
return this.condition;
}
private boolean hasChain(String chainId){
return FlowBus.containChain(chainId);
}
}

View File

@ -1,16 +1,14 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.NodeBuildException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.monitor.MonitorFile;
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
@ -18,163 +16,131 @@ import java.util.Objects;
public class LiteFlowNodeBuilder {
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private final Node node;
private final Node node;
public static LiteFlowNodeBuilder createNode() {
return new LiteFlowNodeBuilder();
}
public static LiteFlowNodeBuilder createNode() {
return new LiteFlowNodeBuilder();
}
public static LiteFlowNodeBuilder createCommonNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.COMMON);
}
public static LiteFlowNodeBuilder createCommonNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.COMMON);
}
public static LiteFlowNodeBuilder createSwitchNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH);
}
public static LiteFlowNodeBuilder createSwitchNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH);
}
public static LiteFlowNodeBuilder createBooleanNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN);
}
public static LiteFlowNodeBuilder createIfNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.IF);
}
public static LiteFlowNodeBuilder createForNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR);
}
public static LiteFlowNodeBuilder createScriptNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
}
public static LiteFlowNodeBuilder createIteratorNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.ITERATOR);
}
public static LiteFlowNodeBuilder createScriptSwitchNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH_SCRIPT);
}
public static LiteFlowNodeBuilder createScriptNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
}
public static LiteFlowNodeBuilder createScriptIfNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.IF_SCRIPT);
}
public static LiteFlowNodeBuilder createScriptSwitchNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH_SCRIPT);
}
public LiteFlowNodeBuilder() {
this.node = new Node();
}
public static LiteFlowNodeBuilder createScriptBooleanNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN_SCRIPT);
}
public LiteFlowNodeBuilder(NodeTypeEnum type) {
this.node = new Node();
this.node.setType(type);
}
public static LiteFlowNodeBuilder createScriptForNode() {
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR_SCRIPT);
}
public LiteFlowNodeBuilder setId(String nodeId) {
if (StrUtil.isBlank(nodeId)) {
return this;
}
this.node.setId(nodeId.trim());
return this;
}
public LiteFlowNodeBuilder() {
this.node = new Node();
}
public LiteFlowNodeBuilder setName(String name) {
if (StrUtil.isBlank(name)) {
return this;
}
this.node.setName(name.trim());
return this;
}
public LiteFlowNodeBuilder(NodeTypeEnum type) {
this.node = new Node();
this.node.setType(type);
}
public LiteFlowNodeBuilder setClazz(String clazz) {
if (StrUtil.isBlank(clazz)) {
return this;
}
this.node.setClazz(clazz.trim());
return this;
}
public LiteFlowNodeBuilder setId(String nodeId) {
if (StrUtil.isBlank(nodeId)) {
return this;
}
this.node.setId(nodeId.trim());
return this;
}
public LiteFlowNodeBuilder setClazz(Class<?> clazz) {
assert clazz != null;
setClazz(clazz.getName());
return this;
}
public LiteFlowNodeBuilder setName(String name) {
if (StrUtil.isBlank(name)) {
return this;
}
this.node.setName(name.trim());
return this;
}
public LiteFlowNodeBuilder setType(NodeTypeEnum type) {
this.node.setType(type);
return this;
}
public LiteFlowNodeBuilder setClazz(String clazz) {
if (StrUtil.isBlank(clazz)) {
return this;
}
this.node.setClazz(clazz.trim());
return this;
}
public LiteFlowNodeBuilder setScript(String script) {
this.node.setScript(script);
return this;
}
public LiteFlowNodeBuilder setClazz(Class<?> clazz) {
assert clazz != null;
setClazz(clazz.getName());
return this;
}
public LiteFlowNodeBuilder setFile(String filePath) {
if (StrUtil.isBlank(filePath)) {
return this;
}
String script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", filePath.trim()));
return setScript(script);
}
public LiteFlowNodeBuilder setType(NodeTypeEnum type) {
this.node.setType(type);
return this;
}
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.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());
}
} catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}]", this.node.getId());
LOG.error(errMsg, e);
throw new NodeBuildException(errMsg);
}
}
public LiteFlowNodeBuilder setScript(String script) {
this.node.setScript(script);
return this;
}
public LiteFlowNodeBuilder setFile(String filePath) {
if (StrUtil.isBlank(filePath)) {
return this;
}
try {
List<String> scriptList = PathContentParserHolder.loadContextAware()
.parseContent(ListUtil.toList(filePath));
String script = CollUtil.getFirst(scriptList);
setScript(script);
// 添加脚本文件监听
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware()
.getFileAbsolutePath(ListUtil.toList(filePath));
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
}
catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(),
e.getMessage());
throw new NodeBuildException(errMsg);
}
return this;
}
public LiteFlowNodeBuilder setLanguage(String language) {
if (StrUtil.isNotBlank(language)){
this.node.setLanguage(language);
}
return this;
}
public void build() {
checkBuild();
try {
// 用于处理脚本 node
if (this.node.getType().isScript()) {
FlowBus.addScriptNode(this.node.getId(), this.node.getName(), this.node.getType(),
this.node.getScript(), this.node.getLanguage());
}
// 用于处理普通 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());
LOG.error(errMsg, e);
throw new NodeBuildException(errMsg);
}
}
/**
* build 前简单校验
*/
private void checkBuild() {
List<String> errorList = new ArrayList<>();
if (StrUtil.isBlank(this.node.getId())) {
errorList.add("id is blank");
}
if (Objects.isNull(this.node.getType())) {
errorList.add("type is null");
}
if (CollUtil.isNotEmpty(errorList)) {
throw new NodeBuildException(CollUtil.join(errorList, ",", "[", "]"));
}
}
/**
* build 前简单校验
*/
private void checkBuild() {
List<String> errorList = new ArrayList<>();
if (StrUtil.isBlank(this.node.getId())) {
errorList.add("id is blank");
}
if (Objects.isNull(this.node.getType())) {
errorList.add("type is null");
}
if (CollUtil.isNotEmpty(errorList)) {
throw new NodeBuildException(CollUtil.join(errorList, ",", "[", "]"));
}
}
}

View File

@ -0,0 +1,66 @@
package com.yomahub.liteflow.builder;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.common.LocalDefaultFlowConstant;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
/**
* WhenCondition基于代码形式的组装器
* 这个为LiteFlowConditionBuilder的子类因为when有单独的设置项所以区分开
* @author Bryan.Zhang
* @since 2.6.8
*/
public class LiteFlowWhenConditionBuilder extends LiteFlowConditionBuilder{
public LiteFlowWhenConditionBuilder(Condition condition) {
super(condition);
}
public LiteFlowWhenConditionBuilder setErrorResume(boolean errorResume){
WhenCondition whenCondition = (WhenCondition) this.condition;
whenCondition.setErrorResume(errorResume);
return this;
}
public LiteFlowWhenConditionBuilder setErrorResume(String errorResume){
if (StrUtil.isBlank(errorResume)){
return this;
}
return setErrorResume(Boolean.parseBoolean(errorResume));
}
public LiteFlowWhenConditionBuilder setGroup(String group){
WhenCondition whenCondition = (WhenCondition) this.condition;
if (StrUtil.isBlank(group)){
whenCondition.setGroup(LocalDefaultFlowConstant.DEFAULT);
}else{
whenCondition.setGroup(group);
}
return this;
}
public LiteFlowWhenConditionBuilder setAny(boolean any){
WhenCondition whenCondition = (WhenCondition) this.condition;
whenCondition.setAny(any);
return this;
}
public LiteFlowWhenConditionBuilder setAny(String any){
if (StrUtil.isBlank(any)){
return this;
}
return setAny(Boolean.parseBoolean(any));
}
public LiteFlowWhenConditionBuilder setThreadExecutorClass(String executorServiceName){
WhenCondition whenCondition = (WhenCondition) this.condition;
if (StrUtil.isBlank(executorServiceName)) {
return this;
}
whenCondition.setThreadExecutorClass(executorServiceName);
return this;
}
}

View File

@ -1,408 +1,154 @@
package com.yomahub.liteflow.builder.el;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.*;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.enums.ParseModeEnum;
import com.yomahub.liteflow.exception.*;
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.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.AndOrCondition;
import com.yomahub.liteflow.flow.element.condition.NotCondition;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.util.ElRegexUtil;
import com.yomahub.liteflow.flow.element.condition.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Chain基于代码形式的组装器 EL表达式规则专属组装器
*
* Chain基于代码形式的组装器
* EL表达式规则专属组装器
* @author Bryan.Zhang
* @since 2.8.0
*/
public class LiteFlowChainELBuilder {
private static final LFLog LOG = LFLoggerManager.getLogger(LiteFlowChainELBuilder.class);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private static ObjectMapper objectMapper = new ObjectMapper();
private Chain chain;
private Chain chain;
//这是主体的Condition不包含前置和后置
//声明这个变量而不是用chain.getConditionList的目的是为了辅助平滑加载
//虽然FlowBus里面的map都是CopyOnWrite类型的但是在buildCondition的时候为了平滑加载所以不能事先把chain.getConditionList给设为空List
//所以在这里做一个缓存等conditionList全部build完毕后再去一次性替换chain里面的conditionList
private final List<Condition> conditionList;
/**
* 这是route EL的文本
*/
private Executable route;
//前置处理Condition用来区别主体的Condition
private final List<Condition> preConditionList;
/**
* 这是主体的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> finallyConditionList;
/**
* EL解析引擎
*/
public final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
//EL解析引擎
private final ExpressRunner expressRunner;
static {
// 初始化QLExpress的Runner
EXPRESS_RUNNER.addFunction(ChainConstant.THEN, new ThenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHEN, new WhenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.SER, new ThenOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.PAR, 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.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.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.CATCH, new CatchOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.AND, new AndOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.OR, new OrOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NOT, new NotOperator());
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.MUST, Object.class, new MustOperator());
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.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());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PARALLEL, Object.class, new ParallelOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY, Object.class, new RetryOperator());
}
public static LiteFlowChainELBuilder createChain() {
return new LiteFlowChainELBuilder();
}
public static LiteFlowChainELBuilder createChain() {
return new LiteFlowChainELBuilder();
}
public LiteFlowChainELBuilder() {
chain = new Chain();
conditionList = new ArrayList<>();
preConditionList = new ArrayList<>();
finallyConditionList = new ArrayList<>();
public LiteFlowChainELBuilder() {
chain = new Chain();
conditionList = new ArrayList<>();
}
//初始化QLExpress的Runner
expressRunner = new ExpressRunner();
expressRunner.addFunction("THEN", new ThenOperator());
expressRunner.addFunction("WHEN", new WhenOperator());
expressRunner.addFunction("SWITCH", new SwitchOperator());
expressRunner.addFunction("PRE", new PreOperator());
expressRunner.addFunction("FINALLY", new FinallyOperator());
expressRunner.addFunction("IF", new IfOperator());
expressRunner.addFunctionAndClassMethod("ELSE", Object.class, new ElseOperator());
expressRunner.addFunctionAndClassMethod("ELIF", Object.class, new ElifOperator());
expressRunner.addFunctionAndClassMethod("to", Object.class, new ToOperator());
expressRunner.addFunctionAndClassMethod("tag", Object.class, new TagOperator());
expressRunner.addFunctionAndClassMethod("any", Object.class, new AnyOperator());
expressRunner.addFunctionAndClassMethod("id", Object.class, new IdOperator());
expressRunner.addFunctionAndClassMethod("ignoreError", Object.class, new IgnoreErrorOperator());
expressRunner.addFunctionAndClassMethod("threadPool", Object.class, new ThreadPoolOperator());
expressRunner.addFunction("node", new NodeOperator());
}
// 在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
// 2.6.8之后取消了递归的模式两段式组装先把带有chainName的chain对象放进去第二段再组装chain里面的condition
// 所以这里setChainName的时候需要判断下
//在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
//2.6.8之后取消了递归的模式两段式组装先把带有chainName的chain对象放进去第二段再组装chain里面的condition
//所以这里setChainName的时候需要判断下
public LiteFlowChainELBuilder setChainName(String chainName) {
if (FlowBus.containChain(chainName)) {
this.chain = FlowBus.getChain(chainName);
} else {
this.chain.setChainName(chainName);
}
return this;
}
/**
* @return LiteFlowChainELBuilder
* @deprecated 请使用 {@link #setChainId(String)}
*/
public LiteFlowChainELBuilder setChainName(String chainName) {
if (FlowBus.containChain(chainName)) {
this.chain = FlowBus.getChain(chainName);
} else {
this.chain.setChainName(chainName);
}
return this;
}
public LiteFlowChainELBuilder setEL(String elStr) {
if (StrUtil.isBlank(elStr)){
String errMsg = StrUtil.format("no conditionList in this chain[{}]", chain.getChainName());
throw new FlowSystemException(errMsg);
}
public LiteFlowChainELBuilder setChainId(String chainId) {
if (FlowBus.containChain(chainId)) {
this.chain = FlowBus.getChain(chainId);
} else {
this.chain.setChainId(chainId);
}
return this;
}
List<String> errorList = new ArrayList<>();
try{
DefaultContext<String, Object> context = new DefaultContext<>();
public LiteFlowChainELBuilder setRoute(String routeEl){
if (StrUtil.isBlank(routeEl)) {
return this;
}
List<String> errorList = new ArrayList<>();
try {
DefaultContext<String, Object> context = new DefaultContext<>();
//这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
//往上下文里放入所有的chain是的el表达式可以直接引用到chain
FlowBus.getChainMap().values().forEach(chain -> context.put(chain.getChainName(), chain));
// 往上下文里放入所有的node使得el表达式可以直接引用到nodeId
FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.getNode(nodeId)));
//往上下文里放入所有的node使得el表达式可以直接引用到nodeId
FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.getNode(nodeId)));
// 解析route el成为一个executable
Executable routeExecutable = (Executable) EXPRESS_RUNNER.execute(routeEl, context, errorList, true, true);
//解析el成为一个Condition
//为什么这里只是一个Condition而不是一个List<Condition>
//这里无论多复杂的外面必定有一个最外层的Condition所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
Condition condition = (Condition) expressRunner.execute(elStr, context, errorList, true, true);
// 判断routeEL是不是符合规范
if (!(routeExecutable instanceof AndOrCondition || routeExecutable instanceof NotCondition || routeExecutable instanceof Node)){
throw new RouteELInvalidException("the route EL can only be a boolean node, or an AND or OR expression.");
}
//从condition的第一层嵌套结构里拿出Pre和Finally节点
//为什么只寻找第一层而不往下寻找了呢
//因为这是一个规范如果在后面的层级中出现pre和finally语义上也不好理解所以pre和finally只能定义在第一层
//如果硬是要在后面定义则执行的时候会忽略相关代码已做了判断
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.route = routeExecutable;
return this;
} catch (QLException e) {
// EL 底层会包装异常这里是曲线处理
if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
// 构建错误信息
String msg = buildDataNotFoundExceptionMsg(routeEl);
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
}else{
throw new ELParseException(e.getMessage());
}
}catch (RouteELInvalidException e){
throw e;
}catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
}
}
//把主要的condition加入
this.conditionList.add(condition);
return this;
}catch (QLException e){
throw new ELParseException(e.getCause().getMessage());
}catch (Exception e){
throw new ELParseException(e.getMessage());
}
}
public LiteFlowChainELBuilder setEL(String elStr) {
if (StrUtil.isBlank(elStr)) {
String errMsg = StrUtil.format("no el in this chain[{}]", chain.getChainId());
throw new FlowSystemException(errMsg);
}
public void build() {
this.chain.setConditionList(this.conditionList);
this.chain.setPreConditionList(this.preConditionList);
this.chain.setFinallyConditionList(this.finallyConditionList);
this.chain.setEl(elStr);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
// 如果设置了不检查Node是否存在那么这里是不解析的
if (liteflowConfig.getParseMode().equals(ParseModeEnum.PARSE_ONE_ON_FIRST_EXEC)){
this.chain.setCompiled(false);
return this;
}
checkBuild();
List<String> errorList = new ArrayList<>();
try {
DefaultContext<String, Object> context = new DefaultContext<>();
// 这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
// 往上下文里放入所有的chain是的el表达式可以直接引用到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所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
Condition condition = (Condition) EXPRESS_RUNNER.execute(elStr, context, errorList, true, true);
if (Objects.isNull(condition)){
throw new QLException(StrUtil.format("parse el fail,el:[{}]", elStr));
}
// 把主要的condition加入
this.conditionList.add(condition);
return this;
} catch (QLException e) {
// EL 底层会包装异常这里是曲线处理
if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
// 构建错误信息
String msg = buildDataNotFoundExceptionMsg(elStr);
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
}else{
throw new ELParseException(e.getMessage());
}
} catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
}
}
public LiteFlowChainELBuilder setNamespace(String nameSpace){
if (StrUtil.isBlank(nameSpace)) {
nameSpace = ChainConstant.DEFAULT_NAMESPACE;
}
this.chain.setNamespace(nameSpace);
return this;
}
/**
* EL表达式校验
* @param elStr EL表达式
* @return true 校验成功 false 校验失败
*/
public static boolean validate(String elStr) {
try {
// 移除注释
elStr = ElRegexUtil.removeComments(elStr);
LiteFlowChainELBuilder.createChain().setEL(elStr);
return Boolean.TRUE;
} catch (Exception e) {
LOG.error("validate error",e);
}
return Boolean.FALSE;
}
public void build() {
this.chain.setRouteItem(this.route);
this.chain.setConditionList(this.conditionList);
//暂且去掉循环依赖检测因为有发现循环依赖检测在对大的EL进行检测的时候会导致CPU飙升也或许是jackson低版本的问题
//checkBuild();
FlowBus.addChain(this.chain);
}
// #region private method
/**
* build 前简单校验
*/
private void checkBuild() {
List<String> errorList = new ArrayList<>();
if (StrUtil.isBlank(this.chain.getChainId())) {
errorList.add("name is blank");
}
if (CollUtil.isNotEmpty(errorList)) {
throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
}
// 对每一个 chain 进行循环引用检测
try {
objectMapper.writeValueAsString(this.chain);
} catch (Exception e) {
if (e instanceof JsonMappingException) {
throw new CyclicDependencyException(StrUtil.format("There is a circular dependency in the chain[{}], please check carefully.", chain.getChainId(), e));
} else {
throw new ParseException(e.getMessage());
}
}
}
/**
* 解析 EL 表达式查找未定义的 id 并构建错误信息
* @param elStr el 表达式
*/
private static String buildDataNotFoundExceptionMsg(String elStr) {
String msg = String.format("[node/chain is not exist or node/chain not register]\n EL: %s",
StrUtil.trim(elStr));
try {
InstructionSet parseResult = EXPRESS_RUNNER.getInstructionSetFromLocalCache(elStr);
if (parseResult == null) {
return msg;
}
String[] outAttrNames = parseResult.getOutAttrNames();
if (ArrayUtil.isEmpty(outAttrNames)) {
return msg;
}
List<String> chainIds = CollUtil.map(FlowBus.getChainMap().values(), Chain::getChainId, true);
List<String> nodeIds = CollUtil.map(FlowBus.getNodeMap().values(), Node::getId, true);
for (String attrName : outAttrNames) {
if (!chainIds.contains(attrName) && !nodeIds.contains(attrName)) {
msg = String.format(
"[%s] is not exist or [%s] is not registered, you need to define a node or chain with id [%s] and register it \n EL: ",
attrName, attrName, attrName);
// 去除 EL 表达式中的空格和换行符
String sourceEl = StrUtil.removeAll(elStr, CharUtil.SPACE, CharUtil.LF, CharUtil.CR);
// 这里需要注意的是nodeId chainId 可能是关键字的一部分如果直接 indexOf(attrName) 会出现误判
// 所以需要判断 attrName 前后是否有 ","
int commaRightIndex = sourceEl.indexOf(attrName + StrUtil.COMMA);
if (commaRightIndex != -1) {
// 需要加上 "EL: " 的长度 4再加上 "^" 的长度 1indexOf 0 开始所以还需要加 1
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaRightIndex + 6, true);
}
int commaLeftIndex = sourceEl.indexOf(StrUtil.COMMA + attrName);
if (commaLeftIndex != -1) {
// 需要加上 "EL: " 的长度 4再加上 "^" 的长度 1再加上 "," 的长度 1indexOf 0
// 开始所以还需要加 1
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 7, true);
}
// 还有一种特殊情况就是 EL 表达式中的节点使用 node("a")
int nodeIndex = sourceEl.indexOf(String.format("node(\"%s\")", attrName));
if (nodeIndex != -1) {
// 需要加上 "EL: " 的长度 4再加上 node("” 长度 6再加上 "^" 的长度 1indexOf 从 0
// 开始所以还需要加 1
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 12, true);
}
}
}
} catch (Exception ex) {
// ignore
}
return msg;
}
public static void buildUnCompileChain(Chain chain){
if (StrUtil.isBlank(chain.getEl())){
throw new FlowSystemException(StrUtil.format("no el content in this unCompile chain[{}]", chain.getChainId()));
}
// 如果chain已经有Condition了那说明已经解析过了这里只对未解析的chain进行解析
if (CollUtil.isNotEmpty(chain.getConditionList())){
return;
}
List<String> errorList = new ArrayList<>();
try {
DefaultContext<String, Object> context = new DefaultContext<>();
// 这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
// 往上下文里放入所有的chain是的el表达式可以直接引用到chain
FlowBus.getChainMap().values().forEach(chainItem -> context.put(chainItem.getChainId(), chainItem));
// 往上下文里放入所有的node使得el表达式可以直接引用到nodeId
FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.getNode(nodeId)));
// 放入当前主chain的ID
context.put(ChainConstant.CURR_CHAIN_ID, chain.getChainId());
// 解析el成为一个Condition
// 为什么这里只是一个Condition而不是一个List<Condition>
// 这里无论多复杂的外面必定有一个最外层的Condition所以这里只有一个内部可以嵌套很多层这点和以前的不太一样
Condition condition = (Condition) EXPRESS_RUNNER.execute(chain.getEl(), context, errorList, true, true);
if (Objects.isNull(condition)){
throw new QLException(StrUtil.format("parse el fail,el:[{}]", chain.getEl()));
}
// 把主要的condition加入
chain.setConditionList(CollUtil.toList(condition));
// 把chain的isCompiled设置为true
chain.setCompiled(true);
FlowBus.addChain(chain);
} catch (QLException e) {
// EL 底层会包装异常这里是曲线处理
if (ObjectUtil.isNotNull(e.getCause()) && Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
// 构建错误信息
String msg = buildDataNotFoundExceptionMsg(chain.getEl());
throw new ELParseException(msg);
}else if (ObjectUtil.isNotNull(e.getCause())){
throw new ELParseException(e.getCause().getMessage());
}else{
throw new ELParseException(e.getMessage());
}
} catch (Exception e) {
String errMsg = StrUtil.format("parse el fail in this chain[{}];\r\n", chain.getChainId());
throw new ELParseException(errMsg + e.getMessage());
}
}
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, ",", "[", "]"));
}
}
}

View File

@ -1,33 +0,0 @@
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.AndOrCondition;
import com.yomahub.liteflow.flow.element.condition.BooleanConditionTypeEnum;
/**
* EL表达式中AND关键字的操作
* 主要用于适用于产生布尔类型结果的表达式中比如IF(AND(a,b)),WHILE(AND(a,b))
*
* @author Bryan.Zhang
* @since 2.10.2
*/
public class AndOperator extends BaseOperator<AndOrCondition> {
@Override
public AndOrCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
AndOrCondition andOrCondition = new AndOrCondition();
andOrCondition.setBooleanConditionType(BooleanConditionTypeEnum.AND);
for (Object object : objects){
OperatorHelper.checkObjMustBeBooleanTypeItem(object);
Executable item = OperatorHelper.convert(object, Executable.class);
andOrCondition.addItem(item);
}
return andOrCondition;
}
}

View File

@ -1,27 +1,55 @@
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.enums.ParallelStrategyEnum;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的any的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class AnyOperator extends BaseOperator<WhenCondition> {
public class AnyOperator extends Operator {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
@Override
public WhenCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
Boolean any = OperatorHelper.convert(objects[1], Boolean.class);
whenCondition.setParallelStrategy(any ? ParallelStrategyEnum.ANY : ParallelStrategyEnum.ALL);
return whenCondition;
}
if (objects.length != 2) {
throw new QLException("parameter error");
}
WhenCondition whenCondition;
if (objects[0] instanceof WhenCondition) {
whenCondition = (WhenCondition) objects[0];
} else {
throw new QLException("The caller must be when condition item");
}
boolean any = false;
if (objects[1] instanceof Boolean) {
any = Boolean.parseBoolean(objects[1].toString());
} else {
throw new QLException("the parameter must be boolean type");
}
whenCondition.setAny(any);
return whenCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,31 +0,0 @@
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.LoopCondition;
/**
* EL规则中的BREAK的操作符 有两种用法 FOR...DO...BREAK WHILE...DO...BREAK
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class BreakOperator extends BaseOperator<LoopCondition> {
@Override
public LoopCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
// DO关键字有可能用在FOR后面也有可能用于WHILE后面所以这里要进行判断是不是这两种类型的超类LoopCondition
String errorMsg = "The caller must be ForCondition or WhileCondition item";
LoopCondition condition = OperatorHelper.convert(objects[0], LoopCondition.class, errorMsg);
// 获得需要执行的可执行表达式
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[1]);
Executable breakItem = OperatorHelper.convert(objects[1], Executable.class);
condition.setBreakItem(breakItem);
return condition;
}
}

View File

@ -1,39 +0,0 @@
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.Node;
import com.yomahub.liteflow.flow.element.condition.CatchCondition;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
/**
* EL规则中的CATCH的操作符 用法CATCH...DO...
*
* @author Bryan.Zhang
* @since 2.10.0
*/
public class CatchOperator extends BaseOperator<CatchCondition> {
@Override
public CatchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
OperatorHelper.checkObjMustBeCommonTypeItem(objects[0]);
Executable catchItem = OperatorHelper.convert(objects[0], Executable.class);
CatchCondition catchCondition = new CatchCondition();
//如果是单个Node的话要包装成THEN的CONDITION模式否则CATCH不到异常
if (catchItem instanceof Node){
ThenCondition thenCondition = new ThenCondition();
thenCondition.addExecutable(catchItem);
catchCondition.setCatchItem(thenCondition);
}else{
catchCondition.setCatchItem(catchItem);
}
return catchCondition;
}
}

View File

@ -1,28 +0,0 @@
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.Node;
/**
* EL规则中的data的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class DataOperator extends BaseOperator<Node> {
@Override
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Node node = OperatorHelper.convert(objects[0], Node.class);
String cmpData = OperatorHelper.convert(objects[1], String.class);
node.setCmpData(cmpData);
return node;
}
}

View File

@ -1,32 +0,0 @@
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);
String errorMsg = "The caller must be SwitchCondition item";
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class, errorMsg);
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
Executable target = OperatorHelper.convert(objects[1], Executable.class);
switchCondition.setDefaultExecutor(target);
return switchCondition;
}
}

View File

@ -1,48 +0,0 @@
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;
import com.yomahub.liteflow.flow.element.condition.CatchCondition;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.condition.LoopCondition;
/**
* EL规则中的DO的操作符 有三种用法 FOR...DO...BREAK WHILE...DO...BREAK CATCH...DO
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class DoOperator extends BaseOperator<Condition> {
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
if (objects[0] instanceof CatchCondition) {
String errorMsg = "The caller must be CatchCondition item";
CatchCondition condition = OperatorHelper.convert(objects[0], CatchCondition.class, errorMsg);
// 获得需要执行的可执行表达式
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
Executable doExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
condition.setDoItem(doExecutableItem);
return condition;
}
else if (objects[0] instanceof LoopCondition) {
String errorMsg = "The caller must be LoopCondition item";
// DO关键字有可能用在FOR后面也有可能用于WHILE后面所以这里要进行判断是不是这两种类型的超类LoopCondition
LoopCondition condition = OperatorHelper.convert(objects[0], LoopCondition.class, errorMsg);
// 获得需要执行的可执行表达式
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
Executable doExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
condition.setDoExecutor(doExecutableItem);
return condition;
}
else {
String errorMsg = "The caller must be LoopCondition or CatchCondition item";
throw new QLException(errorMsg);
}
}
}

View File

@ -1,53 +1,81 @@
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 cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.IfCondition;
import java.util.List;
/**
* EL规则中的ELIF的操作符
*
* @author Bryan.Zhang
* @since 2.8.5
*/
public class ElifOperator extends BaseOperator<IfCondition> {
public class ElifOperator extends Operator {
@Override
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqThree(objects);
@Override
public Object executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
// 解析caller
String errorMsg = "The caller must be IfCondition item";
IfCondition ifCondition = OperatorHelper.convert(objects[0], IfCondition.class, errorMsg);
//参数只能是3个第一个是caller后面只能跟2个参数
if (objects.length != 3) {
throw new QLException("parameter error");
}
// 解析第一个参数
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[1]);
Executable ifItem = OperatorHelper.convert(objects[1], Executable.class);
//解析caller
IfCondition ifCondition;
if (objects[0] instanceof IfCondition){
ifCondition = (IfCondition) objects[0];
}else{
throw new QLException("elif caller must be IfCondition");
}
// 解析第二个参数
OperatorHelper.checkObjMustBeCommonTypeItem(objects[2]);
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
//解析第一个参数
Node ifNode;
if (objects[1] instanceof Node) {
ifNode = (Node) objects[1];
// 构建一个内部的IfCondition
IfCondition ifConditionItem = new IfCondition();
ifConditionItem.setIfItem(ifItem);
ifConditionItem.setTrueCaseExecutableItem(trueCaseExecutableItem);
if (!ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(ifNode.getType())) {
throw new QLException("The first parameter must be If item");
}
} else {
throw new QLException("The first parameter must be Node item");
}
// 因为可能会有多个ELIF所以每一次拿到的caller总是最开始大的if需要遍历到没有falseCaseExecutable的地方
// 塞进去是一个新的IfCondition
IfCondition loopIfCondition = ifCondition;
while (true) {
if (loopIfCondition.getFalseCaseExecutableItem() == null) {
loopIfCondition.setFalseCaseExecutableItem(ifConditionItem);
break;
}
else {
loopIfCondition = (IfCondition) loopIfCondition.getFalseCaseExecutableItem();
}
}
//解析第二个参数
Executable trueCaseExecutableItem = (Executable) objects[2];
return ifCondition;
}
//构建一个内部的IfCondition
IfCondition ifConditionItem = new IfCondition();
ifConditionItem.setExecutableList(ListUtil.toList(ifNode));
ifConditionItem.setTrueCaseExecutableItem(trueCaseExecutableItem);
//因为可能会有多个ELIF所以每一次拿到的caller总是最开始大的if需要遍历到没有falseCaseExecutable的地方
//塞进去是一个新的IfCondition
IfCondition loopIfCondition = ifCondition;
while (true){
if (loopIfCondition.getFalseCaseExecutableItem() == null){
loopIfCondition.setFalseCaseExecutableItem(ifConditionItem);
break;
}else{
loopIfCondition = (IfCondition) loopIfCondition.getFalseCaseExecutableItem();
}
}
return ifCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,44 +1,61 @@
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 cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.IfCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的ELSE的操作符
*
* @author Bryan.Zhang
* @since 2.8.5
*/
public class ElseOperator extends BaseOperator<IfCondition> {
public class ElseOperator extends Operator {
@Override
public IfCondition build(Object[] objects) throws Exception {
// 参数只能是1个但这里为什么是2个呢第一个是caller第二个才是参数
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Override
public IfCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
String errorMsg = "The caller must be IfCondition item";
IfCondition ifCondition = OperatorHelper.convert(objects[0], IfCondition.class, errorMsg);
//参数只能是1个但这里为什么是2个呢第一个是caller第二个才是参数
if (objects.length != 2) {
throw new QLException("parameter error");
}
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
Executable elseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
IfCondition ifCondition;
if (objects[0] instanceof IfCondition) {
ifCondition = (IfCondition) objects[0];
} else {
throw new QLException("The caller must be IfCondition item");
}
// 因为当中可能会有多个ELIF所以并不知道这个ELSE前面有没有ELIF
// 每一次拿到的caller总是最开始大的if需要遍历到没有falseCaseExecutable的地方
// 塞进去是一个elseExecutableItem
IfCondition loopIfCondition = ifCondition;
while (true) {
if (loopIfCondition.getFalseCaseExecutableItem() == null) {
loopIfCondition.setFalseCaseExecutableItem(elseExecutableItem);
break;
}
else {
loopIfCondition = (IfCondition) loopIfCondition.getFalseCaseExecutableItem();
}
}
Executable elseExecutableItem = (Executable) objects[1];
return ifCondition;
}
//因为当中可能会有多个ELIF所以并不知道这个ELSE前面有没有ELIF
//每一次拿到的caller总是最开始大的if需要遍历到没有falseCaseExecutable的地方
//塞进去是一个elseExecutableItem
IfCondition loopIfCondition = ifCondition;
while (true){
if (loopIfCondition.getFalseCaseExecutableItem() == null){
loopIfCondition.setFalseCaseExecutableItem(elseExecutableItem);
break;
}else{
loopIfCondition = (IfCondition) loopIfCondition.getFalseCaseExecutableItem();
}
}
return ifCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,30 +1,43 @@
package com.yomahub.liteflow.builder.el.operator;
import com.ql.util.express.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.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.FinallyCondition;
import com.yomahub.liteflow.flow.element.condition.PreCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的THEN的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class FinallyOperator extends BaseOperator<FinallyCondition> {
public class FinallyOperator extends Operator {
@Override
public FinallyCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
FinallyCondition finallyCondition = new FinallyCondition();
for (Object obj : objects) {
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
Executable item = OperatorHelper.convert(obj, Executable.class);
finallyCondition.addExecutable(item);
}
return finallyCondition;
}
@Override
public FinallyCondition executeInner(Object[] objects) throws Exception {
try {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
FinallyCondition finallyCondition = new FinallyCondition();
for (Object obj : objects) {
if (obj instanceof Executable) {
finallyCondition.addExecutable((Executable) obj);
} else {
throw new QLException("parameter must be executable item!");
}
}
return finallyCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,54 +0,0 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
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.core.NodeComponent;
import com.yomahub.liteflow.core.NodeForComponent;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.ForCondition;
/**
* EL规则中的FOR的操作符
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ForOperator extends BaseOperator<ForCondition> {
@Override
public ForCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
Node node;
if (objects[0] instanceof Node) {
OperatorHelper.checkObjMustBeForTypeItem(objects[0]);
node = OperatorHelper.convert(objects[0], Node.class);
}
else if (objects[0] instanceof Integer) {
Integer forCount = OperatorHelper.convert(objects[0], Integer.class);
node = new Node();
NodeForComponent nodeForComponent = new NodeForComponent() {
@Override
public int processFor() {
return forCount;
}
};
nodeForComponent.setSelf(nodeForComponent);
nodeForComponent.setNodeId(StrUtil.format("LOOP_{}", forCount));
node.setInstance(nodeForComponent);
}
else {
throw new QLException("The parameter must be Node item");
}
ForCondition forCondition = new ForCondition();
forCondition.setForNode(node);
return forCondition;
}
}

View File

@ -1,29 +1,56 @@
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.Condition;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.condition.Condition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的id的操作符,只有condition可加id
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class IdOperator extends BaseOperator<Condition> {
public class IdOperator extends Operator {
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
String errorMsg = "The caller must be Condition item";
Condition condition = OperatorHelper.convert(objects[0], Condition.class, errorMsg);
@Override
public Condition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
String id = OperatorHelper.convert(objects[1], String.class);
if (objects.length != 2) {
throw new QLException("parameter error");
}
condition.setId(id);
Condition condition;
if (objects[0] instanceof Condition) {
condition = (Condition) objects[0];
} else {
throw new QLException("The caller must be condition item");
}
return condition;
}
String id;
if (objects[1] instanceof String) {
id = objects[1].toString();
} else {
LOG.error("the parameter must be String type!");
throw new QLException("the parameter must be String type");
}
condition.setId(id);
return condition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,43 +1,68 @@
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 cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.IfCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的IF的操作符
*
* @author Bryan.Zhang
* @since 2.8.5
*/
public class IfOperator extends BaseOperator<IfCondition> {
public class IfOperator extends Operator {
@Override
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 2, 3);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
// 解析第一个参数
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[0]);
Executable ifItem = OperatorHelper.convert(objects[0], Executable.class);
@Override
public IfCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
//参数只能是2个或者3个
if (objects.length != 2 && objects.length != 3) {
throw new QLException("parameter error");
}
// 解析第二个参数
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
//解析第一个参数
Node ifNode;
if (objects[0] instanceof Node) {
ifNode = (Node) objects[0];
// 解析第三个参数如果有的话
Executable falseCaseExecutableItem = null;
if (objects.length == 3) {
OperatorHelper.checkObjMustBeCommonTypeItem(objects[2]);
falseCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
}
if (!ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(ifNode.getType())) {
throw new QLException("The first parameter must be If item");
}
} else {
throw new QLException("The first parameter must be Node item");
}
IfCondition ifCondition = new IfCondition();
ifCondition.setIfItem(ifItem);
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
return ifCondition;
}
//解析第二个参数
Executable trueCaseExecutableItem = (Executable) objects[1];
//解析第三个参数如果有的话
Executable falseCaseExecutableItem = null;
if (objects.length == 3) {
falseCaseExecutableItem = (Executable) objects[2];
}
IfCondition ifCondition = new IfCondition();
ifCondition.setExecutableList(ListUtil.toList(ifNode));
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
return ifCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,29 +1,57 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.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.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的ignoreError的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class IgnoreErrorOperator extends BaseOperator<WhenCondition> {
public class IgnoreErrorOperator extends Operator {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
String errorMsg = "The caller must be WhenCondition item";
WhenCondition condition = OperatorHelper.convert(objects[0], WhenCondition.class, errorMsg);
@Override
public Condition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
Boolean ignoreError = OperatorHelper.convert(objects[1], Boolean.class);
condition.setIgnoreError(ignoreError);
if (objects.length != 2) {
throw new QLException("parameter error");
}
return condition;
}
WhenCondition condition;
if (objects[0] instanceof WhenCondition) {
condition = (WhenCondition) objects[0];
} else {
throw new QLException("The caller must be executable item");
}
boolean ignoreError = false;
if (objects[1] instanceof Boolean) {
ignoreError = Boolean.parseBoolean(objects[1].toString());
} else {
throw new QLException("The parameter must be boolean type");
}
condition.setErrorResume(ignoreError);
return condition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,27 +0,0 @@
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.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.IteratorCondition;
public class IteratorOperator extends BaseOperator<IteratorCondition> {
@Override
public IteratorCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
OperatorHelper.checkObjMustBeIteratorTypeItem(objects[0]);
Node node = OperatorHelper.convert(objects[0], Node.class);
IteratorCondition iteratorCondition = new IteratorCondition();
iteratorCondition.setIteratorNode(node);
return iteratorCondition;
}
}

View File

@ -1,24 +0,0 @@
package com.yomahub.liteflow.builder.el.operator;
import com.yomahub.liteflow.common.ChainConstant;
import java.util.concurrent.TimeUnit;
/**
* EL 规则中的 maxWaitMilliseconds 的操作符
*
* @author Kugaaa
* @since 2.11.1
*/
public class MaxWaitMillisecondsOperator extends MaxWaitTimeOperator {
@Override
TimeUnit getMaxWaitTimeUnit() {
return TimeUnit.MILLISECONDS;
}
@Override
String operatorName() {
return ChainConstant.MAX_WAIT_MILLISECONDS;
}
}

View File

@ -1,24 +0,0 @@
package com.yomahub.liteflow.builder.el.operator;
import com.yomahub.liteflow.common.ChainConstant;
import java.util.concurrent.TimeUnit;
/**
* EL 规则中的 maxWaitSeconds 的操作符
*
* @author DaleLee
* @since 2.11.0
*/
public class MaxWaitSecondsOperator extends MaxWaitTimeOperator {
@Override
TimeUnit getMaxWaitTimeUnit() {
return TimeUnit.SECONDS;
}
@Override
String operatorName() {
return ChainConstant.MAX_WAIT_SECONDS;
}
}

View File

@ -1,122 +0,0 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrFormatter;
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;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.ConditionKey;
import com.yomahub.liteflow.flow.element.condition.FinallyCondition;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
import com.yomahub.liteflow.flow.element.condition.TimeoutCondition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* EL 规则中
* - maxWaitSeconds {@link MaxWaitSecondsOperator}
* - MaxWaitMilliseconds {@link MaxWaitMillisecondsOperator}
* 操作符的抽象
*
* @author Kugaaa
* @since 2.11.1
*/
public abstract class MaxWaitTimeOperator extends BaseOperator<Condition> {
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Executable executable = OperatorHelper.convert(objects[0], Executable.class);
// 获取传入的时间参数
Integer maxWaitTime = OperatorHelper.convert(objects[1], Integer.class);
if (executable instanceof WhenCondition) {
// WhenCondition直接设置等待时间
WhenCondition whenCondition = OperatorHelper.convert(executable, WhenCondition.class);
whenCondition.setMaxWaitTime(maxWaitTime);
whenCondition.setMaxWaitTimeUnit(getMaxWaitTimeUnit());
return whenCondition;
} else if (executable instanceof FinallyCondition) {
// FINALLY报错
String errorMsg = StrFormatter.format("The caller [{}] cannot use the keyword \"{}'\"", executable.toString(), operatorName());
throw new QLException(errorMsg);
} else if (containsFinally(executable)) {
// 处理 THEN 中的 FINALLY
ThenCondition thenCondition = OperatorHelper.convert(executable, ThenCondition.class);
return handleFinally(thenCondition, maxWaitTime);
} else {
// 其他情况 WHEN 包装
return wrappedByTimeout(executable, maxWaitTime);
}
}
/**
* 将一个 Executable 包装为带有单独超时控制的 TimeoutCondition
*
* @param executable 待包装对象
* @param maxWaitTime 最大等待时间
* @return 包装后的 TimeoutCondition
*/
private TimeoutCondition wrappedByTimeout(Executable executable, Integer maxWaitTime) {
TimeoutCondition timeoutCondition = new TimeoutCondition();
timeoutCondition.addExecutable(executable);
timeoutCondition.setMaxWaitTime(maxWaitTime);
timeoutCondition.setMaxWaitTimeUnit(getMaxWaitTimeUnit());
return timeoutCondition;
}
/**
* 判断 THEN 中是否含有 FINALLY 组件
*
* @param executable 判断对象
* @return 含有 FINALLY 组件返回 true否则返回 false
*/
private boolean containsFinally(Executable executable) {
return executable instanceof ThenCondition
&& CollUtil.isNotEmpty(((ThenCondition) executable).getFinallyConditionList());
}
/**
* FINALLY 排除在超时控制之外
*
* @param thenCondition 待处理的 ThenCondition
* @param maxWaitTime 最大等待时间
* @return 处理后的 ThenCondition
*/
private ThenCondition handleFinally(ThenCondition thenCondition, Integer maxWaitTime) {
// 进行如下转换
// THEN(PRE(a),b,FINALLY(c))
// => THEN(
// WHEN(THEN(PRE(a),b)),
// FINALLY(c))
// 定义外层 THEN
ThenCondition outerThenCondition = new ThenCondition();
// FINALLY 转移到外层 THEN
List<Executable> finallyList = thenCondition.getExecutableList(ConditionKey.FINALLY_KEY);
finallyList.forEach(executable
-> outerThenCondition
.addFinallyCondition((FinallyCondition) executable));
finallyList.clear();
// 包装内部 THEN
WhenCondition whenCondition = wrappedByTimeout(thenCondition, maxWaitTime);
outerThenCondition.addExecutable(whenCondition);
return outerThenCondition;
}
/**
* 获取等待时间单位
*/
abstract TimeUnit getMaxWaitTimeUnit();
/**
* 操作符名称
*/
abstract String operatorName();
}

View File

@ -1,44 +0,0 @@
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.enums.ParallelStrategyEnum;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import java.util.HashSet;
import java.util.Set;
/**
* EL 规则中的 must 的操作符
*
* @author luo yi
* @since 2.11.0
*/
public class MustOperator extends BaseOperator<WhenCondition> {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
String errorMsg = "The caller must be WhenCondition item";
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class, errorMsg);
// 解析指定完成的任务 ID 集合
Set<String> specifyIdSet = new HashSet<>();
for (int i = 1; i < objects.length; i++) {
Object task = objects[i];
if (task instanceof String) {
specifyIdSet.add(OperatorHelper.convert(task, String.class));
} else if (task instanceof Executable) {
specifyIdSet.add(OperatorHelper.convert(task, Executable.class).getId());
}
}
whenCondition.setSpecifyIdSet(specifyIdSet);
whenCondition.setParallelStrategy(ParallelStrategyEnum.SPECIFY);
return whenCondition;
}
}

View File

@ -1,32 +1,69 @@
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 cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.FallbackNode;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Predicate;
/**
* EL规则中的node的操作符
*
* @author Bryan.Zhang
* @author DaleLee
* @since 2.8.3
*/
public class NodeOperator extends BaseOperator<Node> {
public class NodeOperator extends Operator {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Override
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqOne(objects);
String nodeId = OperatorHelper.convert(objects[0], String.class);
public Node executeInner(Object[] objects) throws Exception {
try{
if (ArrayUtil.isEmpty(objects)){
throw new QLException("parameter is empty");
}
if (FlowBus.containNode(nodeId)) {
// 找到对应节点
return FlowBus.getNode(nodeId);
} else {
// 生成代理节点
return new FallbackNode(nodeId);
if (objects.length != 1){
throw new QLException("parameter error");
}
String nodeId;
if (objects[0] instanceof String){
nodeId = (String) objects[0];
}else{
throw new QLException("The value must be Node item!");
}
if (FlowBus.containNode(nodeId)){
return FlowBus.getNode(nodeId);
}else{
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (StrUtil.isNotBlank(liteflowConfig.getSubstituteCmpClass())){
Node substituteNode = FlowBus.getNodeMap().values().stream().filter(node
-> node.getInstance().getClass().getName().equals(liteflowConfig.getSubstituteCmpClass())).findFirst().orElse(null);
if (ObjectUtil.isNotNull(substituteNode)){
return substituteNode;
}else{
String error = StrUtil.format("This node[{}] cannot be found", nodeId);
throw new QLException(error);
}
}else{
String error = StrUtil.format("This node[{}] cannot be found, or you can configure an substitute node", nodeId);
throw new QLException(error);
}
}
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,29 +0,0 @@
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.NotCondition;
/**
* EL表达式中NOT关键字的操作表示非的操作
* 主要用于适用于产生布尔类型结果的表达式中比如IF(NOT(a)),WHILE(NOT(a))
*
* @author Bryan.Zhang
* @since 2.10.2
*/
public class NotOperator extends BaseOperator<NotCondition> {
@Override
public NotCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqOne(objects);
Object object = objects[0];
OperatorHelper.checkObjMustBeBooleanTypeItem(object);
Executable item = OperatorHelper.convert(object, Executable.class);
NotCondition notCondition = new NotCondition();
notCondition.setItem(item);
return notCondition;
}
}

View File

@ -1,33 +0,0 @@
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.AndOrCondition;
import com.yomahub.liteflow.flow.element.condition.BooleanConditionTypeEnum;
/**
* EL表达式中OR关键字的操作
* 主要用于适用于产生布尔类型结果的表达式中比如IF(OR(a,b)),WHILE(OR(a,b))
*
* @author Bryan.Zhang
* @since 2.10.2
*/
public class OrOperator extends BaseOperator<AndOrCondition> {
@Override
public AndOrCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
AndOrCondition andOrCondition = new AndOrCondition();
andOrCondition.setBooleanConditionType(BooleanConditionTypeEnum.OR);
for (Object object : objects){
OperatorHelper.checkObjMustBeBooleanTypeItem(object);
Executable item = OperatorHelper.convert(object, Executable.class);
andOrCondition.addItem(item);
}
return andOrCondition;
}
}

View File

@ -1,26 +0,0 @@
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.condition.LoopCondition;
/**
* EL规则中的parallel的操作符
*
* @author zhhhhy
* @since 2.11.0
*/
public class ParallelOperator extends BaseOperator<LoopCondition> {
@Override
public LoopCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
String errorMsg = "The caller must be LoopCondition item";
LoopCondition loopCondition = OperatorHelper.convert(objects[0], LoopCondition.class, errorMsg);
Boolean parallel = OperatorHelper.convert(objects[1], Boolean.class);
loopCondition.setParallel(parallel);
return loopCondition;
}
}

View File

@ -1,28 +1,43 @@
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.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.PreCondition;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的THEN的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class PreOperator extends BaseOperator<PreCondition> {
public class PreOperator extends Operator {
@Override
public PreCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
PreCondition preCondition = new PreCondition();
for (Object obj : objects) {
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
preCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return preCondition;
}
@Override
public PreCondition executeInner(Object[] objects) throws Exception {
try {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
PreCondition preCondition = new PreCondition();
for (Object obj : objects) {
if (obj instanceof Executable) {
preCondition.addExecutable((Executable) obj);
} else {
throw new QLException("parameter must be executable item");
}
}
return preCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,35 +0,0 @@
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.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.RetryCondition;
/**
*
* @author Rain
* @since 2.12.0
*
*/
public class RetryOperator extends BaseOperator<Condition> {
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
Executable executable = OperatorHelper.convert(objects[0], Executable.class);
Integer retryTimes = OperatorHelper.convert(objects[1], Integer.class);
RetryCondition retryCondition = new RetryCondition();
retryCondition.addExecutable(executable);
retryCondition.setRetryTimes(retryTimes);
if(objects.length > 2) {
Class[] forExceptions = new Class[objects.length - 2];
for(int i = 2; i < objects.length; i ++) {
String className = OperatorHelper.convert(objects[i], String.class);
forExceptions[i - 2] = Class.forName(className);
}
retryCondition.setRetryForExceptions(forExceptions);
}
return retryCondition;
}
}

View File

@ -1,32 +1,56 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.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.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的SWITCH的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class SwitchOperator extends BaseOperator<SwitchCondition> {
public class SwitchOperator extends Operator {
@Override
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqOne(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
OperatorHelper.checkObjMustBeSwitchTypeItem(objects[0]);
Node switchNode = OperatorHelper.convert(objects[0], Node.class);
@Override
public SwitchCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
SwitchCondition switchCondition = new SwitchCondition();
switchCondition.setSwitchNode(switchNode);
if (objects.length != 1) {
throw new QLException("parameter error");
}
return switchCondition;
}
Node switchNode;
if (objects[0] instanceof Node) {
switchNode = (Node) objects[0];
if (!ListUtil.toList(NodeTypeEnum.SWITCH, NodeTypeEnum.SWITCH_SCRIPT).contains(switchNode.getType())) {
throw new QLException("The caller must be Switch item");
}
} else {
throw new QLException("The caller must be Switch item");
}
SwitchCondition switchCondition = new SwitchCondition();
switchCondition.setSwitchNode(switchNode);
return switchCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,42 +1,61 @@
package com.yomahub.liteflow.builder.el.operator;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.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.core.NodeComponent;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的tag的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class TagOperator extends BaseOperator<Executable> {
public class TagOperator extends Operator {
@Override
public Executable build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
Executable refObj = OperatorHelper.convert(objects[0], Executable.class);
@Override
public Node executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
String tag = OperatorHelper.convert(objects[1], String.class);
if (objects.length != 2) {
throw new QLException("parameter error");
}
//如果解析的对象是一个Chain由于Chain对象全局唯一无法再进行复制
//所以如果要给chain设置tag则需要套上一个THEN在THEN上面设置tag
if (refObj instanceof Chain){
ThenCondition wrapperChainCondition = new ThenCondition();
wrapperChainCondition.setExecutableList(ListUtil.toList(refObj));
wrapperChainCondition.setTag(tag);
return wrapperChainCondition;
}else{
refObj.setTag(tag);
return refObj;
}
}
Node node;
if (objects[0] instanceof Node) {
node = (Node) objects[0];
} else {
throw new QLException("The caller must be Node item");
}
String tag = null;
if (objects[1] instanceof String) {
tag = objects[1].toString();
} else {
throw new QLException("the parameter must be String type");
}
//这里为什么要clone一个呢
//因为tag是跟着chain走的而在el上下文里的放的都是同一个node如果多个同样的node tag不同则这里必须copy
Node copyNode = FlowBus.copyNode(node.getId());
copyNode.setTag(tag);
return copyNode;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,28 +1,42 @@
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.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.ThenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的THEN的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ThenOperator extends BaseOperator<ThenCondition> {
public class ThenOperator extends Operator {
@Override
public ThenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
ThenCondition thenCondition = new ThenCondition();
for (Object obj : objects) {
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
thenCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
}
return thenCondition;
}
@Override
public ThenCondition executeInner(Object[] objects) throws Exception {
try {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
ThenCondition thenCondition = new ThenCondition();
for (Object obj : objects) {
if (obj instanceof Executable) {
thenCondition.addExecutable((Executable) obj);
} else {
throw new QLException("parameter must be executable item");
}
}
return thenCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,27 +1,55 @@
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 cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的threadPool的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ThreadPoolOperator extends BaseOperator<WhenCondition> {
public class ThreadPoolOperator extends Operator {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
String errorMsg = "The caller must be WhenCondition item";
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class, errorMsg);
@Override
public WhenCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
whenCondition.setThreadExecutorClass(OperatorHelper.convert(objects[1], String.class));
if (objects.length != 2) {
throw new QLException("parameter error");
}
return whenCondition;
}
WhenCondition whenCondition;
if (objects[0] instanceof WhenCondition) {
whenCondition = (WhenCondition) objects[0];
} else {
throw new QLException("The caller must be when condition item");
}
String threadPoolClazz = null;
if (objects[1] instanceof String) {
threadPoolClazz = objects[1].toString();
} else {
throw new QLException("the parameter must be String type");
}
whenCondition.setThreadExecutorClass(threadPoolClazz);
return whenCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,31 +1,54 @@
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 cn.hutool.core.util.ArrayUtil;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的TO的操作符用法须和SWITCH联合使用
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ToOperator extends BaseOperator<SwitchCondition> {
public class ToOperator extends Operator {
@Override
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
String errorMsg = "The caller must be SwitchCondition item";
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class, errorMsg);
@Override
public SwitchCondition executeInner(Object[] objects) throws Exception {
try {
if (ArrayUtil.isEmpty(objects)) {
throw new QLException("parameter is empty");
}
for (int i = 1; i < objects.length; i++) {
OperatorHelper.checkObjMustBeCommonTypeItem(objects[i]);
Executable target = OperatorHelper.convert(objects[i], Executable.class);
switchCondition.addTargetItem(target);
}
return switchCondition;
}
if (objects.length <= 1) {
throw new QLException("parameter error");
}
SwitchCondition switchCondition;
if (objects[0] instanceof SwitchCondition) {
switchCondition = (SwitchCondition) objects[0];
} else {
throw new QLException("The caller must be SwitchCondition item");
}
for (int i = 1; i < objects.length; i++) {
if (objects[i] instanceof Executable) {
Executable target = (Executable) objects[i];
switchCondition.addTargetItem(target);
} else {
throw new QLException("The parameter must be Executable item");
}
}
return switchCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,33 +1,42 @@
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.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* EL规则中的WHEN的操作符
*
* @author Bryan.Zhang
* @since 2.8.0
*/
public class WhenOperator extends BaseOperator<WhenCondition> {
public class WhenOperator extends Operator {
@Override
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
WhenCondition whenCondition = new WhenCondition();
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
for (Object obj : objects) {
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
whenCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
whenCondition.setThreadExecutorClass(liteflowConfig.getThreadExecutorClass());
}
return whenCondition;
}
@Override
public WhenCondition executeInner(Object[] objects) throws Exception {
try {
if (objects.length == 0) {
throw new QLException("parameter error");
}
WhenCondition whenCondition = new WhenCondition();
for (Object obj : objects) {
if (obj instanceof Executable) {
whenCondition.addExecutable((Executable) obj);
} else {
throw new QLException("parameter error");
}
}
return whenCondition;
}catch (QLException e){
throw e;
}catch (Exception e){
throw new ELParseException("errors occurred in EL parsing");
}
}
}

View File

@ -1,28 +0,0 @@
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.WhileCondition;
/**
* EL规则中的WHILE的操作符
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class WhileOperator extends BaseOperator<WhileCondition> {
@Override
public WhileCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqOne(objects);
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[0]);
Executable whileItem = OperatorHelper.convert(objects[0], Executable.class);
WhileCondition whileCondition = new WhileCondition();
whileCondition.setWhileItem(whileItem);
return whileCondition;
}
}

View File

@ -1,38 +0,0 @@
package com.yomahub.liteflow.builder.el.operator.base;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
/**
* BaseOperator 为了强化 executeInner 方法会捕获抛出的 QLException 错误输出友好的错误提示
*
* @author gaibu
* @since 2.8.6
*/
public abstract class BaseOperator<T extends Executable> extends Operator {
@Override
public T executeInner(Object[] objects) throws Exception {
try {
OperatorHelper.checkItemNotNull(objects);
return build(objects);
}
catch (QLException e) {
throw e;
}
catch (Exception e) {
throw new ELParseException("errors occurred in EL parsing");
}
}
/**
* 构建 EL 条件
* @param objects objects
* @return Condition
* @throws Exception Exception
*/
public abstract T build(Object[] objects) throws Exception;
}

View File

@ -1,234 +0,0 @@
package com.yomahub.liteflow.builder.el.operator.base;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.enums.ExecuteableTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.DataNotFoundException;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import java.util.Objects;
/**
* Operator 常用工具类
*
* @author gaibu
* @since 2.8.6
*/
public class OperatorHelper {
/**
* 检查参数数量大于 0
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGtZero(Object[] objects) throws QLException {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
}
/**
* 检查参数数量大于等于 2
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGteTwo(Object[] objects) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length < 2) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量等于 1
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqOne(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 1);
}
/**
* 检查参数数量等于 2
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqTwo(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 2);
}
/**
* 检查参数数量等于 3
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqThree(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 3);
}
/**
* 检查参数数量等于 size
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量等于 size1 size2
* @param objects objects
* @param size1 参数数量1
* @param size2 参数数量2
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size1, int size2) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size1 && objects.length != size2) {
throw new QLException("parameter error");
}
}
/**
* 转换 object 为指定的类型 如果是Node类型的则进行copy
* 为什么要进行copy呢因为原先的Node都是存放在FlowBus的NodeMap中的有些属性在EL中不是全局的属于当前这个chain的 所以要进行copy动作
*/
public static <T> T convert(Object object, Class<T> clazz) throws QLException {
String errorMsg = StrUtil.format("The parameter must be {} item", clazz.getSimpleName());
return convert(object, clazz, errorMsg);
}
/**
* 转换 object 为指定的类型,自定义错误信息 如果是Node类型的则进行copy
*/
public static <T> T convert(Object object, Class<T> clazz, String errorMsg) throws QLException {
try {
if (clazz.isAssignableFrom(object.getClass())) {
if (object instanceof Node) {
Node node = (Node) object;
return (T) node.clone();
}
else {
return (T) object;
}
}
}
catch (Exception e) {
throw new QLException("An error occurred while copying an object");
}
throw new QLException(errorMsg);
}
public static void checkItemNotNull(Object[] objects) throws QLException {
for (Object object : objects) {
if (Objects.isNull(object)) {
throw new QLException(DataNotFoundException.MSG);
}
}
}
/**
* 检查对象是否为一个正常可执行的对象
* 大部分的Node,Condition,Chain都是正常可执行的对象这个检查是为了避免THEN(sw,if)类的情况(sw是选择组件if是条件组件)这种就不能放在THEN里
*/
public static void checkObjMustBeCommonTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.COMMON, NodeTypeEnum.SCRIPT, NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be a common type component", node.getId()));
}
}
}
/**
* 所谓Boolean item指的是那些最终的结果值为布尔类型的Item
* 布尔类型的items有ifwhilebreak类型的Node以及AndOrCondition以及NotCondition
*/
public static void checkObjMustBeBooleanTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.BOOLEAN,
NodeTypeEnum.BOOLEAN_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be boolean type Node.", node.getId()));
}
}else if(item.getExecuteType().equals(ExecuteableTypeEnum.CONDITION)){
Condition condition = (Condition) item;
if (!ListUtil.toList(ConditionTypeEnum.TYPE_AND_OR_OPT, ConditionTypeEnum.TYPE_NOT_OPT).contains(condition.getConditionType())){
throw new QLException(StrUtil.format("The condition[{}] must be boolean type Condition.", condition.getId()));
}
}else{
throw new QLException("The parameter error.");
}
}
public static void checkObjMustBeForTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.FOR,
NodeTypeEnum.FOR_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be For type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
}
}
public static void checkObjMustBeIteratorTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.ITERATOR,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be Iterator type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
}
}
public static void checkObjMustBeSwitchTypeItem(Object object) throws Exception{
if (!(object instanceof Executable)){
throw new QLException("The parameter must be Executable item.");
}
Executable item = (Executable) object;
if (item.getExecuteType().equals(ExecuteableTypeEnum.NODE)){
Node node = (Node) item;
if (!ListUtil.toList(NodeTypeEnum.SWITCH,
NodeTypeEnum.SWITCH_SCRIPT,
NodeTypeEnum.FALLBACK).contains(node.getType())){
throw new QLException(StrUtil.format("The node[{}] must be Switch type Node.", node.getId()));
}
}else{
throw new QLException("The parameter error.");
}
}
}

View File

@ -0,0 +1,65 @@
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

@ -90,5 +90,4 @@ public class ChainPropBean {
this.conditionType = conditionType;
return this;
}
}

View File

@ -35,11 +35,6 @@ public class NodePropBean {
*/
String file;
/**
* 脚本语言
*/
String language;
public String getId() {
return id;
}
@ -93,14 +88,4 @@ public class NodePropBean {
this.file = file;
return this;
}
public String getLanguage() {
return language;
}
public NodePropBean setLanguage(String language) {
this.language = language;
return this;
}
}

View File

@ -6,104 +6,34 @@ package com.yomahub.liteflow.common;
* @author tangkc
*/
public interface ChainConstant {
String PARALLEL = "parallel";
String CHAIN = "chain";
String CHAIN = "chain";
String ROUTE = "route";
String FLOW = "flow";
String BODY = "body";
String NODES = "nodes";
String FLOW = "flow";
String NODE = "node";
String NODES = "nodes";
String ID = "id";
String NODE = "node";
String _CLASS = "class";
String ID = "id";
String FILE = "file";
String _CLASS = "class";
String NAME = "name";
String FILE = "file";
String VALUE = "value";
String NAME = "name";
String ERROR_RESUME = "errorResume";
String ENABLE = "enable";
String GROUP = "group";
String LANGUAGE = "language";
String ANY = "any";
String NAMESPACE = "namespace";
String THREAD_EXECUTOR_CLASS = "threadExecutorClass";
String DEFAULT_NAMESPACE = "default";
String VALUE = "value";
String ANY = "any";
String MUST = "must";
String TYPE = "type";
String THEN = "THEN";
String WHEN = "WHEN";
String SER = "SER";
String PAR = "PAR";
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 ITERATOR = "ITERATOR";
String MONITOR_BUS = "monitorBus";
String CURR_CHAIN_ID = "currChainId";
String DEFAULT = "DEFAULT";
String CATCH = "CATCH";
String AND = "AND";
String OR = "OR";
String NOT = "NOT";
String MAX_WAIT_SECONDS = "maxWaitSeconds";
String MAX_WAIT_MILLISECONDS = "maxWaitMilliseconds";
String EXTENDS = "extends";
String RETRY = "retry";
String CONDITION = "condition";
String TYPE = "type";
}

View File

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

View File

@ -1,25 +0,0 @@
package com.yomahub.liteflow.context;
import com.yomahub.liteflow.annotation.LFAliasFor;
import java.lang.annotation.*;
/**
* 用于标注上下文bean的别名以便在脚本或者组件中通过别名来获取上下文对象
*
* @since 2.9.7
* @author Tingliang Wang
*/
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ContextBean {
@LFAliasFor("name")
String value() default "";
@LFAliasFor("value")
String name() 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.common.ChainConstant;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.monitor.MonitorBus;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
@ -15,67 +15,62 @@ import com.yomahub.liteflow.spi.holder.LiteflowComponentSupportHolder;
/**
* 组件初始化器
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public class ComponentInitializer {
private static ComponentInitializer instance;
private static ComponentInitializer instance;
public static ComponentInitializer loadInstance() {
if (ObjectUtil.isNull(instance)) {
instance = new ComponentInitializer();
}
return 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) {
nodeComponent.setNodeId(nodeId);
nodeComponent.setSelf(nodeComponent);
nodeComponent.setType(type);
public NodeComponent initComponent(NodeComponent nodeComponent, NodeTypeEnum type, String desc, String nodeId){
nodeComponent.setNodeId(nodeId);
nodeComponent.setSelf(nodeComponent);
nodeComponent.setType(type);
// 设置MonitorBus如果没有就不注入
if (ContextAwareHolder.loadContextAware().hasBean(ChainConstant.MONITOR_BUS)) {
MonitorBus monitorBus = ContextAwareHolder.loadContextAware().getBean(MonitorBus.class);
if (ObjectUtil.isNotNull(monitorBus)) {
nodeComponent.setMonitorBus(monitorBus);
}
}
//设置MonitorBus如果没有就不注入
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())) {
nodeComponent
.setName(LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent));
}
//先取传进来的name值(配置文件中配置的)再看有没有配置@LiteflowComponent标注
//@LiteflowComponent标注只在spring体系下生效这里用了spi机制取到相应环境下的实现类
nodeComponent.setName(desc);
if (ListUtil.toList(NodeTypeEnum.COMMON, NodeTypeEnum.SWITCH, NodeTypeEnum.IF).contains(type)
&& StrUtil.isBlank(nodeComponent.getName())){
String name = LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent);
nodeComponent.setName(name);
}
// 先从组件上取@RetryCount标注如果没有则看全局配置全局配置如果不配置的话默认是0
// 默认retryForExceptions为Exception.class
LiteflowRetry liteFlowRetryAnnotation = AnnoUtil.getAnnotation(nodeComponent.getClass(), LiteflowRetry.class);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (liteFlowRetryAnnotation != null) {
nodeComponent.setRetryCount(liteFlowRetryAnnotation.retry());
nodeComponent.setRetryForExceptions(liteFlowRetryAnnotation.forExceptions());
}
else {
nodeComponent.setRetryCount(liteflowConfig.getRetryCount());
}
nodeComponent.setNodeExecutorClass(buildNodeExecutorClass(liteflowConfig));
//先从组件上取@RetryCount标注如果没有则看全局配置全局配置如果不配置的话默认是0
//默认retryForExceptions为Exception.class
LiteflowRetry liteflowRetryAnnotation = AnnoUtil.getAnnotation(nodeComponent.getClass(), LiteflowRetry.class);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (ObjectUtil.isNotNull(liteflowRetryAnnotation)) {
nodeComponent.setRetryCount(liteflowRetryAnnotation.retry());
nodeComponent.setRetryForExceptions(liteflowRetryAnnotation.forExceptions());
} else {
nodeComponent.setRetryCount(liteflowConfig.getRetryCount());
}
nodeComponent.setNodeExecutorClass(buildNodeExecutorClass(liteflowConfig));
return nodeComponent;
}
private Class<? extends NodeExecutor> buildNodeExecutorClass(LiteflowConfig liteflowConfig) {
Class<?> nodeExecutorClass;
try {
nodeExecutorClass = Class.forName(liteflowConfig.getNodeExecutorClass());
}
catch (ClassNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
return (Class<? extends NodeExecutor>) nodeExecutorClass;
}
return nodeComponent;
}
private Class<? extends NodeExecutor> buildNodeExecutorClass(LiteflowConfig liteflowConfig) {
Class<?> nodeExecutorClass;
try {
nodeExecutorClass = Class.forName(liteflowConfig.getNodeExecutorClass());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
return (Class<? extends NodeExecutor>) nodeExecutorClass;
}
}

View File

@ -8,45 +8,32 @@
*/
package com.yomahub.liteflow.core;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.enums.ChainExecuteModeEnum;
import com.yomahub.liteflow.enums.InnerChainTypeEnum;
import com.yomahub.liteflow.enums.ParseModeEnum;
import com.yomahub.liteflow.exception.*;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.element.Rollbackable;
import com.yomahub.liteflow.flow.entity.CmpStep;
import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.monitor.MonitorFile;
import com.yomahub.liteflow.parser.base.FlowParser;
import com.yomahub.liteflow.parser.factory.FlowParserProvider;
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* 流程规则主要执行器类
@ -55,579 +42,299 @@ import java.util.stream.Collectors;
*/
public class FlowExecutor {
private static final LFLog LOG = LFLoggerManager.getLogger(FlowExecutor.class);
private static final String PREFIX_FORMAT_CONFIG_REGEX = "xml:|json:|yml:|el_xml:|el_json:|el_yml:";
private LiteflowConfig liteflowConfig;
public FlowExecutor() {
// 设置FlowExecutor的Holder虽然大部分地方都可以通过Spring上下文获取到但放入Holder还是为了某些地方能方便的取到
FlowExecutorHolder.setHolder(this);
// 初始化DataBus
DataBus.init();
}
public FlowExecutor(LiteflowConfig liteflowConfig) {
this.liteflowConfig = liteflowConfig;
// 把liteFlowConfig设到LiteFlowGetter中去
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
// 设置FlowExecutor的Holder虽然大部分地方都可以通过Spring上下文获取到但放入Holder还是为了某些地方能方便的取到
FlowExecutorHolder.setHolder(this);
if (!liteflowConfig.getParseMode().equals(ParseModeEnum.PARSE_ALL_ON_FIRST_EXEC)) {
this.init(true);
}
// 初始化DataBus
DataBus.init();
}
/**
* FlowExecutor的初始化化方式主要用于parse规则文件
* isStart表示是否是系统启动阶段启动阶段要做额外的事情而因为reload所调用的init就不用做
*/
public void init(boolean isStart) {
if (ObjectUtil.isNull(liteflowConfig)) {
throw new ConfigErrorException("config error, please check liteflow config property");
}
// 在相应的环境下进行节点的初始化工作
// 在spring体系下会获得spring扫描后的节点接入元数据
// 在非spring体系下是一个空实现等于不做此步骤
ContextCmpInitHolder.loadContextCmpInit().initCmp();
if (isStart){
// 进行id生成器的初始化
IdGeneratorHolder.init();
}
String ruleSource = liteflowConfig.getRuleSource();
if (StrUtil.isBlank(ruleSource)) {
// 查看有没有Parser的SPI实现
// 所有的Parser的SPI实现都是以custom形式放入的且只支持xml形式
ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
Iterator<ParserClassNameSpi> it = loader.iterator();
if (it.hasNext()) {
ParserClassNameSpi parserClassNameSpi = it.next();
ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
liteflowConfig.setRuleSource(ruleSource);
}
else {
// ruleSource为空而且没有spi形式的扩展那么说明真的没有ruleSource
// 这种情况有可能是基于代码动态构建的
return;
}
}
// 如果有前缀的则不需要再进行分割了说明是一个整体
// 如果没有前缀说明是本地文件可能配置多个所以需要分割
List<String> sourceRulePathList;
if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, ruleSource)) {
sourceRulePathList = ListUtil.toList(ruleSource);
}
else {
String afterHandleRuleSource = ruleSource.replace(StrUtil.SPACE, StrUtil.EMPTY);
sourceRulePathList = ListUtil.toList(afterHandleRuleSource.split(",|;"));
}
FlowParser parser = null;
Set<String> parserNameSet = new HashSet<>();
List<String> rulePathList = new ArrayList<>();
for (String path : sourceRulePathList) {
try {
// 查找对应的解析器
parser = FlowParserProvider.lookup(path);
parserNameSet.add(parser.getClass().getName());
// 替换掉前缀标识xml:/json:保留剩下的完整地址并统一路径格式
path = ReUtil.replaceAll(path, PREFIX_FORMAT_CONFIG_REGEX, "").replace("\\", "/");
rulePathList.add(path);
// 支持多类型的配置文件分别解析
if (BooleanUtil.isTrue(liteflowConfig.isSupportMultipleType())) {
// 解析文件
parser.parseMain(ListUtil.toList(path));
}
}
catch (CyclicDependencyException e) {
LOG.error(e.getMessage());
throw e;
}
catch (Exception e) {
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason:{}", path,
e.getMessage());
LOG.error(e.getMessage(), e);
throw new FlowExecutorNotInitException(errorMsg);
}
}
// 单类型的配置文件需要一起解析
if (BooleanUtil.isFalse(liteflowConfig.isSupportMultipleType())) {
// 检查Parser是否只有一个因为多个不同的parser会造成子流程的混乱
if (parserNameSet.size() > 1) {
String errorMsg = "cannot have multiple different parsers";
LOG.error(errorMsg);
throw new MultipleParsersException(errorMsg);
}
// 进行多个配置文件的一起解析
try {
if (parser != null) {
// 解析文件
parser.parseMain(rulePathList);
}
else {
throw new ConfigErrorException("parse error, please check liteflow config property");
}
}
catch (CyclicDependencyException e) {
LOG.error(e.getMessage(), e);
LOG.error(e.getMessage());
throw e;
}
catch (ChainDuplicateException e) {
LOG.error(e.getMessage(), e);
throw e;
}
catch (RouteELInvalidException e) {
LOG.error(e.getMessage(), e);
throw e;
}
catch (Exception e) {
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason: {}", rulePathList,
e.getMessage());
LOG.error(e.getMessage(), e);
throw new FlowExecutorNotInitException(errorMsg);
}
}
// 如果是ruleSource方式的最后判断下有没有解析出来,如果没有解析出来则报错
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData())
&& MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())) {
if (FlowBus.getChainMap().isEmpty()) {
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]",
liteflowConfig.getRuleSource());
throw new ConfigErrorException(errMsg);
}
}
// 执行钩子
if (isStart) {
FlowInitHook.executeHook();
}
// 文件监听
if (isStart && liteflowConfig.getEnableMonitorFile()) {
try {
addMonitorFilePaths(rulePathList);
MonitorFile.getInstance().create();
}
catch (Exception e) {
String errMsg = StrUtil.format("file monitor init error for path:{}", rulePathList);
throw new MonitorFileInitErrorException(errMsg);
}
}
}
// 此方法就是从原有的配置源主动拉取新的进行刷新
// 和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {
long start = System.currentTimeMillis();
init(false);
LOG.info("reload rules takes {}ms", System.currentTimeMillis() - start);
}
// 隐式流程的调用方法
@Deprecated
public void invoke(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
if (!response.isSuccess()) {
throw response.getCause();
}
}
@Deprecated
public void invokeInAsync(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
if (!response.isSuccess()) {
throw response.getCause();
}
}
public LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex) {
return this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
}
public LiteflowResponse invoke2RespInAsync(String chainId, Object param, Integer slotIndex) {
return this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
}
// 单独调用某一个node
@Deprecated
public void invoke(String nodeId, Integer slotIndex) throws Exception {
Node node = FlowBus.getNode(nodeId);
node.execute(slotIndex);
}
// 调用一个流程并返回LiteflowResponse上下文为默认的DefaultContext初始参数为null
public LiteflowResponse execute2Resp(String chainId) {
return this.execute2Resp(chainId, null, DefaultContext.class);
}
// 调用一个流程并返回LiteflowResponse上下文为默认的DefaultContext
public LiteflowResponse execute2Resp(String chainId, Object param) {
return this.execute2Resp(chainId, param, DefaultContext.class);
}
// 调用一个流程并返回LiteflowResponse允许多上下文的传入
public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray) {
return this.execute2Resp(chainId, param, null, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChain(Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(null, param, null, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(namespace, param, null, contextBeanClazzArray, null);
}
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) {
return this.execute2Resp(chainId, param, null, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChain(Object param, Object... contextBeanArray){
return this.executeWithRoute(null, param, null, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Object... contextBeanArray){
return this.executeWithRoute(namespace, param, null, null, contextBeanArray);
}
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(null, param, requestId, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(namespace, param, requestId, contextBeanClazzArray, null);
}
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
return this.execute2Resp(chainId, param, requestId, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(null, param, requestId, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(namespace, param, requestId, null, contextBeanArray);
}
// 调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray) {
return ExecutorHelper.loadInstance()
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
.submit(() -> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray));
}
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Object... contextBeanArray) {
return ExecutorHelper.loadInstance()
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
.submit(() -> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanArray));
}
public Future<LiteflowResponse> execute2FutureWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return ExecutorHelper.loadInstance()
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
.submit(() -> FlowExecutorHolder.loadInstance().execute2RespWithRid(chainId, param, requestId, contextBeanClazzArray));
}
public Future<LiteflowResponse> execute2FutureWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
return ExecutorHelper.loadInstance()
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
.submit(() -> FlowExecutorHolder.loadInstance().execute2RespWithRid(chainId, param, requestId, contextBeanArray));
}
// 调用一个流程返回默认的上下文适用于简单的调用
@Deprecated
public DefaultContext execute(String chainId, Object param) throws Exception {
LiteflowResponse response = this.execute2Resp(chainId, param, DefaultContext.class);
if (!response.isSuccess()) {
throw response.getCause();
}
else {
return response.getFirstContextBean();
}
}
private LiteflowResponse execute2Resp(String chainId, Object param, String requestId, Class<?>[] contextBeanClazzArray,
Object[] contextBeanArray) {
Slot slot = doExecute(chainId, param, requestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.BODY);
return LiteflowResponse.newMainResponse(slot);
}
private List<LiteflowResponse> executeWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
List<Slot> slotList = doExecuteWithRoute(namespace, param, requestId, contextBeanClazzArray, contextBeanArray);
return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList());
}
private LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex,
InnerChainTypeEnum innerChainType) {
Slot slot = doExecute(chainId, param, null, null, null, slotIndex, innerChainType, ChainExecuteModeEnum.BODY);
return LiteflowResponse.newInnerResponse(chainId, slot);
}
private Slot doExecute(String chainId, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray,
Integer slotIndex, InnerChainTypeEnum innerChainType, ChainExecuteModeEnum chainExecuteModeEnum) {
if (FlowBus.needInit()) {
init(true);
}
// 如果不是隐式流程那么需要分配Slot
if (innerChainType.equals(InnerChainTypeEnum.NONE) && ObjectUtil.isNull(slotIndex)) {
// 这里可以根据class分配也可以根据bean去分配
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)) {
slotIndex = DataBus.offerSlotByClass(ListUtil.toList(contextBeanClazzArray));
}
else {
slotIndex = DataBus.offerSlotByBean(ListUtil.toList(contextBeanArray));
}
if (slotIndex == -1) {
throw new NoAvailableSlotException("there is no available slot");
}
}
Slot slot = DataBus.getSlot(slotIndex);
if (ObjectUtil.isNull(slot)) {
throw new NoAvailableSlotException(StrUtil.format("the slot[{}] is not exist", slotIndex));
}
//如果传入了用户的RequestId则用这个请求Id如果没传入则进行生成
if (StrUtil.isNotBlank(requestId)){
slot.putRequestId(requestId);
LFLoggerManager.setRequestId(requestId);
}else if(StrUtil.isBlank(slot.getRequestId())){
slot.generateRequestId();
LFLoggerManager.setRequestId(slot.getRequestId());
LOG.info("requestId has generated");
}
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
LOG.info("slot[{}] offered", slotIndex);
}
// 如果是隐式流程事先把subException给置空然后把隐式流程的chainId放入slot元数据中
// 我知道这在多线程调用隐式流程中会有问题但是考虑到这种场景的不会多也有其他的转换方式
// 所以暂且这么做以后再优化
if (!innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.removeSubException(chainId);
slot.addSubChain(chainId);
}
if (ObjectUtil.isNotNull(param)) {
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.setRequestData(param);
}
else if (innerChainType.equals(InnerChainTypeEnum.IN_SYNC)) {
slot.setChainReqData(chainId, param);
}
else if (innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)) {
slot.setChainReqData2Queue(chainId, param);
}
}
Chain chain = null;
try {
chain = FlowBus.getChain(chainId);
if (ObjectUtil.isNull(chain)) {
String errorMsg = StrUtil.format("couldn't find chain with the id[{}]", chainId);
throw new ChainNotFoundException(errorMsg);
}
// 根据chain执行模式执行chain
if (chainExecuteModeEnum.equals(ChainExecuteModeEnum.BODY)){
chain.execute(slotIndex);
}else if(chainExecuteModeEnum.equals(ChainExecuteModeEnum.ROUTE)){
chain.executeRoute(slotIndex);
}else{
throw new LiteFlowException("chain execute mode error");
}
}
catch (ChainEndException e) {
if (ObjectUtil.isNotNull(chain)) {
String warnMsg = StrUtil.format("chain[{}] execute end on slot[{}]", chain.getChainId(), slotIndex);
LOG.warn(warnMsg);
}
}
catch (Exception e) {
if (ObjectUtil.isNotNull(chain)) {
String errMsg = StrUtil.format("chain[{}] execute error on slot[{}]", chain.getChainId(), slotIndex);
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.error(errMsg, e);
}
else {
LOG.error(errMsg);
}
}
else {
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.error(e.getMessage(), e);
}
else {
LOG.error(e.getMessage());
}
}
// 如果是正常流程需要把异常设置到slot的exception属性里
// 如果是隐式流程则需要设置到隐式流程的exception属性里
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.setException(e);
}
else {
slot.setSubException(chainId, e);
}
Deque<CmpStep> executeSteps = slot.getExecuteSteps();
try {
Iterator<CmpStep> cmpStepIterator = executeSteps.descendingIterator();
while(cmpStepIterator.hasNext()) {
CmpStep cmpStep = cmpStepIterator.next();
if(cmpStep.getInstance().isRollback()) {
Rollbackable rollbackItem = cmpStep.getRefNode();
rollbackItem.rollback(slotIndex);
}
}
} catch (Exception exception) {
LOG.error(exception.getMessage());
}
finally {
slot.printRollbackStep();
}
}
finally {
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.printStep();
DataBus.releaseSlot(slotIndex);
LFLoggerManager.removeRequestId();
}
}
return slot;
}
public LiteflowConfig getLiteflowConfig() {
return liteflowConfig;
}
public void setLiteflowConfig(LiteflowConfig liteflowConfig) {
this.liteflowConfig = liteflowConfig;
// 把liteFlowConfig设到LiteFlowGetter中去
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
}
/**
* 添加监听文件路径
* @param pathList 文件路径
*/
private void addMonitorFilePaths(List<String> pathList) throws Exception {
// 添加规则文件监听
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(pathList);
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
}
private List<Slot> doExecuteWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
if (FlowBus.needInit()) {
init(true);
}
if (StrUtil.isBlank(namespace)){
namespace = ChainConstant.DEFAULT_NAMESPACE;
}
String finalNamespace = namespace;
List<Chain> routeChainList = FlowBus.getChainMap().values().stream()
.filter(chain -> chain.getNamespace().equals(finalNamespace))
.filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList());
if (CollUtil.isEmpty(routeChainList)){
String errorMsg = StrUtil.format("no route found for namespace[{}]", finalNamespace);
throw new RouteChainNotFoundException(errorMsg);
}
String finalRequestId;
if (StrUtil.isBlank(requestId)){
finalRequestId = IdGeneratorHolder.getInstance().generate();
}else{
finalRequestId = requestId;
}
// 异步执行route el
List<Tuple> routeTupleList = new ArrayList<>();
for (Chain routeChain : routeChainList){
CompletableFuture<Slot> f = CompletableFuture.supplyAsync(
() -> doExecute(routeChain.getChainId(), param, finalRequestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.ROUTE)
);
routeTupleList.add(new Tuple(routeChain, f));
}
CompletableFuture<?> resultRouteCf = CompletableFuture.allOf(routeTupleList.stream().map(
(Function<Tuple, CompletableFuture<?>>) tuple -> tuple.get(1)
).collect(Collectors.toList()).toArray(new CompletableFuture[] {}));
try{
resultRouteCf.get();
}catch (Exception e){
throw new LiteFlowException("There is An error occurred while executing the route.", e);
}
// 把route执行为true都过滤出来
List<Chain> matchedRouteChainList = routeTupleList.stream().filter(tuple -> {
try{
CompletableFuture<Slot> f = tuple.get(1);
Slot slot = f.get();
return BooleanUtil.isTrue(slot.getRouteResult());
}catch (Exception e){
return false;
}
}).map(
(Function<Tuple, Chain>) tuple -> tuple.get(0)
).collect(Collectors.toList());
if (CollUtil.isEmpty(matchedRouteChainList)){
throw new NoMatchedRouteChainException("there is no matched route chain");
}
// 异步分别执行这些chain
List<CompletableFuture<Slot>> executeChainCfList = new ArrayList<>();
for (Chain chain : matchedRouteChainList){
CompletableFuture<Slot> cf = CompletableFuture.supplyAsync(
() -> doExecute(chain.getChainId(), param, finalRequestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.BODY)
);
executeChainCfList.add(cf);
}
CompletableFuture<?> resultChainCf = CompletableFuture.allOf(executeChainCfList.toArray(new CompletableFuture[] {}));
try{
resultChainCf.get();
}catch (Exception e){
throw new LiteFlowException("There is An error occurred while executing the matched chain.", e);
}
List<Slot> resultSlotList = executeChainCfList.stream().map(slotCompletableFuture -> {
try{
return slotCompletableFuture.get();
}catch (Exception e){
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
LOG.info("chain namespace:[{}], total size:[{}], matched size:[{}]", namespace, routeChainList.size(), resultSlotList.size());
return resultSlotList;
}
private static final Logger LOG = LoggerFactory.getLogger(FlowExecutor.class);
private static final String PREFIX_FORMAT_CONFIG_REGEX = "xml:|json:|yml:|el_xml:|el_json:|el_yml:";
private LiteflowConfig liteflowConfig;
public FlowExecutor() {
//设置FlowExecutor的Holder虽然大部分地方都可以通过Spring上下文获取到但放入Holder还是为了某些地方能方便的取到
FlowExecutorHolder.setHolder(this);
//初始化DataBus
DataBus.init();
}
public FlowExecutor(LiteflowConfig liteflowConfig) {
this.liteflowConfig = liteflowConfig;
//把liteFlowConfig设到LiteFlowGetter中去
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
//设置FlowExecutor的Holder虽然大部分地方都可以通过Spring上下文获取到但放入Holder还是为了某些地方能方便的取到
FlowExecutorHolder.setHolder(this);
if (BooleanUtil.isTrue(liteflowConfig.isParseOnStart())) {
this.init();
}
//初始化DataBus
DataBus.init();
}
/**
* FlowExecutor的初始化化方式主要用于parse规则文件
*/
public void init() {
if (ObjectUtil.isNull(liteflowConfig)) {
throw new ConfigErrorException("config error, please check liteflow config property");
}
//在相应的环境下进行节点的初始化工作
//在spring体系下会获得spring扫描后的节点接入元数据
//在非spring体系下是一个空实现等于不做此步骤
ContextCmpInitHolder.loadContextCmpInit().initCmp();
//进行id生成器的初始化
IdGeneratorHolder.init();
//如果没有配置规则文件路径就停止初始化
//规则文件路径不是一定要有因为liteflow分基于规则和基于代码两种有可能是动态代码构建的
if (StrUtil.isBlank(liteflowConfig.getRuleSource())) {
return;
}
//如果有前缀的则不需要再进行分割了说明是一个整体
//如果没有前缀说明是本地文件可能配置多个所以需要分割
List<String> sourceRulePathList;
if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, liteflowConfig.getRuleSource())){
sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource());
}else{
sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource().split(",|;"));
}
FlowParser parser = null;
Set<String> parserNameSet = new HashSet<>();
List<String> rulePathList = new ArrayList<>();
for (String path : sourceRulePathList) {
try {
// 查找对应的解析器
parser = FlowParserProvider.lookup(path);
parserNameSet.add(parser.getClass().getName());
// 替换掉前缀标识xml:/json:保留剩下的完整地址
path = ReUtil.replaceAll(path, PREFIX_FORMAT_CONFIG_REGEX, "");
rulePathList.add(path);
//支持多类型的配置文件分别解析
if (BooleanUtil.isTrue(liteflowConfig.isSupportMultipleType())) {
parser.parseMain(ListUtil.toList(path));
}
} catch (CyclicDependencyException e) {
LOG.error(e.getMessage());
throw e;
} catch (Exception e) {
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason:{}", path, e.getMessage());
LOG.error(e.getMessage(), e);
throw new FlowExecutorNotInitException(errorMsg);
}
}
//单类型的配置文件需要一起解析
if (BooleanUtil.isFalse(liteflowConfig.isSupportMultipleType())) {
//检查Parser是否只有一个因为多个不同的parser会造成子流程的混乱
if (parserNameSet.size() > 1) {
String errorMsg = "cannot have multiple different parsers";
LOG.error(errorMsg);
throw new MultipleParsersException(errorMsg);
}
//进行多个配置文件的一起解析
try {
if (parser != null) {
parser.parseMain(rulePathList);
} else {
throw new ConfigErrorException("parse error, please check liteflow config property");
}
} catch (CyclicDependencyException e) {
LOG.error(e.getMessage(), e);
LOG.error(e.getMessage());
throw e;
} catch (ChainDuplicateException e) {
LOG.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason: {}", rulePathList, e.getMessage());
LOG.error(e.getMessage(), e);
throw new FlowExecutorNotInitException(errorMsg);
}
}
}
//此方法就是从原有的配置源主动拉取新的进行刷新
//和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {
init();
}
//隐式流程的调用方法
public void invoke(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.execute2Resp(chainId, param, null, null, slotIndex, InnerChainTypeEnum.IN_SYNC);
if (!response.isSuccess()){
throw response.getCause();
}
}
public LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex) {
return this.execute2Resp(chainId, param, null, null, slotIndex, InnerChainTypeEnum.IN_SYNC);
}
public void invokeInAsync(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.execute2Resp(chainId, param, null, null, slotIndex, InnerChainTypeEnum.IN_ASYNC);
if (!response.isSuccess()){
throw response.getCause();
}
}
public LiteflowResponse invoke2RespInAsync(String chainId, Object param, Integer slotIndex) {
return this.execute2Resp(chainId, param, null, null, slotIndex, InnerChainTypeEnum.IN_ASYNC);
}
//单独调用某一个node
public void invoke(String nodeId, Integer slotIndex) throws Exception {
Node node = FlowBus.getNode(nodeId);
node.execute(slotIndex);
}
//调用一个流程并返回LiteflowResponse上下文为默认的DefaultContext初始参数为null
public LiteflowResponse execute2Resp(String chainId) {
return this.execute2Resp(chainId, null, DefaultContext.class);
}
//调用一个流程并返回LiteflowResponse上下文为默认的DefaultContext
public LiteflowResponse execute2Resp(String chainId, Object param) {
return this.execute2Resp(chainId, param, DefaultContext.class);
}
//调用一个流程并返回LiteflowResponse允许多上下文的传入
public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray) {
return this.execute2Resp(chainId, param, contextBeanClazzArray, null, null, InnerChainTypeEnum.NONE);
}
//调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray) {
return ExecutorHelper.loadInstance().buildMainExecutor(liteflowConfig.getMainExecutorClass()).submit(()
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray,null, null, InnerChainTypeEnum.NONE));
}
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) {
return this.execute2Resp(chainId, param, null, contextBeanArray, null, InnerChainTypeEnum.NONE);
}
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Object... contextBeanArray) {
return ExecutorHelper.loadInstance().buildMainExecutor(liteflowConfig.getMainExecutorClass()).submit(()
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, null, contextBeanArray, null, InnerChainTypeEnum.NONE));
}
//调用一个流程返回默认的上下文适用于简单的调用
public DefaultContext execute(String chainId, Object param) throws Exception{
LiteflowResponse response = this.execute2Resp(chainId, param, DefaultContext.class);
if (!response.isSuccess()){
throw response.getCause();
}else{
return response.getFirstContextBean();
}
}
private LiteflowResponse execute2Resp(String chainId,
Object param,
Class<?>[] contextBeanClazzArray,
Object[] contextBeanArray,
Integer slotIndex, InnerChainTypeEnum innerChainType) {
Slot slot = doExecute(chainId, param, contextBeanClazzArray, contextBeanArray, slotIndex, innerChainType);
return new LiteflowResponse(slot);
}
private Slot doExecute(String chainId,
Object param,
Class<?>[] contextBeanClazzArray,
Object[] contextBeanArray,
Integer slotIndex,
InnerChainTypeEnum innerChainType) {
if (FlowBus.needInit()) {
init();
}
if (innerChainType.equals(InnerChainTypeEnum.NONE) && ObjectUtil.isNull(slotIndex)) {
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)){
slotIndex = DataBus.offerSlotByClass(ListUtil.toList(contextBeanClazzArray));
}else{
slotIndex = DataBus.offerSlotByBean(ListUtil.toList(contextBeanArray));
}
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.info("slot[{}] offered", slotIndex);
}
}
if (slotIndex == -1) {
throw new NoAvailableSlotException("there is no available slot");
}
Slot slot = DataBus.getSlot(slotIndex);
if (ObjectUtil.isNull(slot)) {
throw new NoAvailableSlotException(StrUtil.format("the slot[{}] is not exist", slotIndex));
}
if (StrUtil.isBlank(slot.getRequestId())) {
slot.generateRequestId();
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.info("requestId[{}] has generated", slot.getRequestId());
}
}
if (ObjectUtil.isNotNull(param)) {
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.setRequestData(param);
} else if(innerChainType.equals(InnerChainTypeEnum.IN_SYNC)){
slot.setChainReqData(chainId, param);
} else if(innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)){
slot.setChainReqData2Queue(chainId, param);
}
}
Chain chain = null;
try {
chain = FlowBus.getChain(chainId);
if (ObjectUtil.isNull(chain)) {
String errorMsg = StrUtil.format("[{}]:couldn't find chain with the id[{}]", slot.getRequestId(), chainId);
throw new ChainNotFoundException(errorMsg);
}
// 执行chain
chain.execute(slotIndex);
} catch (ChainEndException e) {
if (ObjectUtil.isNotNull(chain)) {
String warnMsg = StrUtil.format("[{}]:chain[{}] execute end on slot[{}]", slot.getRequestId(), chain.getChainName(), slotIndex);
LOG.warn(warnMsg);
}
} catch (Exception e) {
if (ObjectUtil.isNotNull(chain)) {
String errMsg = StrUtil.format("[{}]:chain[{}] execute error on slot[{}]", slot.getRequestId(), chain.getChainName(), slotIndex);
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
LOG.error(errMsg, e);
}else{
LOG.error(errMsg);
}
}else{
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
LOG.error(e.getMessage(), e);
}else{
LOG.error(e.getMessage());
}
}
slot.setException(e);
} finally {
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.printStep();
DataBus.releaseSlot(slotIndex);
}
}
return slot;
}
public LiteflowConfig getLiteflowConfig() {
return liteflowConfig;
}
public void setLiteflowConfig(LiteflowConfig liteflowConfig) {
this.liteflowConfig = liteflowConfig;
//把liteFlowConfig设到LiteFlowGetter中去
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
}
}

View File

@ -4,33 +4,29 @@ 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;
private static FlowExecutor flowExecutor;
public static FlowExecutor loadInstance(LiteflowConfig liteflowConfig) {
if (ObjectUtil.isNull(flowExecutor)) {
flowExecutor = new FlowExecutor(liteflowConfig);
}
return flowExecutor;
}
public static FlowExecutor loadInstance(LiteflowConfig liteflowConfig){
if (ObjectUtil.isNull(flowExecutor)){
flowExecutor = new FlowExecutor(liteflowConfig);
}
return flowExecutor;
}
public static FlowExecutor loadInstance() {
if (ObjectUtil.isNull(flowExecutor)) {
throw new FlowExecutorNotInitException("flow executor is not initialized yet");
}
return flowExecutor;
}
public static FlowExecutor loadInstance(){
if (ObjectUtil.isNull(flowExecutor)){
throw new FlowExecutorNotInitException("flow executor is not initialized yet");
}
return flowExecutor;
}
public static void setHolder(FlowExecutor flowExecutor) {
FlowExecutorHolder.flowExecutor = flowExecutor;
}
public static void clean() {
flowExecutor = null;
}
public static void setHolder(FlowExecutor flowExecutor){
FlowExecutorHolder.flowExecutor = flowExecutor;
}
public static void clean(){
flowExecutor = null;
}
}

View File

@ -1,33 +0,0 @@
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> SUPPLIER_LIST = new ArrayList<>();
public static void executeHook() {
if (CollUtil.isNotEmpty(SUPPLIER_LIST)) {
SUPPLIER_LIST.forEach(BooleanSupplier::getAsBoolean);
}
}
public static void addHook(BooleanSupplier hookSupplier) {
SUPPLIER_LIST.add(hookSupplier);
}
public static void cleanHook() {
SUPPLIER_LIST.clear();
}
}

View File

@ -1,26 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.DataBus;
/**
* BOOLEAN类型的抽象节点
*
* @author Bryan.Zhang
* @since 2.12.0
*/
public abstract class NodeBooleanComponent extends NodeComponent {
@Override
public void process() throws Exception {
boolean result = this.processBoolean();
this.getSlot().setIfResult(this.getMetaValueKey(), result);
}
public abstract boolean processBoolean() throws Exception;
@Override
@SuppressWarnings("unchecked")
public Boolean getItemResultMetaValue(Integer slotIndex) {
return DataBus.getSlot(slotIndex).getIfResult(this.getMetaValueKey());
}
}

View File

@ -11,35 +11,30 @@ import cn.hutool.core.date.StopWatch;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
import com.yomahub.liteflow.enums.CmpStepTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.entity.CmpStep;
import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
import com.yomahub.liteflow.flow.executor.NodeExecutor;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.monitor.CompStatistics;
import com.yomahub.liteflow.monitor.MonitorBus;
import com.yomahub.liteflow.flow.executor.DefaultNodeExecutor;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.spi.holder.CmpAroundAspectHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.yomahub.liteflow.flow.entity.CmpStep;
import com.yomahub.liteflow.enums.CmpStepTypeEnum;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.spi.holder.CmpAroundAspectHolder;
import com.yomahub.liteflow.util.JsonUtil;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.monitor.CompStatistics;
import com.yomahub.liteflow.monitor.MonitorBus;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Map;
/**
* 普通组件抽象类
*
* @author Bryan.Zhang
* @author luo yi
*/
public abstract class NodeComponent{
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private MonitorBus monitorBus;
@ -49,99 +44,85 @@ public abstract class NodeComponent{
private NodeTypeEnum type;
// 这是自己的实例取代this
// 为何要设置这个用this不行么因为如果有aop去切的话this在spring的aop里是切不到的self对象有可能是代理过的对象
//这是自己的实例取代this
//为何要设置这个用this不行么因为如果有aop去切的话this在spring的aop里是切不到的self对象有可能是代理过的对象
private NodeComponent self;
// 重试次数
//重试次数
private int retryCount = 0;
// 是否重写了rollback方法
private boolean isRollback = false;
// 在目标异常抛出时才重试
private Class<? extends Exception>[] retryForExceptions = new Class[] { Exception.class };
//在目标异常抛出时才重试
private Class<? extends Exception>[] retryForExceptions = new Class[]{Exception.class};
/** 节点执行器的类全名 */
private Class<? extends NodeExecutor> nodeExecutorClass = DefaultNodeExecutor.class;
/** 当前对象为单例注册进spring上下文但是node实例不是单例这里通过对node实例的引用来获得一些链路属性 **/
/********************以下的属性为线程附加属性,并非不变属性********************/
private final TransmittableThreadLocal<Node> refNodeTL = new TransmittableThreadLocal<>();
//当前slot的index
private final TransmittableThreadLocal<Integer> slotIndexTL = new TransmittableThreadLocal<>();
/**
******************* 以下的属性为线程附加属性********************
* 线程属性是指每一个request的值都是不一样的
* 这里NodeComponent是单例所以要用ThreadLocal来修饰
*/
//是否结束整个流程这个只对串行流程有效并行流程无效
private final TransmittableThreadLocal<Boolean> isEndTL = new TransmittableThreadLocal<>();
//tag标签
private final TransmittableThreadLocal<String> tagTL = new TransmittableThreadLocal<>();
//当前流程名字
private final TransmittableThreadLocal<String> currChainNameTL = new TransmittableThreadLocal<>();
public NodeComponent() {
// 反射判断是否重写了rollback方法
Class<?> clazz = this.getClass();
try {
Method method = clazz.getDeclaredMethod("rollback");
if(ObjectUtil.isNotNull(method)){
this.setRollback(true);
}
} catch (Exception ignored) {}
}
public void execute() throws Exception {
public void execute() throws Exception{
Slot slot = this.getSlot();
// 在元数据里加入step信息
//在元数据里加入step信息
CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE);
cmpStep.setTag(this.getTag());
cmpStep.setInstance(this);
cmpStep.setRefNode(this.getRefNode());
cmpStep.setStartTime(new Date());
cmpStep.setTag(tagTL.get());
slot.addStep(cmpStep);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
// 前置处理
self.beforeProcess();
// 主要的处理逻辑
try{
//前置处理
self.beforeProcess(this.getNodeId(), slot);
//主要的处理逻辑
self.process();
// 成功后回调方法
//成功后回调方法
self.onSuccess();
// 步骤状态设为true
//步骤状态设为true
cmpStep.setSuccess(true);
}
catch (Exception e) {
// 步骤状态设为false并加入异常
} catch (Exception e){
//步骤状态设为false并加入异常
cmpStep.setSuccess(false);
cmpStep.setException(e);
// 执行失败后回调方法
// 这里要注意失败方法本身抛出错误只打出堆栈往外抛出的还是主要的异常
try {
self.onError(e);
}
catch (Exception ex) {
String errMsg = StrUtil.format("component[{}] onError method happens exception", this.getDisplayName());
LOG.error(errMsg, ex);
//执行失败后回调方法
//这里要注意失败方法本身抛出错误只打出堆栈往外抛出的还是主要的异常
try{
self.onError();
}catch (Exception ex){
String errMsg = StrUtil.format("[{}]:component[{}] onError method happens exception",slot.getRequestId(),this.getDisplayName());
LOG.error(errMsg);
}
throw e;
}
finally {
// 后置处理
self.afterProcess();
} finally {
stopWatch.stop();
final long timeSpent = stopWatch.getTotalTimeMillis();
LOG.info("component[{}] finished in {} milliseconds", this.getDisplayName(), timeSpent);
LOG.debug("[{}]:component[{}] finished in {} milliseconds",slot.getRequestId(),this.getDisplayName(),timeSpent);
cmpStep.setEndTime(new Date());
// 往CmpStep中放入时间消耗信息
//往CmpStep中放入时间消耗信息
cmpStep.setTimeSpent(timeSpent);
//后置处理
self.afterProcess(this.getNodeId(), slot);
// 性能统计
if (ObjectUtil.isNotNull(monitorBus)) {
CompStatistics statistics = new CompStatistics(this.getClass().getSimpleName(), timeSpent);
@ -150,118 +131,80 @@ public abstract class NodeComponent{
}
}
public void doRollback() throws Exception {
Slot slot = this.getSlot();
boolean alreadyRollback = slot.getRollbackSteps().stream().anyMatch(cmpStep -> cmpStep.getRefNode().equals(getRefNode()));
if (alreadyRollback){
return;
}
CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE);
cmpStep.setTag(this.getTag());
cmpStep.setInstance(this);
cmpStep.setRefNode(this.getRefNode());
slot.addRollbackStep(cmpStep);
StopWatch stopWatch = new StopWatch();
stopWatch.start();
try {
self.rollback();
}
catch (Exception e) {
throw e;
}
finally {
stopWatch.stop();
final long timeSpent = stopWatch.getTotalTimeMillis();
LOG.info("component[{}] rollback in {} milliseconds", this.getDisplayName(), timeSpent);
// 往CmpStep中放入时间消耗信息
cmpStep.setRollbackTimeSpent(timeSpent);
}
}
public void beforeProcess() {
// 全局切面只在spring体系下生效这里用了spi机制取到相应环境下的实现类
// 非spring环境下全局切面为空实现
CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(this.self);
public <T> void beforeProcess(String nodeId, Slot slot){
//全局切面只在spring体系下生效这里用了spi机制取到相应环境下的实现类
//非spring环境下全局切面为空实现
CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(nodeId, slot);
}
public abstract void process() throws Exception;
public void rollback() throws Exception{
// 如果需要失败后回滚某个方法请覆盖这个方法
};
public void onSuccess() throws Exception {
// 如果需要在成功后回调某一个方法请覆盖这个方法
// 全局切面只在spring体系下生效这里用了spi机制取到相应环境下的实现类
// 非spring环境下全局切面为空实现
CmpAroundAspectHolder.loadCmpAroundAspect().onSuccess(this.self);
public void onSuccess() throws Exception{
//如果需要在成功后回调某一个方法请覆盖这个方法
}
public void onError(Exception e) throws Exception {
// 如果需要在抛错后回调某一段逻辑请覆盖这个方法
// 全局切面只在spring体系下生效这里用了spi机制取到相应环境下的实现类
// 非spring环境下全局切面为空实现
CmpAroundAspectHolder.loadCmpAroundAspect().onError(this.self, e);
public void onError() throws Exception{
//如果需要在抛错后回调某一段逻辑请覆盖这个方法
}
public void afterProcess() {
CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(this.self);
public <T> void afterProcess(String nodeId, Slot slot){
CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(nodeId, slot);
}
// 是否进入该节点
public boolean isAccess() {
//是否进入该节点
public boolean isAccess(){
return true;
}
// 出错是否继续执行
//出错是否继续执行(这个只适用于串行流程并行节点不起作用)
public boolean isContinueOnError() {
return false;
}
// 是否结束整个流程(不往下继续执行)
//是否结束整个流程(不往下继续执行)
public boolean isEnd() {
Boolean isEnd = this.refNodeTL.get().getIsEnd();
if (ObjectUtil.isNull(isEnd)) {
Boolean isEnd = isEndTL.get();
if(ObjectUtil.isNull(isEnd)){
return false;
}else {
return isEnd;
} else {
return isEndTL.get();
}
}
// 设置是否结束整个流程
public void setIsEnd(boolean isEnd) {
this.refNodeTL.get().setIsEnd(isEnd);
//设置是否结束整个流程
public void setIsEnd(boolean isEnd){
this.isEndTL.set(isEnd);
}
public void setIsContinueOnError(boolean isContinueOnError) {
this.refNodeTL.get().setIsContinueOnErrorResult(isContinueOnError);
public void removeIsEnd(){
this.isEndTL.remove();
}
public NodeComponent setSlotIndex(Integer slotIndex) {
this.slotIndexTL.set(slotIndex);
return this;
}
public Integer getSlotIndex() {
return this.refNodeTL.get().getSlotIndex();
return this.slotIndexTL.get();
}
public Slot getSlot() {
return DataBus.getSlot(this.getSlotIndex());
public void removeSlotIndex(){
this.slotIndexTL.remove();
}
public <T> T getFirstContextBean() {
public Slot getSlot(){
return DataBus.getSlot(this.slotIndexTL.get());
}
public <T> T getFirstContextBean(){
return this.getSlot().getFirstContextBean();
}
public <T> T getContextBean(Class<T> contextBeanClazz) {
public <T> T getContextBean(Class<T> contextBeanClazz){
return this.getSlot().getContextBean(contextBeanClazz);
}
public <T> T getContextBean(String contextName) {
return this.getSlot().getContextBean(contextName);
}
public String getNodeId() {
return nodeId;
}
@ -294,11 +237,11 @@ public abstract class NodeComponent{
this.type = type;
}
public <T> void sendPrivateDeliveryData(String nodeId, T t) {
public <T> void sendPrivateDeliveryData(String nodeId, T t){
this.getSlot().setPrivateDeliveryData(nodeId, t);
}
public <T> T getPrivateDeliveryData() {
public <T> T getPrivateDeliveryData(){
return this.getSlot().getPrivateDeliveryData(this.getNodeId());
}
@ -326,8 +269,16 @@ public abstract class NodeComponent{
this.nodeExecutorClass = nodeExecutorClass;
}
public String getTag() {
return this.refNodeTL.get().getTag();
public void setTag(String tag){
this.tagTL.set(tag);
}
public String getTag(){
return this.tagTL.get();
}
public void removeTag(){
this.tagTL.remove();
}
public MonitorBus getMonitorBus() {
@ -338,84 +289,42 @@ public abstract class NodeComponent{
this.monitorBus = monitorBus;
}
public <T> T getRequestData() {
public <T> T getRequestData(){
return getSlot().getRequestData();
}
public <T> T getSubChainReqData() {
return getSlot().getChainReqData(this.getCurrChainId());
public <T> T getSubChainReqData(){
return getSlot().getChainReqData(this.getCurrChainName());
}
public <T> T getSubChainReqDataInAsync() {
return getSlot().getChainReqDataFromQueue(this.getCurrChainId());
public <T> T getSubChainReqDataInAsync(){
return getSlot().getChainReqDataFromQueue(this.getCurrChainName());
}
public boolean isRollback() {
return isRollback;
}
public void setRollback(boolean rollback) {
isRollback = rollback;
}
/**
* @deprecated 请使用 {@link #getChainId()}
* @return String
*/
@Deprecated
public String getChainName() {
public String getChainName(){
return getSlot().getChainName();
}
public String getChainId() {
return getSlot().getChainId();
}
public String getDisplayName() {
if (StrUtil.isEmpty(this.name)) {
public String getDisplayName(){
if(StrUtil.isEmpty(this.name)){
return this.nodeId;
}
else {
}else {
return StrUtil.format("{}({})", this.nodeId, this.name);
}
}
public String getCurrChainId() {
return getRefNode().getCurrChainId();
public void setCurrChainName(String currChainName){
this.currChainNameTL.set(currChainName);
}
public Node getRefNode() {
return this.refNodeTL.get();
public String getCurrChainName(){
return this.currChainNameTL.get();
}
public void setRefNode(Node refNode) {
this.refNodeTL.set(refNode);
public void removeCurrChainName(){
this.currChainNameTL.remove();
}
public void removeRefNode() {
this.refNodeTL.remove();
}
public <T> T getCmpData(Class<T> clazz) {
String cmpData = getRefNode().getCmpData();
if (StrUtil.isBlank(cmpData)) {
return null;
}
if (clazz.equals(String.class) || clazz.equals(Object.class)) {
return (T) cmpData;
}
return JsonUtil.parseObject(cmpData, clazz);
}
public Integer getLoopIndex() {
return this.refNodeTL.get().getLoopIndex();
}
public <T> T getCurrLoopObj() {
return this.refNodeTL.get().getCurrLoopObject();
}
@Deprecated
public void invoke(String chainId, Object param) throws Exception {
FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());
}
@ -424,7 +333,6 @@ 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());
}
@ -432,13 +340,4 @@ public abstract class NodeComponent{
public LiteflowResponse invoke2RespInAsync(String chainId, Object param) {
return FlowExecutorHolder.loadInstance().invoke2RespInAsync(chainId, param, this.getSlotIndex());
}
public <T> T getItemResultMetaValue(Integer slotIndex){
return null;
}
protected String getMetaValueKey(){
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
return originalClass.getName();
}
}

View File

@ -1,30 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
/**
* FOR计数节点抽象类
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public abstract class NodeForComponent extends NodeComponent {
@Override
public void process() throws Exception {
int forCount = processFor();
Slot slot = this.getSlot();
slot.setForResult(this.getMetaValueKey(), forCount);
}
public abstract int processFor() throws Exception;
@Override
@SuppressWarnings("unchecked")
public Integer getItemResultMetaValue(Integer slotIndex) {
return DataBus.getSlot(slotIndex).getForResult(this.getMetaValueKey());
}
}

View File

@ -0,0 +1,20 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* IF节点抽象类
* @author Bryan.Zhang
* @since 2.8.5
*/
public abstract class NodeIfComponent extends NodeComponent{
@Override
public void process() throws Exception {
boolean result = this.processIf();
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
this.getSlot().setIfResult(originalClass.getName(), result);
}
public abstract boolean processIf() throws Exception;
}

View File

@ -1,32 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
import java.util.Iterator;
/**
* ITERATOR迭代器循环组件抽象类
*
* @author Bryan.Zhang
* @since 2.9.7
*/
public abstract class NodeIteratorComponent extends NodeComponent {
@Override
public void process() throws Exception {
Iterator<?> it = processIterator();
Slot slot = this.getSlot();
slot.setIteratorResult(this.getMetaValueKey(), it);
}
public abstract Iterator<?> processIterator() throws Exception;
@Override
@SuppressWarnings("unchecked")
public Iterator<?> getItemResultMetaValue(Integer slotIndex) {
return DataBus.getSlot(slotIndex).getIteratorResult(this.getMetaValueKey());
}
}

View File

@ -7,12 +7,10 @@
*/
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
/**
* 条件路由节点抽象类
*
* @author Bryan.Zhang
*/
public abstract class NodeSwitchComponent extends NodeComponent {
@ -20,16 +18,11 @@ public abstract class NodeSwitchComponent extends NodeComponent {
@Override
public void process() throws Exception {
String nodeId = this.processSwitch();
this.getSlot().setSwitchResult(this.getMetaValueKey(), nodeId);
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
this.getSlot().setSwitchResult(originalClass.getName(), nodeId);
}
// 用以返回路由节点的beanId
//用以返回路由节点的beanId
public abstract String processSwitch() throws Exception;
@Override
@SuppressWarnings("unchecked")
public String getItemResultMetaValue(Integer slotIndex) {
return DataBus.getSlot(slotIndex).getSwitchResult(this.getMetaValueKey());
}
}

View File

@ -1,27 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
/**
* 脚本BOOLEAN节点
*
* @author Bryan.Zhang
* @since 2.12.0
*/
public class ScriptBooleanComponent extends NodeBooleanComponent implements ScriptComponent {
@Override
public boolean processBoolean() throws Exception {
ScriptExecuteWrap wrap = this.buildWrap(this);
return (boolean) ScriptExecutorFactory.loadInstance()
.getScriptExecutor(this.getRefNode().getLanguage())
.execute(wrap);
}
@Override
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -1,34 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* 脚本组件类
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public class ScriptCommonComponent extends NodeComponent implements ScriptComponent {
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
@Override
public void process() throws Exception {
ScriptExecuteWrap wrap = this.buildWrap(this);
ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script, String language) {
LOG.info("load script for component[{}]", getDisplayName());
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -1,48 +1,25 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import java.util.HashMap;
import java.util.Map;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 脚本接口
*
* 脚本组件类
* @author Bryan.Zhang
* @since 2.9.0
* @since 2.6.0
*/
public interface ScriptComponent {
public class ScriptComponent extends NodeComponent{
/**
* 用于维护脚本类型和脚本 cmp 的映射关系
*/
Map<NodeTypeEnum, Class<?>> ScriptComponentClassMap = new HashMap<NodeTypeEnum, Class<?>>() {
{
put(NodeTypeEnum.SCRIPT, ScriptCommonComponent.class);
put(NodeTypeEnum.SWITCH_SCRIPT, ScriptSwitchComponent.class);
put(NodeTypeEnum.BOOLEAN_SCRIPT, ScriptBooleanComponent.class);
put(NodeTypeEnum.FOR_SCRIPT, ScriptForComponent.class);
}
};
private final Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 加载脚本
* @param script
*/
void loadScript(String script, String language);
default ScriptExecuteWrap buildWrap(NodeComponent cmp){
ScriptExecuteWrap wrap = new ScriptExecuteWrap();
wrap.setCurrChainId(cmp.getCurrChainId());
wrap.setNodeId(cmp.getNodeId());
wrap.setSlotIndex(cmp.getSlotIndex());
wrap.setTag(cmp.getTag());
wrap.setCmpData(cmp.getCmpData(Map.class));
wrap.setLoopIndex(cmp.getLoopIndex());
wrap.setLoopObject(cmp.getCurrLoopObj());
wrap.setCmp(cmp);
return wrap;
}
@Override
public void process() throws Exception {
ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
log.info("load script for component[{}]", getDisplayName());
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@ -1,29 +0,0 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import java.util.Map;
/**
* 脚本FOR节点
*
* @author Bryan.Zhang
* @since 2.9.0
*/
public class ScriptForComponent extends NodeForComponent implements ScriptComponent {
@Override
public int processFor() throws Exception {
ScriptExecuteWrap wrap = this.buildWrap(this);
return (int) ScriptExecutorFactory.loadInstance()
.getScriptExecutor(this.getRefNode().getLanguage())
.execute(wrap);
}
@Override
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -0,0 +1,19 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
/**
* 脚本IF节点
* @author Bryan.Zhang
* @since 2.8.5
*/
public class ScriptIfComponent extends NodeIfComponent{
@Override
public boolean processIf() throws Exception {
return (boolean)ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@ -1,29 +1,20 @@
package com.yomahub.liteflow.core;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import java.util.Map;
/**
* 脚本条件节点
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public class ScriptSwitchComponent extends NodeSwitchComponent implements ScriptComponent {
public class ScriptSwitchComponent extends NodeSwitchComponent {
@Override
public String processSwitch() throws Exception {
ScriptExecuteWrap wrap = this.buildWrap(this);
return (String) ScriptExecutorFactory.loadInstance()
.getScriptExecutor(this.getRefNode().getLanguage())
.execute(wrap);
}
@Override
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
@Override
public String processSwitch() throws Exception {
return (String)ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(this.getCurrChainName(), getNodeId(), getSlotIndex());
}
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
}
}

View File

@ -0,0 +1,134 @@
package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.*;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import com.yomahub.liteflow.util.SerialsUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 声明式组件的代理核心生成类
* @author Bryan.Zhang
* @since 2.6.14
*/
public class ComponentProxy {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
private final String nodeId;
private final Object bean;
private final Class<?> clazz;
public ComponentProxy(String nodeId, Object bean, Class<?> clazz) {
this.nodeId = nodeId;
this.bean = bean;
this.clazz = clazz;
}
public Object getProxy() throws Exception{
//这里要判断bean是否是spring代理过的bean如果是代理过的bean需要取到原class对象
Class<?> beanClazz;
if (LiteFlowProxyUtil.isCglibProxyClass(bean.getClass())){
beanClazz = LiteFlowProxyUtil.getUserClass(bean.getClass());
}else{
beanClazz = bean.getClass();
}
//得到当前bean里所覆盖的组件方法(一定是被@LiteFlowMethod修饰的)自己定义的不算
List<String> methodStrList = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
m -> m.getAnnotation(LiteflowMethod.class) != null
).map(m -> {
LiteflowMethod liteflowMethod = m.getAnnotation(LiteflowMethod.class);
return liteflowMethod.value().getMethodName();
}).collect(Collectors.toList());
//创建对象
//这里package进行了重设放到了被代理对象的所在目录
//生成的对象也加了上被代理对象拥有的注解
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
return new ByteBuddy().subclass(clazz)
.name(StrUtil.format("{}.ByteBuddy${}${}",
ClassUtil.getPackage(bean.getClass()),
nodeId,
SerialsUtil.generateShortUUID()))
.method(ElementMatchers.namedOneOf(methodStrList.toArray(new String[]{})))
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
.annotateType(bean.getClass().getAnnotations())
.make()
.load(ComponentProxy.class.getClassLoader())
.getLoaded()
.newInstance();
}
public class AopInvocationHandler implements InvocationHandler {
private final Object bean;
public AopInvocationHandler(Object bean) {
this.bean = bean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里做了2件事情
//先是从普通的bean里过滤出含有@LiteFlowMethod这个标注的方法
//然后进行转换成LiteFlowMethodBean对象List,形成<methodName,Method>键值对的对象
List<LiteFlowMethodBean> liteFlowMethodBeanList = Arrays.stream(ReflectUtil.getMethods(bean.getClass())).filter(m -> {
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return ObjectUtil.isNotNull(liteFlowMethod);
}).map(m -> {
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return new LiteFlowMethodBean(liteFlowMethod.value().getMethodName(), m);
}).collect(Collectors.toList());
//获取当前调用方法是否在被代理的对象方法里面(根据@LiteFlowMethod这个标注去判断)
//如果在里面则返回那个LiteFlowMethodBean不在则返回null
LiteFlowMethodBean liteFlowMethodBean = liteFlowMethodBeanList.stream().filter(
liteFlowMethodBean1 -> liteFlowMethodBean1.getMethodName().equals(method.getName())
).findFirst().orElse(null);
//如果被代理的对象里有此标注标的方法则调用此被代理的对象里的方法如果没有则调用父类里的方法
//beforeProcess和afterProcess这2个方法除外
if (!ListUtil.toList("beforeProcess","afterProcess").contains(liteFlowMethodBean.getMethodName())) {
//进行检查检查被代理的bean里是否有且仅有NodeComponent这个类型的参数
boolean checkFlag = liteFlowMethodBean.getMethod().getParameterTypes().length == 1
&& Arrays.asList(liteFlowMethodBean.getMethod().getParameterTypes()).contains(NodeComponent.class);
if (!checkFlag) {
String errMsg = StrUtil.format("Method[{}.{}] must have NodeComponent parameter(and only one parameter)", bean.getClass().getName(), liteFlowMethodBean.getMethod().getName());
LOG.error(errMsg);
throw new ComponentMethodDefineErrorException(errMsg);
}
try{
return liteFlowMethodBean.getMethod().invoke(bean, proxy);
}catch (Exception e){
InvocationTargetException targetEx = (InvocationTargetException)e;
throw targetEx.getTargetException();
}
}
try{
return liteFlowMethodBean.getMethod().invoke(bean, args);
}catch (Exception e){
InvocationTargetException targetEx = (InvocationTargetException)e;
throw targetEx.getTargetException();
}
}
}
}

View File

@ -1,208 +0,0 @@
package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.exceptions.InvocationTargetRuntimeException;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.exception.ParameterFactException;
import com.yomahub.liteflow.exception.ProxyException;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.util.SerialsUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 声明式组件的代理核心生成类
* @author Bryan.Zhang
* @since 2.11.4
*/
public class DeclComponentProxy {
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
private final DeclWarpBean declWarpBean;
public DeclComponentProxy(DeclWarpBean declWarpBean) {
this.declWarpBean = declWarpBean;
}
public NodeComponent getProxy() throws Exception {
// 获取当前节点的原有注解LiteFlowRetry 之类的规则注解
Annotation[] beanClassAnnotation = declWarpBean.getRawClazz().getAnnotations();
// nodeType去重
List<? extends Class<? extends NodeComponent>> classes = declWarpBean.getMethodWrapBeanList().stream()
.map((Function<MethodWrapBean, Class<? extends NodeComponent>>) methodWrapBean -> methodWrapBean.getLiteflowMethod().nodeType().getMappingClazz())
.distinct()
.collect(Collectors.toList());
// 相同nodeId里只能定义同一种的类型的NodeComponent
boolean legal = classes.size() == 1;
if (!legal) {
throw new LiteFlowException("The cmpClass of the same nodeId must be the same,you declared nodeId:"
+ declWarpBean.getNodeId() + ",cmpClass:" + declWarpBean.getRawClazz());
}
//获取方法上的LiteflowRetry如果有的话一般都是方法级声明式
LiteflowRetry liteflowRetry = declWarpBean.getMethodWrapBeanList().stream().filter(methodWrapBean -> {
LiteflowRetry lfr = methodWrapBean.getLiteflowRetry();
LiteflowMethod lfm = methodWrapBean.getLiteflowMethod();
return lfr != null && lfm.value().isMainMethod();
}).map(MethodWrapBean::getLiteflowRetry).findFirst().orElse(null);
//如果存在LiteflowRetry则加到类上的注解数组中
if (liteflowRetry != null){
beanClassAnnotation = ArrayUtil.append(beanClassAnnotation, liteflowRetry);
}
try {
// 创建对象
// 这里package进行了重设放到了被代理对象的所在目录
// 生成的对象也加了上被代理对象拥有的注解
// 被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
Object instance = new ByteBuddy().subclass(declWarpBean.getNodeType().getMappingClazz())
.name(StrUtil.format("{}$ByteBuddy${}${}", declWarpBean.getRawClazz().getName(), declWarpBean.getNodeId(), SerialsUtil.generateShortUUID()))
.implement(declWarpBean.getRawClazz().getInterfaces())
.method(ElementMatchers.namedOneOf(declWarpBean.getMethodWrapBeanList().stream().map(wrap -> wrap.getLiteflowMethod().value().getMethodName()).toArray(String[]::new)))
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(declWarpBean)))
.annotateType(beanClassAnnotation)
.make()
.load(DeclComponentProxy.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION)
.getLoaded()
.newInstance();
NodeComponent nodeComponent = (NodeComponent) instance;
// 重设nodeId
nodeComponent.setNodeId(declWarpBean.getNodeId());
// 重设nodeName
nodeComponent.setName(declWarpBean.getNodeName());
return nodeComponent;
}
catch (Exception e) {
throw new ProxyException(e);
}
}
public class AopInvocationHandler implements InvocationHandler {
private DeclWarpBean declWarpBean;
public AopInvocationHandler(DeclWarpBean declWarpBean) {
this.declWarpBean = declWarpBean;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取当前调用方法是否在被代理的对象方法里面(根据@LiteFlowMethod这个标注去判断)
// 如果在里面则返回那个LiteFlowMethodBean不在则返回null
MethodWrapBean currentMethodWrapBean = declWarpBean.getMethodWrapBeanList().stream()
.filter(methodWrapBean -> methodWrapBean.getLiteflowMethod().value().getMethodName().equals(method.getName()))
.findFirst()
.orElse(null);
if (currentMethodWrapBean == null){
throw new ProxyException("currentMethodWrapBean is null");
}
// 如果被代理的对象里有此标注标的方法则调用此被代理的对象里的方法如果没有则调用父类里的方法
// 进行检查检查被代理的bean里是否第一个参数为NodeComponent这个类型的
boolean checkFlag = currentMethodWrapBean.getMethod().getParameterTypes().length > 0
&& currentMethodWrapBean.getMethod().getParameterTypes()[0].equals(NodeComponent.class);
if (!checkFlag) {
String errMsg = StrUtil.format(
"Method[{}.{}] must have NodeComponent parameter(first parameter is NodeComponent)",
declWarpBean.getRawClazz().getName(), currentMethodWrapBean.getMethod().getName());
LOG.error(errMsg);
throw new ComponentMethodDefineErrorException(errMsg);
}
// 这里是针对于参数的处理
// 首先需要保证第一个参数是NodeComponent
// 其次需要针对于@LiteflowFact做处理
try {
Object[] realArgs = loadMethodParameter(proxy, currentMethodWrapBean);
return ReflectUtil.invoke(declWarpBean.getRawBean(), currentMethodWrapBean.getMethod(), realArgs);
}catch (InvocationTargetRuntimeException e) {
InvocationTargetException targetEx = (InvocationTargetException) e.getCause();
throw targetEx.getTargetException();
}
}
}
private final ExpressRunner expressRunner = new ExpressRunner();
private Object[] loadMethodParameter(Object proxy, MethodWrapBean methodWrapBean){
NodeComponent thisNodeComponent = (NodeComponent) proxy;
return methodWrapBean.getParameterWrapBeanList().stream().map(parameterWrapBean -> {
// 如果参数是NodeComponent那就返回proxy本身
if (parameterWrapBean.getParameterType().isAssignableFrom(NodeComponent.class)) {
return proxy;
}
// 如果没有@LiteflowFact标注那么不处理直接赋值null
if (parameterWrapBean.getFact() == null) {
return null;
}
// 把上下文数据转换成map形式的key为别名value为上下文
Map<String, Object> contextMap = DataBus.getSlot(thisNodeComponent.getSlotIndex()).getContextBeanList().stream().collect(
Collectors.toMap(tuple -> tuple.get(0), tuple -> tuple.get(1))
);
List<String> errorList = new ArrayList<>();
Object result = null;
// 根据表达式去上下文里搜索相匹配的数据
for(Map.Entry<String, Object> entry : contextMap.entrySet()){
try{
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(entry.getKey() + "." + parameterWrapBean.getFact().value());
DefaultContext<String, Object> context = new DefaultContext<>();
context.put(entry.getKey(), entry.getValue());
result = expressRunner.execute(instructionSet, context, errorList, false, false);
if (result != null){
break;
}
}catch (Exception ignore){}
}
if (result == null){
try{
// 如果没有搜到那么尝试推断表达式是指定的上下文按照指定上下文的方式去再获取
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache("contextMap." + parameterWrapBean.getFact().value());
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("contextMap", contextMap);
result = expressRunner.execute(instructionSet, context, errorList, false, false);
}catch (Exception ignore){}
}
return result;
}).toArray();
}
}

View File

@ -1,74 +0,0 @@
package com.yomahub.liteflow.core.proxy;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import java.lang.reflect.Method;
import java.util.List;
/**
* 声明式组件BeanDefinition的包装类
* @author Bryan.Zhang
* @since 2.11.4
*/
public class DeclWarpBean {
private String nodeId;
private String nodeName;
private NodeTypeEnum nodeType;
private Object rawBean;
private Class<?> rawClazz;
private List<MethodWrapBean> methodWrapBeanList;
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public Object getRawBean() {
return rawBean;
}
public void setRawBean(Object rawBean) {
this.rawBean = rawBean;
}
public List<MethodWrapBean> getMethodWrapBeanList() {
return methodWrapBeanList;
}
public void setMethodWrapBeanList(List<MethodWrapBean> methodWrapBeanList) {
this.methodWrapBeanList = methodWrapBeanList;
}
public Class<?> getRawClazz() {
return rawClazz;
}
public void setRawClazz(Class<?> rawClazz) {
this.rawClazz = rawClazz;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
public NodeTypeEnum getNodeType() {
return nodeType;
}
public void setNodeType(NodeTypeEnum nodeType) {
this.nodeType = nodeType;
}
}

View File

@ -2,34 +2,30 @@ package com.yomahub.liteflow.core.proxy;
import java.lang.reflect.Method;
/**
* @author Bryan.Zhang
*/
public class LiteFlowMethodBean {
private String methodName;
private String methodName;
private Method method;
private Method method;
public LiteFlowMethodBean(String methodName, Method method) {
this.methodName = methodName;
this.method = method;
}
public LiteFlowMethodBean(String methodName, Method method) {
this.methodName = methodName;
this.method = method;
}
public String getMethodName() {
return methodName;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}

View File

@ -1,69 +0,0 @@
package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ComponentProxyErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import java.util.Arrays;
/**
* 组件代理类通用方法 主要用于声明式组件
*
* @author Bryan.Zhang
* @since 2.6.14
*/
public class LiteFlowProxyUtil {
private static final LFLog LOG = LFLoggerManager.getLogger(LiteFlowProxyUtil.class);
/**
* 判断一个bean是否是声明式组件
*/
public static boolean isDeclareCmp(Class<?> clazz) {
// 查看bean里的method是否有方法标记了@LiteflowMethod标注
// 这里的bean有可能是cglib加强过的class所以要先进行个判断
Class<?> targetClass = getUserClass(clazz);
// 判断是否有方法标记了@LiteflowMethod标注有则为声明式组件
return Arrays.stream(targetClass.getMethods())
.anyMatch(method -> method.getAnnotation(LiteflowMethod.class) != null);
}
/**
* 对一个满足声明式的bean进行代理,生成代理类
*/
public static NodeComponent proxy2NodeComponent(DeclWarpBean declWarpBean) {
try {
DeclComponentProxy proxy = new DeclComponentProxy(declWarpBean);
return proxy.getProxy();
}catch (LiteFlowException liteFlowException) {
throw liteFlowException;
}catch (Exception e) {
String errMsg = StrUtil.format("Error while proxying bean[{}]", declWarpBean.getRawClazz().getName());
LOG.error(errMsg);
throw new ComponentProxyErrorException(errMsg);
}
}
public static boolean isCglibProxyClass(Class<?> clazz) {
return (clazz != null && isCglibProxyClassName(clazz.getName()));
}
public static Class<?> getUserClass(Class<?> clazz) {
if (clazz.getName().contains("$$")) {
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
return superclass;
}
}
return clazz;
}
private static boolean isCglibProxyClassName(String className) {
return (className != null && className.contains("$$"));
}
}

View File

@ -1,62 +0,0 @@
package com.yomahub.liteflow.core.proxy;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import java.lang.reflect.Method;
import java.util.List;
/**
* LiteflowMethod的包装类
* @author Bryan.Zhang
* @since 2.11.4
*/
public class MethodWrapBean {
private Method method;
private LiteflowMethod liteflowMethod;
private LiteflowRetry liteflowRetry;
private List<ParameterWrapBean> parameterWrapBeanList;
public MethodWrapBean(Method method, LiteflowMethod liteflowMethod, LiteflowRetry liteflowRetry, List<ParameterWrapBean> parameterWrapBeanList) {
this.method = method;
this.liteflowMethod = liteflowMethod;
this.liteflowRetry = liteflowRetry;
this.parameterWrapBeanList = parameterWrapBeanList;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public LiteflowMethod getLiteflowMethod() {
return liteflowMethod;
}
public void setLiteflowMethod(LiteflowMethod liteflowMethod) {
this.liteflowMethod = liteflowMethod;
}
public LiteflowRetry getLiteflowRetry() {
return liteflowRetry;
}
public void setLiteflowRetry(LiteflowRetry liteflowRetry) {
this.liteflowRetry = liteflowRetry;
}
public List<ParameterWrapBean> getParameterWrapBeanList() {
return parameterWrapBeanList;
}
public void setParameterWrapBeanList(List<ParameterWrapBean> parameterWrapBeanList) {
this.parameterWrapBeanList = parameterWrapBeanList;
}
}

View File

@ -1,47 +0,0 @@
package com.yomahub.liteflow.core.proxy;
import com.yomahub.liteflow.annotation.LiteflowFact;
/**
* 声明式的包装类
* @author Bryan.Zhang
* @since 2.12.1
*/
public class ParameterWrapBean {
private Class<?> parameterType;
private LiteflowFact fact;
private int index;
public ParameterWrapBean(Class<?> parameterType, LiteflowFact fact, int index) {
this.parameterType = parameterType;
this.fact = fact;
this.index = index;
}
public Class<?> getParameterType() {
return parameterType;
}
public void setParameterType(Class<?> parameterType) {
this.parameterType = parameterType;
}
public LiteflowFact getFact() {
return fact;
}
public void setFact(LiteflowFact fact) {
this.fact = fact;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -1,14 +0,0 @@
package com.yomahub.liteflow.enums;
/**
* Chain执行模式分为两种
* 第一种是执行Chain的本体EL为BODY
* 第二种是执行Chain的决策路由为ROUTE
*
* @author Bryan.Zhang
* @since 2.12.0
*/
public enum ChainExecuteModeEnum {
BODY,ROUTE
}

View File

@ -9,11 +9,10 @@ package com.yomahub.liteflow.enums;
/**
* 组件步骤类型
*
* @author Bryan.Zhang
*/
public enum CmpStepTypeEnum {
START, END, SINGLE;
START,
END,
SINGLE;
}

View File

@ -1,68 +1,44 @@
package com.yomahub.liteflow.enums;
/**
* @author Yun
*/
public enum ConditionTypeEnum {
TYPE_THEN("then","then"),
TYPE_WHEN("when","when"),
TYPE_SWITCH("switch", "switch"),
TYPE_THEN("then", "then"),
TYPE_IF("if", "if"),
TYPE_PRE("pre","pre"),
TYPE_FINALLY("finally","finally")
;
private String type;
private String name;
TYPE_WHEN("when", "when"),
ConditionTypeEnum(String type, String name) {
this.type = type;
this.name = name;
}
TYPE_SWITCH("switch", "switch"),
public String getType() {
return type;
}
TYPE_IF("if", "if"),
public void setType(String type) {
this.type = type;
}
TYPE_PRE("pre", "pre"),
public String getName() {
return name;
}
TYPE_FINALLY("finally", "finally"),
TYPE_FOR("for", "for"),
TYPE_WHILE("while", "while"),
TYPE_ITERATOR("iterator", "iterator"),
TYPE_CATCH("catch", "catch"),
TYPE_AND_OR_OPT("and_or_opt", "and_or_opt"),
TYPE_NOT_OPT("not_opt", "not_opt"),
TYPE_ABSTRACT("abstract", "abstract");
private String type;
private String name;
ConditionTypeEnum(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static ConditionTypeEnum getEnumByCode(String code) {
for (ConditionTypeEnum e : ConditionTypeEnum.values()) {
if (e.getType().equals(code)) {
return e;
}
}
return null;
}
public void setName(String name) {
this.name = name;
}
public static ConditionTypeEnum getEnumByCode(String code) {
for (ConditionTypeEnum e : ConditionTypeEnum.values()) {
if (e.getType().equals(code)) {
return e;
}
}
return null;
}
}

View File

@ -0,0 +1,16 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.enums;
/**
* 可执行节点枚举
* @author Bryan.Zhang
*/
public enum ExecuteTypeEnum {
CHAIN,CONDITION,NODE
}

View File

@ -1,19 +0,0 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.enums;
/**
* 可执行节点枚举
*
* @author Bryan.Zhang
*/
public enum ExecuteableTypeEnum {
CHAIN, CONDITION, NODE
}

View File

@ -5,33 +5,35 @@ package com.yomahub.liteflow.enums;
* @since 2.5.0
*/
public enum FlowParserTypeEnum {
TYPE_XML("xml", "xml"),
TYPE_YML("yml", "yml"),
TYPE_JSON("json", "json"),
TYPE_EL_XML("el_xml", "el_xml"),
TYPE_EL_JSON("el_json", "el_json"),
TYPE_EL_YML("el_yml", "el_yml")
;
private String type;
private String name;
TYPE_XML("xml", "xml"), TYPE_YML("yml", "yml"), TYPE_JSON("json", "json"), TYPE_EL_XML("el_xml", "el_xml"),
TYPE_EL_JSON("el_json", "el_json"), TYPE_EL_YML("el_yml", "el_yml");
FlowParserTypeEnum(String type, String name) {
this.type = type;
this.name = name;
}
private String type;
public String getType() {
return type;
}
private String name;
public void setType(String type) {
this.type = type;
}
FlowParserTypeEnum(String type, String name) {
this.type = type;
this.name = name;
}
public String getName() {
return name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -2,17 +2,14 @@ package com.yomahub.liteflow.enums;
/**
* 隐式流程类型
*
* @author Bryan.Zhang
* @since 2.8.5
*/
public enum InnerChainTypeEnum {
// 不是隐式chain
NONE,
// 在串行环境中执行
IN_SYNC,
// 在并行环境中执行
IN_ASYNC
//不是隐式chain
NONE,
//在串行环境中执行
IN_SYNC,
//在并行环境中执行
IN_ASYNC
}

View File

@ -1,57 +1,35 @@
package com.yomahub.liteflow.enums;
public enum LiteFlowMethodEnum {
PROCESS("process"),
PROCESS_SWITCH("processSwitch"),
PROCESS_IF("processIf"),
IS_ACCESS("isAccess"),
PROCESS("process", true),
PROCESS_SWITCH("processSwitch", true),
PROCESS_BOOLEAN("processBoolean", true),
PROCESS_FOR("processFor", true),
IS_END("isEnd"),
IS_CONTINUE_ON_ERROR("isContinueOnError"),
PROCESS_ITERATOR("processIterator", true),
GET_NODE_EXECUTOR_CLASS("getNodeExecutorClass"),
IS_ACCESS("isAccess", false),
ON_SUCCESS("onSuccess"),
IS_END("isEnd", false),
IS_CONTINUE_ON_ERROR("isContinueOnError", false),
ON_ERROR("onError"),
GET_NODE_EXECUTOR_CLASS("getNodeExecutorClass", false),
BEFORE_PROCESS("beforeProcess"),
ON_SUCCESS("onSuccess", false),
AFTER_PROCESS("afterProcess");
ON_ERROR("onError", false),
private String methodName;
BEFORE_PROCESS("beforeProcess", false),
LiteFlowMethodEnum(String methodName){
this.methodName = methodName;
}
AFTER_PROCESS("afterProcess", false),
GET_DISPLAY_NAME("getDisplayName", false),
ROLLBACK("rollback", false)
;
private String methodName;
private boolean isMainMethod;
LiteFlowMethodEnum(String methodName, boolean isMainMethod) {
this.methodName = methodName;
this.isMainMethod = isMainMethod;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public boolean isMainMethod() {
return isMainMethod;
}
public void setMainMethod(boolean mainMethod) {
isMainMethod = mainMethod;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
}

View File

@ -1,194 +1,52 @@
package com.yomahub.liteflow.enums;
import cn.hutool.core.annotation.AnnotationUtil;
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.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* 节点类型枚举
*
* @author Bryan.Zhang
* @since 2.6.0
*/
public enum NodeTypeEnum {
COMMON("common", "普通", false, NodeComponent.class),
COMMON("common","普通"),
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
SWITCH("switch", "选择"),
BOOLEAN("boolean", "布尔", false, NodeBooleanComponent.class),
IF("if", "条件"),
SCRIPT("script","脚本"),
SWITCH_SCRIPT("switch_script","选择脚本"),
FOR("for", "循环次数", false, NodeForComponent.class),
IF_SCRIPT("if_script", "条件脚本")
;
private String code;
private String name;
ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class),
NodeTypeEnum(String code, String name) {
this.code = code;
this.name = name;
}
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
public String getCode() {
return code;
}
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
public void setCode(String code) {
this.code = code;
}
BOOLEAN_SCRIPT("boolean_script", "布尔脚本", true, ScriptBooleanComponent.class),
public String getName() {
return name;
}
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
FALLBACK("fallback", "降级", false, null);
private static final LFLog LOG = LFLoggerManager.getLogger(NodeTypeEnum.class);
private String code;
private String name;
private boolean isScript;
private Class<? extends NodeComponent> mappingClazz;
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
this.code = code;
this.name = name;
this.isScript = isScript;
this.mappingClazz = mappingClazz;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isScript() {
return isScript;
}
public void setScript(boolean script) {
isScript = script;
}
public Class<? extends NodeComponent> getMappingClazz() {
return mappingClazz;
}
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
this.mappingClazz = mappingClazz;
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
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;
}
}
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getMappingClazz() != null && e.getMappingClazz().equals(superClazz)) {
return e;
}
}
return null;
}
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;
}
});
// 表示存在不一样的类型
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();
}
// 再尝试声明式组件这部分的推断
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;
}
public void setName(String name) {
this.name = name;
}
public static NodeTypeEnum getEnumByCode(String code) {
for (NodeTypeEnum e : NodeTypeEnum.values()) {
if (e.getCode().equals(code)) {
return e;
}
}
return null;
}
}

View File

@ -1,57 +0,0 @@
package com.yomahub.liteflow.enums;
import com.yomahub.liteflow.flow.parallel.strategy.AllOfParallelExecutor;
import com.yomahub.liteflow.flow.parallel.strategy.AnyOfParallelExecutor;
import com.yomahub.liteflow.flow.parallel.strategy.ParallelStrategyExecutor;
import com.yomahub.liteflow.flow.parallel.strategy.SpecifyParallelExecutor;
/**
* 并行策略枚举类
*
* @author luo yi
* @since 2.11.0
*/
public enum ParallelStrategyEnum {
ANY("anyOf", "完成任一任务", AnyOfParallelExecutor.class),
ALL("allOf", "完成全部任务", AllOfParallelExecutor.class),
SPECIFY("must", "完成指定 ID 任务", SpecifyParallelExecutor.class);
private String strategyType;
private String description;
private Class<? extends ParallelStrategyExecutor> clazz;
ParallelStrategyEnum(String strategyType, String description, Class<? extends ParallelStrategyExecutor> clazz) {
this.strategyType = strategyType;
this.description = description;
this.clazz = clazz;
}
public String getStrategyType() {
return strategyType;
}
public void setStrategyType(String strategyType) {
this.strategyType = strategyType;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Class<? extends ParallelStrategyExecutor> getClazz() {
return clazz;
}
public void setClazz(Class<? extends ParallelStrategyExecutor> clazz) {
this.clazz = clazz;
}
}

View File

@ -1,17 +0,0 @@
package com.yomahub.liteflow.enums;
/**
* 解析模式
* PARSE_ALL_ON_START 启动时解析所有的规则
* PARSE_ALL_ON_FIRST_EXEC 第一次执行链路时解析所有的规则
* PARSE_ONE_ON_FIRST_EXEC 第一次执行相关链路时解析当前的规则
*
* @author Bryan.Zhang
* @since 2.12.0
*/
public enum ParseModeEnum {
PARSE_ALL_ON_START,
PARSE_ALL_ON_FIRST_EXEC,
PARSE_ONE_ON_FIRST_EXEC
}

View File

@ -1,63 +0,0 @@
package com.yomahub.liteflow.enums;
public enum ScriptTypeEnum {
CUSTOM("custom", "custom"),
GROOVY("groovy", "groovy"),
QLEXPRESS("qlexpress", "qlexpress"),
JS("javascript", "js"),
PYTHON("python", "python"),
LUA("luaj", "lua"),
AVIATOR("AviatorScript", "aviator"),
JAVA("java", "java"),
KOTLIN("kotlin", "kotlin");
private String engineName;
private String displayName;
ScriptTypeEnum(String engineName, String displayName) {
this.engineName = engineName;
this.displayName = displayName;
}
public String getEngineName() {
return engineName;
}
public void setEngineName(String engineName) {
this.engineName = engineName;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public static ScriptTypeEnum getEnumByDisplayName(String displayName) {
for (ScriptTypeEnum e : ScriptTypeEnum.values()) {
if (e.getDisplayName().equals(displayName)) {
return e;
}
}
return null;
}
/**
* 校验脚本类型是否合法
* @param scriptType 脚本类型
* @return true:合法false:不合法
*/
public static boolean checkScriptType(String scriptType) {
for (ScriptTypeEnum e : ScriptTypeEnum.values()) {
if (e.getDisplayName().equals(scriptType)) {
return true;
}
}
return false;
}
}

View File

@ -1,25 +0,0 @@
package com.yomahub.liteflow.exception;
public class AndOrConditionException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public AndOrConditionException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -1,30 +0,0 @@
package com.yomahub.liteflow.exception;
/**
* 类型错误异常
*
* @author Yun
*/
public class CatchErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public CatchErrorException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -7,24 +7,24 @@ package com.yomahub.liteflow.exception;
*/
public class ChainDuplicateException extends RuntimeException {
private static final long serialVersionUID = 1L;
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
/**
* 异常信息
*/
private String message;
public ChainDuplicateException(String message) {
this.message = message;
}
public ChainDuplicateException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -1,11 +1,6 @@
package com.yomahub.liteflow.exception;
/**
* 链端异常
*
* @author Bryan.Zhang
*/
public class ChainEndException extends RuntimeException {
private static final long serialVersionUID = 1L;
@ -25,5 +20,4 @@ public class ChainEndException extends RuntimeException {
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -1,10 +1,5 @@
package com.yomahub.liteflow.exception;
/**
* 链端不存在
*
* @author Bryan.Zhang
*/
public class ChainNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
@ -24,5 +19,4 @@ public class ChainNotFoundException extends RuntimeException {
public void setMessage(String message) {
this.message = message;
}
}

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