Compare commits
No commits in common. "master" and "v2.8.0" have entirely different histories.
|
|
@ -80,4 +80,4 @@ atlassian-ide-plugin.xml
|
|||
/logs
|
||||
*/logs
|
||||
|
||||
.flattened-pom.xml
|
||||
/payClear-timer/.tern-project
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
java-baseline=8
|
||||
93
README.md
93
README.md
|
|
@ -1,25 +1,17 @@
|
|||
<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>
|
||||
|
||||
[中文](README.zh-CN.md)
|
||||
|
||||
<h3>Your star is my motivation to keep going. If you like LiteFlow, please help me with a star in the upper right corner.</h3>
|
||||
|
||||
## 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 is a lightweight , fast component-based process engine framework / rules engine , component orchestration , component reuse , to help decouple business code , so that each business fragment is an elegant component , and support hot-loading rule configuration , to achieve on-the-fly modification .
|
||||
|
||||
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 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.
|
||||
|
||||
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/)
|
||||
## Demo projects: [DEMO1](https://github.com/bryan31/message-demo) | [DEMO2](https://gitee.com/bryan31/liteflow-example)
|
||||
## Website url: [Click here to access the official website](https://liteflow.yomahub.com/)
|
||||
## Documents url: [Click here to enter the documentation to learn](https://liteflow.yomahub.com/pages/5816c5/)
|
||||
## Example projects: [Click here to enter the sample project](https://gitee.com/bryan31/liteflow-example)
|
||||
|
||||
## Feature
|
||||
* **Component definition unified:** All logic is a component, for all logic to provide a unified component implementation, small size, large energy.
|
||||
|
|
@ -30,8 +22,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.
|
||||
|
|
@ -41,79 +32,11 @@ You can find out how to join the community on the official website!
|
|||
* **Excellent performance:** the framework itself consumes little extra performance, depending on the efficiency of your component execution.
|
||||
* **Built-in simple monitoring:** Built-in a command line monitoring framework, you can know the running time ranking of each component.
|
||||
|
||||
## What scenarios apply
|
||||
|
||||
LiteFlow is a choreographed rules engine that is best at decoupling your systems. If you have a complex system with bloated code, the LiteFlow framework is a great solution.
|
||||
|
||||
LiteFlow uses regular expressions to drive the engine that you define. Have you ever thought about how to write multithreaded process choreography like the following?
|
||||
|
||||
<img src="static/img/flow_e1.svg" style="zoom: 80%">
|
||||
<img src="static/img/flow_e2.svg" style="zoom: 80%">
|
||||
|
||||
These processes can be easily solved with LiteFlow! The framework has a very low threshold to learn the expression language, but it can accomplish extremely high complexity of the choreography.
|
||||
|
||||
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!
|
||||
|
||||
Looking forward to your use!
|
||||
|
||||
**DISCORD LINK**
|
||||
|
||||
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
|
||||
|
||||

|
||||

