Compare commits
No commits in common. "master" and "v2.3.4" 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
|
||||
132
README.md
132
README.md
|
|
@ -1,120 +1,36 @@
|
|||
<p align="center">
|
||||
<a href="https://liteflow.cc/">
|
||||
<img src="static/img/logo-main.svg" width="50%" height="auto" alt="logo">
|
||||
<a href="http://yomahub.com/liteflow/">
|
||||
<img width="500" src="static/img/logo.png" alt="logo">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
[中文](README.zh-CN.md)
|
||||
<h3>您的star是我继续前进的动力,如果喜欢请右上角帮忙点个star</h3>
|
||||
|
||||
<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>
|
||||
## Gitee Star趋势图
|
||||
[](https://giteye.net/chart/3NHPVWVK)
|
||||
|
||||
## 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.
|
||||
## 贡献者列表
|
||||
[](https://giteye.net/chart/DHU59WRM)
|
||||
|
||||
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是一个轻量,快速的组件式流程引擎框架,组件编排,帮助解耦业务代码,让每一个业务片段都是一个组件,并支持热加载规则配置,实现即时修改。
|
||||
|
||||
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.
|
||||
## 项目主页请点击:[项目主页](http://yomahub.com/liteflow)
|
||||
## 示例工程请点击:[示例工程](https://gitee.com/bryan31/liteflow-example)
|
||||
|
||||
You can find out how to join the community on the official website!
|
||||
## 特性
|
||||
* 复杂业务的解耦利器,为所有组件提供统一的实现协议
|
||||
* 基于规则文件来编排流程,并可进行热编排
|
||||
* 框架中支持zookeeper流程配置,即时推送修改内容
|
||||
* 能自由扩展配置持久化源,提供扩展接口
|
||||
* 支持springboot的自动装配,也支持spring的配置和非spring的项目
|
||||
* 提供串行和并行2种模式,提供常见常见的表达式语句
|
||||
* 提供无级嵌套子流程模式
|
||||
* 数据槽高并发隔离机制
|
||||
* 自带简单的监控,能够知道每个组件的运行耗时排行
|
||||
|
||||
## 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)
|
||||
**微信公众号**
|
||||
|
||||
## Feature
|
||||
* **Component definition unified:** All logic is a component, for all logic to provide a unified component implementation, small size, large energy.
|
||||
* **Rules lightweight:** based on the rules file to arrange the process, learning the rules entry only takes 5 minutes, a read both understand.
|
||||
* **Rule diversification:** rules support XML, JSON, YML three rule file writing methods, which you like to use.
|
||||
* **Arbitrary arrangement:** Synchronous asynchronous mixing, no matter how complex the logic process, using LiteFlow rules, are easy to do, see the rules file to see how the logic works.
|
||||
* **Rules can be loaded from anywhere:** The framework provides implementations of local file configuration sources and ZK configuration sources, as well as an extension interface that allows you to store rules anywhere.
|
||||
* **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.
|
||||
* **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.
|
||||
* **Declarative component support:** You can make any of your classes become components in seconds.
|
||||
* **Detailed step information:** How your link performs, how much time each component takes, what errors are reported at a glance.
|
||||
* **Stable and reliable:** has been iterating for more than 2 years, running stably on the core systems of major companies.
|
||||
* **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.
|
||||
关注公众号回复`liteflow`即可加入讨论群
|
||||
|
||||
## 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
|
||||
|
||||
<img src="static/img/support.png" alt="support" width="258" />
|
||||

|
||||
|
|
|
|||
114
README.zh-CN.md
114
README.zh-CN.md
|
|
@ -1,114 +0,0 @@
|
|||
<p align="center">
|
||||
<a href="https://liteflow.cc/">
|
||||
<img src="static/img/logo-main.svg" width="50%" height="auto" alt="logo">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h3>您的star是我继续前进的动力,如果喜欢LiteFlow请右上角帮忙点个star</h3>
|
||||
|
||||
## 🌈概述
|
||||
LiteFlow是一个轻量且强大的国产规则引擎框架,可用于复杂的组件化业务的编排领域,独有的DSL规则驱动整个复杂业务,并可实现平滑刷新热部署,支持多种脚本语言规则的嵌入。帮助系统变得更加丝滑且灵活。
|
||||
|
||||
LiteFlow于2020年正式开源,直到现在已经是国内开源规则引擎中不可忽视的存在,而且最关键的是,LiteFlow还是一个持续高速迭代的开源项目。
|
||||
|
||||
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。您也可以自己扩展,把规则存储在任何地方。
|
||||
* **优雅热刷新机制:** 规则变化,无需重启您的应用,即时改变应用的规则。高并发下不会因为刷新规则导致正在执行的规则有任何错乱。
|
||||
* **支持广泛:** 不管你的项目是不是基于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也是支持的。
|
||||
* **规则嵌套支持:** 只要你想的出,你可以利用简单的表达式完成多重嵌套的复杂逻辑编排。
|
||||
* **组件重试支持:** 组件可以支持重试,每个组件均可自定义重试配置和指定异常。
|
||||
* **上下文隔离机制:** 可靠的上下文隔离机制,你无需担心高并发情况下的数据串流。
|
||||
* **声明式组件支持:** 你可以让你的任意类秒变组件。
|
||||
* **详细的步骤信息:** 你的链路如何执行的,每个组件耗时多少,报了什么错,一目了然。
|
||||
* **稳定可靠:** 历时2年多的迭代,在各大公司的核心系统上稳定运行。
|
||||
* **性能卓越:** 框架本身几乎不消耗额外性能,性能取决你的组件执行效率。
|
||||
* **自带简单监控:** 框架内自带一个命令行的监控,能够知道每个组件的运行耗时排行。
|
||||
|
||||
## ☘️什么场景适用
|
||||
|
||||
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>
|
||||
|
||||
**微信公众号**
|
||||
|
||||
社区群需要邀请入群。关注公众号后点击`个人微信`加我,我可以拉你入群
|
||||
|
||||

|
||||
|
||||
开源不易,支持就请赞助LiteFlow,请我喝一杯咖啡吧
|
||||
|
||||
<img src="static/img/support.png" alt="support" width="258" />
|
||||
|
|
@ -9,50 +9,88 @@
|
|||
<parent>
|
||||
<groupId>com.yomahub</groupId>
|
||||
<artifactId>liteflow</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
<version>2.3.4</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<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>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dom4j</groupId>
|
||||
<groupId>dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</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>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<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>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
<artifactId>curator-recipes</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -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 +0,0 @@
|
|||
package com.yomahub.liteflow.annotation;
|
||||
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
@Target({ ElementType.TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@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 +0,0 @@
|
|||
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 })
|
||||
@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;
|
||||
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.yomahub.liteflow.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* LiteFlow组件重试次数
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@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;
|
||||
|
||||
@LFAliasFor("value")
|
||||
int retry() default 0;
|
||||
|
||||
Class<? extends Exception>[] forExceptions() default { Exception.class };
|
||||
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
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 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) {
|
||||
String cacheKey = StrUtil.format("{}-{}", annotatedElement, annotationType.getSimpleName());
|
||||
|
||||
if (annoCacheMap.containsKey(cacheKey)){
|
||||
return (A)annoCacheMap.get(cacheKey);
|
||||
}
|
||||
|
||||
A annotation = AnnotationUtil.getAnnotation(annotatedElement, annotationType);
|
||||
if (ObjectUtil.isNull(annotation)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,29 +1,17 @@
|
|||
/**
|
||||
* <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;
|
||||
import com.yomahub.liteflow.entity.data.Slot;
|
||||
|
||||
/**
|
||||
* 全局组件拦截器接口 实现这个接口并注入到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(Slot slot);
|
||||
|
||||
void afterProcess(Slot slot);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,180 +0,0 @@
|
|||
package com.yomahub.liteflow.builder;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.exception.NodeBuildException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.log.LFLog;
|
||||
import com.yomahub.liteflow.log.LFLoggerManager;
|
||||
import com.yomahub.liteflow.monitor.MonitorFile;
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class LiteFlowNodeBuilder {
|
||||
|
||||
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
|
||||
|
||||
private final Node node;
|
||||
|
||||
public static LiteFlowNodeBuilder createNode() {
|
||||
return new LiteFlowNodeBuilder();
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createCommonNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.COMMON);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createSwitchNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createBooleanNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createForNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createIteratorNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.ITERATOR);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptSwitchNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SWITCH_SCRIPT);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptBooleanNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.BOOLEAN_SCRIPT);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptForNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.FOR_SCRIPT);
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder() {
|
||||
this.node = new Node();
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder(NodeTypeEnum type) {
|
||||
this.node = new Node();
|
||||
this.node.setType(type);
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setId(String nodeId) {
|
||||
if (StrUtil.isBlank(nodeId)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setId(nodeId.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setName(String name) {
|
||||
if (StrUtil.isBlank(name)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setName(name.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowNodeBuilder setClazz(String clazz) {
|
||||
if (StrUtil.isBlank(clazz)) {
|
||||
return this;
|
||||
}
|
||||
this.node.setClazz(clazz.trim());
|
||||
return this;
|
||||
}
|
||||
|
||||
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, ",", "[", "]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,408 +0,0 @@
|
|||
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 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.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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Chain基于代码形式的组装器 EL表达式规则专属组装器
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class LiteFlowChainELBuilder {
|
||||
|
||||
private static final LFLog LOG = LFLoggerManager.getLogger(LiteFlowChainELBuilder.class);
|
||||
|
||||
private static ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private Chain chain;
|
||||
|
||||
/**
|
||||
* 这是route EL的文本
|
||||
*/
|
||||
private Executable route;
|
||||
|
||||
/**
|
||||
* 这是主体的Condition //声明这个变量,而不是用chain.getConditionList的目的,是为了辅助平滑加载
|
||||
* 虽然FlowBus里面的map都是CopyOnWrite类型的,但是在buildCondition的时候,为了平滑加载,所以不能事先把chain.getConditionList给设为空List
|
||||
* 所以在这里做一个缓存,等conditionList全部build完毕后,再去一次性替换chain里面的conditionList
|
||||
*/
|
||||
private final List<Condition> conditionList;
|
||||
|
||||
/**
|
||||
* EL解析引擎
|
||||
*/
|
||||
public final static ExpressRunner EXPRESS_RUNNER = new 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 LiteFlowChainELBuilder() {
|
||||
chain = new Chain();
|
||||
conditionList = new ArrayList<>();
|
||||
}
|
||||
|
||||
// 在parser中chain的build是2段式的,因为涉及到依赖问题,以前是递归parser
|
||||
// 2.6.8之后取消了递归的模式,两段式组装,先把带有chainName的chain对象放进去,第二段再组装chain里面的condition
|
||||
// 所以这里setChainName的时候需要判断下
|
||||
|
||||
/**
|
||||
* @return LiteFlowChainELBuilder
|
||||
* @deprecated 请使用 {@link #setChainId(String)}
|
||||
*/
|
||||
public LiteFlowChainELBuilder setChainName(String chainName) {
|
||||
if (FlowBus.containChain(chainName)) {
|
||||
this.chain = FlowBus.getChain(chainName);
|
||||
} else {
|
||||
this.chain.setChainName(chainName);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowChainELBuilder setChainId(String chainId) {
|
||||
if (FlowBus.containChain(chainId)) {
|
||||
this.chain = FlowBus.getChain(chainId);
|
||||
} else {
|
||||
this.chain.setChainId(chainId);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LiteFlowChainELBuilder setRoute(String routeEl){
|
||||
if (StrUtil.isBlank(routeEl)) {
|
||||
return this;
|
||||
}
|
||||
List<String> errorList = new ArrayList<>();
|
||||
try {
|
||||
DefaultContext<String, Object> context = new DefaultContext<>();
|
||||
|
||||
// 往上下文里放入所有的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);
|
||||
|
||||
// 判断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加入
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
public LiteFlowChainELBuilder setEL(String elStr) {
|
||||
if (StrUtil.isBlank(elStr)) {
|
||||
String errMsg = StrUtil.format("no el in this chain[{}]", chain.getChainId());
|
||||
throw new FlowSystemException(errMsg);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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.condition.WhenCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的any的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class AnyOperator extends BaseOperator<WhenCondition> {
|
||||
|
||||
@Override
|
||||
public WhenCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);
|
||||
|
||||
Boolean any = OperatorHelper.convert(objects[1], Boolean.class);
|
||||
whenCondition.setParallelStrategy(any ? ParallelStrategyEnum.ANY : ParallelStrategyEnum.ALL);
|
||||
return whenCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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.FinallyCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的THEN的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class FinallyOperator extends BaseOperator<FinallyCondition> {
|
||||
|
||||
@Override
|
||||
public FinallyCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeGtZero(objects);
|
||||
|
||||
FinallyCondition finallyCondition = new FinallyCondition();
|
||||
for (Object obj : objects) {
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
|
||||
Executable item = OperatorHelper.convert(obj, Executable.class);
|
||||
finallyCondition.addExecutable(item);
|
||||
}
|
||||
return finallyCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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;
|
||||
|
||||
/**
|
||||
* EL规则中的id的操作符,只有condition可加id
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class IdOperator extends BaseOperator<Condition> {
|
||||
|
||||
@Override
|
||||
public Condition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
String errorMsg = "The caller must be Condition item";
|
||||
Condition condition = OperatorHelper.convert(objects[0], Condition.class, errorMsg);
|
||||
|
||||
String id = OperatorHelper.convert(objects[1], String.class);
|
||||
|
||||
condition.setId(id);
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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.condition.WhenCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的ignoreError的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class IgnoreErrorOperator extends BaseOperator<WhenCondition> {
|
||||
|
||||
@Override
|
||||
public WhenCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
String errorMsg = "The caller must be WhenCondition item";
|
||||
WhenCondition condition = OperatorHelper.convert(objects[0], WhenCondition.class, errorMsg);
|
||||
|
||||
Boolean ignoreError = OperatorHelper.convert(objects[1], Boolean.class);
|
||||
condition.setIgnoreError(ignoreError);
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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.PreCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的THEN的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class PreOperator extends BaseOperator<PreCondition> {
|
||||
|
||||
@Override
|
||||
public PreCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeGtZero(objects);
|
||||
|
||||
PreCondition preCondition = new PreCondition();
|
||||
for (Object obj : objects) {
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
|
||||
preCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
|
||||
}
|
||||
return preCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +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.SwitchCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的SWITCH的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class SwitchOperator extends BaseOperator<SwitchCondition> {
|
||||
|
||||
@Override
|
||||
public SwitchCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqOne(objects);
|
||||
|
||||
OperatorHelper.checkObjMustBeSwitchTypeItem(objects[0]);
|
||||
Node switchNode = OperatorHelper.convert(objects[0], Node.class);
|
||||
|
||||
SwitchCondition switchCondition = new SwitchCondition();
|
||||
switchCondition.setSwitchNode(switchNode);
|
||||
|
||||
return switchCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,42 +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.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;
|
||||
|
||||
/**
|
||||
* EL规则中的tag的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class TagOperator extends BaseOperator<Executable> {
|
||||
|
||||
@Override
|
||||
public Executable build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
Executable refObj = OperatorHelper.convert(objects[0], Executable.class);
|
||||
|
||||
String tag = OperatorHelper.convert(objects[1], String.class);
|
||||
|
||||
//如果解析的对象是一个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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.ThenCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的THEN的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class ThenOperator extends BaseOperator<ThenCondition> {
|
||||
|
||||
@Override
|
||||
public ThenCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeGtZero(objects);
|
||||
|
||||
ThenCondition thenCondition = new ThenCondition();
|
||||
for (Object obj : objects) {
|
||||
OperatorHelper.checkObjMustBeCommonTypeItem(obj);
|
||||
thenCondition.addExecutable(OperatorHelper.convert(obj, Executable.class));
|
||||
}
|
||||
return thenCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,27 +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.WhenCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的threadPool的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class ThreadPoolOperator extends BaseOperator<WhenCondition> {
|
||||
|
||||
@Override
|
||||
public WhenCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEqTwo(objects);
|
||||
|
||||
String errorMsg = "The caller must be WhenCondition item";
|
||||
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class, errorMsg);
|
||||
|
||||
whenCondition.setThreadExecutorClass(OperatorHelper.convert(objects[1], String.class));
|
||||
|
||||
return whenCondition;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.SwitchCondition;
|
||||
|
||||
/**
|
||||
* EL规则中的TO的操作符,用法须和SWITCH联合使用
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class ToOperator extends BaseOperator<SwitchCondition> {
|
||||
|
||||
@Override
|
||||
public SwitchCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeGteTwo(objects);
|
||||
|
||||
String errorMsg = "The caller must be SwitchCondition item";
|
||||
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class, errorMsg);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.WhenCondition;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
|
||||
/**
|
||||
* EL规则中的WHEN的操作符
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class WhenOperator extends BaseOperator<WhenCondition> {
|
||||
|
||||
@Override
|
||||
public WhenCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeGtZero(objects);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
package com.yomahub.liteflow.builder.prop;
|
||||
|
||||
import com.yomahub.liteflow.enums.ConditionTypeEnum;
|
||||
|
||||
/**
|
||||
* 构建 chain 的中间属性
|
||||
*/
|
||||
public class ChainPropBean {
|
||||
|
||||
/**
|
||||
* 执行规则
|
||||
*/
|
||||
String condValueStr;
|
||||
|
||||
/**
|
||||
* 分组
|
||||
*/
|
||||
String group;
|
||||
|
||||
/**
|
||||
* 是否抛出异常
|
||||
*/
|
||||
String errorResume;
|
||||
|
||||
/**
|
||||
* 满足任意条件,执行完成
|
||||
*/
|
||||
String any;
|
||||
|
||||
/**
|
||||
* 指定线程池
|
||||
*/
|
||||
String threadExecutorClass;
|
||||
|
||||
/**
|
||||
* chain 类型
|
||||
*/
|
||||
ConditionTypeEnum conditionType;
|
||||
|
||||
public String getCondValueStr() {
|
||||
return condValueStr;
|
||||
}
|
||||
|
||||
public ChainPropBean setCondValueStr(String condValueStr) {
|
||||
this.condValueStr = condValueStr;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public ChainPropBean setGroup(String group) {
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getErrorResume() {
|
||||
return errorResume;
|
||||
}
|
||||
|
||||
public ChainPropBean setErrorResume(String errorResume) {
|
||||
this.errorResume = errorResume;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAny() {
|
||||
return any;
|
||||
}
|
||||
|
||||
public ChainPropBean setAny(String any) {
|
||||
this.any = any;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getThreadExecutorClass() {
|
||||
return threadExecutorClass;
|
||||
}
|
||||
|
||||
public ChainPropBean setThreadExecutorClass(String threadExecutorClass) {
|
||||
this.threadExecutorClass = threadExecutorClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConditionTypeEnum getConditionType() {
|
||||
return conditionType;
|
||||
}
|
||||
|
||||
public ChainPropBean setConditionType(ConditionTypeEnum conditionType) {
|
||||
this.conditionType = conditionType;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,106 +0,0 @@
|
|||
package com.yomahub.liteflow.builder.prop;
|
||||
|
||||
/**
|
||||
* 构建 node 的中间属性
|
||||
*/
|
||||
public class NodePropBean {
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
String id;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
String name;
|
||||
|
||||
/**
|
||||
* 类
|
||||
*/
|
||||
String clazz;
|
||||
|
||||
/**
|
||||
* 脚本
|
||||
*/
|
||||
String script;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
String type;
|
||||
|
||||
/**
|
||||
* 脚本存放位置
|
||||
*/
|
||||
String file;
|
||||
|
||||
/**
|
||||
* 脚本语言
|
||||
*/
|
||||
String language;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public NodePropBean setId(String id) {
|
||||
this.id = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public NodePropBean setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public NodePropBean setClazz(String clazz) {
|
||||
this.clazz = clazz;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public NodePropBean setScript(String script) {
|
||||
this.script = script;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public NodePropBean setType(String type) {
|
||||
this.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public NodePropBean setFile(String file) {
|
||||
this.file = file;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public NodePropBean setLanguage(String language) {
|
||||
this.language = language;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
package com.yomahub.liteflow.common;
|
||||
|
||||
/**
|
||||
* Chain 常量
|
||||
*
|
||||
* @author tangkc
|
||||
*/
|
||||
public interface ChainConstant {
|
||||
String PARALLEL = "parallel";
|
||||
|
||||
String CHAIN = "chain";
|
||||
|
||||
String ROUTE = "route";
|
||||
|
||||
String BODY = "body";
|
||||
|
||||
String FLOW = "flow";
|
||||
|
||||
String NODES = "nodes";
|
||||
|
||||
String NODE = "node";
|
||||
|
||||
String ID = "id";
|
||||
|
||||
String _CLASS = "class";
|
||||
|
||||
String FILE = "file";
|
||||
|
||||
String NAME = "name";
|
||||
|
||||
String ENABLE = "enable";
|
||||
|
||||
String LANGUAGE = "language";
|
||||
|
||||
String NAMESPACE = "namespace";
|
||||
|
||||
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";
|
||||
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.yomahub.liteflow.common;
|
||||
|
||||
/**
|
||||
* @author Yun
|
||||
*/
|
||||
public class LocalDefaultFlowConstant {
|
||||
|
||||
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,81 +0,0 @@
|
|||
package com.yomahub.liteflow.core;
|
||||
|
||||
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.monitor.MonitorBus;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
import com.yomahub.liteflow.spi.holder.ContextAwareHolder;
|
||||
import com.yomahub.liteflow.spi.holder.LiteflowComponentSupportHolder;
|
||||
|
||||
/**
|
||||
* 组件初始化器
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public class ComponentInitializer {
|
||||
|
||||
private static ComponentInitializer 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);
|
||||
|
||||
// 设置MonitorBus,如果没有就不注入
|
||||
if (ContextAwareHolder.loadContextAware().hasBean(ChainConstant.MONITOR_BUS)) {
|
||||
MonitorBus monitorBus = ContextAwareHolder.loadContextAware().getBean(MonitorBus.class);
|
||||
if (ObjectUtil.isNotNull(monitorBus)) {
|
||||
nodeComponent.setMonitorBus(monitorBus);
|
||||
}
|
||||
}
|
||||
|
||||
// 先取传进来的name值(配置文件中配置的),再看有没有配置@LiteflowComponent标注
|
||||
// @LiteflowComponent标注只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
|
||||
nodeComponent.setName(name);
|
||||
if (!type.isScript() && StrUtil.isBlank(nodeComponent.getName())) {
|
||||
nodeComponent
|
||||
.setName(LiteflowComponentSupportHolder.loadLiteflowComponentSupport().getCmpName(nodeComponent));
|
||||
}
|
||||
|
||||
// 先从组件上取@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));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,522 +1,174 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Tuple;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import com.yomahub.liteflow.common.ChainConstant;
|
||||
import com.yomahub.liteflow.enums.ChainExecuteModeEnum;
|
||||
import com.yomahub.liteflow.enums.InnerChainTypeEnum;
|
||||
import com.yomahub.liteflow.enums.ParseModeEnum;
|
||||
import com.yomahub.liteflow.exception.*;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.element.Rollbackable;
|
||||
import com.yomahub.liteflow.flow.entity.CmpStep;
|
||||
import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
|
||||
import com.yomahub.liteflow.log.LFLog;
|
||||
import com.yomahub.liteflow.log.LFLoggerManager;
|
||||
import com.yomahub.liteflow.monitor.MonitorFile;
|
||||
import com.yomahub.liteflow.parser.base.FlowParser;
|
||||
import com.yomahub.liteflow.parser.factory.FlowParserProvider;
|
||||
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.yomahub.liteflow.exception.ConfigErrorException;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
import com.yomahub.liteflow.slot.DataBus;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.slot.Slot;
|
||||
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
import com.yomahub.liteflow.thread.ExecutorHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import com.yomahub.liteflow.entity.flow.Chain;
|
||||
import com.yomahub.liteflow.entity.flow.Node;
|
||||
import com.yomahub.liteflow.entity.data.DataBus;
|
||||
import com.yomahub.liteflow.entity.data.DefaultSlot;
|
||||
import com.yomahub.liteflow.entity.data.Slot;
|
||||
import com.yomahub.liteflow.exception.ChainNotFoundException;
|
||||
import com.yomahub.liteflow.exception.FlowExecutorNotInitException;
|
||||
import com.yomahub.liteflow.exception.NoAvailableSlotException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.parser.LocalXmlFlowParser;
|
||||
import com.yomahub.liteflow.parser.XmlFlowParser;
|
||||
import com.yomahub.liteflow.parser.ZookeeperXmlFlowParser;
|
||||
|
||||
/**
|
||||
* 流程规则主要执行器类
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public class FlowExecutor {
|
||||
|
||||
private static final LFLog LOG = LFLoggerManager.getLogger(FlowExecutor.class);
|
||||
|
||||
private static final String PREFIX_FORMAT_CONFIG_REGEX = "xml:|json:|yml:|el_xml:|el_json:|el_yml:";
|
||||
private static final Logger LOG = LoggerFactory.getLogger(FlowExecutor.class);
|
||||
|
||||
private LiteflowConfig liteflowConfig;
|
||||
|
||||
public FlowExecutor() {
|
||||
// 设置FlowExecutor的Holder,虽然大部分地方都可以通过Spring上下文获取到,但放入Holder,还是为了某些地方能方便的取到
|
||||
FlowExecutorHolder.setHolder(this);
|
||||
// 初始化DataBus
|
||||
DataBus.init();
|
||||
}
|
||||
private String zkNode;
|
||||
|
||||
public FlowExecutor(LiteflowConfig liteflowConfig) {
|
||||
this.liteflowConfig = liteflowConfig;
|
||||
// 把liteFlowConfig设到LiteFlowGetter中去
|
||||
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
|
||||
// 设置FlowExecutor的Holder,虽然大部分地方都可以通过Spring上下文获取到,但放入Holder,还是为了某些地方能方便的取到
|
||||
FlowExecutorHolder.setHolder(this);
|
||||
if (!liteflowConfig.getParseMode().equals(ParseModeEnum.PARSE_ALL_ON_FIRST_EXEC)) {
|
||||
this.init(true);
|
||||
}
|
||||
// 初始化DataBus
|
||||
DataBus.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* FlowExecutor的初始化化方式,主要用于parse规则文件
|
||||
* isStart表示是否是系统启动阶段,启动阶段要做额外的事情,而因为reload所调用的init就不用做
|
||||
*/
|
||||
public void init(boolean isStart) {
|
||||
if (ObjectUtil.isNull(liteflowConfig)) {
|
||||
public void init() {
|
||||
if (ObjectUtil.isNull(liteflowConfig) || StrUtil.isBlank(liteflowConfig.getRuleSource())){
|
||||
throw new ConfigErrorException("config error, please check liteflow config property");
|
||||
}
|
||||
|
||||
// 在相应的环境下进行节点的初始化工作
|
||||
// 在spring体系下会获得spring扫描后的节点,接入元数据
|
||||
// 在非spring体系下是一个空实现,等于不做此步骤
|
||||
ContextCmpInitHolder.loadContextCmpInit().initCmp();
|
||||
List<String> rulePath = Lists.newArrayList(liteflowConfig.getRuleSource().split(",|;"));
|
||||
|
||||
if (isStart){
|
||||
// 进行id生成器的初始化
|
||||
IdGeneratorHolder.init();
|
||||
}
|
||||
|
||||
String ruleSource = liteflowConfig.getRuleSource();
|
||||
if (StrUtil.isBlank(ruleSource)) {
|
||||
// 查看有没有Parser的SPI实现
|
||||
// 所有的Parser的SPI实现都是以custom形式放入的,且只支持xml形式
|
||||
ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
|
||||
Iterator<ParserClassNameSpi> it = loader.iterator();
|
||||
if (it.hasNext()) {
|
||||
ParserClassNameSpi parserClassNameSpi = it.next();
|
||||
ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
|
||||
liteflowConfig.setRuleSource(ruleSource);
|
||||
}
|
||||
else {
|
||||
// ruleSource为空,而且没有spi形式的扩展,那么说明真的没有ruleSource
|
||||
// 这种情况有可能是基于代码动态构建的
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有前缀的,则不需要再进行分割了,说明是一个整体
|
||||
// 如果没有前缀,说明是本地文件,可能配置多个,所以需要分割
|
||||
List<String> sourceRulePathList;
|
||||
if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, ruleSource)) {
|
||||
sourceRulePathList = ListUtil.toList(ruleSource);
|
||||
}
|
||||
else {
|
||||
String afterHandleRuleSource = ruleSource.replace(StrUtil.SPACE, StrUtil.EMPTY);
|
||||
sourceRulePathList = ListUtil.toList(afterHandleRuleSource.split(",|;"));
|
||||
}
|
||||
|
||||
FlowParser parser = null;
|
||||
Set<String> parserNameSet = new HashSet<>();
|
||||
List<String> rulePathList = new ArrayList<>();
|
||||
for (String path : sourceRulePathList) {
|
||||
XmlFlowParser parser = null;
|
||||
for(String path : rulePath){
|
||||
try {
|
||||
// 查找对应的解析器
|
||||
parser = FlowParserProvider.lookup(path);
|
||||
parserNameSet.add(parser.getClass().getName());
|
||||
// 替换掉前缀标识(如:xml:/json:),保留剩下的完整地址,并统一路径格式
|
||||
path = ReUtil.replaceAll(path, PREFIX_FORMAT_CONFIG_REGEX, "").replace("\\", "/");
|
||||
rulePathList.add(path);
|
||||
|
||||
// 支持多类型的配置文件,分别解析
|
||||
if (BooleanUtil.isTrue(liteflowConfig.isSupportMultipleType())) {
|
||||
// 解析文件
|
||||
parser.parseMain(ListUtil.toList(path));
|
||||
}
|
||||
}
|
||||
catch (CyclicDependencyException e) {
|
||||
LOG.error(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason:{}", path,
|
||||
e.getMessage());
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new FlowExecutorNotInitException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// 单类型的配置文件,需要一起解析
|
||||
if (BooleanUtil.isFalse(liteflowConfig.isSupportMultipleType())) {
|
||||
// 检查Parser是否只有一个,因为多个不同的parser会造成子流程的混乱
|
||||
if (parserNameSet.size() > 1) {
|
||||
String errorMsg = "cannot have multiple different parsers";
|
||||
LOG.error(errorMsg);
|
||||
throw new MultipleParsersException(errorMsg);
|
||||
}
|
||||
|
||||
// 进行多个配置文件的一起解析
|
||||
try {
|
||||
if (parser != null) {
|
||||
// 解析文件
|
||||
parser.parseMain(rulePathList);
|
||||
}
|
||||
else {
|
||||
throw new ConfigErrorException("parse error, please check liteflow config property");
|
||||
}
|
||||
}
|
||||
catch (CyclicDependencyException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
LOG.error(e.getMessage());
|
||||
throw e;
|
||||
}
|
||||
catch (ChainDuplicateException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
catch (RouteELInvalidException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String errorMsg = StrUtil.format("init flow executor cause error for path {},reason: {}", rulePathList,
|
||||
e.getMessage());
|
||||
LOG.error(e.getMessage(), e);
|
||||
throw new FlowExecutorNotInitException(errorMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是ruleSource方式的,最后判断下有没有解析出来,如果没有解析出来则报错
|
||||
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData())
|
||||
&& MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())) {
|
||||
if (FlowBus.getChainMap().isEmpty()) {
|
||||
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]",
|
||||
liteflowConfig.getRuleSource());
|
||||
throw new ConfigErrorException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
// 执行钩子
|
||||
if (isStart) {
|
||||
FlowInitHook.executeHook();
|
||||
}
|
||||
|
||||
// 文件监听
|
||||
if (isStart && liteflowConfig.getEnableMonitorFile()) {
|
||||
try {
|
||||
addMonitorFilePaths(rulePathList);
|
||||
MonitorFile.getInstance().create();
|
||||
}
|
||||
catch (Exception e) {
|
||||
String errMsg = StrUtil.format("file monitor init error for path:{}", rulePathList);
|
||||
throw new MonitorFileInitErrorException(errMsg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 此方法就是从原有的配置源主动拉取新的进行刷新
|
||||
// 和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取,一个为被动监听到新的内容进行刷新
|
||||
public void reloadRule() {
|
||||
long start = System.currentTimeMillis();
|
||||
init(false);
|
||||
LOG.info("reload rules takes {}ms", System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
// 隐式流程的调用方法
|
||||
@Deprecated
|
||||
public void invoke(String chainId, Object param, Integer slotIndex) throws Exception {
|
||||
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void invokeInAsync(String chainId, Object param, Integer slotIndex) throws Exception {
|
||||
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
public LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex) {
|
||||
return this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
|
||||
}
|
||||
|
||||
public LiteflowResponse invoke2RespInAsync(String chainId, Object param, Integer slotIndex) {
|
||||
return this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
|
||||
}
|
||||
|
||||
// 单独调用某一个node
|
||||
@Deprecated
|
||||
public void invoke(String nodeId, Integer slotIndex) throws Exception {
|
||||
Node node = FlowBus.getNode(nodeId);
|
||||
node.execute(slotIndex);
|
||||
}
|
||||
|
||||
// 调用一个流程并返回LiteflowResponse,上下文为默认的DefaultContext,初始参数为null
|
||||
public LiteflowResponse execute2Resp(String chainId) {
|
||||
return this.execute2Resp(chainId, null, DefaultContext.class);
|
||||
}
|
||||
|
||||
// 调用一个流程并返回LiteflowResponse,上下文为默认的DefaultContext
|
||||
public LiteflowResponse execute2Resp(String chainId, Object param) {
|
||||
return this.execute2Resp(chainId, param, DefaultContext.class);
|
||||
}
|
||||
|
||||
// 调用一个流程并返回LiteflowResponse,允许多上下文的传入
|
||||
public LiteflowResponse execute2Resp(String chainId, Object param, Class<?>... contextBeanClazzArray) {
|
||||
return this.execute2Resp(chainId, param, null, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChain(Object param, Class<?>... contextBeanClazzArray){
|
||||
return this.executeWithRoute(null, param, null, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Class<?>... contextBeanClazzArray){
|
||||
return this.executeWithRoute(namespace, param, null, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) {
|
||||
return this.execute2Resp(chainId, param, null, null, contextBeanArray);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChain(Object param, Object... contextBeanArray){
|
||||
return this.executeWithRoute(null, param, null, null, contextBeanArray);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Object... contextBeanArray){
|
||||
return this.executeWithRoute(namespace, param, null, null, contextBeanArray);
|
||||
}
|
||||
|
||||
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
|
||||
return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Class<?>... contextBeanClazzArray) {
|
||||
return this.executeWithRoute(null, param, requestId, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Class<?>... contextBeanClazzArray) {
|
||||
return this.executeWithRoute(namespace, param, requestId, contextBeanClazzArray, null);
|
||||
}
|
||||
|
||||
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
|
||||
return this.execute2Resp(chainId, param, requestId, null, contextBeanArray);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Object... contextBeanArray) {
|
||||
return this.executeWithRoute(null, param, requestId, null, contextBeanArray);
|
||||
}
|
||||
|
||||
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Object... contextBeanArray) {
|
||||
return this.executeWithRoute(namespace, param, requestId, null, contextBeanArray);
|
||||
}
|
||||
|
||||
// 调用一个流程并返回Future<LiteflowResponse>,允许多上下文的传入
|
||||
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray) {
|
||||
return ExecutorHelper.loadInstance()
|
||||
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
|
||||
.submit(() -> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray));
|
||||
}
|
||||
|
||||
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Object... contextBeanArray) {
|
||||
return ExecutorHelper.loadInstance()
|
||||
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
|
||||
.submit(() -> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanArray));
|
||||
}
|
||||
|
||||
public Future<LiteflowResponse> execute2FutureWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
|
||||
return ExecutorHelper.loadInstance()
|
||||
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
|
||||
.submit(() -> FlowExecutorHolder.loadInstance().execute2RespWithRid(chainId, param, requestId, contextBeanClazzArray));
|
||||
}
|
||||
|
||||
public Future<LiteflowResponse> execute2FutureWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
|
||||
return ExecutorHelper.loadInstance()
|
||||
.buildMainExecutor(liteflowConfig.getMainExecutorClass())
|
||||
.submit(() -> FlowExecutorHolder.loadInstance().execute2RespWithRid(chainId, param, requestId, contextBeanArray));
|
||||
}
|
||||
|
||||
// 调用一个流程,返回默认的上下文,适用于简单的调用
|
||||
@Deprecated
|
||||
public DefaultContext execute(String chainId, Object param) throws Exception {
|
||||
LiteflowResponse response = this.execute2Resp(chainId, param, DefaultContext.class);
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}
|
||||
else {
|
||||
return response.getFirstContextBean();
|
||||
}
|
||||
}
|
||||
|
||||
private LiteflowResponse execute2Resp(String chainId, Object param, String requestId, Class<?>[] contextBeanClazzArray,
|
||||
Object[] contextBeanArray) {
|
||||
Slot slot = doExecute(chainId, param, requestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.BODY);
|
||||
return LiteflowResponse.newMainResponse(slot);
|
||||
}
|
||||
|
||||
private List<LiteflowResponse> executeWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
|
||||
List<Slot> slotList = doExecuteWithRoute(namespace, param, requestId, contextBeanClazzArray, contextBeanArray);
|
||||
return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private LiteflowResponse invoke2Resp(String chainId, Object param, Integer slotIndex,
|
||||
InnerChainTypeEnum innerChainType) {
|
||||
Slot slot = doExecute(chainId, param, null, null, null, slotIndex, innerChainType, ChainExecuteModeEnum.BODY);
|
||||
return LiteflowResponse.newInnerResponse(chainId, slot);
|
||||
}
|
||||
|
||||
private Slot doExecute(String chainId, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray,
|
||||
Integer slotIndex, InnerChainTypeEnum innerChainType, ChainExecuteModeEnum chainExecuteModeEnum) {
|
||||
if (FlowBus.needInit()) {
|
||||
init(true);
|
||||
}
|
||||
|
||||
// 如果不是隐式流程,那么需要分配Slot
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE) && ObjectUtil.isNull(slotIndex)) {
|
||||
// 这里可以根据class分配,也可以根据bean去分配
|
||||
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)) {
|
||||
slotIndex = DataBus.offerSlotByClass(ListUtil.toList(contextBeanClazzArray));
|
||||
}
|
||||
else {
|
||||
slotIndex = DataBus.offerSlotByBean(ListUtil.toList(contextBeanArray));
|
||||
}
|
||||
|
||||
if (slotIndex == -1) {
|
||||
throw new NoAvailableSlotException("there is no available slot");
|
||||
}
|
||||
}
|
||||
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
if (ObjectUtil.isNull(slot)) {
|
||||
throw new NoAvailableSlotException(StrUtil.format("the slot[{}] is not exist", slotIndex));
|
||||
}
|
||||
|
||||
//如果传入了用户的RequestId,则用这个请求Id,如果没传入,则进行生成
|
||||
if (StrUtil.isNotBlank(requestId)){
|
||||
slot.putRequestId(requestId);
|
||||
LFLoggerManager.setRequestId(requestId);
|
||||
}else if(StrUtil.isBlank(slot.getRequestId())){
|
||||
slot.generateRequestId();
|
||||
LFLoggerManager.setRequestId(slot.getRequestId());
|
||||
LOG.info("requestId has generated");
|
||||
}
|
||||
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
LOG.info("slot[{}] offered", slotIndex);
|
||||
}
|
||||
|
||||
// 如果是隐式流程,事先把subException给置空,然后把隐式流程的chainId放入slot元数据中
|
||||
// 我知道这在多线程调用隐式流程中会有问题。但是考虑到这种场景的不会多,也有其他的转换方式。
|
||||
// 所以暂且这么做,以后再优化
|
||||
if (!innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.removeSubException(chainId);
|
||||
slot.addSubChain(chainId);
|
||||
}
|
||||
|
||||
|
||||
if (ObjectUtil.isNotNull(param)) {
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.setRequestData(param);
|
||||
}
|
||||
else if (innerChainType.equals(InnerChainTypeEnum.IN_SYNC)) {
|
||||
slot.setChainReqData(chainId, param);
|
||||
}
|
||||
else if (innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)) {
|
||||
slot.setChainReqData2Queue(chainId, param);
|
||||
}
|
||||
}
|
||||
|
||||
Chain chain = null;
|
||||
try {
|
||||
chain = FlowBus.getChain(chainId);
|
||||
|
||||
if (ObjectUtil.isNull(chain)) {
|
||||
String errorMsg = StrUtil.format("couldn't find chain with the id[{}]", chainId);
|
||||
throw new ChainNotFoundException(errorMsg);
|
||||
}
|
||||
// 根据chain执行模式执行chain
|
||||
if (chainExecuteModeEnum.equals(ChainExecuteModeEnum.BODY)){
|
||||
chain.execute(slotIndex);
|
||||
}else if(chainExecuteModeEnum.equals(ChainExecuteModeEnum.ROUTE)){
|
||||
chain.executeRoute(slotIndex);
|
||||
}else{
|
||||
throw new LiteFlowException("chain execute mode error");
|
||||
}
|
||||
}
|
||||
catch (ChainEndException e) {
|
||||
if (ObjectUtil.isNotNull(chain)) {
|
||||
String warnMsg = StrUtil.format("chain[{}] execute end on slot[{}]", chain.getChainId(), slotIndex);
|
||||
LOG.warn(warnMsg);
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (ObjectUtil.isNotNull(chain)) {
|
||||
String errMsg = StrUtil.format("chain[{}] execute error on slot[{}]", chain.getChainId(), slotIndex);
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
|
||||
LOG.error(errMsg, e);
|
||||
}
|
||||
else {
|
||||
LOG.error(errMsg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
else {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果是正常流程需要把异常设置到slot的exception属性里
|
||||
// 如果是隐式流程,则需要设置到隐式流程的exception属性里
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.setException(e);
|
||||
}
|
||||
else {
|
||||
slot.setSubException(chainId, e);
|
||||
}
|
||||
Deque<CmpStep> executeSteps = slot.getExecuteSteps();
|
||||
try {
|
||||
Iterator<CmpStep> cmpStepIterator = executeSteps.descendingIterator();
|
||||
while(cmpStepIterator.hasNext()) {
|
||||
CmpStep cmpStep = cmpStepIterator.next();
|
||||
if(cmpStep.getInstance().isRollback()) {
|
||||
Rollbackable rollbackItem = cmpStep.getRefNode();
|
||||
rollbackItem.rollback(slotIndex);
|
||||
if(isLocalConfig(path)) {
|
||||
parser = new LocalXmlFlowParser();
|
||||
}else if(isZKConfig(path)){
|
||||
if(StringUtils.isNotBlank(zkNode)) {
|
||||
parser = new ZookeeperXmlFlowParser(zkNode);
|
||||
}else {
|
||||
parser = new ZookeeperXmlFlowParser();
|
||||
}
|
||||
}else if(isClassConfig(path)) {
|
||||
Class c = Class.forName(path);
|
||||
parser = (XmlFlowParser)c.newInstance();
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
LOG.error(exception.getMessage());
|
||||
}
|
||||
finally {
|
||||
slot.printRollbackStep();
|
||||
parser.parseMain(path);
|
||||
} catch (Exception e) {
|
||||
String errorMsg = MessageFormat.format("init flow executor cause error,cannot parse rule file{0}", path);
|
||||
LOG.error(errorMsg,e);
|
||||
throw new FlowExecutorNotInitException(errorMsg);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
}
|
||||
|
||||
private boolean isZKConfig(String path) {
|
||||
Pattern p = Pattern.compile("[\\w\\d][\\w\\d\\.]+\\:(\\d)+(\\,[\\w\\d][\\w\\d\\.]+\\:(\\d)+)*");
|
||||
Matcher m = p.matcher(path);
|
||||
return m.find();
|
||||
}
|
||||
|
||||
private boolean isLocalConfig(String path) {
|
||||
Pattern p = Pattern.compile("^[\\w_\\-\\@\\/]+\\.xml$");
|
||||
Matcher m = p.matcher(path);
|
||||
return m.find();
|
||||
}
|
||||
|
||||
private boolean isClassConfig(String path) {
|
||||
Pattern p = Pattern.compile("^\\w+(\\.\\w+)*$");
|
||||
Matcher m = p.matcher(path);
|
||||
return m.find();
|
||||
}
|
||||
|
||||
public void reloadRule(){
|
||||
init();
|
||||
}
|
||||
|
||||
public <T extends Slot> T execute(String chainId,Object param) throws Exception{
|
||||
return execute(chainId, param, DefaultSlot.class,null,false);
|
||||
}
|
||||
|
||||
public <T extends Slot> T execute(String chainId,Object param,Class<? extends Slot> slotClazz) throws Exception{
|
||||
return execute(chainId, param, slotClazz,null,false);
|
||||
}
|
||||
|
||||
public void invoke(String chainId,Object param,Class<? extends Slot> slotClazz,Integer slotIndex) throws Exception{
|
||||
execute(chainId, param, slotClazz,slotIndex,true);
|
||||
}
|
||||
|
||||
public <T extends Slot> T execute(String chainId,Object param,Class<? extends Slot> slotClazz,Integer slotIndex,boolean isInnerChain) throws Exception{
|
||||
Slot slot = null;
|
||||
|
||||
if(FlowBus.needInit()) {
|
||||
init();
|
||||
}
|
||||
|
||||
Chain chain = FlowBus.getChain(chainId);
|
||||
|
||||
if(chain == null){
|
||||
String errorMsg = MessageFormat.format("couldn't find chain with the id[{0}]", chainId);
|
||||
throw new ChainNotFoundException(errorMsg);
|
||||
}
|
||||
|
||||
if(!isInnerChain && slotIndex == null) {
|
||||
slotIndex = DataBus.offerSlot(slotClazz);
|
||||
LOG.info("slot[{}] offered",slotIndex);
|
||||
}
|
||||
|
||||
if(slotIndex == -1){
|
||||
throw new NoAvailableSlotException("there is no available slot");
|
||||
}
|
||||
|
||||
slot = DataBus.getSlot(slotIndex);
|
||||
if(slot == null) {
|
||||
throw new NoAvailableSlotException("the slot is not exist");
|
||||
}
|
||||
|
||||
if(StringUtils.isBlank(slot.getRequestId())) {
|
||||
slot.generateRequestId();
|
||||
LOG.info("requestId[{}] has generated",slot.getRequestId());
|
||||
}
|
||||
|
||||
if(!isInnerChain) {
|
||||
slot.setRequestData(param);
|
||||
slot.setChainName(chainId);
|
||||
}else {
|
||||
slot.setChainReqData(chainId, param);
|
||||
}
|
||||
|
||||
try {
|
||||
//执行chain
|
||||
chain.execute(slotIndex);
|
||||
}catch(Exception e){
|
||||
slot.setSuccess(false);
|
||||
slot.setErrorMsg(e.getMessage());
|
||||
}finally{
|
||||
if(!isInnerChain) {
|
||||
slot.printStep();
|
||||
DataBus.releaseSlot(slotIndex);
|
||||
LFLoggerManager.removeRequestId();
|
||||
}
|
||||
}
|
||||
return slot;
|
||||
return (T)slot;
|
||||
}
|
||||
|
||||
public String getZkNode() {
|
||||
return zkNode;
|
||||
}
|
||||
|
||||
public void setZkNode(String zkNode) {
|
||||
this.zkNode = zkNode;
|
||||
}
|
||||
|
||||
public LiteflowConfig getLiteflowConfig() {
|
||||
|
|
@ -525,109 +177,5 @@ public class FlowExecutor {
|
|||
|
||||
public void setLiteflowConfig(LiteflowConfig liteflowConfig) {
|
||||
this.liteflowConfig = liteflowConfig;
|
||||
// 把liteFlowConfig设到LiteFlowGetter中去
|
||||
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听文件路径
|
||||
* @param pathList 文件路径
|
||||
*/
|
||||
private void addMonitorFilePaths(List<String> pathList) throws Exception {
|
||||
// 添加规则文件监听
|
||||
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(pathList);
|
||||
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
|
||||
}
|
||||
|
||||
private List<Slot> doExecuteWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
|
||||
if (FlowBus.needInit()) {
|
||||
init(true);
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(namespace)){
|
||||
namespace = ChainConstant.DEFAULT_NAMESPACE;
|
||||
}
|
||||
|
||||
String finalNamespace = namespace;
|
||||
List<Chain> routeChainList = FlowBus.getChainMap().values().stream()
|
||||
.filter(chain -> chain.getNamespace().equals(finalNamespace))
|
||||
.filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isEmpty(routeChainList)){
|
||||
String errorMsg = StrUtil.format("no route found for namespace[{}]", finalNamespace);
|
||||
throw new RouteChainNotFoundException(errorMsg);
|
||||
}
|
||||
|
||||
String finalRequestId;
|
||||
if (StrUtil.isBlank(requestId)){
|
||||
finalRequestId = IdGeneratorHolder.getInstance().generate();
|
||||
}else{
|
||||
finalRequestId = requestId;
|
||||
}
|
||||
|
||||
// 异步执行route el
|
||||
List<Tuple> routeTupleList = new ArrayList<>();
|
||||
for (Chain routeChain : routeChainList){
|
||||
CompletableFuture<Slot> f = CompletableFuture.supplyAsync(
|
||||
() -> doExecute(routeChain.getChainId(), param, finalRequestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.ROUTE)
|
||||
);
|
||||
|
||||
routeTupleList.add(new Tuple(routeChain, f));
|
||||
}
|
||||
|
||||
CompletableFuture<?> resultRouteCf = CompletableFuture.allOf(routeTupleList.stream().map(
|
||||
(Function<Tuple, CompletableFuture<?>>) tuple -> tuple.get(1)
|
||||
).collect(Collectors.toList()).toArray(new CompletableFuture[] {}));
|
||||
try{
|
||||
resultRouteCf.get();
|
||||
}catch (Exception e){
|
||||
throw new LiteFlowException("There is An error occurred while executing the route.", e);
|
||||
}
|
||||
|
||||
// 把route执行为true都过滤出来
|
||||
List<Chain> matchedRouteChainList = routeTupleList.stream().filter(tuple -> {
|
||||
try{
|
||||
CompletableFuture<Slot> f = tuple.get(1);
|
||||
Slot slot = f.get();
|
||||
return BooleanUtil.isTrue(slot.getRouteResult());
|
||||
}catch (Exception e){
|
||||
return false;
|
||||
}
|
||||
}).map(
|
||||
(Function<Tuple, Chain>) tuple -> tuple.get(0)
|
||||
).collect(Collectors.toList());
|
||||
|
||||
if (CollUtil.isEmpty(matchedRouteChainList)){
|
||||
throw new NoMatchedRouteChainException("there is no matched route chain");
|
||||
}
|
||||
|
||||
// 异步分别执行这些chain
|
||||
List<CompletableFuture<Slot>> executeChainCfList = new ArrayList<>();
|
||||
for (Chain chain : matchedRouteChainList){
|
||||
CompletableFuture<Slot> cf = CompletableFuture.supplyAsync(
|
||||
() -> doExecute(chain.getChainId(), param, finalRequestId, contextBeanClazzArray, contextBeanArray, null, InnerChainTypeEnum.NONE, ChainExecuteModeEnum.BODY)
|
||||
);
|
||||
executeChainCfList.add(cf);
|
||||
}
|
||||
|
||||
CompletableFuture<?> resultChainCf = CompletableFuture.allOf(executeChainCfList.toArray(new CompletableFuture[] {}));
|
||||
try{
|
||||
resultChainCf.get();
|
||||
}catch (Exception e){
|
||||
throw new LiteFlowException("There is An error occurred while executing the matched chain.", e);
|
||||
}
|
||||
|
||||
|
||||
List<Slot> resultSlotList = executeChainCfList.stream().map(slotCompletableFuture -> {
|
||||
try{
|
||||
return slotCompletableFuture.get();
|
||||
}catch (Exception e){
|
||||
return null;
|
||||
}
|
||||
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||
|
||||
LOG.info("chain namespace:[{}], total size:[{}], matched size:[{}]", namespace, routeChainList.size(), resultSlotList.size());
|
||||
|
||||
return resultSlotList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
package com.yomahub.liteflow.core;
|
||||
|
||||
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;
|
||||
|
||||
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 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -7,259 +7,145 @@
|
|||
*/
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
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.entity.flow.Executable;
|
||||
import com.yomahub.liteflow.spring.ComponentScaner;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.yomahub.liteflow.entity.flow.Node;
|
||||
import com.yomahub.liteflow.entity.data.CmpStep;
|
||||
import com.yomahub.liteflow.entity.data.CmpStepType;
|
||||
import com.yomahub.liteflow.entity.data.DataBus;
|
||||
import com.yomahub.liteflow.entity.data.Slot;
|
||||
import com.yomahub.liteflow.entity.monitor.CompStatistics;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.monitor.MonitorBus;
|
||||
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 org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 普通组件抽象类
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @author luo yi
|
||||
*/
|
||||
public abstract class NodeComponent{
|
||||
public abstract class NodeComponent {
|
||||
|
||||
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NodeComponent.class);
|
||||
|
||||
private InheritableThreadLocal<Integer> slotIndexTL = new InheritableThreadLocal<Integer>();
|
||||
|
||||
@Autowired(required = false)
|
||||
private MonitorBus monitorBus;
|
||||
|
||||
private String nodeId;
|
||||
|
||||
private String name;
|
||||
//是否结束整个流程,这个只对串行流程有效,并行流程无效
|
||||
private InheritableThreadLocal<Boolean> isEndTL = new InheritableThreadLocal<>();
|
||||
|
||||
private NodeTypeEnum type;
|
||||
|
||||
// 这是自己的实例,取代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 NodeExecutor> nodeExecutorClass = DefaultNodeExecutor.class;
|
||||
|
||||
/** 当前对象为单例,注册进spring上下文,但是node实例不是单例,这里通过对node实例的引用来获得一些链路属性 **/
|
||||
|
||||
private final TransmittableThreadLocal<Node> refNodeTL = new TransmittableThreadLocal<>();
|
||||
|
||||
/**
|
||||
******************* 以下的属性为线程附加属性********************
|
||||
* 线程属性是指每一个request的值都是不一样的
|
||||
* 这里NodeComponent是单例,所以要用ThreadLocal来修饰
|
||||
*/
|
||||
|
||||
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信息
|
||||
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);
|
||||
|
||||
LOG.info("[{}]:[O]start component[{}] execution",slot.getRequestId(),this.getClass().getSimpleName());
|
||||
slot.addStep(new CmpStep(nodeId, CmpStepType.SINGLE));
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
|
||||
try {
|
||||
// 前置处理
|
||||
self.beforeProcess();
|
||||
|
||||
// 主要的处理逻辑
|
||||
self.process();
|
||||
|
||||
// 成功后回调方法
|
||||
self.onSuccess();
|
||||
|
||||
// 步骤状态设为true
|
||||
cmpStep.setSuccess(true);
|
||||
//process前置处理
|
||||
if(ComponentScaner.cmpAroundAspect != null){
|
||||
ComponentScaner.cmpAroundAspect.beforeProcess(slot);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// 步骤状态设为false,并加入异常
|
||||
cmpStep.setSuccess(false);
|
||||
cmpStep.setException(e);
|
||||
|
||||
// 执行失败后回调方法
|
||||
// 这里要注意,失败方法本身抛出错误,只打出堆栈,往外抛出的还是主要的异常
|
||||
try {
|
||||
self.onError(e);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String errMsg = StrUtil.format("component[{}] onError method happens exception", this.getDisplayName());
|
||||
LOG.error(errMsg, ex);
|
||||
}
|
||||
throw e;
|
||||
process();
|
||||
|
||||
//process后置处理
|
||||
if(ComponentScaner.cmpAroundAspect != null){
|
||||
ComponentScaner.cmpAroundAspect.afterProcess(slot);
|
||||
}
|
||||
finally {
|
||||
// 后置处理
|
||||
self.afterProcess();
|
||||
|
||||
stopWatch.stop();
|
||||
final long timeSpent = stopWatch.getTotalTimeMillis();
|
||||
LOG.info("component[{}] finished in {} milliseconds", this.getDisplayName(), timeSpent);
|
||||
stopWatch.stop();
|
||||
long timeSpent = stopWatch.getTime();
|
||||
|
||||
cmpStep.setEndTime(new Date());
|
||||
// slot.addStep(new CmpStep(nodeId, CmpStepType.END));
|
||||
|
||||
// 往CmpStep中放入时间消耗信息
|
||||
cmpStep.setTimeSpent(timeSpent);
|
||||
//性能统计
|
||||
CompStatistics statistics = new CompStatistics();
|
||||
statistics.setComponentClazzName(this.getClass().getSimpleName());
|
||||
statistics.setTimeSpent(timeSpent);
|
||||
if (ObjectUtil.isNotNull(monitorBus)){
|
||||
monitorBus.addStatistics(statistics);
|
||||
}
|
||||
|
||||
// 性能统计
|
||||
if (ObjectUtil.isNotNull(monitorBus)) {
|
||||
CompStatistics statistics = new CompStatistics(this.getClass().getSimpleName(), timeSpent);
|
||||
monitorBus.addStatistics(statistics);
|
||||
if(this instanceof NodeCondComponent){
|
||||
String condNodeId = slot.getCondResult(this.getClass().getName());
|
||||
if(StringUtils.isNotBlank(condNodeId)){
|
||||
Node thisNode = FlowBus.getNode(nodeId);
|
||||
Executable condExecutor = thisNode.getCondNode(condNodeId);
|
||||
if(condExecutor != null){
|
||||
condExecutor.execute(slotIndexTL.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
LOG.debug("[{}]:componnet[{}] finished in {} milliseconds",slot.getRequestId(),this.getClass().getSimpleName(),timeSpent);
|
||||
}
|
||||
|
||||
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 onError(Exception e) throws Exception {
|
||||
// 如果需要在抛错后回调某一段逻辑,请覆盖这个方法
|
||||
// 全局切面只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
|
||||
// 非spring环境下,全局切面为空实现
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().onError(this.self, e);
|
||||
}
|
||||
|
||||
public void afterProcess() {
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(this.self);
|
||||
}
|
||||
|
||||
// 是否进入该节点
|
||||
public boolean isAccess() {
|
||||
/**
|
||||
* 是否进入该节点
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isAccess(){
|
||||
return true;
|
||||
}
|
||||
|
||||
// 出错是否继续执行
|
||||
/**
|
||||
* 出错是否继续执行(这个只适用于串行流程,并行节点不起作用)
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isContinueOnError() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 是否结束整个流程(不往下继续执行)
|
||||
/**
|
||||
* 是否结束整个流程(不往下继续执行)
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isEnd() {
|
||||
Boolean isEnd = this.refNodeTL.get().getIsEnd();
|
||||
if (ObjectUtil.isNull(isEnd)) {
|
||||
Boolean isEnd = isEndTL.get();
|
||||
if(isEnd == null){
|
||||
return false;
|
||||
}else {
|
||||
return isEnd;
|
||||
}else{
|
||||
return isEndTL.get();
|
||||
}
|
||||
}
|
||||
|
||||
// 设置是否结束整个流程
|
||||
public void setIsEnd(boolean isEnd) {
|
||||
this.refNodeTL.get().setIsEnd(isEnd);
|
||||
/**
|
||||
* 设置是否结束整个流程
|
||||
* @param 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() {
|
||||
return this.getSlot().getFirstContextBean();
|
||||
}
|
||||
|
||||
public <T> T getContextBean(Class<T> contextBeanClazz) {
|
||||
return this.getSlot().getContextBean(contextBeanClazz);
|
||||
}
|
||||
|
||||
public <T> T getContextBean(String contextName) {
|
||||
return this.getSlot().getContextBean(contextName);
|
||||
public <T extends Slot> T getSlot(){
|
||||
return DataBus.getSlot(this.slotIndexTL.get());
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
|
|
@ -269,176 +155,4 @@ public abstract class NodeComponent{
|
|||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public NodeComponent getSelf() {
|
||||
return self;
|
||||
}
|
||||
|
||||
public void setSelf(NodeComponent self) {
|
||||
this.self = self;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NodeTypeEnum getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(NodeTypeEnum type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public <T> void sendPrivateDeliveryData(String nodeId, T t) {
|
||||
this.getSlot().setPrivateDeliveryData(nodeId, t);
|
||||
}
|
||||
|
||||
public <T> T getPrivateDeliveryData() {
|
||||
return this.getSlot().getPrivateDeliveryData(this.getNodeId());
|
||||
}
|
||||
|
||||
public int getRetryCount() {
|
||||
return retryCount;
|
||||
}
|
||||
|
||||
public void setRetryCount(int retryCount) {
|
||||
this.retryCount = retryCount;
|
||||
}
|
||||
|
||||
public Class<? extends Exception>[] getRetryForExceptions() {
|
||||
return retryForExceptions;
|
||||
}
|
||||
|
||||
public void setRetryForExceptions(Class<? extends Exception>[] retryForExceptions) {
|
||||
this.retryForExceptions = retryForExceptions;
|
||||
}
|
||||
|
||||
public Class<? extends NodeExecutor> getNodeExecutorClass() {
|
||||
return nodeExecutorClass;
|
||||
}
|
||||
|
||||
public void setNodeExecutorClass(Class<? extends NodeExecutor> nodeExecutorClass) {
|
||||
this.nodeExecutorClass = nodeExecutorClass;
|
||||
}
|
||||
|
||||
public String getTag() {
|
||||
return this.refNodeTL.get().getTag();
|
||||
}
|
||||
|
||||
public MonitorBus getMonitorBus() {
|
||||
return monitorBus;
|
||||
}
|
||||
|
||||
public void setMonitorBus(MonitorBus monitorBus) {
|
||||
this.monitorBus = monitorBus;
|
||||
}
|
||||
|
||||
public <T> T getRequestData() {
|
||||
return getSlot().getRequestData();
|
||||
}
|
||||
|
||||
public <T> T getSubChainReqData() {
|
||||
return getSlot().getChainReqData(this.getCurrChainId());
|
||||
}
|
||||
|
||||
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() {
|
||||
return getSlot().getChainName();
|
||||
}
|
||||
|
||||
public String getChainId() {
|
||||
return getSlot().getChainId();
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (StrUtil.isEmpty(this.name)) {
|
||||
return this.nodeId;
|
||||
}
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@
|
|||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.cmpStep.cmp;
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("a")
|
||||
public class ACmp extends NodeComponent {
|
||||
public abstract class NodeCondComponent extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() throws Exception {
|
||||
Thread.sleep(5000L);
|
||||
System.out.println("ACmp executed!");
|
||||
String nodeId = this.processCond();
|
||||
this.getSlot().setCondResult(this.getClass().getName(), nodeId);
|
||||
}
|
||||
|
||||
public abstract String processCond() throws Exception;
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
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 {
|
||||
|
||||
@Override
|
||||
public void process() throws Exception {
|
||||
String nodeId = this.processSwitch();
|
||||
this.getSlot().setSwitchResult(this.getMetaValueKey(), nodeId);
|
||||
}
|
||||
|
||||
// 用以返回路由节点的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 +0,0 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
* 脚本接口
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.9.0
|
||||
*/
|
||||
public interface ScriptComponent {
|
||||
|
||||
/**
|
||||
* 用于维护脚本类型和脚本 cmp 的映射关系
|
||||
*/
|
||||
Map<NodeTypeEnum, Class<?>> ScriptComponentClassMap = new HashMap<NodeTypeEnum, Class<?>>() {
|
||||
{
|
||||
put(NodeTypeEnum.SCRIPT, ScriptCommonComponent.class);
|
||||
put(NodeTypeEnum.SWITCH_SCRIPT, ScriptSwitchComponent.class);
|
||||
put(NodeTypeEnum.BOOLEAN_SCRIPT, ScriptBooleanComponent.class);
|
||||
put(NodeTypeEnum.FOR_SCRIPT, ScriptForComponent.class);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 加载脚本
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +0,0 @@
|
|||
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 {
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package com.yomahub.liteflow.core.proxy;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public class LiteFlowMethodBean {
|
||||
|
||||
private String methodName;
|
||||
|
||||
private Method method;
|
||||
|
||||
public LiteFlowMethodBean(String methodName, Method method) {
|
||||
this.methodName = methodName;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getMethodName() {
|
||||
return methodName;
|
||||
}
|
||||
|
||||
public void setMethodName(String methodName) {
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public abstract class AbsSlot implements Slot{
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Slot.class);
|
||||
|
||||
private final String REQUEST = "request";
|
||||
|
||||
private final String RESPONSE = "response";
|
||||
|
||||
private final String CHAINNAME = "chain_name";
|
||||
|
||||
private final String COND_NODE_PREFIX = "cond_";
|
||||
|
||||
private final String NODE_INPUT_PREFIX = "input_";
|
||||
|
||||
private final String NODE_OUTPUT_PREFIX = "output_";
|
||||
|
||||
private final String CHAIN_REQ_PREFIX = "chain_req_";
|
||||
|
||||
private final String REQUEST_ID = "req_id";
|
||||
|
||||
private boolean isSuccess = true;
|
||||
|
||||
private String errorMsg;
|
||||
|
||||
private Deque<CmpStep> executeSteps = new ArrayDeque<CmpStep>();
|
||||
|
||||
protected ConcurrentHashMap<String, Object> dataMap = new ConcurrentHashMap<String, Object>();
|
||||
|
||||
public <T> T getInput(String nodeId){
|
||||
return (T)dataMap.get(NODE_INPUT_PREFIX + nodeId);
|
||||
}
|
||||
|
||||
public <T> T getOutput(String nodeId){
|
||||
return (T)dataMap.get(NODE_OUTPUT_PREFIX + nodeId);
|
||||
}
|
||||
|
||||
public <T> void setInput(String nodeId,T t){
|
||||
dataMap.put(NODE_INPUT_PREFIX + nodeId, t);
|
||||
}
|
||||
|
||||
public <T> void setOutput(String nodeId,T t){
|
||||
dataMap.put(NODE_OUTPUT_PREFIX + nodeId, t);
|
||||
}
|
||||
|
||||
public <T> T getRequestData(){
|
||||
return (T)dataMap.get(REQUEST);
|
||||
}
|
||||
|
||||
public <T> void setRequestData(T t){
|
||||
dataMap.put(REQUEST, t);
|
||||
}
|
||||
|
||||
public <T> T getResponseData(){
|
||||
return (T)dataMap.get(RESPONSE);
|
||||
}
|
||||
|
||||
public <T> void setResponseData(T t){
|
||||
dataMap.put(RESPONSE, t);
|
||||
}
|
||||
|
||||
public <T> T getChainReqData(String chainId) {
|
||||
return (T)dataMap.get(CHAIN_REQ_PREFIX + chainId);
|
||||
}
|
||||
|
||||
public <T> void setChainReqData(String chainId, T t) {
|
||||
dataMap.put(CHAIN_REQ_PREFIX + chainId, t);
|
||||
}
|
||||
|
||||
public <T> T getData(String key){
|
||||
return (T)dataMap.get(key);
|
||||
}
|
||||
|
||||
public <T> void setData(String key, T t){
|
||||
dataMap.put(key, t);
|
||||
}
|
||||
|
||||
public <T> void setCondResult(String key, T t){
|
||||
dataMap.put(COND_NODE_PREFIX + key, t);
|
||||
}
|
||||
|
||||
public <T> T getCondResult(String key){
|
||||
return (T)dataMap.get(COND_NODE_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setChainName(String chainName) {
|
||||
dataMap.put(CHAINNAME, chainName);
|
||||
}
|
||||
|
||||
public String getChainName() {
|
||||
return (String)dataMap.get(CHAINNAME);
|
||||
}
|
||||
|
||||
public void addStep(CmpStep step){
|
||||
this.executeSteps.add(step);
|
||||
}
|
||||
|
||||
public void printStep(){
|
||||
StringBuffer str = new StringBuffer();
|
||||
CmpStep cmpStep = null;
|
||||
for (Iterator<CmpStep> it = executeSteps.iterator(); it.hasNext();) {
|
||||
cmpStep = it.next();
|
||||
str.append(cmpStep);
|
||||
if(it.hasNext()){
|
||||
str.append("==>");
|
||||
}
|
||||
}
|
||||
LOG.info("[{}]:CHAIN_NAME[{}]\n{}",getRequestId(),this.getChainName(),str.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateRequestId() {
|
||||
dataMap.put(REQUEST_ID, new Long(System.nanoTime()).toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestId() {
|
||||
return (String)dataMap.get(REQUEST_ID);
|
||||
}
|
||||
|
||||
public Deque<CmpStep> getExecuteSteps() {
|
||||
return executeSteps;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
isSuccess = success;
|
||||
}
|
||||
|
||||
public String getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
public void setErrorMsg(String errorMsg) {
|
||||
this.errorMsg = errorMsg;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
|
||||
public class CmpStep {
|
||||
private String nodeId;
|
||||
|
||||
private CmpStepType stepType;
|
||||
|
||||
public CmpStep(String nodeId, CmpStepType stepType) {
|
||||
this.nodeId = nodeId;
|
||||
this.stepType = stepType;
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
return nodeId;
|
||||
}
|
||||
|
||||
public void setNodeId(String nodeId) {
|
||||
this.nodeId = nodeId;
|
||||
}
|
||||
|
||||
public CmpStepType getStepType() {
|
||||
return stepType;
|
||||
}
|
||||
|
||||
public void setStepType(CmpStepType stepType) {
|
||||
this.stepType = stepType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if(stepType.equals(CmpStepType.SINGLE)) {
|
||||
return MessageFormat.format("{0}", nodeId);
|
||||
}else {
|
||||
return MessageFormat.format("{0}({1})", nodeId,stepType);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}else {
|
||||
if(getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}else {
|
||||
if(((CmpStep)obj).getNodeId().equals(this.getNodeId())) {
|
||||
return true;
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
public enum CmpStepType {
|
||||
START,
|
||||
END,
|
||||
SINGLE;
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.util.SpringAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DataBus {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DataBus.class);
|
||||
|
||||
public static AtomicInteger OCCUPY_COUNT = new AtomicInteger(0);
|
||||
|
||||
private static Slot[] slots;
|
||||
|
||||
static {
|
||||
LiteflowConfig liteflowConfig = SpringAware.getBean(LiteflowConfig.class);
|
||||
int slotSize = 1024;
|
||||
if (ObjectUtil.isNotNull(liteflowConfig)){
|
||||
if (ObjectUtil.isNotNull(liteflowConfig.getSlotSize())){
|
||||
slotSize = liteflowConfig.getSlotSize();
|
||||
}
|
||||
}
|
||||
slots = new Slot[slotSize];
|
||||
}
|
||||
|
||||
public synchronized static int offerSlot(Class<? extends Slot> slotClazz){
|
||||
try{
|
||||
for(int i = 0; i < slots.length; i++){
|
||||
if(slots[i] == null){
|
||||
slots[i] = slotClazz.newInstance();
|
||||
OCCUPY_COUNT.incrementAndGet();
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}catch(Exception e){
|
||||
LOG.error("offer slot error",e);
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Slot> T getSlot(int slotIndex){
|
||||
return (T)slots[slotIndex];
|
||||
}
|
||||
|
||||
public static void releaseSlot(int slotIndex){
|
||||
if(slots[slotIndex] != null){
|
||||
LOG.info("[{}]:slot[{}] released",slots[slotIndex].getRequestId(),slotIndex);
|
||||
slots[slotIndex] = null;
|
||||
OCCUPY_COUNT.decrementAndGet();
|
||||
}else{
|
||||
LOG.warn("slot[{}] already has been released",slotIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,15 +5,8 @@
|
|||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.enums;
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
/**
|
||||
* 可执行节点枚举
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public enum ExecuteableTypeEnum {
|
||||
|
||||
CHAIN, CONDITION, NODE
|
||||
public class DefaultSlot extends AbsSlot {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.data;
|
||||
|
||||
public interface Slot {
|
||||
public <T> T getInput(String nodeId);
|
||||
|
||||
public <T> T getOutput(String nodeId);
|
||||
|
||||
public <T> void setInput(String nodeId,T t);
|
||||
|
||||
public <T> void setOutput(String nodeId,T t);
|
||||
|
||||
public <T> T getRequestData();
|
||||
|
||||
public <T> void setRequestData(T t);
|
||||
|
||||
public <T> T getResponseData();
|
||||
|
||||
public <T> void setChainReqData(String chainId, T t);
|
||||
|
||||
public <T> T getChainReqData(String chainId);
|
||||
|
||||
public <T> void setResponseData(T t);
|
||||
|
||||
public <T> T getData(String key);
|
||||
|
||||
public <T> void setData(String key, T t);
|
||||
|
||||
public <T> void setCondResult(String key, T t);
|
||||
|
||||
public <T> T getCondResult(String key);
|
||||
|
||||
public void addStep(CmpStep step);
|
||||
|
||||
public void printStep();
|
||||
|
||||
public void generateRequestId();
|
||||
|
||||
public String getRequestId();
|
||||
|
||||
public void setChainName(String chainName);
|
||||
|
||||
public String getChainName();
|
||||
|
||||
public boolean isSuccess();
|
||||
|
||||
public void setSuccess(boolean success);
|
||||
|
||||
public String getErrorMsg();
|
||||
|
||||
public void setErrorMsg(String errorMsg);
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.entity.data.DataBus;
|
||||
import com.yomahub.liteflow.entity.data.Slot;
|
||||
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
|
||||
import com.yomahub.liteflow.exception.ChainEndException;
|
||||
import com.yomahub.liteflow.exception.FlowSystemException;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.util.SpringAware;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Chain implements Executable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Chain.class);
|
||||
|
||||
private String chainName;
|
||||
|
||||
private List<Condition> conditionList;
|
||||
|
||||
private static int whenMaxWaitSeconds;
|
||||
|
||||
static {
|
||||
LiteflowConfig liteflowConfig = SpringAware.getBean(LiteflowConfig.class);
|
||||
if (ObjectUtil.isNotNull(liteflowConfig)) {
|
||||
whenMaxWaitSeconds = liteflowConfig.getWhenMaxWaitSecond();
|
||||
} else {
|
||||
whenMaxWaitSeconds = 15;
|
||||
}
|
||||
}
|
||||
|
||||
public Chain(String chainName, List<Condition> conditionList) {
|
||||
this.chainName = chainName;
|
||||
this.conditionList = conditionList;
|
||||
}
|
||||
|
||||
public List<Condition> getConditionList() {
|
||||
return conditionList;
|
||||
}
|
||||
|
||||
public void setConditionList(List<Condition> conditionList) {
|
||||
this.conditionList = conditionList;
|
||||
}
|
||||
|
||||
public String getChainName() {
|
||||
return chainName;
|
||||
}
|
||||
|
||||
public void setChainName(String chainName) {
|
||||
this.chainName = chainName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Integer slotIndex) throws Exception {
|
||||
if (CollectionUtils.isEmpty(conditionList)) {
|
||||
throw new FlowSystemException("no conditionList in this chain[" + chainName + "]");
|
||||
}
|
||||
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
|
||||
for (Condition condition : conditionList) {
|
||||
if (condition instanceof ThenCondition) {
|
||||
for (Executable executableItem : condition.getNodeList()) {
|
||||
try {
|
||||
executableItem.execute(slotIndex);
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else if (condition instanceof WhenCondition) {
|
||||
final CountDownLatch latch = new CountDownLatch(condition.getNodeList().size());
|
||||
for (Executable executableItem : condition.getNodeList()) {
|
||||
new WhenConditionThread(executableItem, slotIndex, slot.getRequestId(), latch).start();
|
||||
}
|
||||
latch.await(whenMaxWaitSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecuteTypeEnum getExecuteType() {
|
||||
return ExecuteTypeEnum.CHAIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecuteName() {
|
||||
return chainName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Condition {
|
||||
|
||||
private List<Executable> nodeList;
|
||||
|
||||
public Condition(List<Executable> nodeList) {
|
||||
this.nodeList = nodeList;
|
||||
}
|
||||
|
||||
public List<Executable> getNodeList() {
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
public void setNodeList(List<Executable> nodeList) {
|
||||
this.nodeList = nodeList;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
|
||||
|
||||
public interface Executable {
|
||||
|
||||
void execute(Integer slotIndex) throws Exception;
|
||||
|
||||
ExecuteTypeEnum getExecuteType();
|
||||
|
||||
String getExecuteName();
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.entity.data.DataBus;
|
||||
import com.yomahub.liteflow.entity.data.Slot;
|
||||
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
|
||||
import com.yomahub.liteflow.exception.ChainEndException;
|
||||
import com.yomahub.liteflow.exception.FlowSystemException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Node implements Executable{
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Node.class);
|
||||
|
||||
private String id;
|
||||
|
||||
private String clazz;
|
||||
|
||||
private NodeComponent instance;
|
||||
|
||||
private Map<String, Executable> condNodeMap = new HashMap<String, Executable>();
|
||||
|
||||
public Node(){
|
||||
|
||||
}
|
||||
|
||||
public Node(String id, String clazz, NodeComponent instance) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public void setClazz(String clazz) {
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public NodeComponent getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setInstance(NodeComponent instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public Executable getCondNode(String nodeId){
|
||||
return this.condNodeMap.get(nodeId);
|
||||
}
|
||||
|
||||
public void setCondNode(String nodeId, Executable condNode){
|
||||
this.condNodeMap.put(nodeId, condNode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Integer slotIndex) throws Exception {
|
||||
if(instance == null){
|
||||
throw new FlowSystemException("there is no instance for node id " + id);
|
||||
}
|
||||
instance.setSlotIndex(slotIndex);
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
|
||||
try{
|
||||
if(instance.isAccess()){
|
||||
|
||||
instance.execute();
|
||||
|
||||
if(instance.isEnd()){
|
||||
String errorInfo = StrUtil.format("[{}]:component[{}] lead the chain to end",slot.getRequestId(),instance.getClass().getSimpleName());
|
||||
throw new ChainEndException(errorInfo);
|
||||
}
|
||||
}else{
|
||||
LOG.info("[{}]:[X]skip component[{}] execution",slot.getRequestId(),instance.getClass().getSimpleName());
|
||||
}
|
||||
}catch (Exception e){
|
||||
if(instance.isContinueOnError()){
|
||||
String errorMsg = MessageFormat.format("[{0}]:component[{1}] cause error,but flow is still go on", slot.getRequestId(),id);
|
||||
LOG.error(errorMsg,e);
|
||||
}else{
|
||||
String errorMsg = MessageFormat.format("[{0}]:component[{1}] cause error,error:{2}",slot.getRequestId(),id,e.getMessage());
|
||||
LOG.error(errorMsg,e);
|
||||
throw e;
|
||||
}
|
||||
}finally {
|
||||
instance.removeSlotIndex();
|
||||
instance.removeIsEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecuteTypeEnum getExecuteType() {
|
||||
return ExecuteTypeEnum.NODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExecuteName() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ThenCondition extends Condition {
|
||||
|
||||
public ThenCondition(List<Executable> nodeList) {
|
||||
super(nodeList);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class WhenCondition extends Condition{
|
||||
|
||||
public WhenCondition(List<Executable> nodeList) {
|
||||
super(nodeList);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.yomahub.liteflow.entity.flow;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class WhenConditionThread extends Thread {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WhenConditionThread.class);
|
||||
|
||||
private Executable executableItem;
|
||||
|
||||
private Integer slotIndex;
|
||||
|
||||
private String requestId;
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
public WhenConditionThread(Executable executableItem,Integer slotIndex,String requestId,CountDownLatch latch){
|
||||
this.executableItem = executableItem;
|
||||
this.slotIndex = slotIndex;
|
||||
this.requestId = requestId;
|
||||
this.latch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try{
|
||||
executableItem.execute(slotIndex);
|
||||
}catch(Exception e){
|
||||
LOG.error("item [{}] execute cause error",executableItem.getExecuteName(),e);
|
||||
}finally{
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,14 +5,9 @@
|
|||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.monitor;
|
||||
package com.yomahub.liteflow.entity.monitor;
|
||||
|
||||
/**
|
||||
* 统计类
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public class CompStatistics implements Comparable<CompStatistics> {
|
||||
public class CompStatistics {
|
||||
|
||||
private String componentClazzName;
|
||||
|
||||
|
|
@ -20,14 +15,6 @@ public class CompStatistics implements Comparable<CompStatistics> {
|
|||
|
||||
private long memorySpent;
|
||||
|
||||
private long recordTime;
|
||||
|
||||
public CompStatistics(String componentClazzName, long timeSpent) {
|
||||
this.componentClazzName = componentClazzName;
|
||||
this.timeSpent = timeSpent;
|
||||
this.recordTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public String getComponentClazzName() {
|
||||
return componentClazzName;
|
||||
}
|
||||
|
|
@ -51,17 +38,4 @@ public class CompStatistics implements Comparable<CompStatistics> {
|
|||
public void setMemorySpent(long memorySpent) {
|
||||
this.memorySpent = memorySpent;
|
||||
}
|
||||
|
||||
public long getRecordTime() {
|
||||
return recordTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CompStatistics o) {
|
||||
if (o != null) {
|
||||
return this.recordTime >= o.getRecordTime() ? -1 : 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
/**
|
||||
* @author Yun
|
||||
*/
|
||||
public enum ConditionTypeEnum {
|
||||
|
||||
TYPE_THEN("then", "then"),
|
||||
|
||||
TYPE_WHEN("when", "when"),
|
||||
|
||||
TYPE_SWITCH("switch", "switch"),
|
||||
|
||||
TYPE_IF("if", "if"),
|
||||
|
||||
TYPE_PRE("pre", "pre"),
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,13 +7,6 @@
|
|||
*/
|
||||
package com.yomahub.liteflow.enums;
|
||||
|
||||
/**
|
||||
* 组件步骤类型
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
public enum CmpStepTypeEnum {
|
||||
|
||||
START, END, SINGLE;
|
||||
|
||||
public enum ExecuteTypeEnum {
|
||||
CHAIN,NODE
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
/**
|
||||
* @author guodongqing
|
||||
* @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;
|
||||
|
||||
FlowParserTypeEnum(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 +0,0 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
public enum LiteFlowMethodEnum {
|
||||
|
||||
PROCESS("process", true),
|
||||
PROCESS_SWITCH("processSwitch", true),
|
||||
PROCESS_BOOLEAN("processBoolean", true),
|
||||
PROCESS_FOR("processFor", true),
|
||||
|
||||
PROCESS_ITERATOR("processIterator", true),
|
||||
|
||||
IS_ACCESS("isAccess", false),
|
||||
|
||||
IS_END("isEnd", false),
|
||||
IS_CONTINUE_ON_ERROR("isContinueOnError", false),
|
||||
|
||||
GET_NODE_EXECUTOR_CLASS("getNodeExecutorClass", false),
|
||||
|
||||
ON_SUCCESS("onSuccess", false),
|
||||
|
||||
ON_ERROR("onError", false),
|
||||
|
||||
BEFORE_PROCESS("beforeProcess", false),
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
package com.yomahub.liteflow.enums;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.*;
|
||||
import com.yomahub.liteflow.log.LFLog;
|
||||
import com.yomahub.liteflow.log.LFLoggerManager;
|
||||
import com.yomahub.liteflow.core.proxy.LiteFlowProxyUtil;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 节点类型枚举
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public enum NodeTypeEnum {
|
||||
|
||||
COMMON("common", "普通", false, NodeComponent.class),
|
||||
|
||||
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
|
||||
|
||||
BOOLEAN("boolean", "布尔", false, NodeBooleanComponent.class),
|
||||
|
||||
FOR("for", "循环次数", false, NodeForComponent.class),
|
||||
|
||||
ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class),
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue