diff --git a/README.md b/README.md index 5dd0b8fc4d10c108d7f42a7005b9c2a7fb985138..df53acec20fdb4d12814edb79d4e3fa7e93c0d0d 100644 --- a/README.md +++ b/README.md @@ -1,290 +1,424 @@ -## smart-flow - -### 项目背景 +# 项目背景 业务系统在发展的过程中,业务的逻辑越来越复杂,简单的逻辑步骤拆分已经不能快速适应业务的变化,大部分时候都是在主流程上打补丁的方式进行业务支持。 而且大量的业务逻辑被隐藏在实现细节中,无法从全局看到整体的业务流程。所以在此背景下,需要一个业务编排框架帮助我们将代码逻辑以组件化的方式组织起来,达到快速配置和快速了解业务全貌的作用。 -### 项目介绍 +# 项目介绍 smart-flow 是一个轻量、灵活的业务流程编排框架,支持业务流程中常见的条件分支控制、子流程、业务组件异步和降级等功能。 +# 名词简介 +- Engine smart-flow管理流水线的对象,也是流程引擎进行调用的入口 +- Pipeline 由组件构成的线性流水线,流程引擎执行会按照流水线的配置线性执行 +- Component 业务逻辑的包装层,统一称为组件,smart-flow基于组件层提供各种能力,例如分支选择、降级、异步等 +- Context 执行上下文,用于存放流程引擎执行过程的中间信息 +- Condition 流程中用于分支判断和路由的组件 +- Executable 面向开发者的组件,开发者需要实现该接口,编写业务逻辑 + +# 功能特性 -### 功能特性 -- [X] 支持If、 Switch、 子流程编排 -- [X] 支持组件异步、降级、回滚 -- [X] 支持XML配置解析 -- [X] 支持Spring Xml配置集成 -- [X] 支持引擎执行过程可视化 -- [X] 支持引擎结构可视化 -- [ ] 支持适配组件以复用公共子流程 -- [ ] 支持降级回调 -- [X] 支持引擎数据上报 -- [X] 支持组件管理,动态修改组件属性 -- [ ] 支持条件表达式 -### 项目架构 +## 核心功能 +- [X] 支持基于Java代码或者XML配置的线性流程编排 +- [X] 支持IF、CHOOSE条件分支 +- [X] 支持组件异步 +- [X] 支持组件降级以及降级回调 +- [X] 支持IF、CHOOSE、PIPELINE嵌套子流程 +- [X] 支持组件回滚 +- [X] 支持组件适配,以复用子流程 +- [X] 生成执行路径树 -### 接入指南 -#### 名词简介 +## 高级功能 +- [X] 支持Spring环境XML配置集成 +- [X] 支持实时生成流程引擎结构图 +- [X] 支持多种条件表达式(Groovy、OGNL、JavaScript等) +- [X] springboot集成 -- Engine smart-flow的调用入口,也是用户使用的直接对象 -- Pipeline 业务逻辑流水线,流水线中的每个步骤都是组件,组件可以理解为业务实现的一部分 -- Component 组件, smart-flow基于组件提供各种能力,降级、回滚、异步处理等操作,对使用者透明 -- Context 执行上下文,用于存放流程引擎执行过程的中间信息 -- Condition 流程中用于分支判断和路由的组件 -- Executable 面向使用者的组件,smart-flow中真正执行业务逻辑的组件 + +## 管理功能 +- [X] 支持自定义执行数据采集 +- [X] 支持HTTP方式引擎数据上报(包含执行数据) +- [X] 支持组件动态管理(动态降级、启用组件等功能) +- [ ] 简易管理后台 +- [ ] 支持从数据库等存储介质重新加载 + + + + +# 快速接入 -#### 依赖引入 + +## 依赖引入 smart-flow暂未发布至maven仓库,使用者需要手动将工程克隆至本地进行安装 ```shell git clone https://gitee.com/smartboot/smart-flow.git cd smart-flow -mvn clean install +mvn -DskipTests=true clean install ``` 安装完毕后在项目中进行引入 - +`lastest.version` = 1.0.3 ```XML org.smartboot smart-flow-core - 1.0.0 + ${lastest.version} +``` - - - org.smartboot - smart-flow-spring-extension - 1.0.0 - +## 实现Executable +实现`Executable`接口,或者继承`AbstractExecutable`, 实现`execute`方法 +```java +public class ExampleExecutable extends AbstractExecutable { + + @Override + public void execute(Integer request, String result) { + // 执行业务逻辑 + } +} ``` +`Executable`接口自带泛型,分别为流程引擎的入参和出参,如果想要获取流程引擎上下文信息,覆盖另外的`execute`方法即可 +```java +@Override +public void execute(EngineContext context) { + // 执行业务逻辑 +} +``` -#### 编码方式编排业务流程 -smart-flow支持使用Builder风格的流程编排,各编排器如下: +## 编码方式编排业务流程 +smart-flow支持`Builder`风格的流程引擎编排,例如如下示例: +```java +// 创建名为defaultEngine的流程引擎 +EngineBuilder builder = new EngineBuilder<>("defaultEngine"); -- ExecutableBuilder 将一个`Executable`封装为一个基本组件 -- EngineBuilder 构建一个Engine实例 -- PipelineBuilder 构建一个Pipeline实例,支持在PipelineBuilder构建子流程、If分支、Choose分支 -- IfComponentBuilder 构建一个IfComponent,支持then和else逻辑操作 -- ChooseComponentBuilder构建一个ChooseComponent,支持case和default逻辑操作 +// 创建一条流水线,名字叫main process, 包含2个步骤 +builder.pipeline(new PipelineBuilder<>("main process") + .next(new ExampleExecutable()) + .next(new ExampleExecutable()).build()); -例如以下示例(构建名为main process的pipeline): +// 创建引擎,引擎的名字为defaultEngine +FlowEngine engine = builder.build(); +``` -```Java -// 构建一个EngineBuilder -EngineBuilder builder = new EngineBuilder<>(); -// 构建一个PipelineBuilder,其中pipeline的名字为main process -PipelineBuilder pipelineBuilder = new PipelineBuilder<>("main process"); - -ExecutableBuilder execBuilder = ExecutableBuilder.newBuilder(); -// 构建一个基本component,其中Step5是一个Executable实例 -Component nested = execBuilder.newAdapter(new Step5()); -// step1; -// step2; -// if then step3 else ElseStep -// if then if then step5 -// choose: -// case 1 IntegerStep; -// case null NullStep -// default DefaultStep -// subprocess -// step4 -// ErrorStep -pipelineBuilder.next(new Step1()).next(new Step2()) - .next(new IfCondition()).then(new Step3(), new ElseStep()) - .next(new IfCondition2()).then(new IfComponent<>(new IfCondition3(), nested)) - .choose(new ChooseCondition()) - .newBranch("null", new NullStep()) - .newBranch(1, new IntegerStep()).end(new DefaultStep()) - .pipeline("subprocess").next(new Step4()).next(new ErrorStep()).end(); - -// 流程上下文 -EngineContext context = new EngineContext<>(); -context.setReq(1); -context.setResult("hello"); - -// 流程执行 -builder.pipeline(pipelineBuilder.build()).build().execute(context); -LOGGER.info("trace {}", context.getTrace()); + +## 流程引擎执行 +流程引擎执行必须执行参数(参数可以为空),同时对于返回值可以在调用入口指定,也可以在流程引擎内部的组件指定。 +```java +FlowEngine engine = builder.build(); +engine.execute(1); // 指定入参 +engine.execute(1, ""); // 指定入参以及出参 +engine.execute((Integer)null); // 不指定入参 ``` +流程引擎执行后会返回一个context,可以通过`getResult`获取返回结果 +```java +EngineContext context = engine.execute(1); +String result = context.getResult(); +``` -#### xml格式编排业务流程 -smart-flow还支持使用xml的形式对流程进行编排,使用xml的形式编排更加容易,也更加清晰。 相关xml标签的使用简介参考 (xml配置项说明) -使用示例如下: -```XML - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -``` - -解析xml: -```Java -DefaultParser parser = new DefaultParser(); -parser.parse(this.getClass().getResourceAsStream("/flow-example7.xsd")); -// 获取xml配置中有多少engine -LOGGER.info("engines {}", parser.getEngineNames()); +# 核心功能介绍 -// 获取名为testEngine的Engine实例 -FlowEngine testEngine = parser.getEngine("testEngine"); -EngineContext execute = testEngine.execute(1); -LOGGER.info("trace \n{}", execute.getTrace()); + + +## Executable 组件 + +`Executable`是smart-flow中面向开发者的组件,用户需要使用该接口进行业务逻辑的开发。 + +为了方便用户使用,smart-flow提供了抽象实现`AbstractExecutable`, 用户可以继承`AbstractExecutable` +实现`execute`方法进行快速开发。 + +Tip: 为了后续流程维护,推荐覆盖`describe()`方法,返回当前组件的信息。 + + + +## 基于Java Builder的流程编排方式 +smart-flow提供了基于`Builder`风格的流程编排方式,用户可以使用相应的Builder快速对流程进行编排。 +以下为smart-flow提供的Builder列表: +* 流程引擎构造 +* 流水线构造 +* 组件构造 + + + +### 流程引擎构造 +smart-flow中负责流程引擎构造的Builder类是`EngineBuilder`, `EngineBuilder`接受一个name作为构造入参, +对于`EngineBuilder`以下几个参数是必须的: +* name smart-flow中的每个引擎都必须有名字 +* pipeline 流水线作为流程引擎的执行构建,也是必须的 +* executor 仅当流程中含有异步组件时必须指定 + + + +### 流水线构造 +smart-flow中负责流程引擎构造的Builder类是`PipelineBuilder`, `PipelineBuilder`与`EngineBuilder`类似,必须为其指定名字, +对于该Builder方法介绍如下: +* `next(Component/Executable)`接受组件/可执行接口作为流水线的下一步 +* `next(Condition)` 接受一个`Condition`条件, `PipelineBuilder`会返回一个表示IF条件分支的builder对象 +* `choose(Condition)` 接受一个`Condition`条件, `PipelineBuilder`会返回一个表示CHOOSE条件分支的builder对象 +* `pipeline(name)` 表示为当前流水线添加一个名为name的子流程,`PipelineBuilder`会返回一个新的`PipelineBuilder` + +需要注意的是,如果通过`PipelineBuilder`产生的子流程需要作用到父流程,需要调用`end()`方法而不是`build()`方法。 + + + +### 组件构造 + + + +#### 普通组件构造 +smart-flow中表示基本组件构造的是`ExecutableBuilder`, 该组件接受一个`Executable`实例,返回一个普通组件对象。 +```JAVA +Component component = ExecutableBuiler.newBuilder().newAdapter(new ExampleExecutable()); ``` -#### 与spring结合使用xml编排 +#### IF组件构造 +smart-flow中表示IF组件构造的是`IfComponentBuilder`, smart-flow中if分支选择支持then/else +* `then(Component)` -在spring环境中的编排与单独使用XML并无不同, +只有then,没有else +* `then(Component, Component)` -引入smart-flow的xsd约束文件即可。 +第一个参数表示then,第二个参数表示else -示例如下: +同时为了方便使用,还提供了相应的`Executable`接口,含义与以上两个接口一致: +* `then(Executable)` +* `then(Executable, Executable)` -```XML + + +#### CHOOSE组件构造 +smart-flow中表示IF组件构造的是`ChooseBuilder`, smart-flow中choose分支选择支持指定case和default,等价于Java中的switch语句。 +* `newBranch(Object, Component)` +* `newBranch(Object, Executable)` + +表示新增一个分支选择,分支选择的值为Object, 分支执行步骤为Component/Executable +* `end()` + +结束当前choose分支构造,并且将构造完成的choose组件放置到pipeline中 +* `end(Component/Executable)` + +结束当前choose分支构造, 分支的默认逻辑为end方法的参数,并且将构造完成的choose组件放置到pipeline中 + + + +#### 子流程的嵌套 +可以通过`PipelineBuilder`的build方法,产生一个包含子流程的组件 + + + +#### 属性设置 +相关组件Builder提供了以下方法,用于设置组件的属性。smart-flow会基于为组件设置的属性进行相关的操作,例如降级、回滚等操作。 +```JAVA +builder.apply(Attributes.ASYNC, true).apply(Attributes.TIMEOUT, 1000); +``` +例如以上示例为组件设置了异步属性,同时异步超时时间为1000ms, 更多关于可设置的属性请参阅`Attributes`枚举。 + +需要注意的是,部分属性对于IF、CHOOSE、子流程组件可能不会生效, +例如`rollback`, 这是因为他们属于包装类型的组件,是否需要回滚取决于真实组件的属性。 + + + +## 基于XML的编排方式 + + + +### 引入xsd约束 +为了在使用xml方式编排时有相应提示,smart-flow提供了基于xsd格式的约束,开发者可以参考以下的示例进行引入 +```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +``` +* http://org.smartboot/smart-flow http://org.smartboot/smart-flow-1.0.1.xsd +* xml的root元素必须engines - + + +### xsd各项标签介绍 +* engines 根元素,所有的标签必须在engines标签内部 +* engine 定义一个engine +* pipeline 定义一个流水线 +* if 新增一个分支 +* choose 新增choose分支 +* script 新增条件表达式 +* component 新增一个普通组件 + + + +### 标签约束 + + + +#### engines标签 +engines标签下只能存在engine、pipeline、以及script标签 + + + +#### engine标签 +engine标签下不能含有子标签,且必须为其指定属性: +* name 表示引擎的名称 +* pipeline 引擎包含流水线的名称 + +示例: +```xml + ``` -代码中引用,可直接通用依赖注入引用流程引擎实例 -```Java -ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml"); -FlowEngine testEngine = (FlowEngine)context.getBean("testEngine", FlowEngine.class); -EngineContext executeContext = testEngine.execute(1); -LOGGER.info("trace\n {}", executeContext.getTrace()); +#### pipeline标签 +pipeline标签用于定义一条流水线,必须为其指定: +* name 表示引擎的名称,且不存存在重复的流水线 +* 至少2个以及以上的子标签 + +pipeline支持的子标签列表: +* pipeline 用于嵌套子流程 +* if 新增if分支处理 +* choose 新增choose分支处理 +* component 新增普通步骤 + + + +#### component标签 +component标签用于定义一个基本组件或者嵌套子流程,component标签支持以下属性设置 +* subprocess 用来嵌套一个子流程,subprocess的值为嵌套子流程的名称 +* execute 可执行器的class类名 + +同时为component设置subprocess和execute,优先识别subprocess。 + +例如以下例子component将被解析为嵌套子流程组件,组件包含的pipeline为subprocess#2: +```xml + ``` -### 组件介绍和高级特性 -#### Executable 组件 +#### if标签 +if标签用于定义if条件分支,以下设置对于if标签是必须的: +* test 指定条件class或者是一个script标签的名字 +* 必须含有then标签,但else标签不是必须的 +* then和else标签最多出现一次 -`Executable`是smart-flow中面向业务, 用于执行真实业务逻辑的组件, -用户需要实现该接口进行业务逻辑的开发。 +then/else标签类似于component标签和pipeline标签,开发者可以通过属性指定属性`execute`的值为可执行器的类名, +也可以在then/else标签下面嵌套pipeline、if、choose等标签。 + +但如果同时为then/else指定execute属性和子标签,smart-flow将优先以execute的情况进行解析 + +以下为示例: +```xml + + + + + + + + + + + +``` -为了方便用户使用,smart-flow提供了抽象实现`AbstractExecutable`, 用户可以继承`AbstractExecutable` -实现`execute`方法进行快速开发。 例如以下例子: -```Java -/** - * 业务的出入参数为Integer、String - */ -public class Step1 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step1 == " + integer); - } -} +#### choose标签 +choose标签用于定义choose条件分支,choose标签的约束与if标签是类型的: +* test 指定条件class或者是一个script标签的名字 +* case标签必须出现2次以及以上,default标签最多存在一次 + +case/default标签约束与if中then/else标签一致,区别在于case标签还必须额外指定属性`when`, +表示什么情况下选择当前分支。 + +以下为示例: +```xml + + + + + + + + ``` -`AbstractExecutable`带有泛型,用户可以根据业务实际情况定义不同的类型,但请保证在一条pipeline中的所有Executable实现泛型都是一致的。 -如想在多条不同的pipeline中复用同样的子流程片段,后续smart-flow会提供适配组件进行支持。 -Tip: 如果在spring环境中,推荐继承`NamedAbstractExecutable`。 +#### script标签 +script标签用于定义条件表达式,if/choose标签的`test`属性可以指定为script的名字,script标签以下属性是必须的 +* name 唯一标识script,同时也是if/choose标签引用的依据 +* type 指定script脚本类型,type的值支持class和短名称,class优先级 > 短名称 + * class必须为ScriptCondition类的一个子类 + * 短名称,现支持短名称列表:OGNL、Groovy、JavaScript、qlExpress +* 必须为script指定脚本,script中几个默认参数为: + * request 业务请求入参 + * result 业务请求出参 + * context smart-flow流程引擎执行上下文 +以下为相关示例: +```xml + + var local = 2; + local == request; + +``` -#### **组件回滚** -smart-flow中所有组件默认都是不可回滚的,需要使用者手动配置回滚属性。 回滚属性rollback=true, -在流程引擎执行失败或者手动设置回滚后,将按照已执行组件的执行顺序逆序执行回滚方法。 + + +### 为标签设置属性 +与builder方式类似,你可以通过直接在component、if、then等标签上指定属性名进行属性的设置, +smart-flow会识别提取在`Attributes`中定义的属性,并将他们设置到组件上去。 + + + +### Xml的解析 +smart-flow提供默认的Parser解析`DefaultParser`,`DefaultParser`接受一串流或者一串文件作为解析参数,同时提供解析结果的获取。 +* `getEngineNames` 获取当前解析到的engine名称列表 +* `getEngines` 获取当前解析得到的所有engines,返回值为map,key为engine名称,value为engine实例 +* `getEngine(String)` 获取名为参数的engine + +以下为使用示例: +```java +DefaultParser parser = new DefaultParser(); +parser.parse(this.getClass().getResourceAsStream("/flow-example7.xml")); +LOGGER.info("parsed engines {}", parser.getEngineNames()); + +FlowEngine testEngine = parser.getEngine("testEngine"); +EngineContext execute = testEngine.execute(1); +LOGGER.info("trace \n{}", execute.getTrace()); +``` + + + +### 完整xml示例 +参考文件 [flow-example7.xml](https://gitee.com/smartboot/smart-flow/blob/master/smart-flow-example/src/main/resources/flow-example7.xml) + + + + +## **组件回滚** +smart-flow中所有组件默认都是不可回滚的,需要使用者手动配置回滚属性rollback=true, +在流程引擎执行失败或者手动设置回滚后,将按照已执行组件的执行顺序逆序执行回滚方法。 * builder设置回滚属性 ```java builder.apply(Attributes.ROLLBACK, true); @@ -292,20 +426,24 @@ builder.apply(Attributes.ROLLBACK, true); * XML方式设置回滚属性 ```xml - + ``` -关于回滚方法定义,请参考`org.smartboot.flow.core.Rollback`, 同时`Executable`接口继承于`Rollback`, -而为了方便使用,`AbstractExecutable`对相关rollback方法进行了空实现,业务上想要实现业务回滚覆盖相应的方法即可。 +关于回滚方法定义,请参考`org.smartboot.flow.core.Rollback`。 `Executable`接口继承于`Rollback`, +而为了方便使用,`AbstractExecutable`对rollback方法进行了空实现,如果想要实现业务回滚覆盖相应的方法即可。 -需要注意的是, 回滚组件会在以下2种场景下失效: +需要注意的是, 回滚组件会在以下几种场景下失效: * 该组件还未执行到,流程便终止了 * 该组件同时是可降级组件,且降级后的后续组件并未发生异常,正常执行完毕 -#### **组件降级** -smart-flow中所有组件默认都是不可降级的,需要使用者手动配置可降级属性degradable=true, +## **组件降级与回调** + + + +### 组件降级 +smart-flow中所有组件默认都是不可降级的,需要使用者手动配置属性degradable=true, 在流程引擎执行失败后,如果组件是可降级组件,将不会中断流程,而是会继续往下执行。 * builder设置属性 ```java @@ -314,10 +452,32 @@ builder.apply(Attributes.DEGRADABLE, true); * XML方式设置属性 ```xml - + ``` -#### 组件异步 + + +### 降级回调 +降级回调仅对基本组件生效,在基本组件的`Executable`执行失败后,如果当前组件设置了degradable=true,将触发降级回调。 +降级回调接口参照`org.smartboot.flow.core.DegradeCallback`。 + +* builder设置属性 +```java +builder.apply(Attributes.DEGRADE_CALLBACK, "degrade-callback classname or object"); +``` +* XML方式设置属性 + +```xml + +``` + +为了便于使用,如果基本组件的`Executable`对象同时也是`DegradeCallback`的子类,那么`Executable`也将被当作降级回调对象触发降级回调, +但显示指定降级回调对象的回调优先级大于`Executable` + + + + +## 组件异步 smart-flow支持组件异步执行,只需要进行2步即可开启异步执行。 @@ -326,25 +486,25 @@ smart-flow支持组件异步执行,只需要进行2步即可开启异步执行 若使用builder方式构建流程时,可以通过以下方式设置属性: ```java -ExecutableBuilder builder = ExecutableBuilder.newBuilder(); builder.apply(Attributes.ASYNC, true).apply(Attributes.TIMEOUT, 1000); -// 得到含有异步属性的组件 -Component asyncComponent = builder.newAdapter(new Step1()); ``` 若使用XML方式进行编排,只需为组件添加2个属性即可 ```xml - + ``` 异步组件可与dependsOn结合使用,若组件1和组件2异步调用,组件3需要等待组件1和组件2执行完毕,那么可以为组件3设置dependsOn属性 ```xml - + ``` -#### 优雅中断流程 -smart-flow在发生异常时,会对流程进行终止,并对已执行组件进行回滚。 相比这种异常中断的方式而言,smart-flow提供了一种更优雅的中断方式。 -使用者可以通过执行上文手动终止/回滚流程。 + + +## 优雅中断流程 +smart-flow默认发生异常时,会对流程进行终止,并对已执行组件进行回滚。 如果业务上想要中断流程,只能以异常的形式进行中断,但这可能会触发回滚 +相比这种异常中断的方式而言,smart-flow提供了一种更优雅的中断方式。 +使用者可以通过执行上下文对象手动终止/回滚流程。 ```java public class Step1 extends AbstractExecutable { @@ -356,7 +516,9 @@ public class Step1 extends AbstractExecutable { } ``` -#### 组件静默 + + +## 组件静默 smart-flow中所有组件默认都是开启可执行状态,如果需要跳过某个组件的执行,可以通过设置属性enabled=false进行跳过 * builder设置属性 ```java @@ -365,64 +527,25 @@ builder.apply(Attributes.ENABLED, false); * XML方式设置属性 ```xml - + ``` -#### 组件适配 -待开发 -#### 组件管理 -smart-flow中的每一个`FlowEngine`实例默认都会加入`EngineManager`的管理, -用户可以通过使用`DefaultEngineManager`查看指定名称的engine组件数据,并动态地修改组件属性。 -例如以下例子,动态禁用某个组件: -```java -EngineManager defaultManager = DefaultEngineManager.getDefaultManager(); -// 通过engine名称获取engine模型数据 -EngineModel engineModel = defaultManager.getEngineModel("defaultEngine"); -// 获取该engine下所有的组件(包括嵌套子流程、if和choose分支) -Map components = engineModel.getComponents(); -String identifier = null; -// 获取组件的identifier唯一标识符 -for (Map.Entry modelEntry : components.entrySet()) { - if (modelEntry.getValue().getDescribe().contains("pipeline##subprocess##2")) { - identifier = modelEntry.getKey(); - } -} +## 组件适配 -// 使用标识符修改enabled属性为false,禁用该组件 -AttributeHolder attributeHolder = new AttributeHolder(Attributes.ENABLED, false); -defaultManager.changeAttributes(identifier, attributeHolder); -// 再次执行,查看调用路径,可以看到组件子流程未执行 -context = engine.execute(1); -LOGGER.info("trace {}", context.getTrace()); -``` - -注意:由于组件的名称并不是必须的,所有组件的identifier是由`EngineManager`动态生成, -但组件的名称也会是identifier的一部分构成,所以建议所有组件都指定名称。 以下为指定与不指定的对比: -```TEXT -不指定 -defaultEngine-subprocess##2-none-qiewmdn4 -指定为step5 -identifier defaultEngine-DefaultPipeline-step5-5tqygjpx -``` - - -#### 查看执行调用路径 +## 查看组件执行路径 smart-flow默认提供执行路径日志, 在一次执行完毕后,可以通过`context.getTrace()`获取本次调用路径日志, 能够帮助开发者更加清晰的了解调用过程。具体使用以及效果如下: - -完整代码见`AsyncTest.java` - ```Java FlowEngine engine = builder.pipeline(pipelineBuilder.build()).executor(Executors.newFixedThreadPool(10)).build(); engine.execute(context); LOGGER.info("trace {}", context.getTrace()); ``` -输出效果如下(异步组件的调用使用~~~进行表示): +输出效果如下(异步组件的调用使用~~~进行表示): ```text flow-engine##defaultEngine escaped 310ms @@ -459,41 +582,124 @@ flow-engine##defaultEngine escaped 310ms ``` -#### xml配置项说明 - 相关xml标签的使用简介如下: -- engines smart-flow以单独的xml形式进行编排时,engines标签必须为Root标签, engines标签下允许出现engine、pipeline以及component标签 -- engine engine标签定义一个流程引擎,必须为engine标签指定name和pipeline属性,其中pipeline属性表示某个pipeline的名称 -- component 可以使用component定义一个组件,使用component标签必须指定type或者ref、subprocess其中任意一个属性,他们优先级从高到低如下 - - type表示一个`Executable`的实现全称限定类名 - - ref表示一个`Executable`的bean名称,仅在spring环境下生效 - - subprocess表示一个pipeline,它的值为pipeline的名称 -- pipeline 可以使用pipeline定义一条流程,其中name属性是必须的,pipeline下允许存在以下标签 - - pipeline, 用户可以在pipeline下使用pipeline来定义一条匿名子流程,在这种情况下,name不是必须的 - - component 定义一个组件或者引用一条子流程,引用子流程的情况下与使用pipeline是等价的 - - if 定义一个if分支选择 - - choose 定义一个switch分支选择 -- if 等同于Java中的if条件,其中test属性是必须的 - - test表示一个`Condition`的子类的全程限定类名 - - ref属性,在spring环境下,可以不指定test属性,但必须指定ref属性,ref的值表示一个`Condition`的子类bean - - 子标签then和else表示分支处理,其中then是必须的,then和else使用类似与Component,但它们也支持像pipeline一样直接定义子流程 -- choose 等于Java中的switch语句块 - - 条件和属性与if一致 - - 子标签case和default, 其中case是必须的,且必须出现2次以及2次以上,case和default的行为与then和else一致 +# 高级功能介绍 + + + +## 条件表达式 +smart-flow中表达条件表达式的类为抽象类`ScriptCondition`,您可以在xml编排中使用script标签来定义一个条件表达式。 +smart-flow中默认提供了常用条件表达式的实现,您只需要在script标签的type属性指定相应的短语即可(需要引入相关依赖)。 +* OGNL 条件表达式为ognl +```xml + + org.smartboot + smart-flow-script-ognl + 1.0.3 + +``` + +* groovy 条件表达式为groovy脚本 +* javascript 条件表达式为javascript + +groovy与javascript使用相同的依赖 +```xml + + org.smartboot + smart-flow-script-groovy + 1.0.3 + +``` + +* qlexpress 条件表达式为qlexpress脚本 + +```xml + + org.smartboot + smart-flow-script-qlexpress + 1.0.3 + +``` +为了方便使用,当使用短语时,您可以不必区分大小写,例如Groovy与GROOVY、groovy都是等价的。 + + + + +### 接入新的条件表达式 +接入新的条件表达式仅需实现抽象类`ScriptCondition`即可,如果想要在smart-flow使用短语来识别新的条件表达式实例, +也只需要在类路径下META-INF下新增名为smart-flow-script的文件,文件的内容为kv键值对,key为短语,v为相应的类名,示例如下: +```text +groovy=org.smartboot.flow.condition.extension.groovy.GroovyScriptCondition +javascript=org.smartboot.flow.condition.extension.groovy.JavaScriptCondition +``` + + + + + +## 与Spring集成 + + + + +### 依赖引入 +如果想要在spring环境下使用smart-flow,需要引入依赖 +```xml + + org.smartboot + smart-flow-spring-extension + 1.0.3 + +``` + + + +### XML编排方式的改变 +与独立使用smart-flow不同,在spring环境中嵌套smart-flow的标签并不需要所有标签都在engines下, +只需要在相关bean文件中引入xsd约束即可开启使用 +```xml + + + +``` + + -其他: if、choose、else、then、case、 default、component支持定义任意属性,其中能够被smart-flow识别的属性将会被设置到组件上去,例如: -- name string类型 表示名称 -- rollback boolean类型 表示是否可回滚 -- degradable boolean类型 表示是否可降级 -- async boolean类型,表示是否可异步 -- timeout number类型,表示异步超时的时间 +#### 普通组件标签的改变 +if、choose、component、then等组件在单独使用时需要单独指定classname,但在spring环境中,可以替换为bean的名称。 +但请保证相关bean的类型是正确的,即: +* test属性指向的bean必须为Condition的子类 +* execute属性指向的bean必须为Executable的子类 -### 流程可视化 +但在spring环境下,您仍可以指定classname。 -#### PlantUML可视化展示 + + +#### script标签的改变 +script标签的改变在于type不再是必须的,但必须遵循以下规则: +* script的name属性必须为已存在的bean名称 +* script的name属性指向的bean必须为ScriptCondition的一个子类 + +在这种情况下,smart-flow会自动为name指向的bean设置script脚本。 + + + + +### spring bean完整示例 +完整示例请参照文件[bean.xml](https://gitee.com/smartboot/smart-flow/blob/master/smart-flow-example/src/main/resources/bean.xml) + + + +## 实时生成流程引擎结构图 smart-flow内嵌plantuml图像生成处理器,通过使用`PlantumlEngineVisitor`访问生成的`FlowEngine`实例, 在项目指定位置生成plantuml文件,例如以下示例,将在项目根目录下生成名为`engine-flow`的文件 ```Java @@ -512,3 +718,147 @@ plantumlEngineVisitor.visit(engine); + + + +# 管理功能 + + + +## 组件管理接口 +smart-flow中的每个`FlowEngine`实例默认都会加入`EngineManager`的管理, +用户可以通过使用`DefaultEngineManager`查看指定名称的engine组件数据,并动态地修改组件属性。 + +例如以下例子,动态禁用某个组件: +```java +EngineManager defaultManager = DefaultEngineManager.getDefaultManager(); +// 通过engine名称获取engine模型数据 +EngineModel engineModel = defaultManager.getEngineModel("defaultEngine"); +// 获取该engine下所有的组件(包括嵌套子流程、if和choose分支) +Map components = engineModel.getComponents(); +String identifier = null; +// 获取组件的identifier唯一标识符 +for (Map.Entry modelEntry : components.entrySet()) { + if (modelEntry.getValue().getDescribe().contains("pipeline##subprocess##2")) { + identifier = modelEntry.getKey(); + } +} + +// 使用标识符修改enabled属性为false,禁用该组件 +AttributeHolder attributeHolder = new AttributeHolder(Attributes.ENABLED, false); +defaultManager.changeAttributes(identifier, attributeHolder); + +// 再次执行,查看调用路径,可以看到组件子流程未执行 +context = engine.execute(1); +LOGGER.info("trace {}", context.getTrace()); + +``` + +注意:组件的名称并不是必须的,但组件的名称也会是identifier的一部分构成,如果没有指定名字,将会以NULL代替, +为了便于管理,建议所有组件都指定名称。 以下为指定与不指定的对比: +```TEXT +不指定 +defaultEngine-subprocess##2-null +指定为step5 +identifier defaultEngine-DefaultPipeline-step5 +``` + + +TIP: +> EngineManager生成组件、流水线的唯一标识Identifier基于父组件名称,例如名为testEngine的引擎下组件和流水线的Identifier都将以 +> testEngine开头。同时smart-flow为了能在分布式环境下唯一确定一个组件,生成的Identifier并不包含随机因素,所以请保证组件名称不会有重复。 + + +## 自定义数据采集 +smart-flow提供了Listener机制,可以在流程引擎组件执行前后、回滚前后进行相应操作,例如统计执行时间、执行次数等操作。 + +```java +public interface ExecutionListener { + + void start(EngineContext context); + void completed(EngineContext context); + void beforeExecute(EngineContext context, Object object); + void afterExecute(EngineContext context, Object object); + void beforeRollback(EngineContext context, Object object); + void afterRollback(EngineContext context, Object object); +} + +``` + +* context +> 当前上下文信息 + +* object +> 当前执行对象,可能是Engine、Pipeline、Component中的任何一个 + + +默认自带数据采集Listener `org.smartboot.flow.core.metrics.MetricExecutionListener`。 +您也可以自定义采集,例如需要以业务线维度进行采集,可以通过context识别到具体业务,然后设置统计模型创建器 +```java +MetricsManager.setMetricsCreator(new CustomMetricsCreator()); +``` + + +## 引擎数据上报 +smart-flow默认提供了基于smart-http的上报组件,默认会以引擎维度上报引擎的结构以及统计数据相关信息。 + +```xml + + + + + +``` + +例如以上示例,会将数据上报至指定链接,5s上报一次。上报数据结构见示例[http上报数据示例](https://gitee.com/smartboot/smart-flow/blob/master/doc/report.json) +当然您也可以通过组件管理接口获取数据,自定义上报数据。 + +## 组件管理 +smart-flow默认提供了基于smart-http拉模式的http管理器,可以定时从服务器拉取相关操作指令,对引擎数据进行调整。 + +例如以下示例:每隔5s请求远程链接获取指令 + +```xml + + + + + + +``` + +### 请求参数模型 +```json +{ + "address": "localhost", + "ip": "127.0.0.1", + "timestamp": 1669960706283, + "engineNames": ["testEngine"] +} + +``` + +### 返回指令模型 +例如以下指令:将组件step5禁用(enabled=false) +```json +[ + { + "action": "CHANGE_ATTRIBUTES", + "timestamp": 1669960706283, + "identifier": "defaultEngine-DefaultPipeline-step5", + "value": "false", + "name": "enabled" + } +] + +``` + +* timestamp是必须的,默认不会执行小于请求timestamp的指令 +* identifier是必须的,用于确定组件/引擎 +* action是必须的,用于确定操作类型,具体见[ManagerAction](https://gitee.com/smartboot/smart-flow/blob/master/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ManagerAction.java) +* name和value,变更组件属性CHANGE_ATTRIBUTES必须的 + +## reload机制 + + + diff --git a/doc/report.json b/doc/report.json new file mode 100644 index 0000000000000000000000000000000000000000..f66eda1d3d6041357f5b2f8ab478edc3a90c74a6 --- /dev/null +++ b/doc/report.json @@ -0,0 +1,948 @@ +{ + "address":"192.168.102.79", + "data":{ + "identifier":"testEngine", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":742 + }, + "execute-total":{ + "value":975 + }, + "execute-count":{ + "value":3 + } + } + }, + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step1", + "executable":"org.smartboot.flow.example.Step1", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step1", + "degradable":"true", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-step1", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step1", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step2", + "executable":"org.smartboot.flow.example.Step2", + "holders":{ + "rollback":"true", + "async":"false", + "name":"step2", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-step2", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step2", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"example-callback", + "executable":"example-callback", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step112", + "degradable":"true", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-step112", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":1 + }, + "execute-total":{ + "value":1 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step112", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"pipeline@subprocess9", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess9", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-subprocess9", + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.AsyncStep1", + "executable":"org.smartboot.flow.example.AsyncStep1", + "holders":{ + "rollback":"true", + "async":"false", + "name":"step100", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess9-subprocess9-step100", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step100", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.AsyncStep2", + "executable":"org.smartboot.flow.example.AsyncStep2", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step102", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess9-subprocess9-step102", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":102 + }, + "execute-total":{ + "value":303 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step102", + "type":"BASIC" + } + ], + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess9-subprocess9", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":103 + }, + "execute-total":{ + "value":304 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"subprocess9" + }, + "type":"PIPELINE" + }, + { + "components":{ + + }, + "describe":"pipeline@subprocess2", + "holders":{ + + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess2", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-subprocess2", + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step3", + "executable":"org.smartboot.flow.example.Step3", + "holders":{ + "rollback":"true", + "async":"false", + "name":"step3", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess2-subprocess2-step3", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step3", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step4", + "executable":"org.smartboot.flow.example.Step4", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step4", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess2-subprocess2-step4", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"step4", + "type":"BASIC" + } + ], + "identifier":"testEngine-subprocess1-anonymous-pipeline-wrapper-subprocess2-subprocess2", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"subprocess2" + }, + "type":"PIPELINE" + }, + { + "components":{ + "else":{ + "components":{ + + }, + "describe":"org.smartboot.flow.example.DefaultStep", + "executable":"org.smartboot.flow.example.DefaultStep", + "holders":{ + "rollback":"false", + "async":"false", + "name":"default", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-default", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":2 + } + } + }, + "name":"default", + "type":"BASIC" + }, + "then":{ + "components":{ + + }, + "describe":"pipeline@anonymous-pipeline-6", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-7", + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step5", + "executable":"org.smartboot.flow.example.Step5", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step5", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-step5", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step5", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step6", + "executable":"org.smartboot.flow.example.Step6", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step6", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-step6", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step6", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"pipeline@subprocess3", + "holders":{ + + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-anonymous-pipeline-wrapper-subprocess3", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-subprocess3", + "pipeline":{ + "components":[ + { + "components":{ + "then":{ + "components":{ + + }, + "describe":"org.smartboot.flow.example.AsyncStep1", + "executable":"org.smartboot.flow.example.AsyncStep1", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step7", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-anonymous-pipeline-wrapper-subprocess3-subprocess3-anonymous-if-step7", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step7", + "type":"BASIC" + } + }, + "condition":"org.smartboot.flow.example.IfCondition2", + "describe":"if@org.smartboot.flow.example.IfCondition2", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-anonymous-pipeline-wrapper-subprocess3-subprocess3-anonymous-if", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"anonymous-if", + "type":"IF" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.AsyncStep2", + "executable":"org.smartboot.flow.example.AsyncStep2", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step8", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-anonymous-pipeline-wrapper-subprocess3-subprocess3-step8", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":103 + }, + "execute-total":{ + "value":103 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step8", + "type":"BASIC" + } + ], + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6-anonymous-pipeline-wrapper-subprocess3-subprocess3", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":103 + }, + "execute-total":{ + "value":103 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"subprocess3" + }, + "type":"PIPELINE" + } + ], + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-7-anonymous-pipeline-6", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":103 + }, + "execute-total":{ + "value":103 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"anonymous-pipeline-6" + }, + "type":"PIPELINE" + } + }, + "condition":"org.smartboot.flow.example.IfCondition", + "describe":"if@org.smartboot.flow.example.IfCondition", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":103 + }, + "execute-total":{ + "value":103 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"anonymous-if", + "type":"IF" + }, + { + "components":{ + "1":{ + "branch":"1", + "components":{ + + }, + "describe":"pipeline@anonymous-pipeline-14", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-choose-branch-1", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-15", + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.IntegerStep", + "executable":"org.smartboot.flow.example.IntegerStep", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step9", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-choose-branch-1-anonymous-pipeline-14-step9", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step9", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.AsyncStep3", + "executable":"org.smartboot.flow.example.AsyncStep3", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step10", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-choose-branch-1-anonymous-pipeline-14-step10", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":204 + }, + "execute-total":{ + "value":204 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step10", + "type":"BASIC" + } + ], + "identifier":"testEngine-subprocess1-anonymous-choose-branch-1-anonymous-pipeline-14", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":204 + }, + "execute-total":{ + "value":204 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"anonymous-pipeline-14" + }, + "type":"PIPELINE" + }, + "default":{ + "branch":"default", + "components":{ + + }, + "describe":"org.smartboot.flow.example.DefaultStep", + "executable":"org.smartboot.flow.example.DefaultStep", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-choose-default", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"", + "type":"BASIC" + }, + "null":{ + "branch":"null", + "components":{ + + }, + "describe":"org.smartboot.flow.example.NullStep", + "executable":"org.smartboot.flow.example.NullStep", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"true", + "enabled":"true", + "timeout":"0", + "degrade-callback":"exampleCallback" + }, + "identifier":"testEngine-subprocess1-anonymous-choose-branch-null", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"", + "type":"BASIC" + } + }, + "condition":"org.smartboot.flow.example.ChooseCondition", + "describe":"choose@org.smartboot.flow.example.ChooseCondition", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-choose", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":204 + }, + "execute-total":{ + "value":205 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"anonymous-choose", + "type":"CHOOSE" + }, + { + "components":{ + "then":{ + "components":{ + + }, + "describe":"pipeline@anonymous-pipeline-20", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-21", + "metrics":{ + "metrics":{ + + } + }, + "name":"anonymous-pipeline-wrapper-21", + "pipeline":{ + "components":[ + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step5", + "executable":"org.smartboot.flow.example.Step5", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step5", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-21-anonymous-pipeline-20-step5", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step5", + "type":"BASIC" + }, + { + "components":{ + + }, + "describe":"org.smartboot.flow.example.Step6", + "executable":"org.smartboot.flow.example.Step6", + "holders":{ + "rollback":"false", + "async":"false", + "name":"step6", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-21-anonymous-pipeline-20-step6", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"step6", + "type":"BASIC" + } + ], + "identifier":"testEngine-subprocess1-anonymous-if-anonymous-pipeline-wrapper-21-anonymous-pipeline-20", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":0 + }, + "execute-total":{ + "value":0 + }, + "execute-count":{ + "value":1 + } + } + }, + "name":"anonymous-pipeline-20" + }, + "type":"PIPELINE" + } + }, + "condition":"script-javascript", + "describe":"if@script-javascript", + "holders":{ + "rollback":"false", + "async":"false", + "degradable":"false", + "enabled":"true", + "timeout":"0" + }, + "identifier":"testEngine-subprocess1-anonymous-if", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":332 + }, + "execute-total":{ + "value":360 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"anonymous-if", + "type":"IF" + } + ], + "identifier":"testEngine-subprocess1", + "metrics":{ + "metrics":{ + "execute-max":{ + "value":740 + }, + "execute-total":{ + "value":973 + }, + "execute-count":{ + "value":3 + } + } + }, + "name":"subprocess1" + } + }, + "host":"xxx-MacBook-Pro.local", + "timestamp":1669960706283 +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index d524125a82507e7fae8d476c7b6001a8fae7b65a..9775e197211f7a89643a8f68ad42280a509e7ace 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.smartboot flow-engine - 1.0.1 + 1.0.3 4.0.0 pom @@ -13,32 +13,88 @@ smart-flow-core smart-flow-spring-extension smart-flow-manager - smart-flow-example + smart-flow-script-condition + smart-flow-springboot-starter + + + yamikaze + qinluo + 1558642210@qq.com + + smart-flow作者 + + https://gitee.com/yamikaze + GMT+8 + smartboot + https://gitee.com/smartboot + + + yisshengyouni + yisshengyouni + yifengyoujian163@163.com + + smart-flow作者 + + https://gitee.com/yisshengyouni + GMT+8 + smartboot + https://gitee.com/smartboot + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + - flow-engine - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.0 - - 1.8 - 1.8 - - -XDignore.symbol.file - - - -parameters - - UTF-8 - true - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + 1.8 + 1.8 + true + UTF-8 + + -parameters + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + true + + + + package + + jar + + + + attach-sources + deploy + + + + \ No newline at end of file diff --git a/smart-flow-core/pom.xml b/smart-flow-core/pom.xml index 5fefaf48bf2c81e57e34c278dc0398ed7040f5ed..757c4066e8b170b1475649e02fcbc32caf2ca40c 100644 --- a/smart-flow-core/pom.xml +++ b/smart-flow-core/pom.xml @@ -5,7 +5,7 @@ org.smartboot flow-engine - 1.0.1 + 1.0.3 4.0.0 @@ -36,14 +36,7 @@ org.slf4j slf4j-api - 2.0.3 - - - - org.slf4j - slf4j-log4j12 - 2.0.3 - test + 2.0.5 diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/Adapter.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/Adapter.java new file mode 100644 index 0000000000000000000000000000000000000000..c08960bb246bdf06348e56ccd55b6a4a26f8b9b6 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/Adapter.java @@ -0,0 +1,35 @@ +package org.smartboot.flow.core; + + +import org.smartboot.flow.core.common.Pair; + +/** + * 适配器组件 + *

+ * 应用场景:将两个参数类型不同的业务组件适配成统一的类型 + * + *

+ * + * @author huqiang + * @since 2022/12/7 14:35 + */ +public interface Adapter extends Describable { + + + /** + * 适配前处理 + * + * @param context 流程上下文 + * @return 返回需要适配的组件的入参和出参 + */ + Pair before(EngineContext context); + + /** + * 适配后置处理 + * + * @param origin 原流程上下文 + * @param newContext 适配的组件返回的上下文 + */ + void after(EngineContext origin, EngineContext newContext); + +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java index 06a273f30070452f371e45668690882a0f4aea0f..7d3ea68f354bf20dc6a9d91818cc56d73df88a5b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/AsyncCallResult.java @@ -1,9 +1,8 @@ package org.smartboot.flow.core; import org.smartboot.flow.core.component.Component; +import org.smartboot.flow.core.invoker.InvokeListener; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -18,7 +17,12 @@ public class AsyncCallResult { private long timeout; private Future future; private Component source; - private final List> belongs = new ArrayList<>(0); + private InvokeListener listeners; + private volatile boolean called; + + public void setListeners(InvokeListener listeners) { + this.listeners = listeners; + } public String getName() { return name; @@ -52,26 +56,26 @@ public class AsyncCallResult { this.source = source; } - public void setBelongs(List> belongs) { - this.belongs.addAll(belongs); - } + public synchronized void checkAndWait(EngineContext context) { + if (called) { + return; + } + + called = true; - public void checkAndWait(EngineContext context) { try { this.future.get(this.timeout, TimeUnit.MILLISECONDS); } catch (Throwable e) { if (source.isDegradable()) { - context.broken(false); + EngineContext.LOGGER.warn("degrade component {}", source.getName(), e); } else { - EngineContext.LOGGER.error("wait component async-execute failed {}", source.describe(), e); + EngineContext.LOGGER.error("wait component async-execute timeout {}ms {}", timeout, source.describe(), e); context.setFatal(e); context.setRollback(true); context.broken(true); } } finally { - if (source.isRollbackable(context) && !belongs.contains(source)) { - belongs.add(source); - } + listeners.onCompleted(source, context); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java index 83555a1d9f15d8224d0f0f74f2bb8bd92b59b570..24c956b803f00f4406ac3a8905638efc61037b32 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/Condition.java @@ -11,7 +11,9 @@ public abstract class Condition implements Describable { return this.test(context.getReq(), context.getResult()); } - public abstract Object test(T t, S s); + public Object test(T t, S s) { + return null; + } @Override public String describe() { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java new file mode 100644 index 0000000000000000000000000000000000000000..cbc0aacb9d3bdf3ec771bdc262f8ca24c7378c17 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/DegradeCallback.java @@ -0,0 +1,18 @@ +package org.smartboot.flow.core; + +/** + * @author qinluo + * @date 2022-11-28 20:10:46 + * @since 1.0.0 + */ +public interface DegradeCallback extends Describable { + + default void doWithDegrade(EngineContext context, Throwable e) { + + } + + @Override + default String describe() { + return this.getClass().getName(); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java index 6aa5b360b5652d55c2244faed828acc09b68df85..33ee074b3f56bb1368082b397250697dff16810d 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/EngineContext.java @@ -2,45 +2,81 @@ package org.smartboot.flow.core; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.exception.ExceptionHandler; +import org.smartboot.flow.core.invoker.Invoker; import org.smartboot.flow.core.trace.Tracer; import java.io.Serializable; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Future; /** * @author qinluo * @date 2022-11-12 21:00:58 * @since 1.0.0 */ -public class EngineContext { +public class EngineContext{ public static final int DISABLED = -1; public static final int EXECUTING = 1; public static final int ROLLBACK = 2; public static final Logger LOGGER = LoggerFactory.getLogger(EngineContext.class); - private T req; - private S result; - ExecutorService executor; - private final Map, Value> extensions = new ConcurrentHashMap<>(); - private final Tracer tracer = new Tracer(); - private boolean broken; - private Throwable fatal; - private ExceptionHandler handler; - private boolean rollback; - private final Map> asyncInvokes = new ConcurrentHashMap<>(); - private String engineName; - private ExecutionListener listener; + protected T req; + protected S result; + protected ExecutorService executor; + protected Map, Value> extensions = new ConcurrentHashMap<>(); + protected Tracer tracer = new Tracer(); + protected boolean broken; + protected Throwable fatal; + protected ExceptionHandler handler; + protected boolean rollback; + protected Map asyncInvokes = new ConcurrentHashMap<>(); + protected String engineName; + protected ExecutionListener listener; + protected Invoker invoker; /** * 执行状态 */ - private int executing; + protected int executing; + protected EngineContext parent; + + public void copy(EngineContext dest) { + // Reuse extensions. + dest.extensions = this.extensions; + dest.invoker = this.invoker; + dest.tracer = this.tracer; + dest.engineName = this.engineName; + dest.executing = this.executing; + dest.listener = this.listener; + dest.handler = this.handler; + dest.executor = this.executor; + dest.fatal = this.fatal; + dest.broken = this.broken; + dest.asyncInvokes = this.asyncInvokes; + } + + /** + * New context for subprocess. + * + */ + public EngineContext newContext() { + EngineContext newContext = new EngineContext<>(); + newContext.setReq(req); + newContext.setResult(result); + // Reuse extensions. + newContext.extensions = this.extensions; + newContext.invoker = this.invoker; + newContext.tracer = this.tracer; + newContext.engineName = this.engineName; + newContext.executing = this.executing; + newContext.listener = this.listener; + newContext.handler = this.handler; + newContext.parent = this; + newContext.executor = this.executor; + return newContext; + } /** * Returns current invoked trace. @@ -51,6 +87,14 @@ public class EngineContext { return tracer.getTrace(); } + public Invoker getInvoker() { + return invoker; + } + + public void setInvoker(Invoker invoker) { + this.invoker = invoker; + } + public boolean getRollback() { return rollback; } @@ -71,10 +115,23 @@ public class EngineContext { return broken; } + /** + * Break current pipeline. + */ public void broken(boolean broken) { this.broken = broken; } + /** + * Broken full pipeline + */ + public void brokenAll(boolean broken) { + this.broken = broken; + if (this.parent != null) { + this.parent.brokenAll(broken); + } + } + public T getReq() { return req; } @@ -123,14 +180,16 @@ public class EngineContext { this.engineName = engineName; } - public void addAsyncInvoke(Component component, Future future, List> belongs) { - AsyncCallResult result = new AsyncCallResult<>(); - result.setFuture(future); - result.setName(component.getName()); - result.setTimeout(component.getTimeout()); - result.setSource(component); - result.setBelongs(belongs); - this.asyncInvokes.put(component.getName(), result); + public ExecutorService getExecutor() { + return executor; + } + + public void setExecutor(ExecutorService executor) { + this.executor = executor; + } + + public void addAsyncInvoke(AsyncCallResult result) { + this.asyncInvokes.put(result.getName(), result); } public AsyncCallResult getAsyncCall(String name) { @@ -144,9 +203,9 @@ public class EngineContext { public void enter(Object obj) { String message = this.executing == ROLLBACK ? "rollback " : ""; if (obj instanceof Describable) { - message += ("rollback " + ((Describable) obj).describe()); + message += (((Describable) obj).describe()); } else if (obj instanceof String) { - message += ("rollback " + obj); + message += (obj); } this.tracer.enter(message); @@ -188,6 +247,16 @@ public class EngineContext { extensions.put(key, value); } + @SuppressWarnings("unchecked") + public

P remove(Key

key) { + Value removed = extensions.remove(key); + if (removed == null || removed == Value.NULL) { + return null; + } + + return (P) removed.get(); + } + public void clear() { this.tracer.reset(); this.asyncInvokes.clear(); @@ -198,6 +267,15 @@ public class EngineContext { this.listener = null; this.engineName = null; this.executing = DISABLED; + this.executor = null; + this.parent = null; + } + + /** + * Apply subprocess fields to parent ctx. + */ + public void apply() { + } private static class Value implements Serializable { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java index 88328754706b07fedb262a843fadfb6034e47c1e..d9b624fe919316479b17f76f5422347b58d78201 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListener.java @@ -7,6 +7,9 @@ package org.smartboot.flow.core; */ public interface ExecutionListener { + void start(EngineContext context); + void completed(EngineContext context); + void beforeExecute(EngineContext context, Object object); void afterExecute(EngineContext context, Object object); void beforeRollback(EngineContext context, Object object); diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java index f8062d7cfdc5031e65b3faa1e0108d0c24867f0e..c502bc07e33c560f8bd7d27057f5a4ce88d9bf0d 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListenerSupport.java @@ -7,6 +7,16 @@ package org.smartboot.flow.core; */ public class ExecutionListenerSupport implements ExecutionListener { + @Override + public void start(EngineContext context) { + + } + + @Override + public void completed(EngineContext context) { + + } + @Override public void beforeExecute(EngineContext context, Object object) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java index 5e62709104ec5b9e6f13070b312dacceec22bd74..e4eb24997c2c40baaaec6f7a538601f72f16d0d5 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/ExecutionListeners.java @@ -20,6 +20,28 @@ public class ExecutionListeners implements ExecutionListener { this.listeners = listeners; } + @Override + public void start(EngineContext context) { + for (ExecutionListener listener : listeners) { + try { + listener.start(context); + } catch (Throwable e) { + LOGGER.warn("execute listener {} failed", listener.getClass().getName(), e); + } + } + } + + @Override + public void completed(EngineContext context) { + for (ExecutionListener listener : listeners) { + try { + listener.completed(context); + } catch (Throwable e) { + LOGGER.warn("execute listener {} failed", listener.getClass().getName(), e); + } + } + } + @Override public void beforeExecute(EngineContext context, Object object) { for (ExecutionListener listener : listeners) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java index 3389146a518fe809f5add7e3e5cf023510a9ab4e..6b2e71bf517be5df738ea630718da99f75927503 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/FlowEngine.java @@ -1,7 +1,9 @@ package org.smartboot.flow.core; +import org.smartboot.flow.core.exception.DefaultExceptionHandler; import org.smartboot.flow.core.exception.ExceptionHandler; +import org.smartboot.flow.core.invoker.Invoker; import org.smartboot.flow.core.manager.DefaultEngineManager; import org.smartboot.flow.core.metrics.MetricExecutionListener; import org.smartboot.flow.core.util.AssertUtil; @@ -19,7 +21,7 @@ import java.util.concurrent.ExecutorService; public class FlowEngine implements Describable, Validator, Measurable { private Pipeline pipeline; - private ExceptionHandler exceptionHandler; + private ExceptionHandler exceptionHandler = new DefaultExceptionHandler(); private String name; private volatile boolean validateCalled = false; @@ -44,6 +46,9 @@ public class FlowEngine implements Describable, Validator, Measurable { initContext(context); + // fire start + start(context); + context.enter(this); boolean rollback = false; @@ -54,8 +59,6 @@ public class FlowEngine implements Describable, Validator, Measurable { rollback = true; } - context.ensureFinished(); - if (rollback || context.getRollback()) { context.setExecuting(EngineContext.ROLLBACK); pipeline.rollback(context); @@ -65,6 +68,9 @@ public class FlowEngine implements Describable, Validator, Measurable { context.setExecuting(EngineContext.EXECUTING); context.exit(this); + // complete execute. + complete(context); + if (context.getFatal() != null && exceptionHandler != null) { context.getHandler().handle(context, context.getFatal()); } @@ -72,12 +78,23 @@ public class FlowEngine implements Describable, Validator, Measurable { return context; } + private void start(EngineContext context) { + context.listener.start(context); + } + + private void complete(EngineContext context) { + context.listener.completed(context); + } + + + protected void initContext(EngineContext context) { context.clear(); context.setHandler(exceptionHandler); context.executor = executor; context.setEngineName(this.name); context.setExecuting(EngineContext.EXECUTING); + context.setInvoker(new Invoker()); // Execution Listener. ExecutionListeners listeners = new ExecutionListeners(ExecutionListenerRegistry.getRegistered()); @@ -120,8 +137,10 @@ public class FlowEngine implements Describable, Validator, Measurable { public void visit(EngineVisitor engineVisitor) { AssertUtil.notNull(engineVisitor, "visitor must not be null!"); - engineVisitor.visit(this.name); - PipelineVisitor pipelineVisitor = engineVisitor.visitPipeline(pipeline); + engineVisitor.visit(this.name, this.executor); + engineVisitor.visitSource(this); + + PipelineVisitor pipelineVisitor = engineVisitor.visitPipeline(pipeline.describe()); pipeline.visit(pipelineVisitor); // Visit completed. diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java index b9d845758b0b2539464067363fb5f49b987c4eec..9ca9da774d5c9b690db468e51946ed48da152d6b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/Pipeline.java @@ -1,12 +1,16 @@ package org.smartboot.flow.core; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.smartboot.flow.core.component.Component; +import org.smartboot.flow.core.invoker.InvokeListener; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.visitor.ComponentVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Future; @@ -17,8 +21,11 @@ import java.util.concurrent.Future; */ public class Pipeline implements Rollback, Describable, Validator, Measurable { + private static final Logger LOGGER = LoggerFactory.getLogger(Pipeline.class); + private final List> components = new ArrayList<>(); private String name; + private volatile boolean validateCalled = false; public void setName(String name) { this.name = name; @@ -29,9 +36,9 @@ public class Pipeline implements Rollback, Describable, Validator, M return name; } - public void execute(EngineContext context) throws Exception { + public void execute(EngineContext context) throws Throwable { // Executed sequences. - List> executed = new ArrayList<>(); + List executed = new ArrayList<>(); context.putExt(Key.of(this), executed); // Enter record track @@ -42,78 +49,26 @@ public class Pipeline implements Rollback, Describable, Validator, M break; } - // component is disabled. - if (!component.isEnabled()) { - continue; - } - - // Ensure async dependencies are called and finished. - ensureAllDependsExecuted(component, context); - - // Compatible-check after ensureAllDependsExecuted called. - if (context.isBroken()) { - break; - } - - if (component.isAsync()) { - AssertUtil.notNull(context.executor, "executor must not be null"); - - Future submitted = context.executor.submit(() -> executeComponent(component, context, executed)); - context.addAsyncInvoke(component, submitted, executed); - continue; - } - - executeComponent(component, context, executed); + context.getInvoker().invoke(context, component, new InvokeListener() { + @Override + public void onCompleted(Component component, EngineContext context) { + // rollback + if (component.isRollbackable(context) && !executed.contains(component)) { + executed.add(component); + } + } + }); } // Exit record track context.exit(this); - } - - private void ensureAllDependsExecuted(Component component, EngineContext context) { - for (String depends : component.getDependsOn()) { - AsyncCallResult asyncCall = context.getAsyncCall(depends); - if (asyncCall == null) { - EngineContext.LOGGER.warn("could not find dependsOn call on component {} with dependency {}", component.getName(), depends); - continue; - } - - // Await current future finished. - asyncCall.checkAndWait(context); - if (context.isBroken()) { - break; - } - } - } - - private int executeComponent(Component component, - EngineContext context, - List> executed) { - try { - component.invoke(context); - } catch (Throwable e) { - if (component.isDegradable()) { - context.broken(false); - } else { - EngineContext.LOGGER.error("execute component failed {}", component.describe(), e); - context.setFatal(e); - context.setRollback(true); - context.broken(true); - } - } finally { - // rollback - if (component.isRollbackable(context)) { - executed.add(component); - } - } - - return 1; + context.ensureFinished(); } @Override - public void rollback(EngineContext context) { - List> executed = context.getExt(Key.of(this)); + public synchronized void rollback(EngineContext context) { + List> executed = context.remove(Key.of(this)); if (executed == null || executed.size() == 0) { return; } @@ -126,7 +81,7 @@ public class Pipeline implements Rollback, Describable, Validator, M try { component.rollback(context); } catch (Exception e) { - EngineContext.LOGGER.error("rollback failed {}", component.describe()); + LOGGER.error("{} rollback failed {}", this.name, component.describe()); } } @@ -142,21 +97,27 @@ public class Pipeline implements Rollback, Describable, Validator, M this.components.addAll(components); } - public void addComponent(Component component) { - this.components.add(component); - } - public void visit(PipelineVisitor pipelineVisitor) { + pipelineVisitor.visitSource(this); + for (Component component : components) { - ComponentVisitor visitor = pipelineVisitor.visitComponent(component); - component.visit(visitor); + ComponentVisitor visitor = pipelineVisitor.visitComponent(component.getType(), component.getName(), component.describe()); + if (visitor != null) { + component.visit(visitor); + } } } @Override public void validate() { + if (validateCalled) { + return; + } + AssertUtil.notNull(components, "pipeline [" + describe() + "] components must not be null"); AssertUtil.isTrue(components.size() != 0, "pipeline [" + describe() + "] components must not be null"); components.forEach(Validator::validate); + + validateCalled = true; } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java similarity index 92% rename from smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java rename to smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java index f40eada7ed52d9bc79d2b573f6c931f375676d43..1cac30fc4b325e85e237e19082c4d2568064f1a6 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AttributeHolder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeHolder.java @@ -1,4 +1,4 @@ -package org.smartboot.flow.core.component; +package org.smartboot.flow.core.attribute; /** * @author qinluo diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..86e7e28ebe1503dbbd68423d0023c7a81e3fedad --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/AttributeValueResolver.java @@ -0,0 +1,101 @@ +package org.smartboot.flow.core.attribute; + +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.parser.DefaultObjectCreator; +import org.smartboot.flow.core.parser.ObjectCreator; +import org.smartboot.flow.core.util.AssertUtil; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +/** + * @author qinluo + * @date 2022-11-28 20:41:17 + * @since 1.0.0 + */ +public class AttributeValueResolver { + + private ObjectCreator objectCreator = DefaultObjectCreator.getInstance(); + + public void setObjectCreator(ObjectCreator objectCreator) { + this.objectCreator = objectCreator; + } + + public Object resolve(Attributes attribute, Object value) { + if (value == null) { + return null; + } + + if (attribute.accept(value)) { + return value; + } + + Class accepted = attribute.accept; + // Must as string + String strValue = String.valueOf(value); + + // For string + if (attribute.accept(strValue)) { + return strValue; + } + + // For boolean + if (accepted == Boolean.class || accepted == boolean.class) { + return Boolean.parseBoolean(strValue); + } + + // For numbers + if (Number.class.isAssignableFrom(accepted) || accepted.isPrimitive()) { + Double numeric = Double.valueOf(strValue); + + if (accepted == Long.class || accepted == long.class) { + return numeric.longValue(); + } + + if (accepted == Double.class || accepted == double.class) { + return numeric; + } + + if (accepted == Integer.class || accepted == int.class) { + return numeric.intValue(); + } + + if (accepted == Float.class || accepted == float.class) { + return numeric.floatValue(); + } + + if (accepted == Short.class || accepted == short.class) { + return numeric.shortValue(); + } + + if (accepted == Byte.class || accepted == byte.class) { + return numeric.byteValue(); + } + + if (accepted == Character.class || accepted == char.class) { + return (char)numeric.intValue(); + } + + if (accepted == BigDecimal.class) { + return new BigDecimal(strValue); + } + + AssertUtil.shouldNotReachHere(); + } + + // For string list. + if (accepted == List.class) { + return Arrays.asList(strValue.split(",")); + } + + // for classname. + try { + return objectCreator.create(strValue, true); + } catch (Exception ignored) { + // Maybe not a class. + } + + throw new FlowException("Can't not resolve attribute [" + attribute.getName() + "] value with " + value); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java similarity index 54% rename from smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java rename to smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java index 566344084e1d4a831791cde5b1f7a009a638670b..bc9eafee613b97af8e96846dd667f05721d2285b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Attributes.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/attribute/Attributes.java @@ -1,9 +1,9 @@ -package org.smartboot.flow.core.component; +package org.smartboot.flow.core.attribute; -import org.smartboot.flow.core.util.AssertUtil; +import org.smartboot.flow.core.DegradeCallback; +import org.smartboot.flow.core.component.Component; -import java.util.Arrays; import java.util.List; @@ -12,6 +12,7 @@ import java.util.List; * @date 2022-11-12 18:46:30 * @since 1.0.0 */ +@SuppressWarnings("unchecked") public enum Attributes { NAME("name", String.class) { @@ -26,9 +27,6 @@ public enum Attributes { @Override public void apply(Component component, Object value) { super.apply(component, value); - if (value instanceof String) { - value = Boolean.parseBoolean((String) value); - } component.setRollback((Boolean) value); } }, @@ -37,9 +35,6 @@ public enum Attributes { @Override public void apply(Component component, Object value) { super.apply(component, value); - if (value instanceof String) { - value = Boolean.parseBoolean((String) value); - } component.setDegradable((Boolean) value); } }, @@ -48,11 +43,6 @@ public enum Attributes { @Override public void apply(Component component, Object value) { super.apply(component, value); - - if (value instanceof String) { - value = Boolean.parseBoolean((String) value); - } - component.setAsync((Boolean) value); } }, @@ -61,52 +51,16 @@ public enum Attributes { @Override public void apply(Component component, Object value) { super.apply(component, value); - - if (value instanceof String) { - value = Long.parseLong((String) value); - } - component.setTimeout(((Number) value).longValue()); } - - @Override - public boolean accept(Object value) { - return value instanceof Long || value instanceof Integer || value instanceof String; - } }, - DEPENDS_ON("dependsOn", Void.class) { + DEPENDS_ON("dependsOn", List.class) { @Override public void apply(Component component, Object value) { super.apply(component, value); - if (value instanceof String) { - String values = (String) value; - component.setDependsOn(Arrays.asList(values.split(",+"))); - } else if (value instanceof List) { - //noinspection unchecked - component.setDependsOn((List)value); - } else { - AssertUtil.shouldNotReachHere(); - } - } - - @Override - public boolean accept(Object value) { - if (value instanceof String) { - return true; - } else if (value instanceof List) { - List values = (List) value; - for (Object item : values) { - if (!(item instanceof String)) { - return false; - } - } - - return true; - } - - AssertUtil.shouldNotReachHere(); - return true; + //noinspection unchecked + component.setDependsOn((List)value); } }, @@ -119,18 +73,27 @@ public enum Attributes { @Override public void apply(Component component, Object value) { super.apply(component, value); - - if (value instanceof String) { - value = Boolean.parseBoolean((String) value); - } - component.setEnabled((Boolean) value); } }, + /** + * 降级回调 + * + * @since 1.0.2 + */ + DEGRADE_CALLBACK("degrade-callback", DegradeCallback.class) { + @Override + public void apply(Component component, Object value) { + super.apply(component, value); + component.setDegradeCallback((DegradeCallback) value); + } + }, + ; private final String name; + /** * Attribute accepted type. */ @@ -146,20 +109,14 @@ public enum Attributes { } public boolean accept(Object value) { - boolean directInstance = accept.isInstance(value); - if (directInstance) { - return true; - } - - // String类型暂时返回true - return value instanceof String; + return accept.isInstance(value); } public void apply(Component component, Object value) { component.addAttribute(new AttributeHolder(this, value)); } - public static Attributes with(String name) { + public static Attributes byName(String name) { for (Attributes v : values()) { if (v.name.equals(name)) { return v; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java index adb528b75d1fc8d5a292ed67e834115150fe3a5a..6ccc39da16a3467fa92f7016710e45712513f1cb 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AbstractComponentBuilder.java @@ -1,7 +1,8 @@ package org.smartboot.flow.core.builder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeValueResolver; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.util.AssertUtil; @@ -15,6 +16,8 @@ import java.util.Map; */ public abstract class AbstractComponentBuilder { + private final AttributeValueResolver valueResolver = new AttributeValueResolver(); + /** * Component attribute attributes. */ @@ -24,8 +27,11 @@ public abstract class AbstractComponentBuilder { public AbstractComponentBuilder apply(Attributes attributes, Object value) { AssertUtil.notNull(attributes, "Unknown attribute"); AssertUtil.notNull(value, "null"); - AssertUtil.isTrue(attributes.accept(value), "Un-matched type"); - this.settings.put(attributes, value); + + Object resolved = valueResolver.resolve(attributes, value); + + AssertUtil.isTrue(attributes.accept(resolved), "Un-matched type"); + this.settings.put(attributes, resolved); return this; } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..af380278e0c59f1de933d1f77e3f1f483646afc9 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/AdapterBuilder.java @@ -0,0 +1,39 @@ +package org.smartboot.flow.core.builder; + +import org.smartboot.flow.core.Adapter; +import org.smartboot.flow.core.attribute.Attributes; +import org.smartboot.flow.core.component.AdapterComponent; +import org.smartboot.flow.core.component.Component; + +/** + * @author huqiang + * @since 2022/12/7 19:36 + */ +public class AdapterBuilder extends ExecutableBuilder { + + private final Adapter adapter; + + private final Component component; + + public AdapterBuilder(Adapter adapter, Component component) { + this.adapter = adapter; + this.component = component; + } + + public AdapterComponent build() { + AdapterComponent adapterComponent = new AdapterComponent<>(); + adapterComponent.setAdapter(adapter); + adapterComponent.setComponent(component); + applyValues(adapterComponent); + return adapterComponent; + } + + /** + * Resolve return type. + */ + public AdapterBuilder apply(Attributes attributes, Object value) { + super.apply(attributes, value); + return this; + } + +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java index 7844778fc49ce66eb2ed560642af0012de942630..fab6a2111e3deb8ccda834a6830b510f0e327976 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ChooseBuilder.java @@ -2,7 +2,7 @@ package org.smartboot.flow.core.builder; import org.smartboot.flow.core.Condition; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.component.ChooseComponent; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.executable.Executable; @@ -44,7 +44,7 @@ public class ChooseBuilder extends ExecutableBuilder { public ChooseBuilder newBranch(Object branch, Executable executable) { AssertUtil.notNull(branch, "branch must not be null"); - branches.put(branch, newAdapter(executable)); + branches.put(branch, asComponent(executable)); return this; } @@ -53,10 +53,10 @@ public class ChooseBuilder extends ExecutableBuilder { } public PipelineBuilder end(Executable defaultBranch) { - return end(newAdapter(defaultBranch)); + return end(asComponent(defaultBranch)); } - public Component build(Component defaultBranch) { + public ChooseComponent build(Component defaultBranch) { ChooseComponent chooseComponent = new ChooseComponent<>(condition, branches, defaultBranch); applyValues(chooseComponent); return chooseComponent; @@ -67,7 +67,7 @@ public class ChooseBuilder extends ExecutableBuilder { } public Component build(Executable defaultBranch) { - return build(newAdapter(defaultBranch)); + return build(asComponent(defaultBranch)); } public PipelineBuilder end(Component defaultBranch) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java index 506f8fe53659b5630025fbd0aa7d95a90d1d4eb2..db2e27e0c86338a8d3eac00bce7771595b96ac8b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/ExecutableBuilder.java @@ -1,7 +1,7 @@ package org.smartboot.flow.core.builder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.executable.Executable; import org.smartboot.flow.core.executable.ExecutableAdapter; @@ -21,6 +21,14 @@ public class ExecutableBuilder extends AbstractComponentBuilder { return adapter; } + /** + * Created as pure component. + */ + public Component asComponent(Executable executable) { + AssertUtil.notNull(executable, "must not be null"); + return new ExecutableAdapter<>(executable); + } + public ExecutableBuilder apply(Attributes attributes, Object value) { super.apply(attributes, value); return this; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java index 8a2fad8a39602058c27e39b03721ae1d5d80f681..f38b26697977a0829646a559bfa3fc01234b4486 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/IfComponentBuilder.java @@ -2,7 +2,7 @@ package org.smartboot.flow.core.builder; import org.smartboot.flow.core.Condition; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.component.IfComponent; import org.smartboot.flow.core.executable.Executable; @@ -51,13 +51,13 @@ public class IfComponentBuilder extends ExecutableBuilder{ } public PipelineBuilder then(Executable executable) { - Component execute = super.newAdapter(executable); + Component execute = super.asComponent(executable); return this.then(execute, null); } public PipelineBuilder then(Executable executable, Executable otherwise) { - Component execute = super.newAdapter(executable); - Component component = otherwise != null ? super.newAdapter(otherwise) : null; + Component execute = super.asComponent(executable); + Component component = otherwise != null ? super.asComponent(otherwise) : null; return then(execute, component); } @@ -74,8 +74,8 @@ public class IfComponentBuilder extends ExecutableBuilder{ } public Component build(Executable executable, Executable otherwise) { - Component execute = super.newAdapter(executable); - Component component = otherwise != null ? super.newAdapter(otherwise) : null; + Component execute = super.asComponent(executable); + Component component = otherwise != null ? super.asComponent(otherwise) : null; return this.build(execute, component); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java index 4b5adff317125ba071336f4b8c55ee80fd9389ac..a7df4ccce59afcff19032e570469b5e2bfcef9a8 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/builder/PipelineBuilder.java @@ -1,6 +1,7 @@ package org.smartboot.flow.core.builder; +import org.smartboot.flow.core.Adapter; import org.smartboot.flow.core.Condition; import org.smartboot.flow.core.Pipeline; import org.smartboot.flow.core.component.Component; @@ -59,6 +60,10 @@ public class PipelineBuilder { return new IfComponentBuilder<>(this, condition); } + public PipelineBuilder adapter(Adapter adapter, Component component) { + return this.next(new AdapterBuilder<>(adapter, component).build()); + } + public ChooseBuilder choose(Condition condition) { AssertUtil.notNull(condition, "must not be null"); return new ChooseBuilder<>(this, condition); diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java index 288a406e8b68c858d5b4be87366201e28bc2b4f8..942b9f279e443bb48a7840e37b162dbc20610ff8 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/ComponentType.java @@ -10,6 +10,6 @@ public enum ComponentType { IF, CHOOSE, PIPELINE, + ADAPTER, - ; } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java new file mode 100644 index 0000000000000000000000000000000000000000..4ef734dafc955ea0a08f6885c226e1efc53d004c --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/common/Pair.java @@ -0,0 +1,39 @@ +package org.smartboot.flow.core.common; + +import java.io.Serializable; + +/** + * @author qinluo + * @date 2022/12/7 21:14 + * @since 1.0.0 + */ +public class Pair implements Serializable { + + private static final long serialVersionUID = -6226657512890578902L; + + private T left; + private S right; + + public static Pair of(T left, S right) { + Pair pair = new Pair<>(); + pair.left = left; + pair.right = right; + return pair; + } + + public T getLeft() { + return left; + } + + public void setLeft(T left) { + this.left = left; + } + + public S getRight() { + return right; + } + + public void setRight(S right) { + this.right = right; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..c2258a1c1d8adb60a8d6e568be9e53e464de4ee2 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterComponent.java @@ -0,0 +1,106 @@ +package org.smartboot.flow.core.component; + +import org.smartboot.flow.core.Adapter; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.Key; +import org.smartboot.flow.core.common.ComponentType; +import org.smartboot.flow.core.common.Pair; +import org.smartboot.flow.core.util.AssertUtil; +import org.smartboot.flow.core.visitor.ComponentVisitor; + +/** + * 适配器组件 + * + * @author huqiang + * @since 2022/12/7 19:01 + */ +public class AdapterComponent extends Component { + + /** + * 业务适配器接口定义 + */ + private Adapter adapter; + + /** + * 待执行的适配流程组件 + */ + private Component component; + + public void setAdapter(Adapter adapter) { + this.adapter = adapter; + } + + public void setComponent(Component component) { + this.component = component; + } + + @Override + public String describe() { + return "adapter@" + adapter.describe(); + } + + @Override + public void rollback(EngineContext context) { + AdapterContext newContext = context.remove(Key.of(this)); + if (newContext == null) { + return; + } + component.rollback(newContext); + } + + @Override + public boolean isRollbackable(EngineContext context) { + AdapterContext newContext = context.getExt(Key.of(this)); + return newContext != null; + } + + @Override + public void doValidate() { + AssertUtil.notNull(adapter, "adapter[" + getName() + "] adapter must not be null"); + AssertUtil.notNull(component, "component[" + getName() + "] component must not be null"); + } + + @Override + public int invoke(EngineContext context) throws Throwable { + context.enter(this); + + // Convert. + Pair pair = adapter.before(context); + AssertUtil.notNull(pair, "adapter[" + getName() + "] result must not be null"); + + // Adapter ctx 本质只是进行参数的转换,并不是像子流程那样新起一个ctx,所以对一些操作类的方法,相关属性需要回设到父流程去 + AdapterContext newContext = new AdapterContext<>(); + newContext.setReq(pair.getLeft()); + newContext.setResult(pair.getRight()); + // copy parent + context.copy(newContext); + newContext.setParent(context); + + // Store converted objects. + context.putExt(Key.of(this), newContext); + + int invoke; + try { + invoke = component.invoke(newContext); + } finally { + context.exit(this); + } + + // Apply result to parent context. + adapter.after(context, newContext); + return invoke; + } + + @Override + public void visit(ComponentVisitor visitor) { + ComponentVisitor componentVisitor = visitor.visitComponent(component.getType(), component.getName(), component.describe()); + if (componentVisitor != null) { + component.visit(componentVisitor); + } + } + + @Override + public ComponentType getType() { + return ComponentType.ADAPTER; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java new file mode 100644 index 0000000000000000000000000000000000000000..caf23c55249eb5b5c708fc1d9cf7c8d8830250c1 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/AdapterContext.java @@ -0,0 +1,47 @@ +package org.smartboot.flow.core.component; + +import org.smartboot.flow.core.EngineContext; + +/** + * @author qinluo + * @date 2022/12/11 20:09 + * @since 1.0.0 + */ +public class AdapterContext extends EngineContext { + + @Override + public String getTrace() { + return this.parent.getTrace(); + } + + @Override + public boolean getRollback() { + return super.getRollback(); + } + + @Override + public void setRollback(boolean rollback) { + this.parent.setRollback(rollback); + super.setRollback(rollback); + } + + @Override + public Throwable getFatal() { + return super.getFatal(); + } + + @Override + public void setFatal(Throwable fatal) { + this.parent.setFatal(fatal); + super.setFatal(fatal); + } + + @Override + public void broken(boolean broken) { + super.brokenAll(broken); + } + + public void setParent(EngineContext ctx) { + this.parent = ctx; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java index 4e4563e5c1a378d9b019b109a931fe287e7a045e..f11520b4fc4a473a62c83583c6bc102939c7a30a 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/ChooseComponent.java @@ -4,6 +4,7 @@ package org.smartboot.flow.core.component; import org.smartboot.flow.core.Condition; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.Key; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.visitor.ComponentVisitor; @@ -28,8 +29,6 @@ public class ChooseComponent extends Component { this.branches = branches; this.condition = condition; this.defaultBranch = defaultBranch; - // Compatible parse from xml. - this.allBranchWasString = branches.keySet().stream().allMatch(p -> p instanceof String); } public void setBranches(Map> branches) { @@ -49,7 +48,7 @@ public class ChooseComponent extends Component { } @Override - public int invoke(EngineContext context) throws Exception { + public int invoke(EngineContext context) throws Throwable { context.enter(this); try { Object branch = condition.test(context); @@ -71,7 +70,7 @@ public class ChooseComponent extends Component { context.putExt(Key.of(this), execute); context.enter("branch##" + branch); try { - return execute.invoke(context); + execute.invoke(context); } finally { context.exit("branch##" + branch); } @@ -91,7 +90,7 @@ public class ChooseComponent extends Component { @Override public void rollback(EngineContext context) { - Component executed = context.getExt(Key.of(this)); + Component executed = context.remove(Key.of(this)); if (executed == null || !executed.isRollbackable(context)) { return; } @@ -114,20 +113,26 @@ public class ChooseComponent extends Component { public void visit(ComponentVisitor visitor) { visitor.visitAttributes(attributes); visitor.visitCondition(condition.describe()); + visitor.visitSource(this); branches.forEach((k, v) -> { - ComponentVisitor branchVisitor = visitor.visitBranch(k, v); - v.visit(branchVisitor); + ComponentVisitor branchVisitor = visitor.visitBranch(k, v.getType(), v.getName(), v.describe()); + if (branchVisitor != null) { + v.visit(branchVisitor); + } }); if (defaultBranch != null) { - ComponentVisitor defaultVisitor = visitor.visitComponent(defaultBranch); - defaultBranch.visit(defaultVisitor); + ComponentVisitor defaultVisitor = visitor.visitComponent(defaultBranch.getType(), defaultBranch.getName(), defaultBranch.describe()); + if (defaultVisitor != null) { + defaultBranch.visit(defaultVisitor); + } + } } @Override - public void validate() { + public void doValidate() { AssertUtil.notNull(condition, "choose[" + getName() + "] condition must not be null"); AssertUtil.notNull(branches, "choose[" + getName() + "] branch must not be null"); AssertUtil.isTrue(branches.size() != 0, "choose[" + getName() + "] branch must not be null"); @@ -137,4 +142,9 @@ public class ChooseComponent extends Component { defaultBranch.validate(); } } + + @Override + public ComponentType getType() { + return ComponentType.CHOOSE; + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Component.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Component.java index 25f24159f0cfbd3fa26610860f1445d8842fad04..f874be307a46e047fb24515a84bb0b5b6deaacd6 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Component.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/Component.java @@ -1,11 +1,14 @@ package org.smartboot.flow.core.component; +import org.smartboot.flow.core.DegradeCallback; import org.smartboot.flow.core.Describable; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.Measurable; import org.smartboot.flow.core.Rollback; import org.smartboot.flow.core.Validator; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.visitor.ComponentVisitor; import java.util.ArrayList; @@ -33,8 +36,18 @@ public abstract class Component implements Rollback, Describable, Va private List dependsOn; private String name; private boolean enabled = true; + private DegradeCallback degradeCallback; protected final List attributes = new ArrayList<>(0); + private volatile boolean validateCalled = false; + + public DegradeCallback getDegradeCallback() { + return degradeCallback; + } + + public void setDegradeCallback(DegradeCallback degradeCallback) { + this.degradeCallback = degradeCallback; + } public boolean isDegradable() { return degradable; @@ -99,12 +112,29 @@ public abstract class Component implements Rollback, Describable, Va this.name = name; } - public abstract int invoke(EngineContext context) throws Exception; + public abstract int invoke(EngineContext context) throws Throwable; public boolean isRollbackable(EngineContext context) { return rollback; } + @Override + public void validate() { + if (validateCalled) { + return; + } + + this.doValidate(); + this.validateCalled = true; + } + + protected void doValidate() { + + } + /* Visit component's structure */ public abstract void visit(ComponentVisitor visitor); + + /* Returns component's type */ + public abstract ComponentType getType(); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/IfComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/IfComponent.java index e04fa635486bb04bdb08029faf666c52974734d3..a06ed12b408d8c96c88def7fabff4e007b064c51 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/IfComponent.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/IfComponent.java @@ -4,6 +4,7 @@ package org.smartboot.flow.core.component; import org.smartboot.flow.core.Condition; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.Key; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.visitor.ComponentVisitor; @@ -39,7 +40,7 @@ public class IfComponent extends Component{ } @Override - public int invoke(EngineContext context) throws Exception { + public int invoke(EngineContext context) throws Throwable { context.enter(this); try { Object test = condition.test(context); @@ -51,9 +52,10 @@ public class IfComponent extends Component{ execute = this.elseComponent; } + if (execute != null) { context.putExt(Key.of(this), execute); - return execute.invoke(context); + execute.invoke(context); } } finally { context.exit(this); @@ -70,7 +72,7 @@ public class IfComponent extends Component{ @Override public void rollback(EngineContext context) { - Component executed = context.getExt(Key.of(this)); + Component executed = context.remove(Key.of(this)); if (executed == null || !executed.isRollbackable(context)) { return; } @@ -92,17 +94,22 @@ public class IfComponent extends Component{ public void visit(ComponentVisitor visitor) { visitor.visitAttributes(attributes); visitor.visitCondition(condition.describe()); - ComponentVisitor thenVisitor = visitor.visitComponent(thenComponent); - thenComponent.visit(thenVisitor); + visitor.visitSource(this); + ComponentVisitor thenVisitor = visitor.visitComponent(thenComponent.getType(), thenComponent.getName(), thenComponent.describe()); + if (thenVisitor != null) { + thenComponent.visit(thenVisitor); + } if (elseComponent != null) { - ComponentVisitor elseVisitor = visitor.visitComponent(elseComponent); - elseComponent.visit(elseVisitor); + ComponentVisitor elseVisitor = visitor.visitComponent(elseComponent.getType(), elseComponent.getName(), elseComponent.describe()); + if (elseVisitor != null) { + elseComponent.visit(elseVisitor); + } } } @Override - public void validate() { + public void doValidate() { AssertUtil.notNull(condition, "IF[" + getName() + "]condition must not be null"); AssertUtil.notNull(thenComponent, "IF[" + getName() + "]branch must not be null"); @@ -111,4 +118,9 @@ public class IfComponent extends Component{ elseComponent.validate(); } } + + @Override + public ComponentType getType() { + return ComponentType.IF; + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/PipelineComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/PipelineComponent.java index 8f8b01b640ac9682c2d9877af7397afc93d7be60..990db74e9f1f6747b0338e8015a2a5776701987f 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/component/PipelineComponent.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/component/PipelineComponent.java @@ -3,6 +3,7 @@ package org.smartboot.flow.core.component; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.visitor.ComponentVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; @@ -28,11 +29,34 @@ public class PipelineComponent extends Component { } @Override - public int invoke(EngineContext context) throws Exception { - pipeline.execute(context); + public int invoke(EngineContext context) throws Throwable { + + // Create ctx for subprocess; + EngineContext subprocess = context.newContext(); + + + try { + pipeline.execute(subprocess); + } finally { + subprocess.apply(); + } + + if (subprocess.getRollback() && !isAnonymous()) { + subprocess.setExecuting(EngineContext.ROLLBACK); + this.rollback(subprocess); + } + + if (subprocess.getFatal() != null) { + throw subprocess.getFatal(); + } + return 1; } + private boolean isAnonymous() { + return pipeline.describe().contains("anonymous-pipeline"); + } + @Override public boolean isRollbackable(EngineContext context) { return pipeline.isRollbackable(context); @@ -43,12 +67,7 @@ public class PipelineComponent extends Component { if (!isRollbackable(context)) { return; } - context.enter(this); - try { - pipeline.rollback(context); - } finally { - context.exit(this); - } + pipeline.rollback(context); } @Override @@ -59,12 +78,20 @@ public class PipelineComponent extends Component { @Override public void visit(ComponentVisitor visitor) { visitor.visitAttributes(attributes); - PipelineVisitor pipelineVisitor = visitor.visitPipeline(pipeline); - pipeline.visit(pipelineVisitor); + visitor.visitSource(this); + PipelineVisitor pipelineVisitor = visitor.visitPipeline(pipeline.describe()); + if (pipelineVisitor != null) { + pipeline.visit(pipelineVisitor); + } } @Override - public void validate() { + public void doValidate() { AssertUtil.notNull(pipeline, "subprocess[" + getName() + "]pipeline must not be null!"); } + + @Override + public ComponentType getType() { + return ComponentType.PIPELINE; + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/ExceptionHandlerSupport.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/DefaultExceptionHandler.java similarity index 72% rename from smart-flow-core/src/main/java/org/smartboot/flow/core/exception/ExceptionHandlerSupport.java rename to smart-flow-core/src/main/java/org/smartboot/flow/core/exception/DefaultExceptionHandler.java index 2b251e000ae9cfd7d054474d47258b373734b8d8..6d74b847a0923f1bedde71b9cf62f5df011787e2 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/ExceptionHandlerSupport.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/DefaultExceptionHandler.java @@ -8,11 +8,11 @@ import org.smartboot.flow.core.EngineContext; * @date 2022-11-11 21:11:59 * @since 1.0.0 */ -public class ExceptionHandlerSupport implements ExceptionHandler { +public class DefaultExceptionHandler implements ExceptionHandler { @Override public void handle(EngineContext context, Throwable e) { - + throw new FlowException(e); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/PipelineExecuteException.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/FlowException.java similarity index 30% rename from smart-flow-core/src/main/java/org/smartboot/flow/core/exception/PipelineExecuteException.java rename to smart-flow-core/src/main/java/org/smartboot/flow/core/exception/FlowException.java index b6da7bcfa5e5801728289f0509a7250a7c8053c0..356f936ccac4e051417191dd21c375187bf1f3fa 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/PipelineExecuteException.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/exception/FlowException.java @@ -5,24 +5,19 @@ package org.smartboot.flow.core.exception; * @date 2022-11-11 21:10:03 * @since 1.0.0 */ -public class PipelineExecuteException extends RuntimeException { +public class FlowException extends RuntimeException { - public PipelineExecuteException() { - } + private static final long serialVersionUID = -7613637640003340281L; - public PipelineExecuteException(String message) { + public FlowException(String message) { super(message); } - public PipelineExecuteException(String message, Throwable cause) { + public FlowException(String message, Throwable cause) { super(message, cause); } - public PipelineExecuteException(Throwable cause) { + public FlowException(Throwable cause) { super(cause); } - - public PipelineExecuteException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/executable/ExecutableAdapter.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/executable/ExecutableAdapter.java index b5423dcca0c4847638ac3f1ccc76953daf703f85..b22dc9c84d1939afa96ec13eb3b4f5fbd6788e88 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/executable/ExecutableAdapter.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/executable/ExecutableAdapter.java @@ -1,8 +1,10 @@ package org.smartboot.flow.core.executable; +import org.smartboot.flow.core.DegradeCallback; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.Rollback; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.visitor.ComponentVisitor; @@ -33,6 +35,19 @@ public class ExecutableAdapter extends Component implements Rollback< try { executable.execute(context); + } catch (Throwable e) { + // 非降级,直接throw + if (!isDegradable()) { + throw e; + } + + // 触发降级回调 + if (getDegradeCallback() != null) { + getDegradeCallback().doWithDegrade(context, e); + } else if (executable instanceof DegradeCallback) { + ((DegradeCallback) executable).doWithDegrade(context, e); + } + } finally { context.exit(this); } @@ -61,10 +76,16 @@ public class ExecutableAdapter extends Component implements Rollback< public void visit(ComponentVisitor visitor) { visitor.visitAttributes(attributes); visitor.visitExecutable(executable.describe()); + visitor.visitSource(this); } @Override - public void validate() { + public void doValidate() { AssertUtil.notNull(executable, "Executable[" + getName() + "]executable must not be null"); } + + @Override + public ComponentType getType() { + return ComponentType.BASIC; + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/AsyncRunner.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/AsyncRunner.java new file mode 100644 index 0000000000000000000000000000000000000000..80137d73c836edb391f0eeea93a1258c728094c5 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/AsyncRunner.java @@ -0,0 +1,28 @@ +package org.smartboot.flow.core.invoker; + +import org.smartboot.flow.core.trace.Node; +import org.smartboot.flow.core.trace.Tracer; + +import java.util.concurrent.Callable; + +/** + * @author qinluo + * @date 2022-12-07 21:57:39 + * @since 1.0.0 + */ +public class AsyncRunner implements Callable { + + private final Callable task; + private final Node root; + + public AsyncRunner(Callable task) { + this.task = task; + this.root = Tracer.get(); + } + + @Override + public Integer call() throws Exception { + Tracer.setNode(root); + return task.call(); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListener.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..545f461b34b984367b03e94162ba9a071ea436d8 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListener.java @@ -0,0 +1,14 @@ +package org.smartboot.flow.core.invoker; + +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.component.Component; + +/** + * @author qinluo + * @date 2022-12-07 21:28:41 + * @since 1.0.0 + */ +public interface InvokeListener { + + void onCompleted(Component component, EngineContext context); +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListeners.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListeners.java new file mode 100644 index 0000000000000000000000000000000000000000..f4e9f2619fa07d588412346281699a2b9bd4d600 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/InvokeListeners.java @@ -0,0 +1,44 @@ +package org.smartboot.flow.core.invoker; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.component.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author qinluo + * @date 2022-12-07 21:37:59 + * @since 1.0.0 + */ +public class InvokeListeners implements InvokeListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(InvokeListeners.class); + + private final List invokeListeners; + + public InvokeListeners(InvokeListener ...invokeListeners) { + if (invokeListeners == null || invokeListeners.length == 0) { + this.invokeListeners = new ArrayList<>(0); + } else { + this.invokeListeners = Stream.of(invokeListeners).filter(Objects::nonNull).collect(Collectors.toList()); + } + } + + @Override + public void onCompleted(Component component, EngineContext context) { + for (InvokeListener listener : invokeListeners) { + + try { + listener.onCompleted(component, context); + } catch (Exception e) { + LOGGER.warn("invoke listener failed, listener = {}", listener.getClass().getName(), e); + } + } + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/Invoker.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/Invoker.java new file mode 100644 index 0000000000000000000000000000000000000000..51ac71793a3ee06fc4d639857d1bf387f0b7f562 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/invoker/Invoker.java @@ -0,0 +1,104 @@ +package org.smartboot.flow.core.invoker; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smartboot.flow.core.AsyncCallResult; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.component.Component; +import org.smartboot.flow.core.util.AssertUtil; + +import java.util.concurrent.Future; + +/** + * @author qinluo + * @date 2022-12-07 20:37:25 + * @since 1.0.0 + */ +public class Invoker { + + private static final Logger LOGGER = LoggerFactory.getLogger(Invoker.class); + + public int invoke(EngineContext context, Component component, InvokeListener ...listeners) { + if (context.isBroken()) { + return -1; + } + + // component is disabled. + if (!component.isEnabled()) { + LOGGER.warn("component {} disabled in engine {}", component.describe(), context.getEngineName()); + return 1; + } + + // Ensure async dependencies are called and finished. + ensureAllDependsExecuted(component, context); + + // Compatible-check after ensureAllDependsExecuted called. + if (context.isBroken()) { + return -1; + } + + InvokeListener listener = new InvokeListeners(listeners); + if (component.isAsync()) { + AssertUtil.notNull(context.getExecutor(), "executor must not be null"); + + AsyncRunner runner = new AsyncRunner(() -> execute(component, context, listener)); + Future submitted = context.getExecutor().submit(runner); + AsyncCallResult result = new AsyncCallResult<>(); + result.setFuture(submitted); + result.setName(component.getName()); + result.setTimeout(component.getTimeout()); + result.setSource(component); + result.setListeners(listener); + context.addAsyncInvoke(result); + return 1; + } + + return execute(component, context, listener); + } + + private int execute(Component component, + EngineContext context, + InvokeListener listeners) { + + Throwable fatal = null; + + try { + component.invoke(context); + } catch (Throwable e) { + fatal = e; + } finally { + // rollback + listeners.onCompleted(component, context); + + if (fatal != null) { + if (component.isDegradable()) { + LOGGER.warn("degrade component {}", component.describe(), fatal); + } else { + LOGGER.error("execute component failed {}", component.describe(), fatal); + context.setFatal(fatal); + context.setRollback(true); + context.broken(true); + } + } + } + + return 1; + } + + private void ensureAllDependsExecuted(Component component, EngineContext context) { + for (String depends : component.getDependsOn()) { + AsyncCallResult asyncCall = context.getAsyncCall(depends); + if (asyncCall == null) { + EngineContext.LOGGER.warn("could not find dependsOn call on component {} with dependency {}", component.getName(), depends); + continue; + } + + // Await current future finished. + asyncCall.checkAndWait(context); + if (context.isBroken()) { + break; + } + } + + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ComponentModel.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ComponentModel.java index 417690129baf31254c63288adc35eeb46ba3e77b..53115a5bda4d212a9c4072ff07f42c1546db6c02 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ComponentModel.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ComponentModel.java @@ -1,9 +1,9 @@ package org.smartboot.flow.core.manager; +import org.smartboot.flow.core.attribute.AttributeValueResolver; import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.common.Uniqueness; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeHolder; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.metrics.Metrics; @@ -20,10 +20,10 @@ import java.util.concurrent.ConcurrentHashMap; @SuppressWarnings("rawtypes") public class ComponentModel extends Uniqueness { - private final Map holders = new ConcurrentHashMap<>(); - private final Component component; - private final String name; - private final String describe; + private final Map holders = new ConcurrentHashMap<>(); + private Component component; + private String name; + private String describe; private final Map components = new ConcurrentHashMap<>(); String executable; @@ -33,23 +33,20 @@ public class ComponentModel extends Uniqueness { PipelineModel pipeline; String condition; private String branch; - private final Metrics metrics; + private Metrics metrics; - ComponentModel(String identifier, Component component) { + ComponentModel(ComponentType type, String identifier) { this.identifier = identifier; - this.component = component; - this.name = component.getName(); - this.describe = component.describe(); - this.metrics = component.getMetrics(); + this.type = type; } - public Map getHolders() { + public Map getHolders() { return holders; } public void addAttributes(List attributes) { for (AttributeHolder holder : attributes) { - this.holders.put(holder.getAttribute(), holder.getValue()); + this.holders.put(holder.getAttribute().getName(), holder.getValue()); } } @@ -119,10 +116,14 @@ public class ComponentModel extends Uniqueness { @SuppressWarnings("unchecked") public void changeAttributes(List holders) { - Map snapshot = new HashMap<>(this.holders); + Map snapshot = new HashMap<>(this.holders); + AttributeValueResolver valueResolver = new AttributeValueResolver(); try { - holders.forEach(p -> p.getAttribute().apply(component, p.getValue())); + holders.forEach(p -> { + Object resolvedValue = valueResolver.resolve(p.getAttribute(), p.getValue()); + p.getAttribute().apply(component, resolvedValue); + }); addAttributes(holders); } catch (Exception e) { this.holders.clear(); @@ -139,4 +140,11 @@ public class ComponentModel extends Uniqueness { this.metrics.reset(); components.forEach((k, v) -> v.reset()); } + + public void setComponent(Component component) { + this.component = component; + this.name = component.getName(); + this.describe = component.describe(); + this.metrics = component.getMetrics(); + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/DefaultEngineManager.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/DefaultEngineManager.java index 1af1541724b33aef5c3844bcbca0de7fe86ceb15..3b060d6fc074cd1c60df869eab52bc06665e26fd 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/DefaultEngineManager.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/DefaultEngineManager.java @@ -3,7 +3,7 @@ package org.smartboot.flow.core.manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; import org.smartboot.flow.core.util.AssertUtil; import java.util.ArrayList; @@ -41,7 +41,7 @@ public class DefaultEngineManager implements EngineManager { engine.validate(); RegisterEngineVisitor visitor = new RegisterEngineVisitor(); - visitor.visit(engine); + engine.visit(visitor); EngineModel model = visitor.getEngine(); registeredEngines.put(model.getIdentifier(), model); diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineManager.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineManager.java index 92406ee080e7aae6536d143eef248c272de1b879..40d81b11d8896a75a9f5479b65e6d14373e1a23f 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineManager.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineManager.java @@ -1,7 +1,7 @@ package org.smartboot.flow.core.manager; import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; import java.util.Collections; import java.util.List; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineModel.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineModel.java index cd5ee5a9d272b6923a199fce6a9797d5382817e3..deebab196061ededa9517304bac148dcd081ac67 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineModel.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/EngineModel.java @@ -3,7 +3,7 @@ package org.smartboot.flow.core.manager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smartboot.flow.core.common.Uniqueness; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; import org.smartboot.flow.core.metrics.Metrics; import java.util.HashMap; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisterEngineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisterEngineVisitor.java index 6b66aa47e1e27dfb65865e3a60f0a5136d2c1be7..2743345a6627ea112e5a526be4e12af10a7d85d2 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisterEngineVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisterEngineVisitor.java @@ -1,11 +1,14 @@ package org.smartboot.flow.core.manager; import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.Pipeline; import org.smartboot.flow.core.util.ContactUtils; import org.smartboot.flow.core.visitor.EngineVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Executor; + /** * @author qinluo * @date 2022/11/18 23:02 @@ -14,25 +17,29 @@ import org.smartboot.flow.core.visitor.PipelineVisitor; public class RegisterEngineVisitor extends EngineVisitor { private EngineModel model; + private final Set visited = new HashSet<>(32); @Override - public void visit(FlowEngine flowEngine) { - this.model = new EngineModel(flowEngine.getName()); - this.model.setMetrics(flowEngine.getMetrics()); - super.visit(flowEngine); + public void visit(String name, Executor executor) { + this.model = new EngineModel(name); } @Override - public void visitEnd() { - this.model.collect(); + public void visitSource(FlowEngine flowEngine) { + visited.add(flowEngine); + this.model.setMetrics(flowEngine.getMetrics()); } @Override - public PipelineVisitor visitPipeline(Pipeline pipeline) { - PipelineModel pipelineModel = new PipelineModel(pipeline.describe(), ContactUtils.contact(this.model.getIdentifier(), pipeline.describe())); + public PipelineVisitor visitPipeline(String pipeline) { + PipelineModel pipelineModel = new PipelineModel(pipeline, ContactUtils.contact(this.model.getIdentifier(), pipeline)); this.model.setPipeline(pipelineModel); - pipelineModel.setMetrics(pipeline.getMetrics()); - return new RegisteredPipelineVisitor(pipelineModel); + return new RegisteredPipelineVisitor(pipelineModel, visited); + } + + @Override + public void visitEnd() { + this.model.collect(); } public EngineModel getEngine() { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredComponentVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredComponentVisitor.java index 0193f4acc29b892952365173c9f0e7e94f028fe1..68f43760a2b1663c9eea520da320f690cfba3dc6 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredComponentVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredComponentVisitor.java @@ -1,8 +1,7 @@ package org.smartboot.flow.core.manager; -import org.smartboot.flow.core.Pipeline; import org.smartboot.flow.core.common.ComponentType; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.util.AuxiliaryUtils; import org.smartboot.flow.core.util.ContactUtils; @@ -10,6 +9,7 @@ import org.smartboot.flow.core.visitor.ComponentVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; import java.util.List; +import java.util.Set; /** * @author qinluo @@ -19,39 +19,43 @@ import java.util.List; public class RegisteredComponentVisitor extends ComponentVisitor { private final ComponentModel model; + private final Set visited; - public RegisteredComponentVisitor(ComponentModel comp) { + public RegisteredComponentVisitor(ComponentModel comp, Set visited) { this.model = comp; + this.visited = visited; } @Override - public PipelineVisitor visitPipeline(Pipeline pipeline) { - PipelineModel pipelineModel = new PipelineModel(pipeline.describe(), ContactUtils.contact(model.getIdentifier(), pipeline.describe())); + public PipelineVisitor visitPipeline(String pipeline) { + PipelineModel pipelineModel = new PipelineModel(pipeline, ContactUtils.contact(model.getIdentifier(), pipeline)); this.model.pipeline = pipelineModel; - this.model.type = (ComponentType.PIPELINE); - pipelineModel.setMetrics(pipeline.getMetrics()); - return new RegisteredPipelineVisitor(pipelineModel); + return new RegisteredPipelineVisitor(pipelineModel, visited); + } + + @Override + public void visitSource(Component component) { + this.model.setComponent(component); } @Override public void visitCondition(String condition) { - this.model.type = (ComponentType.IF); this.model.condition = (condition); } @Override - public ComponentVisitor visitComponent(Component component) { - String identifier = ContactUtils.contact(model.getIdentifier(), AuxiliaryUtils.or(component.getName(), component.describe())); + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { + String identifier = ContactUtils.contact(model.getIdentifier(), AuxiliaryUtils.or(name, describe)); String branch = null; if (this.model.type == ComponentType.CHOOSE) { identifier = ContactUtils.contact(model.getIdentifier(), "default"); branch = "default"; } - ComponentModel comp = new ComponentModel(identifier, component); + ComponentModel comp = new ComponentModel(type, identifier); comp.setBranch(branch); this.model.addComponent(comp); - return new RegisteredComponentVisitor(comp); + return new RegisteredComponentVisitor(comp, visited); } @Override @@ -60,17 +64,15 @@ public class RegisteredComponentVisitor extends ComponentVisitor { } @Override - public ComponentVisitor visitBranch(Object branch, Component component) { - ComponentModel model = new ComponentModel(ContactUtils.contact(this.model.getIdentifier(), "branch", String.valueOf(branch)), component); + public ComponentVisitor visitBranch(Object branch, ComponentType type, String name, String describe) { + ComponentModel model = new ComponentModel(type, ContactUtils.contact(this.model.getIdentifier(), "branch", String.valueOf(branch))); model.setBranch((String.valueOf(branch))); - this.model.type = (ComponentType.CHOOSE); this.model.addComponent(model); - return new RegisteredComponentVisitor(model); + return new RegisteredComponentVisitor(model, visited); } @Override public void visitExecutable(String executable) { this.model.executable = (executable); - this.model.type = (ComponentType.BASIC); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredPipelineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredPipelineVisitor.java index 9c62badb17c047310e0570d3977fe343bdb981bf..d2e25ee4df9e337d645646d8e0d9407a5b07385c 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredPipelineVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/RegisteredPipelineVisitor.java @@ -1,11 +1,14 @@ package org.smartboot.flow.core.manager; -import org.smartboot.flow.core.component.Component; +import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.util.AuxiliaryUtils; import org.smartboot.flow.core.util.ContactUtils; import org.smartboot.flow.core.visitor.ComponentVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; +import java.util.Set; + /** * @author qinluo * @date 2022/11/19 12:26 @@ -14,15 +17,29 @@ import org.smartboot.flow.core.visitor.PipelineVisitor; public class RegisteredPipelineVisitor extends PipelineVisitor { private final PipelineModel pipelineModel; + private final Set visited; + private boolean isCycle; - public RegisteredPipelineVisitor(PipelineModel pipelineModel) { + public RegisteredPipelineVisitor(PipelineModel pipelineModel, Set visited) { this.pipelineModel = pipelineModel; + this.visited = visited; } @Override - public ComponentVisitor visitComponent(Component component) { - ComponentModel comp = new ComponentModel(ContactUtils.contact(this.pipelineModel.getIdentifier(), AuxiliaryUtils.or(component.getName(), component.describe())), component); + public void visitSource(Pipeline pipeline) { + this.isCycle = !this.visited.add(pipeline); + pipelineModel.setMetrics(pipeline.getMetrics()); + } + + @Override + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { + // Avoid stackOverflow + if (isCycle) { + return null; + } + + ComponentModel comp = new ComponentModel(type, ContactUtils.contact(this.pipelineModel.getIdentifier(), AuxiliaryUtils.or(name, describe))); this.pipelineModel.addComponent(comp); - return new RegisteredComponentVisitor(comp); + return new RegisteredComponentVisitor(comp, visited); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricExecutionListener.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricExecutionListener.java index 0d441c7b2fbd4fb2081dee56e7d169d501117402..b9d54630b06533b402cb2605478048477f3f3768 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricExecutionListener.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricExecutionListener.java @@ -2,6 +2,7 @@ package org.smartboot.flow.core.metrics; import org.smartboot.flow.core.EngineContext; import org.smartboot.flow.core.ExecutionListener; +import org.smartboot.flow.core.ExecutionListenerSupport; import org.smartboot.flow.core.Key; import org.smartboot.flow.core.Measurable; @@ -13,7 +14,7 @@ import java.util.concurrent.ConcurrentHashMap; * @date 2022-11-25 21:23:28 * @since 1.0.0 */ -public class MetricExecutionListener implements ExecutionListener { +public class MetricExecutionListener extends ExecutionListenerSupport { private static final ExecutionListener INSTANCE = new MetricExecutionListener(); @@ -36,7 +37,7 @@ public class MetricExecutionListener implements ExecutionListener { Measurable measurable = (Measurable) object; Metrics metrics = measurable.getMetrics(); - metrics.addMetric("execute-count", 1); + metrics.addMetric(NamedMetrics.EXECUTE, 1); escaped.put(object, System.currentTimeMillis()); } @@ -59,11 +60,11 @@ public class MetricExecutionListener implements ExecutionListener { Measurable measurable = (Measurable) object; Metrics metrics = measurable.getMetrics(); if (context.getFatal() != null) { - metrics.addMetric("execute-fail-count", 1); + metrics.addMetric(NamedMetrics.FAIL, 1); } long now = System.currentTimeMillis(); - metrics.addMetric("execute-total", (now - start)); - metrics.addMetric(MetricKind.MAX,"execute-max", (now - start)); + metrics.addMetric(NamedMetrics.TOTAL_ESCAPE, (now - start)); + metrics.addMetric(MetricKind.MAX, NamedMetrics.MAX_ESCAPE, (now - start)); } @Override @@ -79,7 +80,7 @@ public class MetricExecutionListener implements ExecutionListener { Measurable measurable = (Measurable) object; Metrics metrics = measurable.getMetrics(); - metrics.addMetric("rollback-count", 1); + metrics.addMetric(NamedMetrics.ROLLBACK, 1); escaped.put(object, System.currentTimeMillis()); } @@ -102,7 +103,7 @@ public class MetricExecutionListener implements ExecutionListener { Measurable measurable = (Measurable) object; Metrics metrics = measurable.getMetrics(); long now = System.currentTimeMillis(); - metrics.addMetric("rollback-total", (now - start)); - metrics.addMetric(MetricKind.MAX,"rollback-max", (now - start)); + metrics.addMetric(NamedMetrics.ROLLBACK_TOTAL_ESCAPE, (now - start)); + metrics.addMetric(MetricKind.MAX, NamedMetrics.ROLLBACK_MAX_ESCAPE, (now - start)); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricKind.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricKind.java index e779d0749c760e42baed80cd7fa3aa62c55d78d0..c2f80b8cf2bd45ef7a55174cb7a10f9c1db38782 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricKind.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricKind.java @@ -10,5 +10,4 @@ public enum MetricKind { ACCUMULATE, MAX, - ; } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricsCreator.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricsCreator.java index 4eabcb97b6d984184aaa6ea02a7bca1b2f824518..231b2a495abc5c4d7a42fd91b786226b83fe9f7c 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricsCreator.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/MetricsCreator.java @@ -8,7 +8,7 @@ package org.smartboot.flow.core.metrics; public interface MetricsCreator { /** - * 创建一个metrics实力 + * 创建一个metrics实例 * * @return metrics */ diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/NamedMetrics.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/NamedMetrics.java new file mode 100644 index 0000000000000000000000000000000000000000..7a81a92735ba3e91d1e23f7bc41d00c8819d89e7 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/metrics/NamedMetrics.java @@ -0,0 +1,17 @@ +package org.smartboot.flow.core.metrics; + +/** + * @author qinluo + * @date 2022/11/23 21:45 + * @since 1.0.0 + */ +public interface NamedMetrics { + + String EXECUTE = "execute"; + String FAIL = "fail"; + String ROLLBACK = "rollback"; + String MAX_ESCAPE = "maxEscape"; + String TOTAL_ESCAPE = "totalEscape"; + String ROLLBACK_MAX_ESCAPE = "rollbackMaxEscape"; + String ROLLBACK_TOTAL_ESCAPE = "rollbackTotalEscape"; +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AbstractElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AbstractElementParser.java index 07e22ba5ca57c75dd28d4fd78abd5a75c755d26f..4bb00f6d61e72e6c2309f68f96a3cd7b5eebc6d7 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AbstractElementParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AbstractElementParser.java @@ -29,7 +29,7 @@ public abstract class AbstractElementParser implements ElementParser { // name String name = element.getAttribute(ParseConstants.NAME); // nested pipeline maybe no name. - if (name.trim().length() == 0 || Objects.equals(name, element.getAttribute(ParseConstants.REF))) { + if (name.trim().length() == 0 || Objects.equals(name, element.getAttribute(ParseConstants.EXECUTE))) { name = context.getIdentifier(element); } @@ -50,7 +50,16 @@ public abstract class AbstractElementParser implements ElementParser { */ public abstract void doParse(Element element, ParserContext context); - protected ElementDefinition parseElementsAsPipeline(Element element, ParserContext context) { + protected ElementDefinition parseSubElements(Element element, ParserContext context) { + List subs = ElementUtils.subElements(element); + AssertUtil.isTrue(subs.size() != 0, "[" + ElementUtils.getName(element) + "] childNodes can't be null"); + + // 只有一个标签时 优化解析,不使用pipeline进行包装 + if (subs.size() == 1) { + Element subElement = subs.get(0); + return parseSubElement(subElement, context); + } + // pipeline identifier. String pipelineIdentifier = context.allocateIdentifier("anonymous-pipeline"); @@ -68,36 +77,11 @@ public abstract class AbstractElementParser implements ElementParser { pipelineDef.setName(pipelineIdentifier); pipelineDef.setIdentifier(pipelineIdentifier); - List subs = ElementUtils.subElements(element); - AssertUtil.isTrue(subs.size() != 0, "[" + ElementUtils.getName(element) + "] childNodes can't be null"); + List subDefinitions = new ArrayList<>(); for (Element sub : subs) { - String subName = ElementUtils.getName(sub); - AssertUtil.isTrue(context.isAllowed(subName), "element " + subName + " not allowed in pipeline"); - ElementParser parser = context.getParser(subName); - AssertUtil.notNull(parser, "Could not find parser for element " + subName); - - String elementIdentifier = getIdentifier(sub, context); - - // nested subprocess - if (Objects.equals(subName, ParseConstants.PIPELINE)) { - PipelineComponentDefinition nestedWrap = new PipelineComponentDefinition(); - String nestedIdentifier = context.allocateIdentifier("anonymous-pipeline-wrapper"); - nestedWrap.setName("anonymous-pipeline-wrapper-" + sub.getAttribute(ParseConstants.NAME)); - nestedWrap.setIdentifier(nestedIdentifier); - nestedWrap.setPipeline(elementIdentifier); - context.register(nestedWrap); - - // Use nested wrap identifier. - elementIdentifier = nestedIdentifier; - } - - parser.parseElement(sub, context); - - // Append sub elements. - ElementDefinition registered = context.getRegistered(elementIdentifier); - AssertUtil.notNull(registered, elementIdentifier + " sub elements parse failed."); + ElementDefinition registered = parseSubElement(sub, context); subDefinitions.add(registered); } @@ -106,4 +90,33 @@ public abstract class AbstractElementParser implements ElementParser { return def; } + + protected ElementDefinition parseSubElement(Element sub, ParserContext context) { + String subName = ElementUtils.getName(sub); + AssertUtil.isTrue(context.isAllowed(subName), "element " + subName + " not allowed in pipeline"); + ElementParser parser = context.getParser(subName); + AssertUtil.notNull(parser, "Could not find parser for element " + subName); + + String elementIdentifier = getIdentifier(sub, context); + + // nested subprocess + if (Objects.equals(subName, ParseConstants.PIPELINE)) { + PipelineComponentDefinition nestedWrap = new PipelineComponentDefinition(); + String nestedIdentifier = context.allocateIdentifier("anonymous-pipeline-wrapper"); + nestedWrap.setName("anonymous-pipeline-wrapper-" + sub.getAttribute(ParseConstants.NAME)); + nestedWrap.setIdentifier(nestedIdentifier); + nestedWrap.setPipeline(elementIdentifier); + context.register(nestedWrap); + + // Use nested wrap identifier. + elementIdentifier = nestedIdentifier; + } + + parser.parseElement(sub, context); + + // Append sub elements. + ElementDefinition registered = context.getRegistered(elementIdentifier); + AssertUtil.notNull(registered, elementIdentifier + " sub elements parse failed."); + return registered; + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AdapterElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AdapterElementParser.java new file mode 100644 index 0000000000000000000000000000000000000000..9f385dd390700ccd3bbe56dbf66f6103e34e60bb --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/AdapterElementParser.java @@ -0,0 +1,39 @@ +package org.smartboot.flow.core.parser; + +import org.smartboot.flow.core.parser.definition.AdapterDefinition; +import org.smartboot.flow.core.parser.definition.ElementDefinition; +import org.smartboot.flow.core.util.AssertUtil; +import org.w3c.dom.Element; + +import java.util.List; + +/** + * @author huqiang + * @since 2022/12/8 11:08 + */ +public class AdapterElementParser extends AbstractElementParser{ + @Override + public String getElementName() { + return ParseConstants.ADAPTER; + } + + @Override + public void doParse(Element element, ParserContext context) { + AdapterDefinition definition = new AdapterDefinition(); + String identifier = getIdentifier(element, context); + + // Check has any elements. + List elements = ElementUtils.subElements(element); + AssertUtil.isTrue(elements.size() != 0, "[adapter] element's sub elements must not be empty"); + + definition.setName(element.getAttribute(ParseConstants.NAME)); + definition.setExecute(element.getAttribute(ParseConstants.EXECUTE)); + definition.setIdentifier(identifier); + definition.getAttributes().addAll(ElementUtils.extraAttributes(element)); + + ElementDefinition def = parseSubElements(element, context); + definition.setPipelineElement(def); + context.register(definition); + + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/BuilderDefinitionVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/BuilderDefinitionVisitor.java index c8521b645ecc4821254d76bb47c8a6ca7baadf68..527c684df3c4e4aeadea09b3ffa332a24af73aaf 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/BuilderDefinitionVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/BuilderDefinitionVisitor.java @@ -1,22 +1,31 @@ package org.smartboot.flow.core.parser; +import org.smartboot.flow.core.Adapter; import org.smartboot.flow.core.Condition; import org.smartboot.flow.core.FlowEngine; import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.builder.AdapterBuilder; import org.smartboot.flow.core.builder.ChooseBuilder; import org.smartboot.flow.core.builder.EngineBuilder; import org.smartboot.flow.core.builder.ExecutableBuilder; import org.smartboot.flow.core.builder.IfComponentBuilder; import org.smartboot.flow.core.builder.PipelineBuilder; +import org.smartboot.flow.core.component.AdapterComponent; +import org.smartboot.flow.core.component.ChooseComponent; import org.smartboot.flow.core.component.Component; import org.smartboot.flow.core.component.PipelineComponent; import org.smartboot.flow.core.executable.Executable; +import org.smartboot.flow.core.parser.definition.AdapterDefinition; import org.smartboot.flow.core.parser.definition.ChooseDefinition; import org.smartboot.flow.core.parser.definition.ElementDefinition; import org.smartboot.flow.core.parser.definition.EngineDefinition; import org.smartboot.flow.core.parser.definition.IfElementDefinition; import org.smartboot.flow.core.parser.definition.PipelineComponentDefinition; import org.smartboot.flow.core.parser.definition.PipelineDefinition; +import org.smartboot.flow.core.parser.definition.ScriptDefinition; +import org.smartboot.flow.core.script.ScriptCondition; +import org.smartboot.flow.core.util.AssertUtil; +import org.smartboot.flow.core.util.AuxiliaryUtils; import java.util.ArrayList; import java.util.List; @@ -32,9 +41,11 @@ import java.util.concurrent.ConcurrentHashMap; public class BuilderDefinitionVisitor implements DefinitionVisitor { private final Map> namedComponents = new ConcurrentHashMap<>(); + private final Map assembledPipelines = new ConcurrentHashMap<>(); private final Map> namedPipelines = new ConcurrentHashMap<>(); private final Map> namedEngines = new ConcurrentHashMap<>(); private final Map> callbacks = new ConcurrentHashMap<>(); + private final Map scriptConditions = new ConcurrentHashMap<>(); private final ObjectCreator objectCreator; private final boolean useCache; @@ -45,7 +56,16 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { @Override public void visit(ElementDefinition ed) { - ed.visit(this); + if (ed instanceof ScriptDefinition) { + ScriptDefinition sed = (ScriptDefinition) ed; + ScriptCondition condition = objectCreator.create(sed.getType(), true); + condition.setName(sed.getName()); + condition.setScript(sed.getScript()); + scriptConditions.put(ed.getIdentifier(), condition); + } else { + ed.visit(this); + } + } @Override @@ -66,6 +86,7 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { Pipeline build = pipelineBuilder.build(); invoke(ed.getIdentifier(), build); + this.assembledPipelines.put(ed.getIdentifier(), build); } @Override @@ -78,8 +99,10 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { pipelineBuilder = new PipelineBuilder<>(ed.getPipeline()); this.namedPipelines.put(ed.getPipeline(), pipelineBuilder); this.collect(ed.getPipeline(), (engineBuilder::pipeline)); + } else if (assembledPipelines.get(ed.getPipeline()) != null) { + engineBuilder.pipeline(assembledPipelines.get(ed.getPipeline())); } else { - engineBuilder.pipeline(pipelineBuilder.build()); + this.collect(ed.getPipeline(), (engineBuilder::pipeline)); } this.namedEngines.put(engineName, engineBuilder); @@ -95,8 +118,10 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { pipelineBuilder = new PipelineBuilder<>(ed.getPipeline()); this.namedPipelines.put(ed.getPipeline(), pipelineBuilder); this.collect(ed.getPipeline(), pipelineComponent::setPipeline); + } else if (assembledPipelines.get(ed.getPipeline()) != null) { + pipelineComponent.setPipeline(assembledPipelines.get(ed.getPipeline())); } else { - pipelineComponent = new PipelineComponent<>(pipelineBuilder.build()); + this.collect(ed.getPipeline(), (pipelineComponent::setPipeline)); } this.namedComponents.put(engineName, pipelineComponent); @@ -123,14 +148,13 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { // resolve value type. builder.apply(p.getAttribute(), p.getValue()); }); - Component component = builder.newAdapter((Executable) newInstance(ed.getType())); + Component component = builder.newAdapter((Executable) newInstance(ed.getExecute())); namedComponents.put(ed.getIdentifier(), component); } private Object newInstance(String type) { try { - Class clazz = Class.forName(type); - return objectCreator.create(clazz, useCache); + return objectCreator.create(type, useCache); } catch (Exception e) { throw new IllegalStateException(type, e); } @@ -138,7 +162,16 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { @Override public void visit(IfElementDefinition ed) { - IfComponentBuilder ifComponentBuilder = new IfComponentBuilder<>((Condition) newInstance(ed.getTest())); + String test = ed.getTest(); + Condition condition = null; + if (AuxiliaryUtils.isType(test)) { + condition = (Condition) newInstance(test); + } else if (ed.getContext().getRegistered(test) != null) { + condition = getInternalObject(test, ed.getContext()); + } + + AssertUtil.notNull(condition, "can't find condition for if-element, test = " + test); + IfComponentBuilder ifComponentBuilder = new IfComponentBuilder<>(condition); ed.getAttributes().forEach(p -> { // resolve value type. ifComponentBuilder.apply(p.getAttribute(), p.getValue()); @@ -158,9 +191,25 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { this.namedComponents.put(ed.getIdentifier(), build); } + public T getInternalObject(String test, ParserContext context) { + ElementDefinition registered = context.getRegistered(test); + AssertUtil.notNull(registered, "registered condition def[" + test + "] not found"); + this.visit(registered); + return (T)scriptConditions.get(test); + } + @Override public void visit(ChooseDefinition ed) { - ChooseBuilder chooseBuilder = new ChooseBuilder<>((Condition) newInstance(ed.getTest())); + String test = ed.getTest(); + Condition condition = null; + if (AuxiliaryUtils.isType(test)) { + condition = (Condition) newInstance(test); + } else if (ed.getContext().getRegistered(test) != null) { + condition = getInternalObject(test, ed.getContext()); + } + + AssertUtil.notNull(condition, "can't find condition for choose-element, test = " + test); + ChooseBuilder chooseBuilder = new ChooseBuilder<>(condition); ed.getAttributes().forEach(p -> { // resolve value type. chooseBuilder.apply(p.getAttribute(), p.getValue()); @@ -186,10 +235,35 @@ public class BuilderDefinitionVisitor implements DefinitionVisitor { defaultBranch = this.namedComponents.get(chooseDefaultRef.getIdentifier()); } - Component build = chooseBuilder.build(defaultBranch); + ChooseComponent build = chooseBuilder.build(defaultBranch); + build.setAllBranchWasString(true); this.namedComponents.put(ed.getIdentifier(), build); } + @Override + public void visit(AdapterDefinition ed) { + String execute = ed.getExecute(); + Object adapter; + if (AuxiliaryUtils.isType(execute)) { + adapter = newInstance(execute); + } else { + adapter = ed.getContext().getRegistered(execute); + } + AssertUtil.notNull(adapter, "can't find adapter , execute=" + execute); + + ed.getPipelineElement().visit(this); + Component component = namedComponents.get(ed.getPipelineElement().getIdentifier()); + + AdapterBuilder adapterBuilder = new AdapterBuilder((Adapter) adapter, component); + ed.getAttributes().forEach(p -> { + // resolve value type. + adapterBuilder.apply(p.getAttribute(), p.getValue()); + }); + + AdapterComponent adapterComponent = adapterBuilder.build(); + this.namedComponents.put(ed.getIdentifier(), adapterComponent); + } + /** * Return all parsed engine names. * diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ChooseElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ChooseElementParser.java index 57f08882fdc2581d510f4f27c89682a85f8436df..65049328cc68bda2248298353fdd9ebad31f4f1b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ChooseElementParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ChooseElementParser.java @@ -30,10 +30,7 @@ public class ChooseElementParser extends AbstractElementParser{ @Override public void doParse(Element element, ParserContext context) { String test = element.getAttribute(ParseConstants.TEST); - String ref = element.getAttribute(ParseConstants.REF); - if (AuxiliaryUtils.isBlank(ref) && AuxiliaryUtils.isBlank(test)) { - throw new IllegalArgumentException("attribute [ref] [type] cannot be null"); - } + AssertUtil.notBlank(test, "attribute [test] cannot be null"); ChooseDefinition chooseDefinition = new ChooseDefinition(); ElementDefinition.build(chooseDefinition, element); @@ -42,7 +39,6 @@ public class ChooseElementParser extends AbstractElementParser{ chooseDefinition.setName(ElementUtils.resolveName(element, "choose")); chooseDefinition.setIdentifier(identifier); chooseDefinition.setTest(test); - chooseDefinition.setRef(ref); List subElements = ElementUtils.subElements(element); List caseList = new ArrayList<>(); @@ -67,14 +63,13 @@ public class ChooseElementParser extends AbstractElementParser{ chooseDefinition.setChooseDefaultRef(currentDef); } - String type = subElement.getAttribute(ParseConstants.TYPE); - ref = subElement.getAttribute(ParseConstants.REF); - if (AuxiliaryUtils.isNotBlank(type) || AuxiliaryUtils.isNotBlank(ref)) { + String type = subElement.getAttribute(ParseConstants.EXECUTE); + if (AuxiliaryUtils.isNotBlank(type)) { continue; } // Wrap sub elements as pipeline. - ElementDefinition def = parseElementsAsPipeline(subElement, context); + ElementDefinition def = parseSubElements(subElement, context); if (elementName.equals(ParseConstants.CASE)) { caseList.remove(currentDef); def.setWhen(subElement.getAttribute(ParseConstants.WHEN)); diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ComponentElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ComponentElementParser.java index 37958392a7ee2f97bba5c5010064c0f5ec46356b..64b9a0eef20240e96364303aa8b1a361005fdd73 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ComponentElementParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ComponentElementParser.java @@ -2,6 +2,7 @@ package org.smartboot.flow.core.parser; import org.smartboot.flow.core.parser.definition.ElementDefinition; import org.smartboot.flow.core.parser.definition.PipelineComponentDefinition; +import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.util.AuxiliaryUtils; import org.w3c.dom.Element; @@ -14,10 +15,6 @@ import org.w3c.dom.Element; */ public class ComponentElementParser extends AbstractElementParser { - /* - - */ - @Override public void doParse(Element element, ParserContext context) { ElementDefinition elementDefinition = new ElementDefinition(); @@ -37,12 +34,7 @@ public class ComponentElementParser extends AbstractElementParser { return; } - String ref = elementDefinition.getRef(); - String type = elementDefinition.getType(); - if (AuxiliaryUtils.isBlank(ref) && AuxiliaryUtils.isBlank(type)) { - throw new IllegalArgumentException("attribute [ref] [type] cannot be null"); - } - + AssertUtil.notBlank(elementDefinition.getExecute(), "attribute [execute] cannot be null"); context.register(elementDefinition); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultObjectCreator.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultObjectCreator.java index 67df7a505396e815ae6e4c607180ed6e346fa73a..66df9a7aca647fa1f5767364f0530c734989881a 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultObjectCreator.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultObjectCreator.java @@ -10,14 +10,30 @@ import java.util.concurrent.ConcurrentHashMap; * @date 2022/11/19 14:29 * @since 1.0.0 */ +@SuppressWarnings("unchecked") public class DefaultObjectCreator implements ObjectCreator { + private static final ObjectCreator INSTANCE = new DefaultObjectCreator(); + private final Map, Object> cachedObjects = new ConcurrentHashMap<>(); + private static Class check(String type) { + try { + return (Class) Class.forName(type); + } catch (Exception e) { + return null; + } + } + + public static ObjectCreator getInstance() { + return INSTANCE; + } + @Override - @SuppressWarnings("unchecked") - public T create(Class type, boolean useCache) { - AssertUtil.notNull(type, "type must not be null!"); + public T create(String typename, boolean useCache) { + AssertUtil.notBlank(typename, "type must not be blank!"); + Class type = check(typename); + AssertUtil.notNull(type, "typename " + typename + " is not class"); T created = null; if (useCache) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultParser.java index 1af113abf2c207636f2c9a44e9eb3152fd47516f..cfd54eef5540d1d83d19c1b92a839bbe97f59548 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefaultParser.java @@ -25,6 +25,7 @@ public class DefaultParser implements Parser { static { ALLOWED.add(ParseConstants.ENGINE); ALLOWED.add(ParseConstants.PIPELINE); + ALLOWED.add(ParseConstants.SCRIPT); } private BuilderDefinitionVisitor visitor; @@ -55,7 +56,7 @@ public class DefaultParser implements Parser { for (Element sub : elements) { String subName = ElementUtils.getName(sub); - AssertUtil.isTrue(ALLOWED.contains(subName), "element " + subName + " not allowed in engines"); + AssertUtil.isTrue(isAllowed(subName), "element " + subName + " not allowed in engines"); ElementParser parser = context.getParser(subName); AssertUtil.notNull(parser, "Could not find parser for element " + subName); parser.parseElement(sub, context); @@ -95,8 +96,12 @@ public class DefaultParser implements Parser { this.parse(fstream, streams); } + protected boolean isAllowed(String name) { + return ALLOWED.contains(name); + } + protected ObjectCreator getObjectCreator() { - return new DefaultObjectCreator(); + return DefaultObjectCreator.getInstance(); } public FlowEngine getEngine(String name) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefinitionVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefinitionVisitor.java index 229db01a3e4865732ad5bb404b23eb3edddd942e..aa76f68234a83de01cc315a66902f6126c8c25bd 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefinitionVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/DefinitionVisitor.java @@ -1,5 +1,6 @@ package org.smartboot.flow.core.parser; +import org.smartboot.flow.core.parser.definition.AdapterDefinition; import org.smartboot.flow.core.parser.definition.ChooseDefinition; import org.smartboot.flow.core.parser.definition.ElementDefinition; import org.smartboot.flow.core.parser.definition.EngineDefinition; @@ -37,8 +38,11 @@ public interface DefinitionVisitor { void visit(PipelineComponentDefinition ed); - void visitBasic(ElementDefinition ed); + void visit(IfElementDefinition ed); + void visit(ChooseDefinition ed); + + void visit(AdapterDefinition adapterDefinition); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementParserRegistry.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementParserRegistry.java index 6a682b6ddd4af1590ed1b3216c6d3a73cc194945..250a95b000dd7a5b0fe8329a0b245168a0b4b2b9 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementParserRegistry.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementParserRegistry.java @@ -19,6 +19,8 @@ public class ElementParserRegistry { this.register("pipeline", new PipelineElementParser()); this.register("choose", new ChooseElementParser()); this.register("engine", new EngineElementParser()); + this.register("script", new ScriptElementParser()); + this.register("adapter", new AdapterElementParser()); } public static ElementParserRegistry getInstance() { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementUtils.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementUtils.java index 35143acded5689c2a216a6c61c66c19d20eb93a7..66921fba70843eb13b0d2d5c7a354690080481b1 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementUtils.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ElementUtils.java @@ -1,7 +1,7 @@ package org.smartboot.flow.core.parser; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.util.AuxiliaryUtils; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; @@ -27,7 +27,7 @@ public final class ElementUtils { for (int i = 0; i < attributes.getLength(); i++) { Node item = attributes.item(i); Attributes attribute; - if ((attribute = Attributes.with(item.getNodeName())) == null) { + if ((attribute = Attributes.byName(item.getNodeName())) == null) { continue; } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/IfElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/IfElementParser.java index 2fa9b95f842b7d7a11fa4b9322b132256d87c349..02c2018727c86a5dfef2f3a3f0cc9e8c75b9864d 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/IfElementParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/IfElementParser.java @@ -32,19 +32,14 @@ public class IfElementParser extends AbstractElementParser { @Override public void doParse(Element element, ParserContext context) { String test = element.getAttribute(ParseConstants.TEST); - String ref = element.getAttribute(ParseConstants.REF); - if (AuxiliaryUtils.isBlank(ref) && AuxiliaryUtils.isBlank(test)) { - throw new IllegalArgumentException("attribute [ref] [type] cannot be null"); - } + AssertUtil.notBlank(test, "attribute [test] cannot be null"); IfElementDefinition ifDef = new IfElementDefinition(); ElementDefinition.build(ifDef, element); ifDef.setName(ElementUtils.resolveName(element, "if")); ifDef.setIdentifier(super.getIdentifier(element, context)); - ifDef.setTest(element.getAttribute(ParseConstants.TEST)); - ifDef.setRef(ref); List elementList = ElementUtils.subElements(element); AssertUtil.isTrue(elementList.size() != 0, "[if] childNodes can't be null"); @@ -70,14 +65,13 @@ public class IfElementParser extends AbstractElementParser { thenDef = thenDefinition; } - String type = subElement.getAttribute(ParseConstants.TYPE); - ref = subElement.getAttribute(ParseConstants.REF); - if (AuxiliaryUtils.isNotBlank(type) || AuxiliaryUtils.isNotBlank(ref)) { + String type = subElement.getAttribute(ParseConstants.EXECUTE); + if (AuxiliaryUtils.isNotBlank(type)) { continue; } // Wrap sub elements as pipeline. - ElementDefinition def = parseElementsAsPipeline(subElement, context); + ElementDefinition def = parseSubElements(subElement, context); if (Objects.equals(elementName, ParseConstants.ELSE)) { elseDef = def; } else if (Objects.equals(elementName, ParseConstants.THEN)) { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ObjectCreator.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ObjectCreator.java index 28c05500ca77a616eb22d2136818b7262dbccf13..a2a837b0ad4aef4b5037f1c61aa12d2c5402928e 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ObjectCreator.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ObjectCreator.java @@ -14,5 +14,5 @@ public interface ObjectCreator { * @param useCache useCache * @return instance. */ - T create(Class type, boolean useCache); + T create(String type, boolean useCache); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParseConstants.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParseConstants.java index cbb12f5844cae2520078b68e8b17bfba18353cf5..2689c8decb31ce9318cbef630852eaefa47ad253 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParseConstants.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParseConstants.java @@ -6,8 +6,6 @@ package org.smartboot.flow.core.parser; */ public interface ParseConstants { - String NAMESPACE = "http://org.smartboot/smart-flow"; - /** * tags */ @@ -17,13 +15,14 @@ public interface ParseConstants { String IF = "if"; String CHOOSE = "choose"; String COMPONENT = "component"; + String ADAPTER = "adapter"; + String SCRIPT = "script"; /** * Attributes; */ String NAME = "name"; - String TYPE = "type"; - String REF = "ref"; + String EXECUTE = "execute"; String TEST = "test"; String THEN = "then"; String WHEN = "when"; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParserContext.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParserContext.java index a6659eca37c042122f7ebb99d23c8ca0e54f2725..d15e1edb46933fa2990af11c1f73dd9bb05cb881 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParserContext.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ParserContext.java @@ -17,16 +17,6 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ParserContext { - /** - * ElementParser registry - */ - private final ElementParserRegistry parserRegistry = ElementParserRegistry.getInstance(); - - /* Registered Element definitions */ - private final Map registered = new ConcurrentHashMap<>(); - private final Map generatedIdentifiers = new ConcurrentHashMap<>(); - private IdentifierManager identifierManager = new DefaultIdentifierManager(); - /** * Allowed sub elements. */ @@ -36,8 +26,19 @@ public class ParserContext { ALLOWED.add(ParseConstants.IF); ALLOWED.add(ParseConstants.CHOOSE); ALLOWED.add(ParseConstants.COMPONENT); + ALLOWED.add(ParseConstants.ADAPTER); } + /** + * ElementParser registry + */ + private final ElementParserRegistry parserRegistry = ElementParserRegistry.getInstance(); + + /* Registered Element definitions */ + private final Map registered = new ConcurrentHashMap<>(); + private final Map generatedIdentifiers = new ConcurrentHashMap<>(); + private IdentifierManager identifierManager = new DefaultIdentifierManager(); + public IdentifierManager getIdentifierManager() { return identifierManager; } @@ -93,5 +94,4 @@ public class ParserContext { public boolean isAllowed(String name) { return ALLOWED.contains(name); } - } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/PipelineElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/PipelineElementParser.java index 64576f8c2f6ea464626b32565ea9d274fc998164..569c8c23ba09ad64ac1d7a52c7e548cd92d1f5af 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/PipelineElementParser.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/PipelineElementParser.java @@ -1,14 +1,12 @@ package org.smartboot.flow.core.parser; import org.smartboot.flow.core.parser.definition.ElementDefinition; -import org.smartboot.flow.core.parser.definition.PipelineComponentDefinition; import org.smartboot.flow.core.parser.definition.PipelineDefinition; import org.smartboot.flow.core.util.AssertUtil; import org.w3c.dom.Element; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * Parse pipeline tag. @@ -46,32 +44,7 @@ public class PipelineElementParser extends AbstractElementParser { List subDefinitions = new ArrayList<>(); for (Element sub : elements) { - String subName = ElementUtils.getName(sub); - AssertUtil.isTrue(context.isAllowed(subName), "element " + subName + " not allowed in pipeline"); - ElementParser parser = context.getParser(subName); - AssertUtil.notNull(parser, "Could not find parser for element " + subName); - - identifier = super.getIdentifier(sub, context); - - // nested subprocess - if (Objects.equals(subName, ParseConstants.PIPELINE)) { - PipelineComponentDefinition nestedWrap = new PipelineComponentDefinition(); - String nestedIdentifier = context.allocateIdentifier("anonymous-pipeline-wrapper"); - nestedWrap.setName("anonymous-pipeline-wrapper-" + sub.getAttribute(ParseConstants.NAME)); - nestedWrap.setIdentifier(nestedIdentifier); - nestedWrap.setPipeline(identifier); - context.register(nestedWrap); - - // Use nested wrap identifier. - identifier = nestedIdentifier; - } - - parser.parseElement(sub, context); - - // Append sub elements. - ElementDefinition registered = context.getRegistered(identifier); - AssertUtil.notNull(registered, identifier + " sub elements parse failed."); - subDefinitions.add(registered); + subDefinitions.add(parseSubElement(sub, context)); } definition.setChildren(subDefinitions); diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ScriptElementParser.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ScriptElementParser.java new file mode 100644 index 0000000000000000000000000000000000000000..38dcec0441ef1c16bd502c8ef3777b4331ccf2dc --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/ScriptElementParser.java @@ -0,0 +1,34 @@ +package org.smartboot.flow.core.parser; + +import org.smartboot.flow.core.parser.definition.ScriptDefinition; +import org.smartboot.flow.core.util.AssertUtil; +import org.w3c.dom.Element; + +/** + * Parse script element. + * + * @author qinluo + * @date 2022-11-15 13:00:59 + * @since 1.0.0 + */ +public class ScriptElementParser extends AbstractElementParser { + + @Override + public void doParse(Element element, ParserContext context) { + ScriptDefinition elementDefinition = new ScriptDefinition(); + String name = element.getAttribute(ParseConstants.NAME); + AssertUtil.notBlank(name, "script name must not be blank"); + String type = element.getAttribute("type"); + + elementDefinition.setName(name); + elementDefinition.setIdentifier(name); + elementDefinition.setType(type); + elementDefinition.setScript(element.getTextContent().trim()); + context.register(elementDefinition); + } + + @Override + public String getElementName() { + return ParseConstants.SCRIPT; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/AdapterDefinition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/AdapterDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..6a91dc04d4485a90c051206647296a6258cff396 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/AdapterDefinition.java @@ -0,0 +1,40 @@ +package org.smartboot.flow.core.parser.definition; + +import org.smartboot.flow.core.component.AdapterComponent; +import org.smartboot.flow.core.parser.DefinitionVisitor; +import org.smartboot.flow.core.util.AssertUtil; + +/** + * 适配器定义 + * + * @author huqiang + * @since 2022/12/8 10:58 + */ +public class AdapterDefinition extends ElementDefinition { + + private ElementDefinition pipelineElement; + + public ElementDefinition getPipelineElement() { + return this.pipelineElement; + } + + public void setPipelineElement(ElementDefinition pipelineElement) { + this.pipelineElement = pipelineElement; + } + + @Override + public void validate() { + AssertUtil.notBlank(getExecute(), " attribute[execute] must not be null"); + AssertUtil.notNull(pipelineElement, "pipeline children must not be empty"); + } + + @Override + public Class resolveType() { + return AdapterComponent.class; + } + + @Override + protected void visit0(DefinitionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ElementDefinition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ElementDefinition.java index a889fe8ae9f2843dd00f3e982e1ee67011c17e6c..1aa0e4a104443310896efeb96f0801f6e2a05e31 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ElementDefinition.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ElementDefinition.java @@ -2,7 +2,7 @@ package org.smartboot.flow.core.parser.definition; import org.smartboot.flow.core.Validator; import org.smartboot.flow.core.common.Uniqueness; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; import org.smartboot.flow.core.executable.ExecutableAdapter; import org.smartboot.flow.core.parser.DefinitionVisitor; import org.smartboot.flow.core.parser.ElementUtils; @@ -21,8 +21,7 @@ import java.util.List; public class ElementDefinition extends Uniqueness implements Validator { protected String name; - private String type; - private String ref; + private String execute; private String test; private String when; protected ParserContext context; @@ -52,36 +51,27 @@ public class ElementDefinition extends Uniqueness implements Validator { } public static void build(ElementDefinition definition, Element element) { - definition.setRef(element.getAttribute(ParseConstants.REF)); - definition.setType(element.getAttribute(ParseConstants.TYPE)); definition.setName(element.getAttribute(ParseConstants.NAME)); + definition.setExecute(element.getAttribute(ParseConstants.EXECUTE)); definition.setWhen(element.getAttribute(ParseConstants.WHEN)); // Extra well-known attributes. definition.getAttributes().addAll(ElementUtils.extraAttributes(element)); } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; + public String getExecute() { + return execute; } - public String getType() { - return type; + public void setExecute(String execute) { + this.execute = execute; } - public void setType(String type) { - this.type = type; - } - - public String getRef() { - return ref; + public String getName() { + return name; } - public void setRef(String ref) { - this.ref = ref; + public void setName(String name) { + this.name = name; } public List getAttributes() { diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ScriptDefinition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ScriptDefinition.java new file mode 100644 index 0000000000000000000000000000000000000000..72d31a2eafd37e874b921c5616969adb30d301fb --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/parser/definition/ScriptDefinition.java @@ -0,0 +1,65 @@ +package org.smartboot.flow.core.parser.definition; + +import org.smartboot.flow.core.script.ScriptCondition; +import org.smartboot.flow.core.script.ScriptDetector; +import org.smartboot.flow.core.util.AssertUtil; +import org.smartboot.flow.core.util.AuxiliaryUtils; + +/** + * @author qinluo + * @date 2022-11-15 13:00:20 + * @since 1.0.0 + */ +public class ScriptDefinition extends ElementDefinition { + + private String type; + private String script; + + @Override + public void validate() { + AssertUtil.notBlank(name, "script name must not be blank"); + AssertUtil.notBlank(type, "script type must not be blank"); + AssertUtil.notBlank(script, "script must not be blank"); + + Class javaType; + if (!AuxiliaryUtils.isType(type)) { + javaType = ScriptDetector.get().getJavaType(type); + } else { + javaType = AuxiliaryUtils.asClass(type); + } + + AssertUtil.notNull(javaType, "script type must be a javaType"); + AssertUtil.isTrue(ScriptCondition.class.isAssignableFrom(javaType), "script type must be a subclass of ScriptCondition"); + AssertUtil.isTrue(ScriptCondition.class != javaType, "script type must be a subclass of ScriptCondition"); + } + + /** + * Return script java type. + * + * @return maybe null in spring container. + */ + public Class getJavaType() { + Class javaType = AuxiliaryUtils.asClass(type); + if (javaType == null) { + javaType = ScriptDetector.get().getJavaType(type); + } + + return javaType; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptCondition.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..f75ccc6f1ed14464aa8f932e8edbc056b50a3fb9 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptCondition.java @@ -0,0 +1,48 @@ +package org.smartboot.flow.core.script; + +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.NamedCondition; + +/** + * @author qinluo + * @date 2022/11/28 19:41 + * @since 1.0.0 + */ +public abstract class ScriptCondition extends NamedCondition { + + /** + * 执行脚本 + */ + protected String script; + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + /** + * 获取脚本条件的类型 + * + * @return type string + */ + public abstract String getType(); + + /** + * Don't override this method. + */ + @Override + public final Object test(T t, S s) { + return null; + } + + @Override + public abstract Object test(EngineContext context); + + @Override + public String describe() { + return "script-" + getType(); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptConstants.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..8d20f40c910493e4192090ed48106195143a0a21 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptConstants.java @@ -0,0 +1,26 @@ +package org.smartboot.flow.core.script; + +/** + * Constants definitions in script. + * + * @author qinluo + * @date 2022/11/28 19:41 + * @since 1.0.0 + */ +public interface ScriptConstants { + + /** + * Request params. + */ + String REQ = "request"; + + /** + * Result mode. + */ + String RESULT = "result"; + + /** + * Flow engine execution context. + */ + String CONTEXT = "context"; +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptDetector.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptDetector.java new file mode 100644 index 0000000000000000000000000000000000000000..989b0b28b3d9d78ce2495951daad2f159740c541 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/script/ScriptDetector.java @@ -0,0 +1,73 @@ +package org.smartboot.flow.core.script; + +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.util.AssertUtil; +import org.smartboot.flow.core.util.AuxiliaryUtils; + +import java.net.URL; +import java.util.Enumeration; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author qinluo + * @date 2022/11/28 19:41 + * @since 1.0.0 + */ +public class ScriptDetector { + + /** + * The phrase for {@link org.smartboot.flow.core.script.ScriptCondition} location. + */ + private static final String LOCATION = "META-INF/smart-flow-script"; + + /** + * The stored phrase for {@link org.smartboot.flow.core.script.ScriptCondition} + */ + private final Map> javaTypes = new ConcurrentHashMap<>(); + + /** + * The singleton instance for detector. + */ + private static final ScriptDetector DETECTOR = new ScriptDetector(); + + public static ScriptDetector get() { + return DETECTOR; + } + + private ScriptDetector() { + try { + Enumeration resources = this.getClass().getClassLoader().getResources(LOCATION); + while (resources.hasMoreElements()) { + URL url = resources.nextElement(); + + // phrase files must be properties. + Properties properties = new Properties(); + properties.load(url.openStream()); + + // key : phrase, value: full java classname. + Set keys = properties.stringPropertyNames(); + for (String key : keys) { + AssertUtil.notBlank(key, "script key must not be blank"); + String type = properties.getProperty(key); + AssertUtil.notBlank(type, "script " + key + " type must not be blank"); + Class javaType = AuxiliaryUtils.asClass(type); + AssertUtil.notNull(javaType, "script " + key + " type must be a javaType"); + AssertUtil.isTrue(ScriptCondition.class.isAssignableFrom(javaType), "script " + key + " type must be a subclass of ScriptCondition"); + AssertUtil.isTrue(ScriptCondition.class != (javaType), "script " + key + " type must be a subclass of ScriptCondition"); + javaTypes.put(key.toLowerCase(), javaType); + } + } + } catch (Exception e) { + throw new FlowException(e); + } + + } + + public Class getJavaType(String type) { + AssertUtil.notBlank(type, "type must not blank"); + return javaTypes.get(type.trim().toLowerCase()); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Node.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Node.java index 74eb46bd6996b26c13f01d52f6723f0eee0a6850..243deb20c19e273860d94cc80bfa04957dbdf913 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Node.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Node.java @@ -1,6 +1,7 @@ package org.smartboot.flow.core.trace; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; /** @@ -19,22 +20,19 @@ public class Node { * Node escaped time in mills. */ private long escaped; + private final long timestamp; /** * Parent Node. */ private Node parent; + private boolean async; /** * Children nodes. */ private final List children = new ArrayList<>(16); - /** - * Children nodes in another threads. - */ - private final List threadChildren = new ArrayList<>(16); - /** * Create this node object's thread. */ @@ -56,10 +54,6 @@ public class Node { return children; } - public List getThreadChildren() { - return threadChildren; - } - public long getThreadId() { return threadId; } @@ -74,14 +68,14 @@ public class Node { public Node(String message) { this.message = message; this.escaped = System.currentTimeMillis(); + this.timestamp = escaped; } public void addChild(Node node) { - if (node.threadId == threadId) { - children.add(node); - } else { - threadChildren.add(node); + if (node.threadId != threadId) { + node.async = true; } + children.add(node); } public Node getParent() { @@ -99,34 +93,38 @@ public class Node { public void dump(String prefix, StringBuilder sb) { sb.append(prefix).append(wrap(message)).append(" escaped ").append(escaped).append("ms"); sb.append("\n"); - if (children.isEmpty() && threadChildren.isEmpty()) { + if (children.isEmpty()) { return; } - int i = 0; - for (Node child : children) { - String nextPrefix = generatePrefix(prefix, false,i++ >= children.size()); - child.dump(nextPrefix, sb); + children.sort((Comparator.comparingLong(o -> o.timestamp))); + boolean hasNextBrother = hasNextBrother(); + + for (Node node : children) { + String nextPrefix = generatePrefix(prefix, node.async, hasNextBrother); + node.dump(nextPrefix, sb); } + } - i = 0; - for (Node child : threadChildren) { - String nextPrefix = generatePrefix(prefix, true, i++ >= threadChildren.size()); - child.dump(nextPrefix, sb); + private boolean hasNextBrother() { + if (parent == null || parent.children.isEmpty()) { + return false; } + + return parent.children.indexOf(this) < parent.children.size() - 1; } - private String generatePrefix(String prefix, boolean async, boolean i) { + private String generatePrefix(String prefix, boolean async, boolean hasNext) { if (prefix == null || prefix.length() == 0) { return "|---"; } if (prefix.length() == 4) { - return (i ? "|" : " ") + " |" + (async ? "~~~" : "---"); + return (hasNext ? "|" : " ") + " |" + (async ? "~~~" : "---"); } int length = prefix.length() - 5; - return prefix.substring(0, length) + (i ? " |" : " ") + " |" + (async ? "~~~" : "---"); + return prefix.substring(0, length) + (hasNext ? " |" : " ") + " |" + (async ? "~~~" : "---"); } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Tracer.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Tracer.java index 406327e11c544049aa4972f2ab635d20fedb197a..b94ea3a2977ccf3a1e5b64c0e7571c785e11f3ed 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Tracer.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/trace/Tracer.java @@ -1,6 +1,8 @@ package org.smartboot.flow.core.trace; /** + * 以类似栈的方式记录调用路径(支持追踪异步调用路径) + * * @author qinluo * @date 2022-11-11 21:53:04 * @since 1.0.0 @@ -13,6 +15,10 @@ public class Tracer { * Record invoking tree path string. */ private String trace; + + /** + * The root node. + */ private Node root; public String getTrace() { @@ -67,4 +73,12 @@ public class Tracer { this.trace = null; TOP.remove(); } + + public static Node get() { + return TOP.get(); + } + + public static void setNode(Node n) { + TOP.set(n); + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractEngineQuery.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractEngineQuery.java new file mode 100644 index 0000000000000000000000000000000000000000..b7bdbd6b4132887c84912b50f04b1df49ce44957 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractEngineQuery.java @@ -0,0 +1,21 @@ +package org.smartboot.flow.core.useful; + +import java.io.Serializable; + +/** + * @author qinluo + * @version 1.0.0 + * @since 2019-05-20 14:23 + */ +public abstract class AbstractEngineQuery implements Serializable { + + private static final long serialVersionUID = -4493559976724466867L; + + /** + * 得到组装字符串 + * + * @return 组装的字符串,由各个实现自由决定 + */ + public abstract String getKey(); + +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractExecutorSelector.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractExecutorSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..eae0904a7e5edd8149d22c2f72cb033da7b81269 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/AbstractExecutorSelector.java @@ -0,0 +1,43 @@ +package org.smartboot.flow.core.useful; + + +import org.smartboot.flow.core.FlowEngine; + +import java.util.Map; + +/** + * @author qinluo + * @version 1.0.0 + * @date 2020-04-07 15:13 + */ +@SuppressWarnings({"rawtypes"}) +public abstract class AbstractExecutorSelector implements ExecutorSelector { + + private String selectorName; + + public String getSelectorName() { + return selectorName; + } + + public void setSelectorName(String selectorName) { + this.selectorName = selectorName; + } + + @Override + public FlowEngine select(Map engineMap, AbstractEngineQuery query) { + if (engineMap == null || engineMap.size() == 0) { + throw new IllegalStateException(selectorName + " there are no available executors, because map is empty!"); + } + + return doSelect(engineMap, query); + } + + /** + * Select a engine executor. + * + * @param engineMap candidate engine executor + * @param query engine query + * @return engine executor + */ + public abstract FlowEngine doSelect(Map engineMap, AbstractEngineQuery query); +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultEngineQuery.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultEngineQuery.java new file mode 100644 index 0000000000000000000000000000000000000000..4c9cca2eb1fc84e420e4febc16d4c18905613884 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultEngineQuery.java @@ -0,0 +1,24 @@ +package org.smartboot.flow.core.useful; + +/** + * 通过engineName查询engine + * + * @author qinluo + * @date 2022-12-07 14:30:08 + * @since 1.0.0 + */ +public class DefaultEngineQuery extends AbstractEngineQuery { + + private static final long serialVersionUID = -383072247314629042L; + + private final String engineName; + + public DefaultEngineQuery(String name) { + this.engineName = name; + } + + @Override + public String getKey() { + return engineName; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultExecutorSelector.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultExecutorSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..db681dcaf21de1991ed00357f59f6b8fd7dfa0cb --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/DefaultExecutorSelector.java @@ -0,0 +1,28 @@ +package org.smartboot.flow.core.useful; + +import org.smartboot.flow.core.FlowEngine; + +import java.util.Map; +import java.util.Objects; + +/** + * @author qinluo + * @date 2022-12-06 21:29:32 + * @since 1.0.0 + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class DefaultExecutorSelector extends AbstractExecutorSelector { + + @Override + public FlowEngine doSelect(Map engineMap, AbstractEngineQuery query) { + String key = query.getKey(); + + for (FlowEngine flowEngine : engineMap.values()) { + if (Objects.equals(key, flowEngine.getName())) { + return flowEngine; + } + } + + return null; + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/EngineExecutorManager.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/EngineExecutorManager.java new file mode 100644 index 0000000000000000000000000000000000000000..4a269340399e28046c54c5b063be5297ea061c7e --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/EngineExecutorManager.java @@ -0,0 +1,83 @@ +package org.smartboot.flow.core.useful; + + + +import org.smartboot.flow.core.FlowEngine; +import org.smartboot.flow.core.Validator; +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.util.AssertUtil; + +import java.util.Map; + +/** + * 引擎管理器抽象类 + * + * @author qinluo + * @version 1.0.0 + * @since 2019-05-20 14:18 + */ +@SuppressWarnings({"rawtypes"}) +public class EngineExecutorManager implements Validator { + + /** + * Managed engines. + */ + private Map flowEngines; + + /** + * Default selector by name. + */ + private ExecutorSelector selector = new DefaultExecutorSelector(); + + public ExecutorSelector getSelector() { + return selector; + } + + public void setSelector(ExecutorSelector selector) { + if (selector == null) { + throw new IllegalStateException("selector must not be null!"); + } + this.selector = selector; + } + + public void setSelectorName(String selectorName) { + if (selector instanceof AbstractExecutorSelector) { + ((AbstractExecutorSelector) selector).setSelectorName(selectorName); + } + } + + public Map getFlowEngines() { + return flowEngines; + } + + public void setFlowEngines(Map flowEngines) { + this.flowEngines = flowEngines; + } + + public FlowEngine getEngine(AbstractEngineQuery query) { + AssertUtil.notNull(query, "engine query must not be null!"); + + this.validate(); + + FlowEngine engineExecutor = selector.select(flowEngines, query); + if (engineExecutor == null) { + throw new FlowException("select engine executor error. selector return null!"); + } + + return engineExecutor; + } + + public FlowEngine getEngine(String engineName) { + AssertUtil.notBlank(engineName, "engineName must not be blank"); + return getEngine(new DefaultEngineQuery(engineName)); + } + + @Override + public void validate() { + if (flowEngines == null || flowEngines.size() == 0) { + throw new FlowException("there is no available engine, please check!"); + } + + AssertUtil.notNull(selector, "selector must not be null!"); + } +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/ExecutorSelector.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/ExecutorSelector.java new file mode 100644 index 0000000000000000000000000000000000000000..7236ff8de4ac966db48c9ede832dde13341af847 --- /dev/null +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/useful/ExecutorSelector.java @@ -0,0 +1,23 @@ +package org.smartboot.flow.core.useful; + + +import org.smartboot.flow.core.FlowEngine; + +import java.util.Map; + +/** + * @author qinluo + * @version 1.0.0 + * @since 2019-06-04 15:55 + */ +@SuppressWarnings("rawtypes") +public interface ExecutorSelector { + + /** + * 从候选执行引擎中选择一个返回 + * @param engineMap 执行引擎列表Map + * @param query 查询对象 + * @return 选中的执行引擎 + */ + FlowEngine select(Map engineMap, AbstractEngineQuery query); +} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AssertUtil.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AssertUtil.java index 9c6af05616673c2eec741111f1b49cc1d3b1b040..f23d8a478c6d3b1ecc0c8419ad1d7fad2a1cb847 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AssertUtil.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AssertUtil.java @@ -1,5 +1,7 @@ package org.smartboot.flow.core.util; +import org.smartboot.flow.core.exception.FlowException; + import java.util.Objects; /** @@ -15,36 +17,42 @@ public final class AssertUtil { public static void shouldNotReachHere() { - throw new IllegalStateException("should not reach here"); + throw new FlowException("should not reach here"); } public static void notNull(Object value, String message) { if (value == null) { - throw new IllegalArgumentException(message); + throw new FlowException(message); } } public static void assertNull(Object value, String message) { if (value != null) { - throw new IllegalArgumentException(message); + throw new FlowException(message); } } public static void notBlank(String value, String message) { if (value == null || value.trim().length() == 0) { - throw new IllegalArgumentException(message); + throw new FlowException(message); } } public static void isTrue(boolean value, String message) { if (!value) { - throw new IllegalArgumentException(message); + throw new FlowException(message); + } + } + + public static void isFalse(boolean value, String message) { + if (value) { + throw new FlowException(message); } } public static void assertEquals(Object expect, Object actual, String message) { if (!Objects.equals(expect, actual)) { - throw new IllegalArgumentException(message); + throw new FlowException(message); } } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AuxiliaryUtils.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AuxiliaryUtils.java index c6d6694593d84d82406bae9b9a772b9c6c357daa..ed890c377cd59e2c0864d28992ac148dec42de99 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AuxiliaryUtils.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/util/AuxiliaryUtils.java @@ -22,4 +22,21 @@ public final class AuxiliaryUtils { public static boolean isNotBlank(String value) { return !isBlank(value); } + + public static boolean isType(String typename) { + try { + Class.forName(typename); + return true; + } catch (Exception e) { + return false; + } + } + + public static Class asClass(String typename) { + try { + return Class.forName(typename); + } catch (Exception e) { + return null; + } + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponent.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponent.java index 9602c7a6d277b6365f03b95528480bc48805c51f..cc81ab4f09bfcc55add81f0565d2a137dae6d06b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponent.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponent.java @@ -1,109 +1,85 @@ package org.smartboot.flow.core.view.plantuml; import org.smartboot.flow.core.common.ComponentType; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.util.AuxiliaryUtils; +import org.smartboot.flow.core.visitor.ComponentVisitor; +import org.smartboot.flow.core.visitor.PipelineVisitor; import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * @author yamikaze * @date 2022/11/14 */ -public class PlantumlComponent { +public class PlantumlComponent extends ComponentVisitor { private final List attributes = new ArrayList<>(); - private String name; - private String describe; + private final String name; + private final String describe; // basic, if, choose, pipeline; - private ComponentType type; + private final ComponentType type; // Used with type pipeline. private PlantumlPipeline pipeline; private String condition; private String branch; private final List components = new ArrayList<>(); - private String executable; + private volatile boolean eraserCalled; + /** + * 记录访问过的对象 + */ + private final Set visited; - public PlantumlComponent(String name, String describe) { + public PlantumlComponent(ComponentType type, String name, String describe, Set visited) { this.name = name; this.describe = describe; - } - - public List getAttributes() { - return attributes; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescribe() { - return describe; - } - - public void setDescribe(String describe) { - this.describe = describe; - } - - public ComponentType getType() { - if (type == null && condition != null) { - type = ComponentType.IF; - } - - return type; - } - - public void setType(ComponentType type) { this.type = type; + this.visited = visited; } - public PlantumlPipeline getPipeline() { - return pipeline; - } - - public void setPipeline(PlantumlPipeline pipeline) { - this.pipeline = pipeline; + @Override + public PipelineVisitor visitPipeline(String pipeline) { + this.pipeline = new PlantumlPipeline(pipeline, visited); + return this.pipeline; } - public String getCondition() { - return condition; - } - - public void setCondition(String condition) { + @Override + public void visitCondition(String condition) { this.condition = condition; } - public String getBranch() { - return branch; - } - - public void setBranch(String branch) { - this.branch = branch; - } - - public void addComponent(PlantumlComponent component) { + @Override + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { + PlantumlComponent component = new PlantumlComponent(type, name, describe, visited); this.components.add(component); + return component; } - public String getExecutable() { - return executable; + @Override + public void visitAttributes(List attributes) { + this.attributes.addAll(attributes); } - public void setExecutable(String executable) { - this.executable = executable; + @Override + public ComponentVisitor visitBranch(Object branch, ComponentType type, String name, String describe) { + PlantumlComponent component = new PlantumlComponent(type, name, describe, visited); + component.branch = (String.valueOf(branch)); + this.components.add(component); + return component; } public void generate(StringBuilder content) { String nodeName = AuxiliaryUtils.isBlank(name) ? describe : name; + ComponentType type = this.type; - if (getType() == ComponentType.BASIC) { + if (type == ComponentType.BASIC) { String serialAttributes = serialAttributes(); + if (isAsync()) { + content.append("-[dashed]->异步;\n"); + } content.append(getColor()).append(":").append(nodeName).append(";\n"); if (serialAttributes.trim().length() > 0) { @@ -111,7 +87,10 @@ public class PlantumlComponent { .append("\nend note\n"); } - } else if (getType() == ComponentType.IF) { + } else if (type == ComponentType.IF) { + if (isAsync()) { + content.append("-[dashed]->异步;\n"); + } content.append("if (").append(condition).append(") then (true)\n"); components.get(0).generate(content); if (components.size() >= 2) { @@ -120,11 +99,14 @@ public class PlantumlComponent { } content.append("endif\n"); - } else if (getType() == ComponentType.CHOOSE) { + } else if (type == ComponentType.CHOOSE) { + if (isAsync()) { + content.append("-[dashed]->异步;\n"); + } content.append("switch (").append(condition).append(")\n"); for (PlantumlComponent component : components) { - if (component.getBranch() != null) { + if (component.branch != null) { content.append("case (branch ").append(component.branch).append(")\n"); } else { content.append("case (default) \n"); @@ -134,10 +116,30 @@ public class PlantumlComponent { content.append("endswitch\n"); - } else if (getType() == ComponentType.PIPELINE) { + } else if (type == ComponentType.PIPELINE) { + if (isAsync()) { + content.append("-[dashed]->异步;\n"); + } + if (pipeline.isCycle()) { + content.append(": goto ").append(pipeline.getName()).append(";\n"); + // kill this branch. + content.append("kill\n "); + return; + } + content.append("partition 子流程:").append(pipeline.getName()).append("{ \n"); pipeline.generate(content); content.append(" } \n"); + } else if (type == ComponentType.ADAPTER) { + if (isAsync()) { + content.append("-[dashed]->异步;\n"); + } + PlantumlComponent pipeline = components.get(0); + content.append("partition 适配器:").append(nodeName) + .append(" 子流程:") + .append(pipeline.pipeline.getName()).append("{ \n"); + pipeline.pipeline.generate(content); + content.append(" } \n"); } } @@ -151,6 +153,16 @@ public class PlantumlComponent { return sb.toString(); } + private boolean isAsync() { + for (AttributeHolder holder : attributes) { + if (holder.getAttribute() == Attributes.ASYNC) { + return true; + } + } + + return false; + } + private String getColor() { List colors = new ArrayList<>(); for (AttributeHolder holder : attributes) { @@ -177,4 +189,29 @@ public class PlantumlComponent { return base; } + + public void eraser(boolean directPipeline) { + if (eraserCalled) { + return; + } + + eraserCalled = true; + + // 未直接在流水线中,需要擦除所有属性,除了name, 如果是基本组件,可以保留degradable和callback + if (!directPipeline) { + this.attributes.removeIf(p -> { + if (type != ComponentType.BASIC) { + return p.getAttribute() != Attributes.NAME; + } + + return p.getAttribute() != Attributes.DEGRADABLE && p.getAttribute() != Attributes.DEGRADE_CALLBACK; + }); + } + + if (this.type == ComponentType.PIPELINE) { + this.pipeline.eraser(); + } else if (this.type != ComponentType.BASIC) { + this.components.forEach(p -> p.eraser(false)); + } + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponentVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponentVisitor.java deleted file mode 100644 index 5eb8a4119eed22a4f8126f1b6aa6850a1f6f325d..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlComponentVisitor.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.smartboot.flow.core.view.plantuml; - -import org.smartboot.flow.core.Pipeline; -import org.smartboot.flow.core.common.ComponentType; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Component; -import org.smartboot.flow.core.visitor.ComponentVisitor; -import org.smartboot.flow.core.visitor.PipelineVisitor; - -import java.util.List; - -/** - * @author qinluo - * @date 2022-11-14 19:48:45 - * @since 1.0.0 - */ -public class PlantumlComponentVisitor extends ComponentVisitor { - - private final PlantumlComponent plantumlComponent; - - public PlantumlComponentVisitor(PlantumlComponent plantumlComponent) { - this.plantumlComponent = plantumlComponent; - } - - @Override - public PipelineVisitor visitPipeline(Pipeline pipeline) { - PlantumlPipeline plantumlPipeline = new PlantumlPipeline(); - plantumlPipeline.setName(pipeline.describe()); - plantumlComponent.setPipeline(plantumlPipeline); - plantumlComponent.setType(ComponentType.PIPELINE); - return new PlantumlPipelineVisitor(plantumlPipeline); - } - - @Override - public void visitCondition(String condition) { - plantumlComponent.setCondition(condition); - } - - @Override - public ComponentVisitor visitComponent(Component comp) { - PlantumlComponent component = new PlantumlComponent(comp.getName(), comp.describe()); - this.plantumlComponent.addComponent(component); - return new PlantumlComponentVisitor(component); - } - - @Override - public void visitAttributes(List attributes) { - this.plantumlComponent.getAttributes().addAll(attributes); - } - - @Override - public ComponentVisitor visitBranch(Object branch, Component comp) { - PlantumlComponent component = new PlantumlComponent(comp.getName(), comp.describe()); - component.setBranch(String.valueOf(branch)); - this.plantumlComponent.addComponent(component); - this.plantumlComponent.setType(ComponentType.CHOOSE); - return new PlantumlComponentVisitor(component); - } - - @Override - public void visitExecutable(String executable) { - this.plantumlComponent.setType(ComponentType.BASIC); - this.plantumlComponent.setExecutable(executable); - } -} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngine.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngine.java deleted file mode 100644 index d03f189d82d298ba31f844547f8f962c35bc56d3..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngine.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.smartboot.flow.core.view.plantuml; - -/** - * @author yamikaze - * @date 2022/11/14 - */ -public class PlantumlEngine { - - /** - * engine name. - */ - private String name; - private PlantumlPipeline pipeline; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public PlantumlPipeline getPipeline() { - return pipeline; - } - - public void setPipeline(PlantumlPipeline pipeline) { - this.pipeline = pipeline; - } -} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngineVisitor.java index 40c013fc510bf4da9eba1933fe3e105c5107f9f6..5413c8a87696202038c30f4aeb07819b408198be 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngineVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlEngineVisitor.java @@ -2,7 +2,7 @@ package org.smartboot.flow.core.view.plantuml; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.FlowEngine; import org.smartboot.flow.core.visitor.EngineVisitor; import org.smartboot.flow.core.visitor.PipelineVisitor; @@ -10,6 +10,9 @@ import java.io.BufferedOutputStream; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.OutputStreamWriter; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Executor; /** * @author qinluo @@ -24,22 +27,37 @@ public class PlantumlEngineVisitor extends EngineVisitor { private static final String END = "@enduml"; private static final String SUFFIX = ".puml"; - private final PlantumlEngine plantumlEngine = new PlantumlEngine(); // plantuml file dest. private final String dest; + /** + * engine name. + */ + private String name; + private PlantumlPipeline pipeline; + + /** + * 记录访问过的对象 + */ + private final Set visited = new HashSet<>(); public PlantumlEngineVisitor(String dest) { this.dest = dest + SUFFIX; } + public void visit(FlowEngine engine) { + engine.visit(this); + } + @Override public void visitEnd() { + // eraser不需要的属性 + this.pipeline.eraser(); + StringBuilder content = new StringBuilder(); content.append(START).append("\n"); content.append("skinparam ConditionEndStyle hline\n"); - content.append("note right\n Engine:").append(plantumlEngine.getName()) - .append(",MainProcess:").append(plantumlEngine.getPipeline().getName()) - .append("\nend note \n").append("-[hidden]->\n"); + content.append("title Engine:").append(this.name) + .append(",MainProcess:").append(pipeline.getName()).append("\n"); content.append("split\n"); for (Color color : Color.values()) { @@ -62,7 +80,7 @@ public class PlantumlEngineVisitor extends EngineVisitor { content.append("\n : start ;\n"); - plantumlEngine.getPipeline().generate(content); + this.pipeline.generate(content); content.append("\n : end ;\n"); @@ -79,15 +97,13 @@ public class PlantumlEngineVisitor extends EngineVisitor { } @Override - public void visit(String engine) { - plantumlEngine.setName(engine); + public void visit(String engine, Executor executor) { + this.name = engine; } @Override - public PipelineVisitor visitPipeline(Pipeline pipeline) { - PlantumlPipeline plantumlPipeline = new PlantumlPipeline(); - plantumlPipeline.setName(pipeline.describe()); - plantumlEngine.setPipeline(plantumlPipeline); - return new PlantumlPipelineVisitor(plantumlPipeline); + public PipelineVisitor visitPipeline(String pipeline) { + this.pipeline = new PlantumlPipeline(pipeline, visited); + return this.pipeline; } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipeline.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipeline.java index 2efe09f965b57c357a28f51657a0059c018306d3..76dced6d22e868299ba2788b58f5d9a06bc44369 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipeline.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipeline.java @@ -1,33 +1,62 @@ package org.smartboot.flow.core.view.plantuml; +import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.common.ComponentType; +import org.smartboot.flow.core.visitor.ComponentVisitor; +import org.smartboot.flow.core.visitor.PipelineVisitor; + import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * @author yamikaze * @date 2022/11/14 */ -public class PlantumlPipeline { +public class PlantumlPipeline extends PipelineVisitor { - private String name; + private volatile boolean eraserCalled; + private final String name; private final List components = new ArrayList<>(); + /** + * 记录访问过的对象 + */ + private final Set visited; + private boolean cycle; public String getName() { return name; } - public void setName(String name) { - this.name = name; + public boolean isCycle() { + return cycle; } - public void addComponent(PlantumlComponent component) { - this.components.add(component); + public PlantumlPipeline(String name, Set visited) { + this.name = name; + this.visited = visited; } public List getComponents() { return components; } + @Override + public void visitSource(Pipeline pipeline) { + this.cycle = !visited.add(pipeline); + } + + @Override + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { + if (this.isCycle()) { + return null; + } + + PlantumlComponent component = new PlantumlComponent(type, name, describe, visited); + this.components.add(component); + return component; + } + public void generate(StringBuilder content) { for (PlantumlComponent component : components) { @@ -35,4 +64,16 @@ public class PlantumlPipeline { } } + + public void eraser() { + if (eraserCalled) { + return; + } + + eraserCalled = true; + + for (PlantumlComponent component : components) { + component.eraser(true); + } + } } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipelineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipelineVisitor.java deleted file mode 100644 index 5919ddca066e3203f6ad05f6cc71e944be0af676..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/view/plantuml/PlantumlPipelineVisitor.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.smartboot.flow.core.view.plantuml; - -import org.smartboot.flow.core.component.Component; -import org.smartboot.flow.core.visitor.ComponentVisitor; -import org.smartboot.flow.core.visitor.PipelineVisitor; - -/** - * @author qinluo - * @date 2022-11-14 19:48:21 - * @since 1.0.0 - */ -public class PlantumlPipelineVisitor extends PipelineVisitor { - - private final PlantumlPipeline pipeline; - - public PlantumlPipelineVisitor(PlantumlPipeline pipeline) { - this.pipeline = pipeline; - } - - @Override - public ComponentVisitor visitComponent(Component comp) { - PlantumlComponent component = new PlantumlComponent(comp.getName(), comp.describe()); - this.pipeline.addComponent(component); - return new PlantumlComponentVisitor(component); - } -} diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/ComponentVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/ComponentVisitor.java index 92910704e15eb34ca09c59d1672b95d6d24e4555..77ff49359c894f3f843e39031d3c382c7fb3eb3b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/ComponentVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/ComponentVisitor.java @@ -1,7 +1,7 @@ package org.smartboot.flow.core.visitor; -import org.smartboot.flow.core.Pipeline; -import org.smartboot.flow.core.component.AttributeHolder; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.common.ComponentType; import org.smartboot.flow.core.component.Component; import java.util.List; @@ -23,7 +23,7 @@ public class ComponentVisitor { this.delegate = visitor; } - public PipelineVisitor visitPipeline(Pipeline pipeline) { + public PipelineVisitor visitPipeline(String pipeline) { if (delegate != null) { return delegate.visitPipeline(pipeline); } @@ -31,15 +31,24 @@ public class ComponentVisitor { return null; } + public void visitSource(Component component) { + if (delegate != null) { + delegate.visitSource(component); + } + } + public void visitCondition(String condition) { if (delegate != null) { delegate.visitCondition(condition); } } - public ComponentVisitor visitComponent(Component component) { + /** + * Visit component with type, name, describe. + */ + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { if (delegate != null) { - return delegate.visitComponent(component); + return delegate.visitComponent(type, name, describe); } return null; @@ -51,9 +60,9 @@ public class ComponentVisitor { } } - public ComponentVisitor visitBranch(Object branch, Component component) { + public ComponentVisitor visitBranch(Object branch, ComponentType type, String name, String describe) { if (delegate != null) { - return delegate.visitBranch(branch, component); + return delegate.visitBranch(branch, type, name, describe); } return null; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/EngineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/EngineVisitor.java index 777ad3fda13f6f919d9f81e5a3759233c289db96..5b800981af6afebd23f4083820d7304251cb297b 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/EngineVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/EngineVisitor.java @@ -1,7 +1,8 @@ package org.smartboot.flow.core.visitor; import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.Pipeline; + +import java.util.concurrent.Executor; /** * @author qinluo @@ -23,28 +24,32 @@ public class EngineVisitor { this.delegate = delegate; } + public void visitEnd() { + if (delegate != null) { + delegate.visitEnd(); + } + } + /** - * Visit engine. + * Visit name and executor. + * + * @param name engine's name. + * @param executor async executor, maybe null. * - * @param flowEngine root. */ - public void visit(FlowEngine flowEngine) { - flowEngine.visit(this); - } - - public void visitEnd() { + public void visit(String name, Executor executor) { if (delegate != null) { - delegate.visitEnd(); + delegate.visit(name, executor); } } - public void visit(String engine) { + public void visitSource(FlowEngine flowEngine) { if (delegate != null) { - delegate.visit(engine); + delegate.visitSource(flowEngine); } } - public PipelineVisitor visitPipeline(Pipeline pipeline) { + public PipelineVisitor visitPipeline(String pipeline) { if (delegate != null) { return delegate.visitPipeline(pipeline); } diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/PipelineVisitor.java b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/PipelineVisitor.java index 25c402b456a7ca3c245dd44dc46f4b54c39643ab..c639b252d5c5195e28b94fdd36eee97b2229b176 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/PipelineVisitor.java +++ b/smart-flow-core/src/main/java/org/smartboot/flow/core/visitor/PipelineVisitor.java @@ -1,6 +1,7 @@ package org.smartboot.flow.core.visitor; -import org.smartboot.flow.core.component.Component; +import org.smartboot.flow.core.Pipeline; +import org.smartboot.flow.core.common.ComponentType; /** * @author qinluo @@ -19,9 +20,21 @@ public class PipelineVisitor { this.delegate = delegate; } - public ComponentVisitor visitComponent(Component component) { + /** + * Visit pipeline source. + */ + public void visitSource(Pipeline pipeline) { if (delegate != null) { - return delegate.visitComponent(component); + delegate.visitSource(pipeline); + } + } + + /** + * Visit component with type, name, describe. + */ + public ComponentVisitor visitComponent(ComponentType type, String name, String describe) { + if (delegate != null) { + return delegate.visitComponent(type, name, describe); } return null; diff --git a/smart-flow-core/src/main/resources/smart-flow-1.0.0.xsd b/smart-flow-core/src/main/resources/smart-flow-1.0.1.xsd similarity index 41% rename from smart-flow-core/src/main/resources/smart-flow-1.0.0.xsd rename to smart-flow-core/src/main/resources/smart-flow-1.0.1.xsd index a7c713086740c00e05316fe508d30ad2f1241fb9..b524c7382ef1f3668581b2f48b6289aa2a1b1147 100644 --- a/smart-flow-core/src/main/resources/smart-flow-1.0.0.xsd +++ b/smart-flow-core/src/main/resources/smart-flow-1.0.1.xsd @@ -3,6 +3,101 @@ targetNamespace="http://org.smartboot/smart-flow" elementFormDefault="qualified"> + + + + + 设置组件是否可回滚,默认为false,不可回滚, 仅对基本类型组件生效 + + + + + + + + 设置组件是否可降级,默认为false,开启降级时,当组件执行发生异常不会中断流程 + + + + + + + + 设置组件是否异步执行,默认false,注意设置此属性需要指定Executor + + + + + + + + 异步组件超时时间,单位毫秒(ms) + + + + + + + + 设置组件的前置依赖组件名称,当前置组件为异步组件时,会等待异步组件执行完毕后开始执行当前组件 + 多个组件名称以,分割 + + + + + + + + 启用/禁用组件,默认启用组件,设置为false,禁用该组件时,该组件不会执行 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -12,6 +107,7 @@ + @@ -26,7 +122,11 @@ - + + + + + @@ -43,12 +143,17 @@ + - + + + + + @@ -58,10 +163,40 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -73,9 +208,13 @@ - - - + + + + + + + @@ -91,11 +230,17 @@ + - - - + + + + + + + + @@ -111,17 +256,21 @@ + - - - + + + + + + + + - - @@ -132,9 +281,13 @@ - - - + + + + + + + @@ -150,11 +303,21 @@ + - - - + + + + + + + + + + + + @@ -170,11 +333,17 @@ + - - - + + + + + + + + diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep1.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep1.java deleted file mode 100644 index b79b2c97649abc9ac840381b7700bbe19023446e..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep1.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class AsyncStep1 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep2.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep2.java deleted file mode 100644 index 91f02ba77b3af1b7448545ea944cbdac968df417..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep2.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class AsyncStep2 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - try { - Thread.sleep(100); - } catch (Exception e) { - - } - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep3.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep3.java deleted file mode 100644 index c3a6eba9e79efb680a2aca469f93de4da8463a97..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/AsyncStep3.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class AsyncStep3 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - try { - Thread.sleep(200); - } catch (Exception e) { - - } - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/BaseTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/BaseTest.java deleted file mode 100644 index 12b0b87551a2c2b06ca0efe252ed1b0cfcf27726..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/BaseTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.smartboot.flow.core; - -import org.junit.jupiter.api.BeforeEach; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.manager.DefaultEngineManager; - -/** - * @author yamikaze - * @date 2022/11/13 - */ -public class BaseTest { - - protected static final Logger LOGGER = LoggerFactory.getLogger(BaseTest.class); - - @BeforeEach - public void setUp() { - DefaultEngineManager.getDefaultManager().detachAll(); - } - -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/ChooseCondition.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/ChooseCondition.java deleted file mode 100644 index 217a2b0f21497c1c01917d898822a224776421bb..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/ChooseCondition.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.smartboot.flow.core; - - -/** - * @author qinluo - * @date 2022-11-11 16:45:21 - * @since 1.0.0 - */ -public class ChooseCondition extends Condition { - - @Override - public Object test(Integer integer, String s) { - if (integer == null) { - return "null"; - } else if (integer == 1) { - return 1; - } else if (integer == 2) { - return 2; - } - return null; - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/DefaultStep.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/DefaultStep.java deleted file mode 100644 index 9cdddb109c7399a20c40103b34694710d42a4cc3..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/DefaultStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 16:44:21 - * @since 1.0.0 - */ -public class DefaultStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/ElseStep.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/ElseStep.java deleted file mode 100644 index 6a88b8b45b03a1b97d867662b8ced76a7eee54e1..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/ElseStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 16:44:21 - * @since 1.0.0 - */ -public class ElseStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/EngineTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/EngineTest.java deleted file mode 100644 index e705f528d12b83b88d81033052394d4456714e4d..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/EngineTest.java +++ /dev/null @@ -1,195 +0,0 @@ -package org.smartboot.flow.core; - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.builder.EngineBuilder; -import org.smartboot.flow.core.builder.ExecutableBuilder; -import org.smartboot.flow.core.builder.PipelineBuilder; -import org.smartboot.flow.core.component.Attributes; -import org.smartboot.flow.core.component.Component; -import org.smartboot.flow.core.component.IfComponent; -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.smartboot.flow.core.executable.ExecutableAdapter; - -import java.util.ArrayList; - -/** - * @author qinluo - * @date 2022-11-12 18:40:45 - * @since 1.0.0 - */ -public class EngineTest { - - private static final Logger LOGGER = LoggerFactory.getLogger(EngineTest.class); - - @Test - public void testSimple() { - FlowEngine engine = new FlowEngine<>(); - Pipeline pipeline = new Pipeline<>(); - engine.setPipeline(pipeline); - engine.setName("test"); - - pipeline.setComponents(new ArrayList<>()); - - ExecutableAdapter component = new ExecutableAdapter<>(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println(o); - System.out.println(o2); - } - }); - pipeline.addComponent(component); - - IfComponent ifComponent = new IfComponent<>(new Condition() { - @Override - public Object test(Integer integer, String s) { - return true; - } - }, new ExecutableAdapter<>(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println("if cocndition"); - } - })); - - pipeline.addComponent(ifComponent); - - - - EngineContext context = new EngineContext<>(); - context.setReq(1); - context.setResult("hello"); - - engine.execute(context); - } - - @Test - public void testSimpleBuilder() { - EngineBuilder builder = new EngineBuilder<>("defaultEngine"); - FlowEngine engine = builder.pipeline(new PipelineBuilder("DefaultPipeline").next(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println(o); - System.out.println(o2); - } - }).next(new Condition() { - @Override - public Object test(Integer integer, String s) { - return false; - } - }).then(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println("if cocndition"); - } - }, new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println("otherwise cocndition"); - } - }).build()).build(); - - EngineContext context = new EngineContext<>(); - context.setReq(1); - context.setResult("hello"); - - engine.execute(context); - } - - @Test - public void testSimpleBuilder02() { - EngineBuilder builder = new EngineBuilder<>("defaultEngine"); - FlowEngine engine = builder.pipeline(new PipelineBuilder("DefaultPipeline").next(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println(o); - System.out.println(o2); - } - }).next(new Condition() { - @Override - public Object test(Integer integer, String s) { - return false; - } - }).then(new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println("if cocndition"); - } - }, new AbstractExecutable() { - @Override - public void execute(Integer o, String o2) { - System.out.println("otherwise cocndition"); - } - }).choose(new Condition() { - @Override - public Object test(Integer integer, String s) { - if (integer == null) { - return "null"; - } else if (integer == 1) { - return 1; - } else if (integer == 2) { - return 2; - } - return null; - } - }).newBranch("null", new AbstractExecutable() { - @Override - public void execute(Integer integer, String s) { - System.out.println("enter branch null"); - } - }).newBranch(1, new AbstractExecutable() { - @Override - public void execute(Integer integer, String s) { - System.out.println("enter branch 1"); - } - }).newBranch(2, new AbstractExecutable() { - @Override - public void execute(Integer integer, String s) { - System.out.println("enter branch 2"); - } - }).end(new AbstractExecutable() { - @Override - public void execute(Integer integer, String s) { - System.out.println("enter default branch"); - } - }) - - .build()).build(); - - EngineContext context = new EngineContext<>(); - context.setReq(1); - context.setResult("hello"); - - engine.execute(context); - } - - @Test - public void testBuildPipeline() { - EngineBuilder builder = new EngineBuilder<>("defaultEngine"); - - PipelineBuilder pipelineBuilder = new PipelineBuilder<>("main process"); - - ExecutableBuilder execBuilder = ExecutableBuilder.newBuilder(); - execBuilder.apply(Attributes.ROLLBACK, true); - Component nested = execBuilder.newAdapter(new Step5()); - - pipelineBuilder.next(new Step1()).next(new Step2()) - .next(new IfCondition()).apply(Attributes.ROLLBACK, true).then(new Step3(), new ElseStep()) - .next(new IfCondition2()).then(new IfComponent<>(new IfCondition3(), nested)) - .choose(new ChooseCondition()) - .newBranch("null", new NullStep()) - .newBranch(1, new IntegerStep()).end(new DefaultStep()) - .pipeline("subprocess").next(new Step4()).next(new ErrorStep()).end(); - - - EngineContext context = new EngineContext<>(); - context.setReq(1); - context.setResult("hello"); - - builder.pipeline(pipelineBuilder.build()).build().execute(context); - - LOGGER.info("trace {}", context.getTrace()); - - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/ErrorStep.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/ErrorStep.java deleted file mode 100644 index d275f2920178db587696430dba80a726a140bdf0..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/ErrorStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class ErrorStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("error executed ======="); - throw new IllegalArgumentException("Error"); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition.java deleted file mode 100644 index f2dc88351f8636f0eeb6c30187f1ca1c235bd06d..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.smartboot.flow.core; - - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -public class IfCondition extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition2.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition2.java deleted file mode 100644 index 6bc14baf2d7b63d9bee350099fa8b2e317f29d1e..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition2.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.smartboot.flow.core; - - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -public class IfCondition2 extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition3.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition3.java deleted file mode 100644 index 4ecfc6be27afebabe448dd3a25b32dcf6c0f72e3..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/IfCondition3.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.smartboot.flow.core; - - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -public class IfCondition3 extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/IntegerStep.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/IntegerStep.java deleted file mode 100644 index 9ddd6075a215ad196d8fe26237f17432f29be786..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/IntegerStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class IntegerStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/NullStep.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/NullStep.java deleted file mode 100644 index aae7289c8aecece92ed5fdd2bf340cf4d3f75911..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/NullStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class NullStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step1.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step1.java deleted file mode 100644 index 64e989c5c62c89572d6f31797d521b0f2c767f8c..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step1.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step1 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step1 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step2.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step2.java deleted file mode 100644 index 7e6dda7ec6cf246689f396ad27f0c0bc7d047abe..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step2.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step2 extends AbstractExecutable { - - public Step2() { - System.out.println("create new step2 instance"); - } - - @Override - public void execute(Integer integer, String s) { - System.out.println("step2 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step3.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step3.java deleted file mode 100644 index 79146374ef10ad3b82ed541b1742422ae927fe27..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step3.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step3 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step3 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step4.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step4.java deleted file mode 100644 index 41812a27c8df13ed9675638b662d505647e68119..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step4.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step4 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step4 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step5.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step5.java deleted file mode 100644 index 26e2b3387aad30fc4052389ddb98a890ba385cda..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step5.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step5 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step5 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step6.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/Step6.java deleted file mode 100644 index 78815b47f451f16f80f2769e02a7f0018fa2192a..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/Step6.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.core; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step6 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step5 == " + integer); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/async/AsyncTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/async/AsyncTest.java deleted file mode 100644 index 54e5afd49718119d3c292b3e6d044bef7dfbe3d3..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/async/AsyncTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.smartboot.flow.core.async; - -import org.junit.jupiter.api.Test; -import org.smartboot.flow.core.AsyncStep1; -import org.smartboot.flow.core.AsyncStep2; -import org.smartboot.flow.core.AsyncStep3; -import org.smartboot.flow.core.BaseTest; -import org.smartboot.flow.core.ChooseCondition; -import org.smartboot.flow.core.DefaultStep; -import org.smartboot.flow.core.ElseStep; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.ErrorStep; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.IfCondition; -import org.smartboot.flow.core.IfCondition2; -import org.smartboot.flow.core.IfCondition3; -import org.smartboot.flow.core.IntegerStep; -import org.smartboot.flow.core.NullStep; -import org.smartboot.flow.core.Step1; -import org.smartboot.flow.core.Step2; -import org.smartboot.flow.core.Step3; -import org.smartboot.flow.core.Step4; -import org.smartboot.flow.core.Step5; -import org.smartboot.flow.core.Step6; -import org.smartboot.flow.core.builder.EngineBuilder; -import org.smartboot.flow.core.builder.ExecutableBuilder; -import org.smartboot.flow.core.builder.PipelineBuilder; -import org.smartboot.flow.core.component.Attributes; -import org.smartboot.flow.core.component.Component; -import org.smartboot.flow.core.component.IfComponent; -import org.smartboot.flow.core.view.plantuml.PlantumlEngineVisitor; - -import java.util.concurrent.Executors; - -/** - * @author qinluo - * @date 2022-11-14 22:34:14 - * @since 1.0.0 - */ -public class AsyncTest extends BaseTest { - - @Test - public void testAsync() { - EngineBuilder builder = new EngineBuilder<>("defaultEngine"); - - PipelineBuilder pipelineBuilder = new PipelineBuilder<>("main process"); - - ExecutableBuilder execBuilder = ExecutableBuilder.newBuilder(); - execBuilder.apply(Attributes.ROLLBACK, true); - Component nested = execBuilder.newAdapter(new Step5()); - - pipelineBuilder.next(new Step1()).next(new Step2()) - .next(new IfCondition()).apply(Attributes.ROLLBACK, true).then(new Step3(), new ElseStep()) - .next(new IfCondition2()).then(new IfComponent<>(new IfCondition3(), nested)) - - .next(execBuilder.apply(Attributes.ASYNC, true).apply(Attributes.NAME, "asyncStep1").apply(Attributes.TIMEOUT, 1000).newAdapter(new AsyncStep1())) - .next(execBuilder.apply(Attributes.ASYNC, true).apply(Attributes.NAME, "asyncStep2").apply(Attributes.TIMEOUT, 1000).newAdapter(new AsyncStep2())) - .next(execBuilder.apply(Attributes.ASYNC, false).apply(Attributes.DEPENDS_ON, "asyncStep1,asyncStep2").newAdapter(new AsyncStep3())) - - .choose(new ChooseCondition()) - .newBranch("null", new NullStep()) - .newBranch(1, new IntegerStep()).end(new DefaultStep()) - .pipeline("subprocess").next(new Step4()).next(execBuilder.apply(Attributes.ASYNC, true).apply(Attributes.TIMEOUT, 1000).newAdapter(new Step6())).next(new ErrorStep()).end(); - - - EngineContext context = new EngineContext<>(); - context.setReq(1); - context.setResult("hello"); - - FlowEngine engine = builder.pipeline(pipelineBuilder.build()).executor(Executors.newFixedThreadPool(10)).build(); - engine.execute(context); - LOGGER.info("trace {}", context.getTrace()); - - // Generate plantuml file named engine-flow - PlantumlEngineVisitor plantumlEngineVisitor = new PlantumlEngineVisitor("engine-flow"); - plantumlEngineVisitor.visit(engine); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/attributes/AttributeTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/attributes/AttributeTest.java deleted file mode 100644 index d16e59fba18e0280edaf1f4baa2271c455d6e633..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/attributes/AttributeTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.smartboot.flow.core.attributes; - -import org.junit.jupiter.api.Test; -import org.smartboot.flow.core.BaseTest; -import org.smartboot.flow.core.ChooseCondition; -import org.smartboot.flow.core.DefaultStep; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.IfCondition; -import org.smartboot.flow.core.IntegerStep; -import org.smartboot.flow.core.Pipeline; -import org.smartboot.flow.core.Step5; -import org.smartboot.flow.core.builder.EngineBuilder; -import org.smartboot.flow.core.builder.ExecutableBuilder; -import org.smartboot.flow.core.builder.PipelineBuilder; -import org.smartboot.flow.core.component.Attributes; -import org.smartboot.flow.core.view.plantuml.PlantumlEngineVisitor; - -/** - * @author qinluo - * @date 2022/11/18 22:23 - * @since 1.0.0 - */ -public class AttributeTest extends BaseTest { - - @Test - public void testEnabled() { - - PipelineBuilder builder = new PipelineBuilder<>("DefaultPipeline"); - Pipeline build = builder.next(new IfCondition()).apply(Attributes.DEGRADABLE, true).then(new DefaultStep()) - .choose(new ChooseCondition()).newBranch(1, new IntegerStep()) - .apply(Attributes.DEGRADABLE, true).end() - .next(ExecutableBuilder.newBuilder().apply(Attributes.ENABLED, false).newAdapter(new Step5())) - .build(); - - EngineBuilder engineBuilder = new EngineBuilder<>("defaultEngine"); - FlowEngine engine = engineBuilder.pipeline(build).build(); - - EngineContext context = engine.execute(1); - LOGGER.info("trace {}", context.getTrace()); - - PlantumlEngineVisitor visitor = new PlantumlEngineVisitor("engine-disabled"); - visitor.visit(engine); - - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/builder/IfComponentBuilderTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/builder/IfComponentBuilderTest.java deleted file mode 100644 index f7677c240c2902f207e8dc9c805c2835540c9a95..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/builder/IfComponentBuilderTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package org.smartboot.flow.core.builder; - -import org.junit.jupiter.api.Test; -import org.smartboot.flow.core.BaseTest; -import org.smartboot.flow.core.ChooseCondition; -import org.smartboot.flow.core.DefaultStep; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.IfCondition; -import org.smartboot.flow.core.IntegerStep; -import org.smartboot.flow.core.Pipeline; -import org.smartboot.flow.core.component.Attributes; - -/** - * @author yamikaze - * @date 2022/11/13 - */ -public class IfComponentBuilderTest extends BaseTest{ - - @Test - public void testBuild() { - - PipelineBuilder builder = new PipelineBuilder<>("DefaultPipeline"); - Pipeline build = builder.next(new IfCondition()).apply(Attributes.DEGRADABLE, true).then(new DefaultStep()) - .choose(new ChooseCondition()).newBranch(1, new IntegerStep()) - .apply(Attributes.DEGRADABLE, true).end().build(); - - EngineBuilder engineBuilder = new EngineBuilder<>("defaultEngine"); - FlowEngine engine = engineBuilder.pipeline(build).build(); - - EngineContext context = engine.execute(1); - LOGGER.info("trace {}", context.getTrace()); - - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/manager/ManagerTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/manager/ManagerTest.java deleted file mode 100644 index b7f40c40f6ef8391cf5aacfa8d7e4150c044c012..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/manager/ManagerTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package org.smartboot.flow.core.manager; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.smartboot.flow.core.AsyncStep1; -import org.smartboot.flow.core.AsyncStep2; -import org.smartboot.flow.core.AsyncStep3; -import org.smartboot.flow.core.BaseTest; -import org.smartboot.flow.core.ChooseCondition; -import org.smartboot.flow.core.DefaultStep; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.IfCondition; -import org.smartboot.flow.core.IntegerStep; -import org.smartboot.flow.core.Pipeline; -import org.smartboot.flow.core.Step1; -import org.smartboot.flow.core.Step2; -import org.smartboot.flow.core.Step3; -import org.smartboot.flow.core.Step5; -import org.smartboot.flow.core.Step6; -import org.smartboot.flow.core.builder.EngineBuilder; -import org.smartboot.flow.core.builder.ExecutableBuilder; -import org.smartboot.flow.core.builder.PipelineBuilder; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; - -import java.util.Map; - -/** - * @author qinluo - * @date 2022/11/18 22:23 - * @since 1.0.0 - */ -public class ManagerTest extends BaseTest { - - @BeforeEach - public void setUp() { - DefaultEngineManager.getDefaultManager().detachAll(); - LOGGER.info("detachAll executed"); - } - - @Test - public void testModifyAttributes() { - - PipelineBuilder builder = new PipelineBuilder<>("DefaultPipeline"); - Pipeline build = builder.next(new IfCondition()).apply(Attributes.DEGRADABLE, true).then(new DefaultStep()) - .choose(new ChooseCondition()).newBranch(1, new IntegerStep()) - .apply(Attributes.DEGRADABLE, true).end() - .next(ExecutableBuilder.newBuilder().apply(Attributes.NAME, "step5").newAdapter(new Step5())) - .next(new Step6()).next(new Step1()).next(new Step2()).next(new Step3()) - .pipeline("subprocess##2").next(new AsyncStep1()).next(new AsyncStep2()).next(new AsyncStep3()).end() - .build(); - - EngineBuilder engineBuilder = new EngineBuilder<>("defaultEngine"); - FlowEngine engine = engineBuilder.pipeline(build).build(); - - EngineContext context = engine.execute(1); - LOGGER.info("trace {}", context.getTrace()); - - LOGGER.info("=================================================================================="); - - EngineManager defaultManager = DefaultEngineManager.getDefaultManager(); - EngineModel engineModel = defaultManager.getEngineModel("defaultEngine"); - Map components = engineModel.getComponents(); - String identifier = null; - for (Map.Entry modelEntry : components.entrySet()) { - if (modelEntry.getValue().getDescribe().contains("pipeline@subprocess##2")) { - identifier = modelEntry.getKey(); - } - - LOGGER.info("identifier {} name {} describe {}", modelEntry.getKey(), modelEntry.getValue().getName(), modelEntry.getValue().getDescribe()); - } - - AttributeHolder attributeHolder = new AttributeHolder(Attributes.ENABLED, false); - defaultManager.changeAttributes(identifier, attributeHolder); - - - context = engine.execute(1); - LOGGER.info("trace {}", context.getTrace()); - } -} diff --git a/smart-flow-core/src/test/java/org/smartboot/flow/core/parser/DefaultParserTest.java b/smart-flow-core/src/test/java/org/smartboot/flow/core/parser/DefaultParserTest.java deleted file mode 100644 index 725bc139ccc989f5469c108f37e8b92cc192231e..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/java/org/smartboot/flow/core/parser/DefaultParserTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.smartboot.flow.core.parser; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.smartboot.flow.core.BaseTest; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.manager.DefaultEngineManager; -import org.smartboot.flow.core.view.plantuml.PlantumlEngineVisitor; - -import java.io.InputStream; - -/** - * @author qinluo - * @date 2022-11-15 23:42:32 - * @since 1.0.0 - */ -public class DefaultParserTest extends BaseTest { - - @BeforeEach - public void setUp() { - DefaultEngineManager.getDefaultManager().detachAll(); - } - - @Test - public void testParseNull() { - DefaultParser parser = new DefaultParser(); - Exception e = Assertions.assertThrows(IllegalArgumentException.class, () -> parser.parse((InputStream) null)); - LOGGER.error("", e); - } - - @Test - public void testParseWithUncorrectedRootElement() { - DefaultParser parser = new DefaultParser(); - Exception e = Assertions.assertThrows(IllegalArgumentException.class, () -> - parser.parse(this.getClass().getResourceAsStream("/flow-example.xsd"))); - LOGGER.error("", e); - } - - @Test - public void testParseSimple() { - DefaultParser parser = new DefaultParser(); - parser.parse(this.getClass().getResourceAsStream("/flow-example3.xsd")); - LOGGER.info("engines {}", parser.getEngineNames()); - - FlowEngine testEngine = parser.getEngine("testEngine"); - testEngine.execute(1); - } - - @Test - public void testParseNestedPipelineInPipeline() { - DefaultParser parser = new DefaultParser(); - parser.parse(this.getClass().getResourceAsStream("/flow-example4.xsd")); - LOGGER.info("engines {}", parser.getEngineNames()); - - FlowEngine testEngine = parser.getEngine("testEngine"); - EngineContext execute = testEngine.execute(1); - LOGGER.info("trace \n{}", execute.getTrace()); - } - - @Test - public void testParseNestedElementsInPipeline() { - DefaultParser parser = new DefaultParser(); - parser.parse(this.getClass().getResourceAsStream("/flow-example5.xsd")); - LOGGER.info("engines {}", parser.getEngineNames()); - - FlowEngine testEngine = parser.getEngine("testEngine"); - EngineContext execute = testEngine.execute(1); - LOGGER.info("trace \n{}", execute.getTrace()); - } - - @Test - public void testParseNestedElementsInPipeline2() { - - DefaultParser parser = new DefaultParser(); - parser.parse(this.getClass().getResourceAsStream("/flow-example6.xsd")); - LOGGER.info("engines {}", parser.getEngineNames()); - - FlowEngine testEngine = parser.getEngine("testEngine"); - EngineContext execute = testEngine.execute(1); - LOGGER.info("trace \n{}", execute.getTrace()); - } - - @Test - public void testParseNestedElementsInPipeline3() { - - DefaultParser parser = new DefaultParser(); - parser.parse(this.getClass().getResourceAsStream("/flow-example7.xsd")); - LOGGER.info("engines {}", parser.getEngineNames()); - - FlowEngine testEngine = parser.getEngine("testEngine"); - EngineContext execute = testEngine.execute(1); - LOGGER.info("trace \n{}", execute.getTrace()); - - PlantumlEngineVisitor visitor = new PlantumlEngineVisitor("engine-flow2"); - visitor.visit(testEngine); - } -} diff --git a/smart-flow-core/src/test/resources/flow-example.xsd b/smart-flow-core/src/test/resources/flow-example.xsd deleted file mode 100644 index 7231d98d739f050894e97494818efee17ddb1670..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example.xsd +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/flow-example3.xsd b/smart-flow-core/src/test/resources/flow-example3.xsd deleted file mode 100644 index 515e723e1dd6546dd8ac14c54bdbd85a155aeef9..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example3.xsd +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/flow-example4.xsd b/smart-flow-core/src/test/resources/flow-example4.xsd deleted file mode 100644 index bf60fa127c544253bf92b9a2e5444bb19d95f890..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example4.xsd +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/flow-example5.xsd b/smart-flow-core/src/test/resources/flow-example5.xsd deleted file mode 100644 index 1373e149fdce2abf6ea13bc84b6492d8820b9f63..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example5.xsd +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/flow-example6.xsd b/smart-flow-core/src/test/resources/flow-example6.xsd deleted file mode 100644 index 7ca81d7335f9f12467c1f881a5996c72253b1e3e..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example6.xsd +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/flow-example7.xsd b/smart-flow-core/src/test/resources/flow-example7.xsd deleted file mode 100644 index 24d33458090ae6d895a08963ebb3f6854ef5676c..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/flow-example7.xsd +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/log4j.properties b/smart-flow-core/src/test/resources/log4j.properties deleted file mode 100644 index 05ad41fac9d47d2348a085c861ed81ed79cc8775..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/log4j.properties +++ /dev/null @@ -1,59 +0,0 @@ -### direct log messages to stdout ### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.debug=org.apache.log4j.ConsoleAppender -log4j.appender.debug.layout=org.apache.log4j.PatternLayout -log4j.appender.debug.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.warn=org.apache.log4j.ConsoleAppender -log4j.appender.warn.layout=org.apache.log4j.PatternLayout -log4j.appender.warn.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.error=org.apache.log4j.ConsoleAppender -log4j.appender.error.layout=org.apache.log4j.PatternLayout -log4j.appender.error.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - - -### direct messages to file hibernate.log ### -#log4j.appender.file=org.apache.log4j.FileAppender -#log4j.appender.file.File=hibernate.log -#log4j.appender.file.layout=org.apache.log4j.PatternLayout -#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -### set log levels - for more verbose logging change 'info' to 'debug' ### - -log4j.rootLogger=info,stdout - -#log4j.logger.org.hibernate=info -#log4j.logger.org.hibernate=debug - -### log HQL query parser activity -#log4j.logger.org.hibernate.hql.ast.AST=debug - -### log just the SQL -#log4j.logger.org.hibernate.SQL=debug - -### log JDBC bind parameters ### -#log4j.logger.org.hibernate.type=info -#log4j.logger.org.hibernate.type=debug - -### log schema export/update ### -log4j.logger.org.hibernate.tool.hbm2ddl=debug - -### log HQL parse trees -#log4j.logger.org.hibernate.hql=debug - -### log cache activity ### -#log4j.logger.org.hibernate.cache=debug - -### log transaction activity -#log4j.logger.org.hibernate.transaction=debug - -### log JDBC resource acquisition -#log4j.logger.org.hibernate.jdbc=debug - -### enable the following line if you want to track down connection ### -### leakages when using DriverManagerConnectionProvider ### -#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace diff --git a/smart-flow-core/src/test/resources/log4j2.properties b/smart-flow-core/src/test/resources/log4j2.properties deleted file mode 100644 index fced116e6bbf41ce9ac55fa93d13717474e19df1..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/log4j2.properties +++ /dev/null @@ -1,6 +0,0 @@ -appender.console.type = Console -appender.console.name = console -appender.console.layout.type = PatternLayout - -rootLogger.level = info -rootLogger.appenderRef.console.ref = console \ No newline at end of file diff --git a/smart-flow-core/src/test/resources/log4jdbc.log4j2.properties b/smart-flow-core/src/test/resources/log4jdbc.log4j2.properties deleted file mode 100644 index 76f24405e5ee2021884670f4e7db25d3c3f7eebd..0000000000000000000000000000000000000000 --- a/smart-flow-core/src/test/resources/log4jdbc.log4j2.properties +++ /dev/null @@ -1,2 +0,0 @@ -#log4jdbc-log4j2默认只支持log4j2,如果想要支持slf4j,则需要增加如下配置: -log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator \ No newline at end of file diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep1.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep1.java deleted file mode 100644 index 67645c080934d72e780a1a37d4ba3c41b151d3e7..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep1.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class AsyncStep1 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep2.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep2.java deleted file mode 100644 index 8bff547fb6efdf87129c223124dce7392e8ee796..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep2.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class AsyncStep2 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - try { - Thread.sleep(100); - } catch (Exception e) { - - } - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep3.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep3.java deleted file mode 100644 index f326a1d82453940fd46b0792ee4fe47ef169ff3d..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/AsyncStep3.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class AsyncStep3 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - try { - Thread.sleep(200); - } catch (Exception e) { - - } - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ChooseCondition.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ChooseCondition.java deleted file mode 100644 index 0bba2773ba6022e03602609f3a4e2c37608bacdc..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ChooseCondition.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.Condition; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 16:45:21 - * @since 1.0.0 - */ -@Service -public class ChooseCondition extends Condition { - - @Override - public Object test(Integer integer, String s) { - if (integer == null) { - return "null"; - } else if (integer == 1) { - return 1; - } else if (integer == 2) { - return 2; - } - return null; - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/DefaultStep.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/DefaultStep.java deleted file mode 100644 index 679f1a74fceb53cf0f760cda6a4c39c18d4cfec2..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/DefaultStep.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 16:44:21 - * @since 1.0.0 - */ -@Service -public class DefaultStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ElseStep.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ElseStep.java deleted file mode 100644 index 22d429501ea6bbcfc6b9e212c8829496dac86400..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ElseStep.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 16:44:21 - * @since 1.0.0 - */ -@Service -public class ElseStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ErrorStep.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ErrorStep.java deleted file mode 100644 index f0b97fe29c4901e6871b3d63d87144cb275146e1..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/ErrorStep.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class ErrorStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("error executed ======="); - throw new IllegalArgumentException("Error"); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition.java deleted file mode 100644 index 99b01b2c10b2d2a8538230b67135557b3f85bd5a..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.Condition; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -@Service -public class IfCondition extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition2.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition2.java deleted file mode 100644 index 4533495f8c9fb90d2afe96e748e8a50d727e375c..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition2.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.Condition; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -@Service -public class IfCondition2 extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition3.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition3.java deleted file mode 100644 index 1fb61d9bfde858a01c6ee351682276d0523ecbf4..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IfCondition3.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.Condition; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:45:21 - * @since 1.0.0 - */ -@Service -public class IfCondition3 extends Condition { - - @Override - public Object test(Integer integer, String s) { - return integer != null && integer == 1; - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IntegerStep.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IntegerStep.java deleted file mode 100644 index 54dbeb4f7a36d00d1ac0cc82ec79957e5a596cd6..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/IntegerStep.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class IntegerStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println(getClass()); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/NullStep.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/NullStep.java deleted file mode 100644 index f3c6512bd1f07faa67a837b8da293fca089d974a..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/NullStep.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class NullStep extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - if (integer == null) { - throw new RuntimeException("null value"); - } - - System.out.println(getClass()); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step1.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step1.java deleted file mode 100644 index a0ff762ee505c3d206ac4635100acd681dc44e93..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step1.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step1 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step1 == " + integer); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step2.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step2.java deleted file mode 100644 index 0fa36ebd7ef490c194adc839dbf4072ee6a7243c..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step2.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -public class Step2 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step2 == " + integer); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step3.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step3.java deleted file mode 100644 index 5c1a1dae20216fbbca97c9b8f41ebe44cec8ee55..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step3.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class Step3 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step3 == " + integer); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step4.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step4.java deleted file mode 100644 index d343a894f930c80d5a0da1bc9935dcce6643cb63..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step4.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class Step4 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step4 == " + integer); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step5.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step5.java deleted file mode 100644 index 3cd8014aaa99839735ddb5a83d94df378373afaa..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step5.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class Step5 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step5 == " + integer); - } -} diff --git a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step6.java b/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step6.java deleted file mode 100644 index 54b3661e7caf28b087776e450650164b02adea95..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/java/org/smartboot/flow/example/extension/Step6.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.smartboot.flow.example.extension; - - -import org.smartboot.flow.core.executable.AbstractExecutable; -import org.springframework.stereotype.Service; - -/** - * @author qinluo - * @date 2022-11-11 21:44:21 - * @since 1.0.0 - */ -@Service -public class Step6 extends AbstractExecutable { - - @Override - public void execute(Integer integer, String s) { - System.out.println("step5 == " + integer); - } -} diff --git a/smart-flow-example/src/main/resources/bean.xml b/smart-flow-example/src/main/resources/bean.xml deleted file mode 100644 index 64a4bd9f2de5c97b572f5fb86f06ae00b26d6b90..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/resources/bean.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/smart-flow-example/src/main/resources/log4j.properties b/smart-flow-example/src/main/resources/log4j.properties deleted file mode 100644 index 05ad41fac9d47d2348a085c861ed81ed79cc8775..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/resources/log4j.properties +++ /dev/null @@ -1,59 +0,0 @@ -### direct log messages to stdout ### -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.debug=org.apache.log4j.ConsoleAppender -log4j.appender.debug.layout=org.apache.log4j.PatternLayout -log4j.appender.debug.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.warn=org.apache.log4j.ConsoleAppender -log4j.appender.warn.layout=org.apache.log4j.PatternLayout -log4j.appender.warn.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -log4j.appender.error=org.apache.log4j.ConsoleAppender -log4j.appender.error.layout=org.apache.log4j.PatternLayout -log4j.appender.error.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - - -### direct messages to file hibernate.log ### -#log4j.appender.file=org.apache.log4j.FileAppender -#log4j.appender.file.File=hibernate.log -#log4j.appender.file.layout=org.apache.log4j.PatternLayout -#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n - -### set log levels - for more verbose logging change 'info' to 'debug' ### - -log4j.rootLogger=info,stdout - -#log4j.logger.org.hibernate=info -#log4j.logger.org.hibernate=debug - -### log HQL query parser activity -#log4j.logger.org.hibernate.hql.ast.AST=debug - -### log just the SQL -#log4j.logger.org.hibernate.SQL=debug - -### log JDBC bind parameters ### -#log4j.logger.org.hibernate.type=info -#log4j.logger.org.hibernate.type=debug - -### log schema export/update ### -log4j.logger.org.hibernate.tool.hbm2ddl=debug - -### log HQL parse trees -#log4j.logger.org.hibernate.hql=debug - -### log cache activity ### -#log4j.logger.org.hibernate.cache=debug - -### log transaction activity -#log4j.logger.org.hibernate.transaction=debug - -### log JDBC resource acquisition -#log4j.logger.org.hibernate.jdbc=debug - -### enable the following line if you want to track down connection ### -### leakages when using DriverManagerConnectionProvider ### -#log4j.logger.org.hibernate.connection.DriverManagerConnectionProvider=trace diff --git a/smart-flow-example/src/main/resources/log4j2.properties b/smart-flow-example/src/main/resources/log4j2.properties deleted file mode 100644 index fced116e6bbf41ce9ac55fa93d13717474e19df1..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/resources/log4j2.properties +++ /dev/null @@ -1,6 +0,0 @@ -appender.console.type = Console -appender.console.name = console -appender.console.layout.type = PatternLayout - -rootLogger.level = info -rootLogger.appenderRef.console.ref = console \ No newline at end of file diff --git a/smart-flow-example/src/main/resources/log4jdbc.log4j2.properties b/smart-flow-example/src/main/resources/log4jdbc.log4j2.properties deleted file mode 100644 index 76f24405e5ee2021884670f4e7db25d3c3f7eebd..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/main/resources/log4jdbc.log4j2.properties +++ /dev/null @@ -1,2 +0,0 @@ -#log4jdbc-log4j2默认只支持log4j2,如果想要支持slf4j,则需要增加如下配置: -log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator \ No newline at end of file diff --git a/smart-flow-example/src/test/java/org/smartboot/flow/example/ParseTest.java b/smart-flow-example/src/test/java/org/smartboot/flow/example/ParseTest.java deleted file mode 100644 index 8cdcda6f2364fd67c98dfd6621abbd443275f1ec..0000000000000000000000000000000000000000 --- a/smart-flow-example/src/test/java/org/smartboot/flow/example/ParseTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.smartboot.flow.example; - - -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.EngineContext; -import org.smartboot.flow.core.FlowEngine; -import org.smartboot.flow.core.view.plantuml.PlantumlEngineVisitor; -import org.springframework.context.support.ClassPathXmlApplicationContext; - -/** - * @author yamikaze - * @version 1.0.0 - * @date 2018/3/20 12:52 - */ -public class ParseTest { - - private static final Logger LOGGER = LoggerFactory.getLogger(ParseTest.class); - - @Test - public void testXml() throws Exception { - ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml"); - Object step = context.getBean("step3"); - LOGGER.info("step is " + step.getClass().getName()); - - FlowEngine testEngine = (FlowEngine)context.getBean("testEngine", FlowEngine.class); - - EngineContext executeContext = testEngine.execute(1); - LOGGER.info("trace\n {}", executeContext.getTrace()); - - testEngine.execute((Integer) null); - - Thread.sleep(60000); - } -} diff --git a/smart-flow-manager/pom.xml b/smart-flow-manager/pom.xml index 39dea515ebf0b2dcd229c357989bafc73f0e8f2a..0cc03750a8e6f6c40e2d6980a66086c79f2c25be 100644 --- a/smart-flow-manager/pom.xml +++ b/smart-flow-manager/pom.xml @@ -5,7 +5,7 @@ flow-engine org.smartboot - 1.0.1 + 1.0.3 4.0.0 @@ -20,7 +20,7 @@ org.smartboot smart-flow-core - 1.0.1 + 1.0.3 @@ -32,7 +32,7 @@ com.alibaba fastjson - 2.0.19 + 2.0.20.graal diff --git a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ChangeModel.java b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ChangeModel.java index d1991831dbada424660e57d3059b848fefbc3888..71419cde49033adca8e09260a56f54cf9d7d02e2 100644 --- a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ChangeModel.java +++ b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ChangeModel.java @@ -13,7 +13,7 @@ public class ChangeModel implements Serializable { /** * Change action * - * @see org.smartboot.flow.core.manager.ManagerAction + * @see ManagerAction */ private String action; diff --git a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/HttpManager.java b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/HttpManager.java index 844d4c4bbe72d0b7162c8c5ec94b35e5f2ad0fab..830117bb287a86b8378c8632affd096e749cdb85 100644 --- a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/HttpManager.java +++ b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/HttpManager.java @@ -3,10 +3,9 @@ package org.smartboot.flow.manager.change; import com.alibaba.fastjson.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.manager.DefaultEngineManager; -import org.smartboot.flow.core.manager.ManagerAction; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.manager.NamedThreadFactory; import org.smartboot.flow.manager.report.HostUtils; @@ -70,14 +69,18 @@ public class HttpManager { headers.forEach(post::addHeader); } + long timestamp = lastest; + try { RequestModel model = new RequestModel(); model.setAddress(HostUtils.getHostIp()); model.setHost(HostUtils.getHostName()); - model.setTimestamp(lastest); + model.setTimestamp(timestamp); // 只请求当前机器有的engines model.setEngineNames(DefaultEngineManager.getDefaultManager().getRegisteredEngineNames()); + + String json = JSON.toJSONString(model); byte[] bytes = json.getBytes(StandardCharsets.UTF_8); @@ -104,14 +107,14 @@ public class HttpManager { continue; } - if (cm.getTimestamp() < lastest) { + if (cm.getTimestamp() < timestamp) { continue; } if (action == ManagerAction.CHANGE_ATTRIBUTES) { AssertUtil.notBlank(cm.getIdentifier(), "identifier must not be null"); AssertUtil.notBlank(cm.getValue(), "value must not be null"); - Attributes attribute = Attributes.with(cm.getName()); + Attributes attribute = Attributes.byName(cm.getName()); if (attribute == null) { LOGGER.error("unknown supported attribute {}, please check version", cm.getName()); continue; diff --git a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ManagerAction.java b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ManagerAction.java similarity index 92% rename from smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ManagerAction.java rename to smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ManagerAction.java index b0d536f8ed0e0e5674d9ba9470dad965a3cdd61d..1aace0a0f8cf15f99ee1481d39e053a3ac6823cf 100644 --- a/smart-flow-core/src/main/java/org/smartboot/flow/core/manager/ManagerAction.java +++ b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/change/ManagerAction.java @@ -1,4 +1,4 @@ -package org.smartboot.flow.core.manager; +package org.smartboot.flow.manager.change; import java.util.Objects; diff --git a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/report/HttpReporter.java b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/report/HttpReporter.java index 7c7d3d51e48060c783fbebdc394cc3e2f769a1f7..d59d9b0c8fafa4fc973faddafa0855ed5959b3c5 100644 --- a/smart-flow-manager/src/main/java/org/smartboot/flow/manager/report/HttpReporter.java +++ b/smart-flow-manager/src/main/java/org/smartboot/flow/manager/report/HttpReporter.java @@ -1,6 +1,7 @@ package org.smartboot.flow.manager.report; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.smartboot.flow.core.manager.EngineModel; @@ -62,9 +63,13 @@ public class HttpReporter extends AbstractReporter { reportModel.setTimestamp(System.currentTimeMillis()); reportModel.setData(model); - String json = JSON.toJSONString(reportModel); + String json = JSON.toJSONString(reportModel, SerializerFeature.WriteEnumUsingToString); byte[] bytes = json.getBytes(StandardCharsets.UTF_8); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("report engine data, engine = {}, data = {}", model.getIdentifier(), json); + } + post.addHeader(HeaderNameEnum.CONTENT_TYPE.getName(), "application/json;charset=UTF-8"); post.addHeader(HeaderNameEnum.CONTENT_LENGTH.getName(), String.valueOf(bytes.length)); diff --git a/smart-flow-script-condition/pom.xml b/smart-flow-script-condition/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..985430ca522aff4bbd96b4c57f6306c50bd16541 --- /dev/null +++ b/smart-flow-script-condition/pom.xml @@ -0,0 +1,34 @@ + + + + flow-engine + org.smartboot + 1.0.3 + + 4.0.0 + pom + + + smart-flow-script-ognl + smart-flow-script-groovy + smart-flow-script-qlexpress + + + smart-flow-script-condition + + + 8 + 8 + + + + + org.smartboot + smart-flow-core + 1.0.3 + + + + \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-groovy/pom.xml b/smart-flow-script-condition/smart-flow-script-groovy/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..b572cb3698c437806d5422827451f6547073c42a --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-groovy/pom.xml @@ -0,0 +1,28 @@ + + + + smart-flow-script-condition + org.smartboot + 1.0.3 + + 4.0.0 + + smart-flow-script-groovy + + + 8 + 8 + + + + + + org.codehaus.groovy + groovy + 3.0.9 + + + + \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/GroovyScriptCondition.java b/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/GroovyScriptCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..b464d08727e097412b804c6dd84d7395fae7815a --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/GroovyScriptCondition.java @@ -0,0 +1,53 @@ +package org.smartboot.flow.condition.extension.groovy; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.script.ScriptCondition; +import org.smartboot.flow.core.script.ScriptConstants; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + +/** + * @author qinluo + * @date 2022/11/29 21:01 + * @since 1.0.0 + */ +public class GroovyScriptCondition extends ScriptCondition { + + private static final Logger LOGGER = LoggerFactory.getLogger(GroovyScriptCondition.class); + private static final ScriptEngineManager engineManager = new ScriptEngineManager(); + + protected String getScriptLang() { + return "groovy"; + } + + @Override + public Object test(EngineContext engineContext) { + try { + ScriptEngine engine = engineManager.getEngineByName(getScriptLang()); + Bindings data = engine.createBindings(); + data.put(ScriptConstants.REQ, engineContext.getReq()); + data.put(ScriptConstants.RESULT, engineContext.getResult()); + data.put(ScriptConstants.CONTEXT, engineContext); + + Object value = engine.eval(script, data); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("evaluate script [{}] finished", script); + LOGGER.debug("evaluate value {}", value); + } + + return value; + } catch (Exception e) { + throw new FlowException(getScriptLang() + " evaluate failed, script : " + script, e); + } + } + + @Override + public String getType() { + return getScriptLang(); + } +} diff --git a/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/JavaScriptCondition.java b/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/JavaScriptCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..e8dfccd0265454823a93cc7d0931f1d7baba8809 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-groovy/src/main/java/org/smartboot/flow/condition/extension/groovy/JavaScriptCondition.java @@ -0,0 +1,13 @@ +package org.smartboot.flow.condition.extension.groovy; + +/** + * @author qinluo + * @date 2022/11/29 21:01 + * @since 1.0.0 + */ +public class JavaScriptCondition extends GroovyScriptCondition { + + protected String getScriptLang() { + return "javascript"; + } +} diff --git a/smart-flow-script-condition/smart-flow-script-groovy/src/main/resources/META-INF/smart-flow-script b/smart-flow-script-condition/smart-flow-script-groovy/src/main/resources/META-INF/smart-flow-script new file mode 100644 index 0000000000000000000000000000000000000000..f679dcb2a498326db0f2e0579a7092c6acac4971 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-groovy/src/main/resources/META-INF/smart-flow-script @@ -0,0 +1,2 @@ +groovy=org.smartboot.flow.condition.extension.groovy.GroovyScriptCondition +javascript=org.smartboot.flow.condition.extension.groovy.JavaScriptCondition \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-ognl/pom.xml b/smart-flow-script-condition/smart-flow-script-ognl/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..f1e2c68a144cd56de0268296dd0e1ec38a193da2 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-ognl/pom.xml @@ -0,0 +1,27 @@ + + + + smart-flow-script-condition + org.smartboot + 1.0.3 + + 4.0.0 + + smart-flow-script-ognl + + + 8 + 8 + + + + + + ognl + ognl + 3.3.4 + + + \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-ognl/src/main/java/org/smartboot/flow/condition/extension/ognl/OgnlScriptCondition.java b/smart-flow-script-condition/smart-flow-script-ognl/src/main/java/org/smartboot/flow/condition/extension/ognl/OgnlScriptCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..e930fcc7a3b6213acfa5d6cbbacb1a911fe59460 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-ognl/src/main/java/org/smartboot/flow/condition/extension/ognl/OgnlScriptCondition.java @@ -0,0 +1,47 @@ +package org.smartboot.flow.condition.extension.ognl; + +import ognl.Ognl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.script.ScriptCondition; +import org.smartboot.flow.core.script.ScriptConstants; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author qinluo + * @date 2022/11/29 21:01 + * @since 1.0.0 + */ +public class OgnlScriptCondition extends ScriptCondition { + + private static final Logger LOGGER = LoggerFactory.getLogger(OgnlScriptCondition.class); + + @Override + public Object test(EngineContext engineContext) { + try { + Map context = new HashMap<>(); + context.put(ScriptConstants.REQ, engineContext.getReq()); + context.put(ScriptConstants.RESULT, engineContext.getResult()); + context.put(ScriptConstants.CONTEXT, engineContext); + + Object value = Ognl.getValue(script, context); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("evaluate script [{}] finished", script); + LOGGER.debug("evaluate value {}", value); + } + + return value; + } catch (Exception e) { + throw new FlowException("Ognl evaluate failed, script : " + script, e); + } + } + + @Override + public String getType() { + return "OGNL"; + } +} diff --git a/smart-flow-script-condition/smart-flow-script-ognl/src/main/resources/META-INF/smart-flow-script b/smart-flow-script-condition/smart-flow-script-ognl/src/main/resources/META-INF/smart-flow-script new file mode 100644 index 0000000000000000000000000000000000000000..a3d233455b668d439a1c0663168587c3f3b1fb21 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-ognl/src/main/resources/META-INF/smart-flow-script @@ -0,0 +1 @@ +ognl=org.smartboot.flow.condition.extension.ognl.OgnlScriptCondition \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-qlexpress/pom.xml b/smart-flow-script-condition/smart-flow-script-qlexpress/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..c4d6f1a9ca12e2dc05a7e350046ba5f8667b26e6 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-qlexpress/pom.xml @@ -0,0 +1,27 @@ + + + + smart-flow-script-condition + org.smartboot + 1.0.3 + + 4.0.0 + + smart-flow-script-qlexpress + + + 8 + 8 + + + + + + com.alibaba + QLExpress + 3.3.0 + + + \ No newline at end of file diff --git a/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/java/org/smartboot/flow/condition/extension/qlexpress/QlExpressScriptCondition.java b/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/java/org/smartboot/flow/condition/extension/qlexpress/QlExpressScriptCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..4623cc47781905b03ecc08d614afb0acfb976c32 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/java/org/smartboot/flow/condition/extension/qlexpress/QlExpressScriptCondition.java @@ -0,0 +1,47 @@ +package org.smartboot.flow.condition.extension.qlexpress; + +import com.ql.util.express.DefaultContext; +import com.ql.util.express.ExpressRunner; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.smartboot.flow.core.EngineContext; +import org.smartboot.flow.core.exception.FlowException; +import org.smartboot.flow.core.script.ScriptCondition; +import org.smartboot.flow.core.script.ScriptConstants; + +/** + * @author qinluo + * @date 2022/11/29 21:01 + * @since 1.0.0 + */ +public class QlExpressScriptCondition extends ScriptCondition { + + private static final Logger LOGGER = LoggerFactory.getLogger(QlExpressScriptCondition.class); + + @Override + public Object test(EngineContext engineContext) { + try { + + DefaultContext qlContext = new DefaultContext<>(); + qlContext.put(ScriptConstants.REQ, engineContext.getReq()); + qlContext.put(ScriptConstants.RESULT, engineContext.getResult()); + qlContext.put(ScriptConstants.CONTEXT, engineContext); + + ExpressRunner runner = new ExpressRunner(); + Object value = runner.execute(script, qlContext, null, true, false); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("evaluate script [{}] finished", script); + LOGGER.debug("evaluate value {}", value); + } + + return value; + } catch (Exception e) { + throw new FlowException("Ognl evaluate failed, script : " + script, e); + } + } + + @Override + public String getType() { + return "qlexpress"; + } +} diff --git a/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/resources/META-INF/smart-flow-script b/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/resources/META-INF/smart-flow-script new file mode 100644 index 0000000000000000000000000000000000000000..ec9ffd12e55d024fd2cb7c2127d5e680d70d8837 --- /dev/null +++ b/smart-flow-script-condition/smart-flow-script-qlexpress/src/main/resources/META-INF/smart-flow-script @@ -0,0 +1 @@ +qlexpress=org.smartboot.flow.condition.extension.qlexpress.QlExpressScriptCondition \ No newline at end of file diff --git a/smart-flow-spring-extension/pom.xml b/smart-flow-spring-extension/pom.xml index 4fb8869f8dff061ce5ef289fef9adddaf7b7bcf1..470e989c728dda4ff69b098dce6b1f40f3400e7b 100644 --- a/smart-flow-spring-extension/pom.xml +++ b/smart-flow-spring-extension/pom.xml @@ -5,7 +5,7 @@ flow-engine org.smartboot - 1.0.1 + 1.0.3 4.0.0 @@ -21,7 +21,7 @@ org.smartboot smart-flow-core - 1.0.1 + 1.0.3 diff --git a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionRegister.java b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionRegister.java new file mode 100644 index 0000000000000000000000000000000000000000..ae844947150706ca07d02d6653c96497e326cdbb --- /dev/null +++ b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionRegister.java @@ -0,0 +1,47 @@ +package org.smartboot.flow.spring.extension; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author qinluo + * @date 2022-12-06 20:47:23 + * @since 1.0.0 + */ +public class BeanDefinitionRegister { + + private final Map registered = new HashMap<>(32); + + /** + * Spring registry + */ + private final BeanDefinitionRegistry registry; + + public BeanDefinitionRegister(BeanDefinitionRegistry registry) { + this.registry = registry; + } + + public BeanDefinition getBeanDefinition(String identifier) { + BeanDefinition beanDefinition = registered.get(identifier); + return beanDefinition != null ? beanDefinition : registry.getBeanDefinition(identifier); + } + + public void registerBeanDefinition(String identifier, BeanDefinition def) { + registerBeanDefinition(identifier, def, false); + } + + public void registerBeanDefinition(String identifier, BeanDefinition def, boolean registerToSpring) { + if (registerToSpring) { + registry.registerBeanDefinition(identifier, def); + } else { + registered.put(identifier, def); + } + } + + public boolean containsBeanDefinition(String identifier) { + return registered.containsKey(identifier) || registry.containsBeanDefinition(identifier); + } +} diff --git a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionVisitor.java b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionVisitor.java index e85745ddda01ce2ba5230d6d8923f82d89b07de0..205dbd1f57cf0d0ec3eec7e23f7f50964e51b011 100644 --- a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionVisitor.java +++ b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/BeanDefinitionVisitor.java @@ -1,14 +1,18 @@ package org.smartboot.flow.spring.extension; -import org.smartboot.flow.core.component.AttributeHolder; -import org.smartboot.flow.core.component.Attributes; +import org.smartboot.flow.core.attribute.AttributeHolder; +import org.smartboot.flow.core.attribute.Attributes; import org.smartboot.flow.core.parser.DefinitionVisitor; +import org.smartboot.flow.core.parser.ParserContext; +import org.smartboot.flow.core.parser.definition.AdapterDefinition; import org.smartboot.flow.core.parser.definition.ChooseDefinition; import org.smartboot.flow.core.parser.definition.ElementDefinition; import org.smartboot.flow.core.parser.definition.EngineDefinition; import org.smartboot.flow.core.parser.definition.IfElementDefinition; import org.smartboot.flow.core.parser.definition.PipelineComponentDefinition; import org.smartboot.flow.core.parser.definition.PipelineDefinition; +import org.smartboot.flow.core.parser.definition.ScriptDefinition; +import org.smartboot.flow.core.script.ScriptCondition; import org.smartboot.flow.core.util.AssertUtil; import org.smartboot.flow.core.util.AuxiliaryUtils; import org.springframework.beans.PropertyValue; @@ -28,15 +32,46 @@ import java.util.List; */ public class BeanDefinitionVisitor implements DefinitionVisitor { - private final BeanDefinitionRegistry registry; + private final ParserContext context; - public BeanDefinitionVisitor(BeanDefinitionRegistry registry) { - this.registry = registry; + /** + * Temporary def register. + */ + private final BeanDefinitionRegister register; + + public BeanDefinitionVisitor(BeanDefinitionRegistry registry, ParserContext context) { + this.context = context; + this.register = new BeanDefinitionRegister(registry); } @Override public void visit(ElementDefinition ed) { - ed.visit(this); + if (ed instanceof ScriptDefinition) { + ScriptDefinition sed = (ScriptDefinition)ed; + Class javaType = sed.getJavaType(); + // Maybe a ref. + if (javaType == null) { + BeanDefinition beanDefinition = register.getBeanDefinition(sed.getName()); + String beanClassName = beanDefinition.getBeanClassName(); + Class defJavaType = AuxiliaryUtils.asClass(beanClassName); + AssertUtil.notNull(defJavaType, "bean " + sed.getName() + " javaType must be subclass of ScriptCondition"); + AssertUtil.isTrue(ScriptCondition.class.isAssignableFrom(defJavaType), "bean " + sed.getName() + " javaType must be subclass of ScriptCondition"); + + // Register script content. + beanDefinition.getPropertyValues().add("script", sed.getScript()); + } else { + RootBeanDefinition definition = new RootBeanDefinition(); + definition.setBeanClass(javaType); + PropertyValue script = new PropertyValue("script", sed.getScript()); + PropertyValue name = new PropertyValue("name", sed.getName()); + definition.getPropertyValues().addPropertyValue(name); + definition.getPropertyValues().addPropertyValue(script); + register.registerBeanDefinition(ed.getIdentifier(), definition, true); + } + + } else { + ed.visit(this); + } } @Override @@ -48,7 +83,7 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { definition.setBeanClass(ed.resolveType()); definition.getPropertyValues().addPropertyValue(name); definition.getPropertyValues().addPropertyValue(pipeline); - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition, true); } @Override @@ -56,7 +91,7 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { RootBeanDefinition definition = new RootBeanDefinition(); PropertyValue name = new PropertyValue("name", ed.getName()); - ManagedList components = new ManagedList<>(); + ManagedList components = new ManagedList<>(); definition.setBeanClass(ed.resolveType()); definition.getPropertyValues().addPropertyValue(name); definition.getPropertyValues().add("components", components); @@ -64,12 +99,15 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { ed.getChildren().forEach(p -> p.visit(this)); for (ElementDefinition elementDef : ed.getChildren()) { - BeanDefinition beanDefinition = registry.getBeanDefinition(elementDef.getIdentifier()); + // Fire sub visit. + elementDef.visit(this); + + BeanDefinition beanDefinition = register.getBeanDefinition(elementDef.getIdentifier()); AssertUtil.notNull(beanDefinition, elementDef.getIdentifier() + " not exist"); - components.add(new RuntimeBeanReference(elementDef.getIdentifier())); + components.add(beanDefinition); } - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition, !isAnonymous(ed.getIdentifier())); } @Override @@ -80,17 +118,27 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { definition.setBeanClass(ed.resolveType()); definition.getPropertyValues().addPropertyValue(name); - definition.getPropertyValues().addPropertyValue(pipeline); + + if (isAnonymous(ed.getPipeline())) { + context.getRegistered(ed.getPipeline()).visit(this); + definition.getPropertyValues().add("pipeline", register.getBeanDefinition(ed.getPipeline())); + } else { + definition.getPropertyValues().addPropertyValue(pipeline); + } appendAttributes(ed, definition); - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition); + } + + private boolean isAnonymous(String name) { + return name.contains("anonymous-pipeline"); } private void appendAttributes(ElementDefinition ed, RootBeanDefinition definition) { List attributes = ed.getAttributes(); for (AttributeHolder holder : attributes) { Attributes attribute = holder.getAttribute(); - if (attribute == Attributes.NAME) { + if (attribute == Attributes.NAME || attribute == Attributes.DEGRADE_CALLBACK) { continue; } PropertyValue value = new PropertyValue(attribute.getName(), holder.getValue()); @@ -108,17 +156,33 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { definition.setBeanClass(ed.resolveType()); definition.getPropertyValues().addPropertyValue(name); - if (AuxiliaryUtils.isNotBlank(ed.getType()) && isType(ed.getType())) { + if (isType(ed.getExecute())) { RootBeanDefinition conditionDef = new RootBeanDefinition(); - conditionDef.setBeanClassName(ed.getType()); + conditionDef.setBeanClassName(ed.getExecute()); definition.getPropertyValues().add("executable", conditionDef); } else { - AssertUtil.notBlank(ed.getRef(), "component element ref must not be null!"); - definition.getPropertyValues().add("executable", new RuntimeBeanReference(ed.getRef())); + AssertUtil.notBlank(ed.getExecute(), "component element ref must not be null!"); + definition.getPropertyValues().add("executable", new RuntimeBeanReference(ed.getExecute())); + } + + AttributeHolder degradeCallback = ed.getAttributes().stream() + .filter(p -> p.getAttribute() == Attributes.DEGRADE_CALLBACK).findFirst().orElse(null); + + if (degradeCallback != null) { + String callback = String.valueOf(degradeCallback.getValue()); + + if (isType(callback)) { + RootBeanDefinition conditionDef = new RootBeanDefinition(); + conditionDef.setBeanClassName(callback); + definition.getPropertyValues().add("degradeCallback", conditionDef); + } else { + AssertUtil.notBlank(callback, "component callback ref must not be null!"); + definition.getPropertyValues().add("degradeCallback", new RuntimeBeanReference(callback)); + } } appendAttributes(ed, definition); - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition); } private boolean isType(String type) { @@ -138,29 +202,29 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { definition.setBeanClass(ed.resolveType()); definition.getPropertyValues().addPropertyValue(name); - if (AuxiliaryUtils.isNotBlank(ed.getTest()) && isType(ed.getTest())) { + if (isType(ed.getTest())) { RootBeanDefinition conditionDef = new RootBeanDefinition(); conditionDef.setBeanClassName(ed.getTest()); definition.getPropertyValues().add("condition", conditionDef); } else { - AssertUtil.notBlank(ed.getRef(), "if element ref must not be null!"); - definition.getPropertyValues().add("condition", new RuntimeBeanReference(ed.getRef())); + AssertUtil.notBlank(ed.getTest(), "if element ref must not be null!"); + definition.getPropertyValues().add("condition", new RuntimeBeanReference(ed.getTest())); } ed.getIfThenRef().visit(this); - BeanDefinition beanDefinition = registry.getBeanDefinition(ed.getIfThenRef().getIdentifier()); + BeanDefinition beanDefinition = register.getBeanDefinition(ed.getIfThenRef().getIdentifier()); AssertUtil.notNull(beanDefinition, ed.getIfThenRef().getIdentifier() + " not exist"); - definition.getPropertyValues().add("thenComponent", new RuntimeBeanReference(ed.getIfThenRef().getIdentifier())); + definition.getPropertyValues().add("thenComponent", beanDefinition); if (ed.getIfElseRef() != null) { ed.getIfElseRef().visit(this); - beanDefinition = registry.getBeanDefinition(ed.getIfElseRef().getIdentifier()); + beanDefinition = register.getBeanDefinition(ed.getIfElseRef().getIdentifier()); AssertUtil.notNull(beanDefinition, ed.getIfElseRef().getIdentifier() + " not exist"); - definition.getPropertyValues().add("elseComponent", new RuntimeBeanReference(ed.getIfElseRef().getIdentifier())); + definition.getPropertyValues().add("elseComponent", beanDefinition); } appendAttributes(ed, definition); - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition); } @Override @@ -172,34 +236,59 @@ public class BeanDefinitionVisitor implements DefinitionVisitor { definition.getPropertyValues().addPropertyValue(name); definition.getPropertyValues().add("allBranchWasString", true); - if (AuxiliaryUtils.isNotBlank(ed.getTest()) && isType(ed.getTest())) { + if (isType(ed.getTest())) { RootBeanDefinition conditionDef = new RootBeanDefinition(); conditionDef.setBeanClassName(ed.getTest()); definition.getPropertyValues().add("condition", conditionDef); } else { - AssertUtil.notBlank(ed.getRef(), "choose element ref must not be null!"); - definition.getPropertyValues().add("condition", new RuntimeBeanReference(ed.getRef())); + definition.getPropertyValues().add("condition", new RuntimeBeanReference(ed.getTest())); } - ManagedMap managedMap = new ManagedMap<>(); + ManagedMap managedMap = new ManagedMap<>(); List chooseCaseList = ed.getChooseCaseList(); for (ElementDefinition caseDef : chooseCaseList) { caseDef.visit(this); - BeanDefinition beanDefinition = registry.getBeanDefinition(caseDef.getIdentifier()); + BeanDefinition beanDefinition = register.getBeanDefinition(caseDef.getIdentifier()); AssertUtil.notNull(beanDefinition, "case " + caseDef.getWhen() + " " + ed.getChooseDefaultRef().getIdentifier() + " not exist"); - managedMap.put(caseDef.getWhen(), new RuntimeBeanReference(caseDef.getIdentifier())); + managedMap.put(caseDef.getWhen(), beanDefinition); } definition.getPropertyValues().add("branches", managedMap); if (ed.getChooseDefaultRef() != null) { ed.getChooseDefaultRef().visit(this); - BeanDefinition beanDefinition = registry.getBeanDefinition(ed.getChooseDefaultRef().getIdentifier()); + BeanDefinition beanDefinition = register.getBeanDefinition(ed.getChooseDefaultRef().getIdentifier()); AssertUtil.notNull(beanDefinition, ed.getChooseDefaultRef().getIdentifier() + " not exist"); - definition.getPropertyValues().add("defaultBranch", new RuntimeBeanReference(ed.getChooseDefaultRef().getIdentifier())); + definition.getPropertyValues().add("defaultBranch", beanDefinition); } appendAttributes(ed, definition); - registry.registerBeanDefinition(ed.getIdentifier(), definition); + register.registerBeanDefinition(ed.getIdentifier(), definition); + } + + @Override + public void visit(AdapterDefinition ed) { + RootBeanDefinition definition = new RootBeanDefinition(); + PropertyValue name = new PropertyValue("name", ed.getName()); + + definition.setBeanClass(ed.resolveType()); + definition.getPropertyValues().addPropertyValue(name); + + if (isType(ed.getExecute())) { + RootBeanDefinition adapterDef = new RootBeanDefinition(); + adapterDef.setBeanClassName(ed.getExecute()); + definition.getPropertyValues().add("adapter", adapterDef); + } else { + AssertUtil.notNull(ed.getExecute(), "adapter[execute] must not be null"); + definition.getPropertyValues().add("adapter", new RuntimeBeanReference(ed.getExecute())); + } + + ed.getPipelineElement().visit(this); + + BeanDefinition beanDefinition = register.getBeanDefinition(ed.getPipelineElement().getIdentifier()); + definition.getPropertyValues().add("component", beanDefinition); + + register.registerBeanDefinition(ed.getIdentifier(), definition); + } } diff --git a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/ProxyParser.java b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/ProxyParser.java index 59e4f8eaf621ec71f622d64b5fedf03b94a05e8b..e151da6d69a51d1c5218cf9a6e7c6a02a512b86a 100644 --- a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/ProxyParser.java +++ b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/ProxyParser.java @@ -26,7 +26,7 @@ public class ProxyParser implements BeanDefinitionParser { AssertUtil.notNull(parser, "Could not find parse for element " + ElementUtils.getName(element)); parser.parseElement(element, context); - BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(parserContext.getRegistry()); + BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(parserContext.getRegistry(), context); context.getRegistered().forEach(visitor::visit); return null; } diff --git a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/SpringNamespaceHandler.java b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/SpringNamespaceHandler.java index 64e473fcd17f58bf36a9a9d925dd06c9c11d2cf7..621c1b35e7c12da0d82815222c4df298e228f509 100644 --- a/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/SpringNamespaceHandler.java +++ b/smart-flow-spring-extension/src/main/java/org/smartboot/flow/spring/extension/SpringNamespaceHandler.java @@ -14,5 +14,6 @@ public class SpringNamespaceHandler extends NamespaceHandlerSupport { ProxyParser proxyParser = new ProxyParser(); this.registerBeanDefinitionParser("pipeline", proxyParser); this.registerBeanDefinitionParser("engine", proxyParser); + this.registerBeanDefinitionParser("script", proxyParser); } } diff --git a/smart-flow-spring-extension/src/main/resources/META-INF/spring.schemas b/smart-flow-spring-extension/src/main/resources/META-INF/spring.schemas index 68ca2f4d968e16a6290378e191351ee78d25cd28..4fcc30474356e8dcca74ffe67c6e99db3a293078 100644 --- a/smart-flow-spring-extension/src/main/resources/META-INF/spring.schemas +++ b/smart-flow-spring-extension/src/main/resources/META-INF/spring.schemas @@ -1 +1 @@ -http\://org.smartboot/smart-flow-1.0.0.xsd=smart-flow-1.0.0.xsd \ No newline at end of file +http\://org.smartboot/smart-flow-1.0.1.xsd=smart-flow-1.0.1.xsd \ No newline at end of file diff --git a/smart-flow-example/pom.xml b/smart-flow-springboot-starter/pom.xml similarity index 55% rename from smart-flow-example/pom.xml rename to smart-flow-springboot-starter/pom.xml index fd01f888e08edf97557e722ef7eb28d07fabefd3..6ad21a82b2d490fb96cc5ce70038dce470b8e55e 100644 --- a/smart-flow-example/pom.xml +++ b/smart-flow-springboot-starter/pom.xml @@ -5,11 +5,11 @@ flow-engine org.smartboot - 1.0.1 + 1.0.3 4.0.0 - smart-flow-example + smart-flow-springboot-starter 8 @@ -18,37 +18,20 @@ - org.smartboot - smart-flow-manager - 1.0.1 + org.springframework.boot + spring-boot-starter + 2.7.5 - org.smartboot smart-flow-spring-extension - 1.0.1 - - - - org.junit.jupiter - junit-jupiter-api - 5.9.1 - test + 1.0.3 - - - org.junit.jupiter - junit-jupiter-engine - 5.9.1 - test - - - org.slf4j - slf4j-log4j12 - 2.0.3 + org.smartboot + smart-flow-manager + 1.0.3 - \ No newline at end of file diff --git a/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpManageConfiguration.java b/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpManageConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..e092c465d8f817ff53ca70bdcd76f669961c4562 --- /dev/null +++ b/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpManageConfiguration.java @@ -0,0 +1,51 @@ +package org.smartboot.flow.springboot.extension; + +import org.smartboot.flow.manager.change.HttpManager; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author huqiang + * @since 2022/12/14 19:15 + */ +@Configuration +@ConfigurationProperties(prefix = "smart.flow.manage.http") +public class FlowHttpManageConfiguration { + + private long timeout; + + private String url; + + private long idle; + + private long delayAtFirst; + + @Bean + public HttpManager getHttpManager(){ + HttpManager httpManager = new HttpManager(); + httpManager.setTimeout(timeout); + httpManager.setUrl(url); + httpManager.setIdle(idle); + httpManager.setDelayAtFirst(delayAtFirst); + httpManager.start(); + return httpManager; + } + + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setIdle(long idle) { + this.idle = idle; + } + + public void setDelayAtFirst(long delayAtFirst) { + this.delayAtFirst = delayAtFirst; + } +} diff --git a/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpReportConfiguration.java b/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpReportConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..5e25516c1736d0d3a74aca3a86dfe13f190a2ebc --- /dev/null +++ b/smart-flow-springboot-starter/src/main/java/org/smartboot/flow/springboot/extension/FlowHttpReportConfiguration.java @@ -0,0 +1,45 @@ +package org.smartboot.flow.springboot.extension; + +import org.smartboot.flow.manager.report.HttpReporter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * smart flow httReport auto config + * + * @author huqiang + * @since 2022/12/15 16:32 + */ +@Configuration +@ConfigurationProperties(prefix = "smart.flow.report.http") +public class FlowHttpReportConfiguration { + + + private long timeout; + + private String url; + + private long idle; + + @Bean + public HttpReporter getHttpReporter() { + HttpReporter reporter = new HttpReporter(); + reporter.setTimeout(this.timeout); + reporter.setUrl(this.url); + reporter.setIdle(this.idle); + return reporter; + } + + public void setTimeout(long timeout) { + this.timeout = timeout; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setIdle(long idle) { + this.idle = idle; + } +} diff --git a/smart-flow-springboot-starter/src/main/resources/META-INF/spring.factories b/smart-flow-springboot-starter/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000000000000000000000000000000000..cd6d66e7d71cbbca3f2d4b242ebb5cbdb51d566f --- /dev/null +++ b/smart-flow-springboot-starter/src/main/resources/META-INF/spring.factories @@ -0,0 +1,3 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + org.smartboot.flow.springboot.extension.FlowHttpManageConfiguration,\ + org.smartboot.flow.springboot.extension.FlowHttpReportConfiguration \ No newline at end of file