|
||||
|
||||
Open source is not easy, please sponsor LiteFlow if you support it
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +1,28 @@
|
|||
<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是一个轻量,快速的组件式流程引擎框架/规则引擎,组件编排,组件复用,帮助解耦业务代码,让每一个业务片段都是一个优雅的组件,并支持热加载规则配置,实现即时修改。
|
||||
|
||||
LiteFlow于2020年正式开源,直到现在已经是国内开源规则引擎中不可忽视的存在,而且最关键的是,LiteFlow还是一个持续高速迭代的开源项目。
|
||||
## 官网链接:[点这里进入官网](https://liteflow.yomahub.com/)
|
||||
## 文档链接:[点这里进入文档进行学习](https://liteflow.yomahub.com/pages/5816c5/)
|
||||
## 示例工程:[点这里进入示例工程项目](https://gitee.com/bryan31/liteflow-example)
|
||||
|
||||
LiteFlow是一个由社区驱动的项目,我们非常重视社区建设,拥有一个5000多人的使用者社区,在使用中碰到任何问题或者建议都可以在社区中反应。
|
||||
|
||||
你在官网中可以找到加入社区的方式!
|
||||
|
||||
## 官网链接:[点这里进入官网](https://liteflow.cc/)
|
||||
## 文档链接:[点这里进入文档进行学习](https://liteflow.cc/pages/5816c5/)
|
||||
## 示例工程:[DEMO1](https://github.com/bryan31/message-demo) | [DEMO2](https://gitee.com/bryan31/liteflow-example)
|
||||
|
||||
## 🍬特性
|
||||
## 特性
|
||||
* **组件定义统一:** 所有的逻辑都是组件,为所有的逻辑提供统一化的组件实现方式,小身材,大能量。
|
||||
* **规则轻量:** 基于规则文件来编排流程,学习规则入门只需要5分钟,一看既懂。
|
||||
* **规则多样化:** 规则支持xml、json、yml三种规则文件写法方式,喜欢哪种用哪个。
|
||||
* **任意编排:** 再复杂的逻辑过程,利用LiteFlow的规则,都是很容易做到的,看规则文件就能知道逻辑是如何运转的。
|
||||
* **规则持久化:** 框架原生支持把规则存储在标准结构化数据库,Nacos,Etcd,Zookeeper,Apollo,redis。您也可以自己扩展,把规则存储在任何地方。
|
||||
* **任意编排:** 同步异步混编,再复杂的逻辑过程,利用LiteFlow的规则,都是很容易做到的,看规则文件就能知道逻辑是如何运转的。
|
||||
* **规则能从任意地方加载:** 框架中提供本地文件配置源和zk配置源的实现,也提供了扩展接口,您可以把规则存储在任何地方。
|
||||
* **优雅热刷新机制:** 规则变化,无需重启您的应用,即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。
|
||||
* **支持广泛:** 不管你的项目是不是基于Springboot,Spring还是任何其他java框架构建,LiteFlow都能游刃有余。
|
||||
* **JDK支持:** 从JDK8到JDK17,全部支持。无需担心JDK版本。
|
||||
* **Springboot支持全面:** 支持Springboot 2.X到最新的Springboot 3.X。
|
||||
* **脚本语言支持:** 可以定义脚本语言节点,支持Groovy,Javascript,QLExpress,Python,Lua,Aviator,Java,Kotlin。未来还会支持更多的脚本语言。
|
||||
* **脚本和Java全打通:** 所有脚本语言均可调用Java方法,甚至于可以引用任意的实例,在脚本中调用RPC也是支持的。
|
||||
* **JDK支持:** 从JDK8到JDK17,统统支持。无需担心JDK版本。
|
||||
* **脚本语言支持:** 可以定义脚本语言节点,支持QLExpress和Groovy两种脚本。未来还会支持更多的脚本语言。
|
||||
* **规则嵌套支持:** 只要你想的出,你可以利用简单的表达式完成多重嵌套的复杂逻辑编排。
|
||||
* **组件重试支持:** 组件可以支持重试,每个组件均可自定义重试配置和指定异常。
|
||||
* **上下文隔离机制:** 可靠的上下文隔离机制,你无需担心高并发情况下的数据串流。
|
||||
|
|
@ -40,75 +32,12 @@ LiteFlow是一个由社区驱动的项目,我们非常重视社区建设,拥
|
|||
* **性能卓越:** 框架本身几乎不消耗额外性能,性能取决你的组件执行效率。
|
||||
* **自带简单监控:** 框架内自带一个命令行的监控,能够知道每个组件的运行耗时排行。
|
||||
|
||||
## ☘️什么场景适用
|
||||
|
||||
LiteFlow是一款编排式的规则引擎,最擅长去解耦你的系统,如果你的系统业务复杂,并且代码臃肿不堪,那LiteFlow框架会是一个非常好的解决方案。
|
||||
|
||||
LiteFlow利用规则表达式为驱动引擎,去驱动你定义的组件。你有想过类似以下的多线程流程编排该如何写吗?
|
||||
|
||||
<img src="static/img/flow_e1.svg" style="zoom: 80%">
|
||||
<img src="static/img/flow_e2.svg" style="zoom: 80%">
|
||||
|
||||
这一切利用LiteFlow轻而易举!框架的表达式语言学习门槛很低,但是却可以完成超高复杂度的编排。
|
||||
|
||||
LiteFlow拥有极其详细易懂的文档体系,能帮助你解决在使用框架的时候95%以上的问题。
|
||||
|
||||
目前为止,LiteFlow拥有1500多个测试用例,并且不断在增加中。完备的文档+覆盖全面的测试用例保障了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人,需要邀请入群。关注公众号后点击`个人微信`加我,我可以拉你入群
|
||||
|
||||

|
||||
|
||||
开源不易,支持就请赞助LiteFlow,请我喝一杯咖啡吧
|
||||
开源不易,支持就请赞助LiteFlow
|
||||
|
||||
<img src="static/img/support.png" alt="support" width="258" />
|
||||
|
|
|
|||
|
|
@ -9,11 +9,16 @@
|
|||
<parent>
|
||||
<groupId>com.yomahub</groupId>
|
||||
<artifactId>liteflow</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.yomahub</groupId>
|
||||
<artifactId>liteflow-script-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
|
|
@ -23,8 +28,8 @@
|
|||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
|
|
@ -34,6 +39,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 +73,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>
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 "";
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
package com.yomahub.liteflow.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
public @interface LiteflowSwitchCmpDefine {
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.util.StrUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
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 = Lists.newArrayList();
|
||||
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 &&
|
||||
CollectionUtil.getLast(this.conditionList).getGroup().equals(condition.getGroup())) {
|
||||
CollectionUtil.getLast(this.conditionList).getExecutableList().addAll(condition.getExecutableList());
|
||||
} else {
|
||||
this.conditionList.add(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,180 +1,134 @@
|
|||
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.google.common.collect.Lists;
|
||||
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;
|
||||
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 createCommonCondNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createBooleanNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN);
|
||||
}
|
||||
public static LiteFlowNodeBuilder createScriptNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createForNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR);
|
||||
}
|
||||
public static LiteFlowNodeBuilder createScriptSwitchNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH_SCRIPT);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createIteratorNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.ITERATOR);
|
||||
}
|
||||
public LiteFlowNodeBuilder() {
|
||||
this.node = new Node();
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
|
||||
}
|
||||
public LiteFlowNodeBuilder(NodeTypeEnum type) {
|
||||
this.node = new Node();
|
||||
this.node.setType(type);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptSwitchNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH_SCRIPT);
|
||||
}
|
||||
public LiteFlowNodeBuilder setId(String nodeId) {
|
||||
if (StrUtil.isBlank(nodeId)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setId(nodeId.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptBooleanNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN_SCRIPT);
|
||||
}
|
||||
public LiteFlowNodeBuilder setName(String name) {
|
||||
if (StrUtil.isBlank(name)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setName(name.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptForNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR_SCRIPT);
|
||||
}
|
||||
public LiteFlowNodeBuilder setClazz(String clazz) {
|
||||
if (StrUtil.isBlank(clazz)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setClazz(clazz.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder() {
|
||||
this.node = new Node();
|
||||
}
|
||||
public LiteFlowNodeBuilder setClazz(Class<?> clazz) {
|
||||
assert clazz != null;
|
||||
setClazz(clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder(NodeTypeEnum type) {
|
||||
this.node = new Node();
|
||||
this.node.setType(type);
|
||||
}
|
||||
public LiteFlowNodeBuilder setType(NodeTypeEnum type) {
|
||||
this.node.setType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setId(String nodeId) {
|
||||
if (StrUtil.isBlank(nodeId)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setId(nodeId.trim());
|
||||
return this;
|
||||
}
|
||||
public LiteFlowNodeBuilder setScript(String script) {
|
||||
this.node.setScript(script);
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setName(String name) {
|
||||
if (StrUtil.isBlank(name)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setName(name.trim());
|
||||
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 setClazz(String clazz) {
|
||||
if (StrUtil.isBlank(clazz)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setClazz(clazz.trim());
|
||||
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.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());
|
||||
}
|
||||
} 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 setClazz(Class<?> clazz) {
|
||||
assert clazz != null;
|
||||
setClazz(clazz.getName());
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setType(NodeTypeEnum type) {
|
||||
this.node.setType(type);
|
||||
return this;
|
||||
}
|
||||
|
||||
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 = Lists.newArrayList();
|
||||
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, ",", "[", "]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package com.yomahub.liteflow.builder;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.common.LocalDefaultFlowConstant;
|
||||
import com.yomahub.liteflow.enums.ConditionTypeEnum;
|
||||
import com.yomahub.liteflow.flow.element.condition.Condition;
|
||||
|
||||
/**
|
||||
* WhenCondition基于代码形式的组装器
|
||||
* 这个为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){
|
||||
this.condition.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){
|
||||
if (StrUtil.isBlank(group)){
|
||||
this.condition.setGroup(LocalDefaultFlowConstant.DEFAULT);
|
||||
}else{
|
||||
this.condition.setGroup(group);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowWhenConditionBuilder setAny(boolean any){
|
||||
this.condition.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){
|
||||
if (StrUtil.isBlank(executorServiceName)) {
|
||||
return this;
|
||||
}
|
||||
this.condition.setThreadExecutorClass(executorServiceName);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,408 +1,156 @@
|
|||
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.collection.CollectionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
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.enums.ConditionTypeEnum;
|
||||
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;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 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.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());
|
||||
}
|
||||
|
||||
// 在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, false, 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 (Exception e){
|
||||
for (String scriptErrorMsg : errorList){
|
||||
LOG.error("\n{}", scriptErrorMsg);
|
||||
}
|
||||
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,再加上 "^" 的长度 1,indexOf 从 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,再加上 "," 的长度 1,indexOf 从 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,再加上 "^" 的长度 1,indexOf 从 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 = Lists.newArrayList();
|
||||
if (StrUtil.isBlank(this.chain.getChainName())) {
|
||||
errorList.add("name is blank");
|
||||
}
|
||||
if (CollUtil.isNotEmpty(errorList)) {
|
||||
throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.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 Exception();
|
||||
}
|
||||
|
||||
Boolean any = OperatorHelper.convert(objects[1], Boolean.class);
|
||||
whenCondition.setParallelStrategy(any ? ParallelStrategyEnum.ANY : ParallelStrategyEnum.ALL);
|
||||
return whenCondition;
|
||||
}
|
||||
if (objects.length != 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
WhenCondition whenCondition;
|
||||
if (objects[0] instanceof WhenCondition){
|
||||
whenCondition = (WhenCondition) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be when condition item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
boolean any = false;
|
||||
if (objects[1] instanceof Boolean){
|
||||
any = Boolean.parseBoolean(objects[1].toString());
|
||||
}else{
|
||||
LOG.error("the parameter must be boolean type!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
whenCondition.setAny(any);
|
||||
|
||||
return whenCondition;
|
||||
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,53 +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.IfCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的ELIF的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.5
|
||||
*/
|
||||
public class ElifOperator extends BaseOperator<IfCondition> {
|
||||
|
||||
@Override
|
||||
public IfCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqThree(objects);
|
||||
|
||||
// 解析caller
|
||||
String errorMsg = "The caller must be IfCondition item";
|
||||
IfCondition ifCondition = OperatorHelper.convert(objects[0], IfCondition.class, errorMsg);
|
||||
|
||||
// 解析第一个参数
|
||||
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[1]);
|
||||
Executable ifItem = OperatorHelper.convert(objects[1], Executable.class);
|
||||
|
||||
// 解析第二个参数
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(objects[2]);
|
||||
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
|
||||
|
||||
// 构建一个内部的IfCondition
|
||||
IfCondition ifConditionItem = new IfCondition();
|
||||
ifConditionItem.setIfItem(ifItem);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.flow.element.Executable;
|
||||
import com.yomahub.liteflow.flow.element.condition.IfCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的ELSE的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.5
|
||||
*/
|
||||
public class ElseOperator extends BaseOperator<IfCondition> {
|
||||
|
||||
@Override
|
||||
public IfCondition build(Object[] objects) throws Exception {
|
||||
// 参数只能是1个,但这里为什么是2个呢?第一个是caller,第二个才是参数
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
String errorMsg = "The caller must be IfCondition item";
|
||||
IfCondition ifCondition = OperatorHelper.convert(objects[0], IfCondition.class, errorMsg);
|
||||
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
|
||||
Executable elseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
|
||||
|
||||
// 因为当中可能会有多个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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,30 +1,42 @@
|
|||
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.ql.util.express.Operator;
|
||||
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 Object executeInner(Object[] objects) throws Exception {
|
||||
try{
|
||||
if (objects.length <= 0){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
FinallyCondition finallyCondition = new FinallyCondition();
|
||||
for (Object obj : objects){
|
||||
if (obj instanceof Executable){
|
||||
finallyCondition.addExecutable((Executable)obj);
|
||||
}else{
|
||||
LOG.error("parameter must be executable item!");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
return finallyCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +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.flow.element.Condition;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.ql.util.express.Operator;
|
||||
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 Exception();
|
||||
}
|
||||
|
||||
String id = OperatorHelper.convert(objects[1], String.class);
|
||||
if (objects.length != 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
condition.setId(id);
|
||||
Condition condition;
|
||||
if (objects[0] instanceof Condition){
|
||||
condition = (Condition) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be condition item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
String id;
|
||||
if (objects[1] instanceof String){
|
||||
id = objects[1].toString();
|
||||
}else{
|
||||
LOG.error("the parameter must be String type!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
condition.setId(id);
|
||||
|
||||
return condition;
|
||||
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,43 +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.IfCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的IF的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.5
|
||||
*/
|
||||
public class IfOperator extends BaseOperator<IfCondition> {
|
||||
|
||||
@Override
|
||||
public IfCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEq(objects, 2, 3);
|
||||
|
||||
// 解析第一个参数
|
||||
OperatorHelper.checkObjMustBeBooleanTypeItem(objects[0]);
|
||||
Executable ifItem = OperatorHelper.convert(objects[0], Executable.class);
|
||||
|
||||
|
||||
// 解析第二个参数
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(objects[1]);
|
||||
Executable trueCaseExecutableItem = OperatorHelper.convert(objects[1], Executable.class);
|
||||
|
||||
// 解析第三个参数,如果有的话
|
||||
Executable falseCaseExecutableItem = null;
|
||||
if (objects.length == 3) {
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(objects[2]);
|
||||
falseCaseExecutableItem = OperatorHelper.convert(objects[2], Executable.class);
|
||||
}
|
||||
|
||||
IfCondition ifCondition = new IfCondition();
|
||||
ifCondition.setIfItem(ifItem);
|
||||
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
|
||||
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
|
||||
return ifCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +1,56 @@
|
|||
package com.yomahub.liteflow.builder.el.operator;
|
||||
|
||||
import com.ql.util.express.exception.QLException;
|
||||
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
|
||||
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
|
||||
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import com.ql.util.express.Operator;
|
||||
import com.yomahub.liteflow.exception.ELParseException;
|
||||
import com.yomahub.liteflow.flow.element.Executable;
|
||||
import com.yomahub.liteflow.flow.element.condition.Condition;
|
||||
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 Exception();
|
||||
}
|
||||
|
||||
Boolean ignoreError = OperatorHelper.convert(objects[1], Boolean.class);
|
||||
condition.setIgnoreError(ignoreError);
|
||||
if (objects.length != 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
Condition condition;
|
||||
if (objects[0] instanceof Condition){
|
||||
condition = (Condition) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be executable item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
boolean ignoreError = false;
|
||||
if (objects[1] instanceof Boolean){
|
||||
ignoreError = Boolean.parseBoolean(objects[1].toString());
|
||||
}else{
|
||||
LOG.error("the parameter must be boolean type!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
condition.setErrorResume(ignoreError);
|
||||
|
||||
return condition;
|
||||
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.FlowBus;
|
||||
import com.yomahub.liteflow.flow.element.FallbackNode;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
|
||||
/**
|
||||
* EL规则中的node的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @author DaleLee
|
||||
* @since 2.8.3
|
||||
*/
|
||||
public class NodeOperator extends BaseOperator<Node> {
|
||||
|
||||
@Override
|
||||
public Node build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqOne(objects);
|
||||
String nodeId = OperatorHelper.convert(objects[0], String.class);
|
||||
|
||||
if (FlowBus.containNode(nodeId)) {
|
||||
// 找到对应节点
|
||||
return FlowBus.getNode(nodeId);
|
||||
} else {
|
||||
// 生成代理节点
|
||||
return new FallbackNode(nodeId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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.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 Object executeInner(Object[] objects) throws Exception {
|
||||
try{
|
||||
if (objects.length <= 0){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
PreCondition preCondition = new PreCondition();
|
||||
for (Object obj : objects){
|
||||
if (obj instanceof Executable){
|
||||
preCondition.addExecutable((Executable)obj);
|
||||
}else{
|
||||
LOG.error("parameter must be executable item!");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
return preCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,32 +1,49 @@
|
|||
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 cn.hutool.core.util.ArrayUtil;
|
||||
import com.ql.util.express.Operator;
|
||||
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 Exception();
|
||||
}
|
||||
|
||||
SwitchCondition switchCondition = new SwitchCondition();
|
||||
switchCondition.setSwitchNode(switchNode);
|
||||
if (objects.length != 1){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
return switchCondition;
|
||||
}
|
||||
Node switchNode;
|
||||
if (objects[0] instanceof Node){
|
||||
switchNode = (Node) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be Node item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
SwitchCondition switchCondition = new SwitchCondition();
|
||||
switchCondition.setSwitchNode(switchNode);
|
||||
|
||||
return switchCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,61 @@
|
|||
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 cn.hutool.core.util.ArrayUtil;
|
||||
import com.ql.util.express.Operator;
|
||||
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 Exception();
|
||||
}
|
||||
|
||||
String tag = OperatorHelper.convert(objects[1], String.class);
|
||||
if (objects.length != 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
//如果解析的对象是一个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{
|
||||
LOG.error("The caller must be Node item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
String tag = null;
|
||||
if (objects[1] instanceof String){
|
||||
tag = objects[1].toString();
|
||||
}else{
|
||||
LOG.error("the parameter must be String type!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
//这里为什么要clone一个呢?
|
||||
//因为tag是跟着chain走的。而在el上下文里的放的都是同一个node,如果多个同样的node tag不同,则这里必须copy
|
||||
Node copyNode = FlowBus.copyNode(node.getId());
|
||||
|
||||
copyNode.setTag(tag);
|
||||
|
||||
return copyNode;
|
||||
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,41 @@
|
|||
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.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 Object executeInner(Object[] objects) throws Exception {
|
||||
try{
|
||||
if (objects.length <= 0){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
ThenCondition thenCondition = new ThenCondition();
|
||||
for (Object obj : objects){
|
||||
if (obj instanceof Executable){
|
||||
thenCondition.addExecutable((Executable)obj);
|
||||
}else{
|
||||
LOG.error("parameter must be executable item!");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
return thenCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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 Exception();
|
||||
}
|
||||
|
||||
whenCondition.setThreadExecutorClass(OperatorHelper.convert(objects[1], String.class));
|
||||
if (objects.length != 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
return whenCondition;
|
||||
}
|
||||
WhenCondition whenCondition;
|
||||
if (objects[0] instanceof WhenCondition){
|
||||
whenCondition = (WhenCondition) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be when condition item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
String threadPoolClazz = null;
|
||||
if (objects[1] instanceof String){
|
||||
threadPoolClazz = objects[1].toString();
|
||||
}else{
|
||||
LOG.error("the parameter must be String type!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
whenCondition.setThreadExecutorClass(threadPoolClazz);
|
||||
|
||||
return whenCondition;
|
||||
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.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 Exception();
|
||||
}
|
||||
|
||||
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 <= 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
SwitchCondition switchCondition;
|
||||
if (objects[0] instanceof SwitchCondition){
|
||||
switchCondition = (SwitchCondition) objects[0];
|
||||
}else{
|
||||
LOG.error("The caller must be SwitchCondition item!");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
for (int i = 1; i < objects.length; i++) {
|
||||
if (objects[i] instanceof Executable) {
|
||||
Executable target = (Executable) objects[i];
|
||||
switchCondition.addTargetItem(target);
|
||||
}else {
|
||||
LOG.error("The parameter must be Executable item!");
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
return switchCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,40 @@
|
|||
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.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){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
WhenCondition whenCondition = new WhenCondition();
|
||||
for (Object obj : objects){
|
||||
if (obj instanceof Executable){
|
||||
whenCondition.addExecutable((Executable)obj);
|
||||
}else{
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
return whenCondition;
|
||||
}catch (Exception e){
|
||||
throw new ELParseException("errors occurred in EL parsing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
|
@ -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有,if,while,break类型的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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.yomahub.liteflow.builder.entity;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
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 = Lists.newArrayList();
|
||||
}
|
||||
this.nodeCondComponents.add(conditionNodeEntity);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
@ -90,5 +90,4 @@ public class ChainPropBean {
|
|||
this.conditionType = conditionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 "";
|
||||
|
||||
}
|
||||
|
|
@ -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).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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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,81 @@ 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<>();
|
||||
|
||||
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());
|
||||
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());
|
||||
//执行失败后回调方法
|
||||
//这里要注意,失败方法本身抛出错误,只打出堆栈,往外抛出的还是主要的异常
|
||||
try{
|
||||
self.onError();
|
||||
}catch (Exception ex){
|
||||
String errMsg = StrUtil.format("[{}]:component[{}] onError method happens exception",slot.getRequestId(),this.getDisplayName());
|
||||
LOG.error(errMsg, ex);
|
||||
}
|
||||
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 +127,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 +233,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 +265,12 @@ 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 MonitorBus getMonitorBus() {
|
||||
|
|
@ -338,107 +281,31 @@ 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.getChainName());
|
||||
}
|
||||
|
||||
public <T> T getSubChainReqDataInAsync() {
|
||||
return getSlot().getChainReqDataFromQueue(this.getCurrChainId());
|
||||
}
|
||||
|
||||
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 Node getRefNode() {
|
||||
return this.refNodeTL.get();
|
||||
}
|
||||
|
||||
public void setRefNode(Node refNode) {
|
||||
this.refNodeTL.set(refNode);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
public LiteflowResponse invoke2Resp(String chainId, Object param) {
|
||||
public LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex) {
|
||||
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());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,12 +7,8 @@
|
|||
*/
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
import com.yomahub.liteflow.slot.DataBus;
|
||||
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
|
||||
|
||||
/**
|
||||
* 条件路由节点抽象类
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public abstract class NodeSwitchComponent extends NodeComponent {
|
||||
|
|
@ -20,16 +16,10 @@ public abstract class NodeSwitchComponent extends NodeComponent {
|
|||
@Override
|
||||
public void process() throws Exception {
|
||||
String nodeId = this.processSwitch();
|
||||
this.getSlot().setSwitchResult(this.getMetaValueKey(), nodeId);
|
||||
this.getSlot().setSwitchResult(this.getClass().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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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(getNodeId(), getSlotIndex());
|
||||
}
|
||||
|
||||
public void loadScript(String script) {
|
||||
log.info("load script for component[{}]", getDisplayName());
|
||||
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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(getNodeId(), getSlotIndex());
|
||||
}
|
||||
|
||||
public void loadScript(String script) {
|
||||
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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("$$"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -9,11 +9,10 @@ package com.yomahub.liteflow.enums;
|
|||
|
||||
/**
|
||||
* 组件步骤类型
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public enum CmpStepTypeEnum {
|
||||
|
||||
START, END, SINGLE;
|
||||
|
||||
START,
|
||||
END,
|
||||
SINGLE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,68 +1,43 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
/**
|
||||
* @author Yun
|
||||
*/
|
||||
public enum ConditionTypeEnum {
|
||||
TYPE_THEN("then","then"),
|
||||
TYPE_WHEN("when","when"),
|
||||
|
||||
TYPE_THEN("then", "then"),
|
||||
TYPE_SWITCH("switch", "switch"),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
/**
|
||||
* 隐式流程类型
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.5
|
||||
*/
|
||||
public enum InnerChainTypeEnum {
|
||||
|
||||
// 不是隐式chain
|
||||
NONE,
|
||||
// 在串行环境中执行
|
||||
IN_SYNC,
|
||||
// 在并行环境中执行
|
||||
IN_ASYNC
|
||||
|
||||
}
|
||||
|
|
@ -1,57 +1,34 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
public enum LiteFlowMethodEnum {
|
||||
PROCESS("process"),
|
||||
PROCESS_SWITCH("processSwitch"),
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,194 +1,47 @@
|
|||
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","普通"),
|
||||
|
||||
COMMON("common", "普通", false, NodeComponent.class),
|
||||
SWITCH("switch", "条件"),
|
||||
SCRIPT("script","脚本"),
|
||||
SWITCH_SCRIPT("switch_script","条件脚本")
|
||||
;
|
||||
private String code;
|
||||
private String name;
|
||||
|
||||
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
|
||||
NodeTypeEnum(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
BOOLEAN("boolean", "布尔", false, NodeBooleanComponent.class),
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
FOR("for", "循环次数", false, NodeForComponent.class),
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class),
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
|
||||
|
||||
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
|
||||
|
||||
BOOLEAN_SCRIPT("boolean_script", "布尔脚本", true, ScriptBooleanComponent.class),
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package com.yomahub.liteflow.exception;
|
||||
|
||||
/**
|
||||
* 执行未完全实现的抽象Chain
|
||||
*
|
||||
* @author zy
|
||||
* @since 2.11.1
|
||||
*/
|
||||
|
||||
public class ChainNotImplementedException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
public ChainNotImplementedException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package com.yomahub.liteflow.exception;
|
||||
|
||||
/**
|
||||
* 组件定义异常
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.11.4
|
||||
*/
|
||||
public class CmpDefinitionException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
public CmpDefinitionException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue