Merge branch 'dev'

This commit is contained in:
everywhere.z 2023-03-16 19:29:53 +08:00
commit 26c24692cf
177 changed files with 3803 additions and 453 deletions

View File

@ -11,7 +11,7 @@ LiteFlow是一个轻量且强大的国产规则引擎框架可用于复杂的
LiteFlow于2020年正式开源2021年获得开源中国年度最受欢迎开源软件殊荣。于2022年获得Gitee最有价值开源项目(GVP)荣誉。是一个正处在高速发展中的开源项目。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个1800多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
LiteFlow是一个由社区驱动的项目我们非常重视社区建设拥有一个2500多人的使用者社区在使用中碰到任何问题或者建议都可以在社区中反应。
你在官网中可以找到加入社区的方式!
@ -52,7 +52,7 @@ LiteFlow利用规则表达式为驱动引擎去驱动你定义的组件。你
LiteFlow拥有极其详细易懂的文档体系能帮助你解决在使用框架的时候95%以上的问题。
目前为止LiteFlow拥有800多个测试用例并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
目前为止LiteFlow拥有900多个测试用例并且不断在增加中。完备的文档+覆盖全面的测试用例保障了LiteFlow框架的稳定性
LiteFlow期待你的了解

View File

@ -50,5 +50,9 @@
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -7,6 +7,7 @@ import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.NodeBuildException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.monitor.MonitorFile;
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -134,6 +135,10 @@ public class LiteFlowNodeBuilder {
List<String> scriptList = PathContentParserHolder.loadContextAware().parseContent(ListUtil.toList(filePath));
String script = CollUtil.getFirst(scriptList);
setScript(script);
// 添加脚本文件监听
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(ListUtil.toList(filePath));
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
} catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(), e.getMessage());
throw new NodeBuildException(errMsg);
@ -141,12 +146,17 @@ public class LiteFlowNodeBuilder {
return this;
}
public LiteFlowNodeBuilder setLanguage(String language) {
this.node.setLanguage(language);
return this;
}
public void build() {
checkBuild();
try {
// 用于处理脚本 node
if (this.node.getType().isScript()){
FlowBus.addScriptNode(this.node.getId(), this.node.getName(), this.node.getType(), this.node.getScript());
FlowBus.addScriptNode(this.node.getId(), this.node.getName(), this.node.getType(), this.node.getScript(), this.node.getLanguage());
}
// 用于处理普通 node
else{

View File

@ -59,6 +59,12 @@ public class LiteFlowChainELBuilder {
EXPRESS_RUNNER.addFunction(ChainConstant.PRE, new PreOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FINALLY, new FinallyOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.IF, new IfOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE.toUpperCase(), new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.CATCH, new CatchOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELSE, Object.class, new ElseOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ELIF, Object.class, new ElifOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TO, Object.class, new ToOperator());
@ -69,11 +75,6 @@ public class LiteFlowChainELBuilder {
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ID, Object.class, new IdOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.IGNORE_ERROR, Object.class, new IgnoreErrorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.THREAD_POOL, Object.class, new ThreadPoolOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE.toUpperCase(), new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
EXPRESS_RUNNER.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DO, Object.class, new DoOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BREAK, Object.class, new BreakOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DATA, Object.class, new DataOperator());

View File

@ -0,0 +1,26 @@
package com.yomahub.liteflow.builder.el.operator;
import com.yomahub.liteflow.builder.el.operator.base.BaseOperator;
import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.CatchCondition;
/**
* EL规则中的CATCH的操作符
* 用法CATCH...DO...
* @author Bryan.Zhang
* @since 2.10.0
*/
public class CatchOperator extends BaseOperator<CatchCondition> {
@Override
public CatchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 1);
Executable catchItem = OperatorHelper.convert(objects[0], Executable.class);
CatchCondition catchCondition = new CatchCondition();
catchCondition.setCatchItem(catchItem);
return catchCondition;
}
}

View File

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

View File

@ -35,7 +35,7 @@ public class ElifOperator extends BaseOperator<IfCondition> {
//构建一个内部的IfCondition
IfCondition ifConditionItem = new IfCondition();
ifConditionItem.setExecutableList(ListUtil.toList(ifNode));
ifConditionItem.setIfNode(ifNode);
ifConditionItem.setTrueCaseExecutableItem(trueCaseExecutableItem);
//因为可能会有多个ELIF所以每一次拿到的caller总是最开始大的if需要遍历到没有falseCaseExecutable的地方

View File

@ -37,7 +37,7 @@ public class IfOperator extends BaseOperator<IfCondition> {
}
IfCondition ifCondition = new IfCondition();
ifCondition.setExecutableList(ListUtil.toList(ifNode));
ifCondition.setIfNode(ifNode);
ifCondition.setTrueCaseExecutableItem(trueCaseExecutableItem);
ifCondition.setFalseCaseExecutableItem(falseCaseExecutableItem);
return ifCondition;

View File

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

View File

@ -23,6 +23,8 @@ public interface ChainConstant {
String NAME = "name";
String LANGUAGE = "language";
String VALUE = "value";
String ANY = "any";
@ -70,4 +72,6 @@ public interface ChainConstant {
String CURR_CHAIN_ID = "currChainId";
String DEFAULT = "DEFAULT";
String CATCH = "CATCH";
}

View File

@ -0,0 +1,25 @@
package com.yomahub.liteflow.context;
import com.yomahub.liteflow.annotation.AliasFor;
import java.lang.annotation.*;
/**
* @description 用于标注上下文bean的别名以便在脚本或者组件中通过别名来获取上下文对象
* @since 2.9.7
* @author Tingliang Wang
* @createTime 2023/2/6 15:06
* @update: [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ContextBean {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
}

View File

@ -18,6 +18,7 @@ import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
import com.yomahub.liteflow.monitor.MonitorFile;
import com.yomahub.liteflow.parser.base.FlowParser;
import com.yomahub.liteflow.parser.factory.FlowParserProvider;
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
@ -27,6 +28,7 @@ import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -89,11 +91,11 @@ public class FlowExecutor {
//所有的Parser的SPI实现都是以custom形式放入的且只支持xml形式
ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
Iterator<ParserClassNameSpi> it = loader.iterator();
if (it.hasNext()){
if (it.hasNext()) {
ParserClassNameSpi parserClassNameSpi = it.next();
ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
liteflowConfig.setRuleSource(ruleSource);
}else{
} else {
//ruleSource为空而且没有spi形式的扩展那么说明真的没有ruleSource
//这种情况有可能是基于代码动态构建的
return;
@ -124,6 +126,7 @@ public class FlowExecutor {
//支持多类型的配置文件分别解析
if (BooleanUtil.isTrue(liteflowConfig.isSupportMultipleType())) {
// 解析文件
parser.parseMain(ListUtil.toList(path));
}
} catch (CyclicDependencyException e) {
@ -148,6 +151,7 @@ public class FlowExecutor {
//进行多个配置文件的一起解析
try {
if (parser != null) {
// 解析文件
parser.parseMain(rulePathList);
} else {
throw new ConfigErrorException("parse error, please check liteflow config property");
@ -167,30 +171,44 @@ public class FlowExecutor {
}
//如果是ruleSource方式的最后判断下有没有解析出来,如果没有解析出来则报错
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())){
if (FlowBus.getChainMap().isEmpty()){
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())) {
if (FlowBus.getChainMap().isEmpty()) {
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
throw new ConfigErrorException(errMsg);
}
}
//执行钩子
if(hook){
if (hook) {
FlowInitHook.executeHook();
}
// 文件监听
if (liteflowConfig.getEnableMonitorFile()) {
try{
addMonitorFilePaths(rulePathList);
MonitorFile.getInstance().create();
}catch (Exception e){
String errMsg = StrUtil.format("file monitor init error for path:{}", rulePathList);
throw new MonitorFileInitErrorException(errMsg);
}
}
}
//此方法就是从原有的配置源主动拉取新的进行刷新
//和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取一个为被动监听到新的内容进行刷新
public void reloadRule() {
long start = System.currentTimeMillis();
init(false);
LOG.info("reload rules takes {}ms", System.currentTimeMillis() - start);
}
//隐式流程的调用方法
@Deprecated
public void invoke(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
if (!response.isSuccess()){
if (!response.isSuccess()) {
throw response.getCause();
}
}
@ -198,7 +216,7 @@ public class FlowExecutor {
@Deprecated
public void invokeInAsync(String chainId, Object param, Integer slotIndex) throws Exception {
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
if (!response.isSuccess()){
if (!response.isSuccess()) {
throw response.getCause();
}
}
@ -240,7 +258,7 @@ public class FlowExecutor {
//调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray) {
return ExecutorHelper.loadInstance().buildMainExecutor(liteflowConfig.getMainExecutorClass()).submit(()
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray,null));
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray, null));
}
@ -251,11 +269,11 @@ public class FlowExecutor {
//调用一个流程返回默认的上下文适用于简单的调用
@Deprecated
public DefaultContext execute(String chainId, Object param) throws Exception{
public DefaultContext execute(String chainId, Object param) throws Exception {
LiteflowResponse response = this.execute2Resp(chainId, param, DefaultContext.class);
if (!response.isSuccess()){
if (!response.isSuccess()) {
throw response.getCause();
}else{
} else {
return response.getFirstContextBean();
}
}
@ -269,8 +287,8 @@ public class FlowExecutor {
}
private LiteflowResponse invoke2Resp(String chainId,
Object param,
Integer slotIndex, InnerChainTypeEnum innerChainType) {
Object param,
Integer slotIndex, InnerChainTypeEnum innerChainType) {
Slot slot = doExecute(chainId, param, null, null, slotIndex, innerChainType);
return LiteflowResponse.newInnerResponse(chainId, slot);
}
@ -288,9 +306,9 @@ public class FlowExecutor {
//如果不是隐式流程那么需要分配Slot
if (innerChainType.equals(InnerChainTypeEnum.NONE) && ObjectUtil.isNull(slotIndex)) {
//这里可以根据class分配也可以根据bean去分配
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)){
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)) {
slotIndex = DataBus.offerSlotByClass(ListUtil.toList(contextBeanClazzArray));
}else{
} else {
slotIndex = DataBus.offerSlotByBean(ListUtil.toList(contextBeanArray));
}
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
@ -311,7 +329,7 @@ public class FlowExecutor {
//如果是隐式流程事先把subException给置空然后把隐式流程的chainId放入slot元数据中
//我知道这在多线程调用隐式流程中会有问题但是考虑到这种场景的不会多也有其他的转换方式
//所以暂且这么做以后再优化
if (!innerChainType.equals(InnerChainTypeEnum.NONE)){
if (!innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.removeSubException(chainId);
slot.addSubChain(chainId);
}
@ -326,9 +344,9 @@ public class FlowExecutor {
if (ObjectUtil.isNotNull(param)) {
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.setRequestData(param);
} else if(innerChainType.equals(InnerChainTypeEnum.IN_SYNC)){
} else if (innerChainType.equals(InnerChainTypeEnum.IN_SYNC)) {
slot.setChainReqData(chainId, param);
} else if(innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)){
} else if (innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)) {
slot.setChainReqData2Queue(chainId, param);
}
}
@ -351,15 +369,15 @@ public class FlowExecutor {
} catch (Exception e) {
if (ObjectUtil.isNotNull(chain)) {
String errMsg = StrUtil.format("[{}]:chain[{}] execute error on slot[{}]", slot.getRequestId(), chain.getChainName(), slotIndex);
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.error(errMsg, e);
}else{
} else {
LOG.error(errMsg);
}
}else{
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
} else {
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
LOG.error(e.getMessage(), e);
}else{
} else {
LOG.error(e.getMessage());
}
}
@ -368,7 +386,7 @@ public class FlowExecutor {
//如果是隐式流程则需要设置到隐式流程的exception属性里
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
slot.setException(e);
}else{
} else {
slot.setSubException(chainId, e);
}
} finally {
@ -389,4 +407,15 @@ public class FlowExecutor {
//把liteFlowConfig设到LiteFlowGetter中去
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
}
/**
* 添加监听文件路径
*
* @param pathList 文件路径
*/
private void addMonitorFilePaths(List<String> pathList) throws Exception {
// 添加规则文件监听
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(pathList);
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
}
}

View File

@ -19,11 +19,11 @@ public class ScriptBreakComponent extends NodeBreakComponent implements ScriptCo
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -24,12 +24,12 @@ public class ScriptCommonComponent extends NodeComponent implements ScriptCompon
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
public void loadScript(String script, String language) {
log.info("load script for component[{}]", getDisplayName());
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -28,5 +28,5 @@ public interface ScriptComponent {
* 加载脚本
* @param script
*/
void loadScript(String script);
void loadScript(String script, String language);
}

View File

@ -19,11 +19,11 @@ public class ScriptForComponent extends NodeForComponent implements ScriptCompon
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
return (int) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
return (int) ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -19,11 +19,11 @@ public class ScriptIfComponent extends NodeIfComponent implements ScriptComponen
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
return (boolean)ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
return (boolean)ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -20,11 +20,11 @@ public class ScriptSwitchComponent extends NodeSwitchComponent implements Script
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
return (String)ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
return (String)ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -20,11 +20,11 @@ public class ScriptWhileComponent extends NodeWhileComponent implements ScriptCo
wrap.setSlotIndex(this.getSlotIndex());
wrap.setTag(this.getTag());
wrap.setCmpData(this.getCmpData(Map.class));
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor().execute(wrap);
return (boolean) ScriptExecutorFactory.loadInstance().getScriptExecutor(this.getRefNode().getLanguage()).execute(wrap);
}
@Override
public void loadScript(String script) {
ScriptExecutorFactory.loadInstance().getScriptExecutor().load(getNodeId(), script);
public void loadScript(String script, String language) {
ScriptExecutorFactory.loadInstance().getScriptExecutor(language).load(getNodeId(), script);
}
}

View File

@ -16,7 +16,9 @@ public enum ConditionTypeEnum {
TYPE_WHILE("while", "while"),
TYPE_ITERATOR("iterator", "iterator")
TYPE_ITERATOR("iterator", "iterator"),
TYPE_CATCH("catch", "catch")
;
private String type;
private String name;

View File

@ -0,0 +1,44 @@
package com.yomahub.liteflow.enums;
public enum ScriptTypeEnum {
GROOVY("groovy", "groovy"),
QLEXPRESS("qlexpress", "qlexpress"),
JS("javascript", "js"),
PYTHON("python", "python"),
LUA("luaj", "lua")
;
private String engineName;
private String displayName;
ScriptTypeEnum(String engineName, String displayName) {
this.engineName = engineName;
this.displayName = displayName;
}
public String getEngineName() {
return engineName;
}
public void setEngineName(String engineName) {
this.engineName = engineName;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public static ScriptTypeEnum getEnumByDisplayName(String displayName) {
for (ScriptTypeEnum e : ScriptTypeEnum.values()) {
if (e.getDisplayName().equals(displayName)) {
return e;
}
}
return null;
}
}

View File

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

View File

@ -0,0 +1,28 @@
package com.yomahub.liteflow.exception;
/**
* 文件监听异常
* @author Bryan.Zhang
* @since 2.10.0
*/
public class MonitorFileInitErrorException extends RuntimeException {
private static final long serialVersionUID = 1L;
/** 异常信息 */
private String message;
public MonitorFileInitErrorException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -106,7 +106,7 @@ public class FlowBus {
* @param cmpClazz 节点组件类
*/
public static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz) {
addNode(nodeId, name, type, cmpClazz, null);
addNode(nodeId, name, type, cmpClazz, null, null);
}
/**
@ -124,7 +124,7 @@ public class FlowBus {
} catch (Exception e) {
throw new ComponentCannotRegisterException(e.getMessage());
}
addNode(nodeId, name, nodeType, cmpClazz, null);
addNode(nodeId, name, nodeType, cmpClazz, null, null);
}
/**
@ -135,11 +135,11 @@ public class FlowBus {
* @param nodeType 节点类型
* @param script 脚本
*/
public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script) {
addNode(nodeId, name, nodeType, ScriptComponent.ScriptComponentClassMap.get(nodeType), script);
public static void addScriptNode(String nodeId, String name, NodeTypeEnum nodeType, String script, String language) {
addNode(nodeId, name, nodeType, ScriptComponent.ScriptComponentClassMap.get(nodeType), script, language);
}
private static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz, String script) {
private static void addNode(String nodeId, String name, NodeTypeEnum type, Class<?> cmpClazz, String script, String language) {
try {
//判断此类是否是声明式的组件如果是声明式的组件就用动态代理生成实例
//如果不是声明式的就用传统的方式进行判断
@ -193,7 +193,8 @@ public class FlowBus {
if (type.isScript()) {
if (StrUtil.isNotBlank(script)) {
node.setScript(script);
((ScriptComponent) cmpInstance).loadScript(script);
node.setLanguage(language);
((ScriptComponent) cmpInstance).loadScript(script, language);
} else {
String errorMsg = StrUtil.format("script for node[{}] is empty", nodeId);
throw new ScriptLoadException(errorMsg);
@ -244,10 +245,7 @@ public class FlowBus {
public static void cleanScriptCache() {
//如果引入了脚本组件SPI则还需要清理脚本的缓存
try {
ScriptExecutor scriptExecutor = ScriptExecutorFactory.loadInstance().getScriptExecutor();
if (ObjectUtil.isNotNull(scriptExecutor)) {
scriptExecutor.cleanCache();
}
ScriptExecutorFactory.loadInstance().cleanScriptCache();
} catch (ScriptSpiException ignored) {
}
}

View File

@ -13,9 +13,7 @@ import java.util.Queue;
* 执行结果封装类
* @author zend.wang
*/
public class LiteflowResponse implements Serializable {
private static final long serialVersionUID = -2792556188993845048L;
public class LiteflowResponse{
private boolean success;

View File

@ -46,6 +46,8 @@ public class Node implements Executable,Cloneable{
private String script;
private String language;
private NodeComponent instance;
private String tag;
@ -255,4 +257,12 @@ public class Node implements Executable,Cloneable{
public void removeCurrLoopObject(){
this.currLoopObject.remove();
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
}

View File

@ -0,0 +1,61 @@
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.exception.CatchErrorException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
/**
* Catch Condition
* @author Bryan.Zhang
* @since 2.10.0
*/
public class CatchCondition extends Condition{
@Override
public void executeCondition(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
try{
Executable catchExecutable = this.getCatchItem();
if (ObjectUtil.isNull(catchExecutable)){
String errorInfo = StrUtil.format("[{}]:no catch item find", slot.getRequestId());
throw new CatchErrorException(errorInfo);
}
catchExecutable.setCurrChainId(this.getCurrChainId());
catchExecutable.execute(slotIndex);
}catch (Exception e){
Executable doExecutable = this.getDoItem();
if (ObjectUtil.isNotNull(doExecutable)){
doExecutable.setCurrChainId(this.getCurrChainId());
doExecutable.execute(slotIndex);
}
//catch之后需要把exception给清除掉
//正如同java的catch一样异常自己处理了属于正常流程了整个流程状态应该是成功的
DataBus.getSlot(slotIndex).removeException();
}
}
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_CATCH;
}
public Executable getCatchItem(){
return this.getExecutableOne(ConditionKey.CATCH_KEY);
}
public void setCatchItem(Executable executable){
this.addExecutable(ConditionKey.CATCH_KEY, executable);
}
public Executable getDoItem(){
return this.getExecutableOne(ConditionKey.DO_KEY);
}
public void setDoItem(Executable executable){
this.addExecutable(ConditionKey.DO_KEY, executable);
}
}

View File

@ -7,12 +7,20 @@
*/
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.enums.ExecuteTypeEnum;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.enums.ConditionTypeEnum;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Condition的抽象类
@ -25,8 +33,7 @@ public abstract class Condition implements Executable{
/**
* 可执行元素的集合
*/
private List<Executable> executableList = new ArrayList<>();
private final Map<String, List<Executable>> executableGroup = new HashMap<>();
/**
* 当前所在的ChainName
@ -34,6 +41,29 @@ public abstract class Condition implements Executable{
*/
private String currChainId;
@Override
public void execute(Integer slotIndex) throws Exception {
try{
executeCondition(slotIndex);
}catch (ChainEndException e){
//这里单独catch ChainEndException是因为ChainEndException是用户自己setIsEnd抛出的异常
//是属于正常逻辑所以会在FlowExecutor中判断这里不作为异常处理
throw e;
}catch (Exception e){
Slot slot = DataBus.getSlot(slotIndex);
String chainId = this.getCurrChainId();
//这里事先取到exception set到slot里为了方便finally取到exception
if (slot.isSubChain(chainId)){
slot.setSubException(chainId, e);
}else{
slot.setException(e);
}
throw e;
}
}
protected abstract void executeCondition(Integer slotIndex) throws Exception;
@Override
public ExecuteTypeEnum getExecuteType() {
return ExecuteTypeEnum.CONDITION;
@ -45,15 +75,44 @@ public abstract class Condition implements Executable{
}
public List<Executable> getExecutableList() {
return getExecutableList(ConditionKey.DEFAULT_KEY);
}
public List<Executable> getExecutableList(String groupKey) {
List<Executable> executableList = this.executableGroup.get(groupKey);
if (CollUtil.isEmpty(executableList)){
executableList = new ArrayList<>();
}
return executableList;
}
public Executable getExecutableOne(String groupKey) {
List<Executable> list = getExecutableList(groupKey);
if (CollUtil.isEmpty(list)){
return null;
}else{
return list.get(0);
}
}
public void setExecutableList(List<Executable> executableList) {
this.executableList = executableList;
this.executableGroup.put(ConditionKey.DEFAULT_KEY, executableList);
}
public void addExecutable(Executable executable) {
this.executableList.add(executable);
addExecutable(ConditionKey.DEFAULT_KEY, executable);
}
public void addExecutable(String groupKey, Executable executable) {
if (ObjectUtil.isNull(executable)){
return;
}
List<Executable> executableList = this.executableGroup.get(groupKey);
if (CollUtil.isEmpty(executableList)){
this.executableGroup.put(groupKey, ListUtil.toList(executable));
}else{
this.executableGroup.get(groupKey).add(executable);
}
}
public abstract ConditionTypeEnum getConditionType();
@ -68,7 +127,6 @@ public abstract class Condition implements Executable{
/**
*
* @return
* @deprecated 请使用 {@link #setCurrChainId(String)}
*/
@Deprecated
@ -84,4 +142,8 @@ public abstract class Condition implements Executable{
public void setCurrChainId(String currChainId) {
this.currChainId = currChainId;
}
public Map<String, List<Executable>> getExecutableGroup() {
return executableGroup;
}
}

View File

@ -0,0 +1,34 @@
package com.yomahub.liteflow.flow.element.condition;
public interface ConditionKey {
String DEFAULT_KEY = "DEFAULT_KEY";
String FOR_KEY = "FOR_KEY";
String IF_KEY = "IF_KEY";
String IF_TRUE_CASE_KEY = "IF_TRUE_CASE_KEY";
String IF_FALSE_CASE_KEY = "IF_FALSE_CASE_KEY";
String ITERATOR_KEY = "ITERATOR_KEY";
String DO_KEY = "DO_KEY";
String BREAK_KEY = "BREAK_KEY";
String SWITCH_KEY = "SWITCH_KEY";
String SWITCH_TARGET_KEY = "SWITCH_TARGET_KEY";
String SWITCH_DEFAULT_KEY = "SWITCH_DEFAULT_KEY";
String PRE_KEY = "PRE_KEY";
String FINALLY_KEY = "FINALLY_KEY";
String WHILE_KEY = "WHILE_KEY";
String CATCH_KEY = "CATCH_KEY";
}

View File

@ -18,7 +18,7 @@ import com.yomahub.liteflow.flow.element.Executable;
public class FinallyCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getExecutableList()){
executableItem.setCurrChainId(this.getCurrChainId());
executableItem.execute(slotIndex);

View File

@ -18,28 +18,35 @@ import com.yomahub.liteflow.util.LiteFlowProxyUtil;
*/
public class ForCondition extends LoopCondition{
private Node forNode;
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
Node forNode = this.getForNode();
if (ObjectUtil.isNull(forNode)){
String errorInfo = StrUtil.format("[{}]:no for-node found", slot.getRequestId());
throw new NoForNodeException(errorInfo);
}
//先去判断isAccess方法如果isAccess方法都返回false整个FOR表达式不执行
if (!this.getForNode().isAccess(slotIndex)){
return;
}
//执行forCount组件
forNode.setCurrChainId(this.getCurrChainId());
forNode.execute(slotIndex);
//这里可能会有spring代理过的bean所以拿到user原始的class
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(this.forNode.getInstance().getClass());
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(forNode.getInstance().getClass());
//获得循环次数
int forCount = slot.getForResult(originalForCountClass.getName());
//获得要循环的可执行对象
Executable executableItem = this.getDoExecutor();
//获取Break节点
Node breakNode = this.getBreakNode();
//循环执行
for (int i = 0; i < forCount; i++) {
executableItem.setCurrChainId(this.getCurrChainId());
@ -51,7 +58,7 @@ public class ForCondition extends LoopCondition{
breakNode.setCurrChainId(this.getCurrChainId());
setLoopIndex(breakNode, i);
breakNode.execute(slotIndex);
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(breakNode.getInstance().getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
if (isBreak){
break;
@ -66,10 +73,10 @@ public class ForCondition extends LoopCondition{
}
public Node getForNode() {
return forNode;
return (Node) this.getExecutableOne(ConditionKey.FOR_KEY);
}
public void setForNode(Node forNode) {
this.forNode = forNode;
this.addExecutable(ConditionKey.FOR_KEY, forNode);
}
}

View File

@ -20,13 +20,14 @@ import com.yomahub.liteflow.util.LiteFlowProxyUtil;
*/
public class IfCondition extends Condition {
private Executable trueCaseExecutableItem;
private Executable falseCaseExecutableItem;
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
if (ListUtil.toList(NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT).contains(getIfNode().getType())){
//先去判断isAccess方法如果isAccess方法都返回false整个IF表达式不执行
if (!this.getIfNode().isAccess(slotIndex)){
return;
}
//先执行IF节点
this.getIfNode().setCurrChainId(this.getCurrChainId());
this.getIfNode().execute(slotIndex);
@ -37,6 +38,9 @@ public class IfCondition extends Condition {
//拿到If执行过的结果
boolean ifResult = slot.getIfResult(originalClass.getName());
Executable trueCaseExecutableItem = this.getTrueCaseExecutableItem();
Executable falseCaseExecutableItem = this.getFalseCaseExecutableItem();
if (ifResult) {
//trueCaseExecutableItem这个不能为空否则执行什么呢
if (ObjectUtil.isNull(trueCaseExecutableItem)) {
@ -51,7 +55,7 @@ public class IfCondition extends Condition {
}
//执行trueCaseExecutableItem
trueCaseExecutableItem.setCurrChainName(this.getCurrChainName());
trueCaseExecutableItem.setCurrChainId(this.getCurrChainId());
trueCaseExecutableItem.execute(slotIndex);
} else {
//falseCaseExecutableItem可以为null但是不为null时就执行否的情况
@ -78,22 +82,26 @@ public class IfCondition extends Condition {
}
public Executable getTrueCaseExecutableItem() {
return trueCaseExecutableItem;
return this.getExecutableOne(ConditionKey.IF_TRUE_CASE_KEY);
}
public void setTrueCaseExecutableItem(Executable trueCaseExecutableItem) {
this.trueCaseExecutableItem = trueCaseExecutableItem;
this.addExecutable(ConditionKey.IF_TRUE_CASE_KEY, trueCaseExecutableItem);
}
public Executable getFalseCaseExecutableItem() {
return falseCaseExecutableItem;
return this.getExecutableOne(ConditionKey.IF_FALSE_CASE_KEY);
}
public void setFalseCaseExecutableItem(Executable falseCaseExecutableItem) {
this.falseCaseExecutableItem = falseCaseExecutableItem;
this.addExecutable(ConditionKey.IF_FALSE_CASE_KEY, falseCaseExecutableItem);
}
public void setIfNode(Node ifNode){
this.addExecutable(ConditionKey.IF_KEY, ifNode);
}
public Node getIfNode() {
return (Node) this.getExecutableList().get(0);
return (Node) this.getExecutableOne(ConditionKey.IF_KEY);
}
}

View File

@ -14,28 +14,36 @@ import java.util.Iterator;
public class IteratorCondition extends LoopCondition{
private Node iteratorNode;
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
Node iteratorNode = this.getIteratorNode();
if (ObjectUtil.isNull(iteratorNode)){
String errorInfo = StrUtil.format("[{}]:no iterator-node found", slot.getRequestId());
throw new NoIteratorNodeException(errorInfo);
}
//先去判断isAccess方法如果isAccess方法都返回false整个ITERATOR表达式不执行
if (!this.getIteratorNode().isAccess(slotIndex)){
return;
}
//执行Iterator组件
iteratorNode.setCurrChainId(this.getCurrChainId());
iteratorNode.execute(slotIndex);
//这里可能会有spring代理过的bean所以拿到user原始的class
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(this.iteratorNode.getInstance().getClass());
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(iteratorNode.getInstance().getClass());
//获得迭代器
Iterator<?> it = slot.getIteratorResult(originalForCountClass.getName());
//获得要循环的可执行对象
Executable executableItem = this.getDoExecutor();
//获取Break节点
Node breakNode = this.getBreakNode();
int index = 0;
while(it.hasNext()){
Object itObj = it.next();
@ -53,7 +61,7 @@ public class IteratorCondition extends LoopCondition{
setLoopIndex(breakNode, index);
setCurrLoopObject(breakNode, itObj);
breakNode.execute(slotIndex);
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(breakNode.getInstance().getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
if (isBreak){
break;
@ -69,10 +77,10 @@ public class IteratorCondition extends LoopCondition{
}
public Node getIteratorNode() {
return iteratorNode;
return (Node) this.getExecutableOne(ConditionKey.ITERATOR_KEY);
}
public void setIteratorNode(Node iteratorNode) {
this.iteratorNode = iteratorNode;
this.addExecutable(ConditionKey.ITERATOR_KEY, iteratorNode);
}
}

View File

@ -4,6 +4,10 @@ import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
/**
* 循环Condition的抽象类
* 主要继承对象有ForCondition和WhileCondition
@ -13,25 +17,27 @@ import com.yomahub.liteflow.flow.element.Node;
*/
public abstract class LoopCondition extends Condition {
protected Node breakNode;
public Node getBreakNode() {
return breakNode;
protected Node getBreakNode() {
return (Node) this.getExecutableOne(ConditionKey.BREAK_KEY);
}
public void setBreakNode(Node breakNode) {
this.breakNode = breakNode;
this.addExecutable(ConditionKey.BREAK_KEY, breakNode);
}
protected Executable getDoExecutor() {
return this.getExecutableOne(ConditionKey.DO_KEY);
}
public void setDoExecutor(Executable executable) {
this.addExecutable(ConditionKey.DO_KEY, executable);
}
protected void setLoopIndex(Executable executableItem, int index){
if (executableItem instanceof Chain){
((Chain)executableItem).getConditionList().forEach(condition -> setLoopIndex(condition, index));
}else if(executableItem instanceof IfCondition){
((Condition)executableItem).getExecutableList().forEach(executable -> setLoopIndex(executable, index));
setLoopIndex(((IfCondition)executableItem).getTrueCaseExecutableItem(), index);
setLoopIndex(((IfCondition)executableItem).getFalseCaseExecutableItem(), index);
}else if(executableItem instanceof Condition){
((Condition)executableItem).getExecutableList().forEach(executable -> setLoopIndex(executable, index));
((Condition) executableItem).getExecutableGroup().forEach((key, value) -> value.forEach(executable -> setLoopIndex(executable, index)));
}else if(executableItem instanceof Node){
((Node)executableItem).setLoopIndex(index);
}
@ -40,18 +46,12 @@ public abstract class LoopCondition extends Condition {
protected void setCurrLoopObject(Executable executableItem, Object obj){
if (executableItem instanceof Chain){
((Chain)executableItem).getConditionList().forEach(condition -> setCurrLoopObject(condition, obj));
}else if(executableItem instanceof IfCondition){
((Condition)executableItem).getExecutableList().forEach(executable -> setCurrLoopObject(executable, obj));
setCurrLoopObject(((IfCondition)executableItem).getTrueCaseExecutableItem(), obj);
setCurrLoopObject(((IfCondition)executableItem).getFalseCaseExecutableItem(), obj);
}else if(executableItem instanceof Condition){
((Condition)executableItem).getExecutableList().forEach(executable -> setCurrLoopObject(executable, obj));
((Condition) executableItem).getExecutableGroup().forEach((key, value) -> value.forEach(executable -> setCurrLoopObject(executable, obj)));
}else if(executableItem instanceof Node){
((Node)executableItem).setCurrLoopObject(obj);
}
}
protected Executable getDoExecutor() {
return this.getExecutableList().get(0);
}
}

View File

@ -18,7 +18,7 @@ import com.yomahub.liteflow.flow.element.Executable;
public class PreCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
for(Executable executableItem : this.getExecutableList()){
executableItem.setCurrChainId(this.getCurrChainId());
executableItem.execute(slotIndex);

View File

@ -23,27 +23,30 @@ import java.util.List;
* @since 2.8.0
*/
public class SwitchCondition extends Condition{
private final List<Executable> targetList = new ArrayList<>();
private final String TAG_PREFIX = "tag";
private final String TAG_FLAG = ":";
private Executable defaultExecutor;
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
if (ListUtil.toList(NodeTypeEnum.SWITCH, NodeTypeEnum.SWITCH_SCRIPT).contains(this.getSwitchNode().getType())){
//获取switch node
Node switchNode = this.getSwitchNode();
//获取target List
List<Executable> targetList = this.getTargetList();
//先去判断isAccess方法如果isAccess方法都返回false整个SWITCH表达式不执行
if (!switchNode.isAccess(slotIndex)){
return;
}
//先执行switch节点
this.getSwitchNode().setCurrChainId(this.getCurrChainId());
this.getSwitchNode().execute(slotIndex);
switchNode.setCurrChainId(this.getCurrChainId());
switchNode.execute(slotIndex);
//根据switch节点执行出来的结果选择
Slot slot = DataBus.getSlot(slotIndex);
//这里可能会有spring代理过的bean所以拿到user原始的class
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getSwitchNode().getInstance().getClass());
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(switchNode.getInstance().getClass());
String targetId = slot.getSwitchResult(originalClass.getName());
Executable targetExecutor = null;
@ -70,7 +73,7 @@ public class SwitchCondition extends Condition{
if (ObjectUtil.isNull(targetExecutor)) {
//没有匹配到执行节点则走默认的执行节点
targetExecutor = defaultExecutor;
targetExecutor = this.getDefaultExecutor();
}
if (ObjectUtil.isNotNull(targetExecutor)) {
@ -98,26 +101,26 @@ public class SwitchCondition extends Condition{
}
public void addTargetItem(Executable executable){
this.targetList.add(executable);
}
public void setSwitchNode(Node switchNode) {
this.getExecutableList().add(switchNode);
this.addExecutable(ConditionKey.SWITCH_TARGET_KEY, executable);
}
public List<Executable> getTargetList(){
return targetList;
return this.getExecutableList(ConditionKey.SWITCH_TARGET_KEY);
}
public void setSwitchNode(Node switchNode) {
this.addExecutable(ConditionKey.SWITCH_KEY, switchNode);
}
public Node getSwitchNode(){
return (Node) this.getExecutableList().get(0);
return (Node) this.getExecutableOne(ConditionKey.SWITCH_KEY);
}
public Executable getDefaultExecutor() {
return defaultExecutor;
return this.getExecutableOne(ConditionKey.SWITCH_DEFAULT_KEY);
}
public void setDefaultExecutor(Executable defaultExecutor) {
this.defaultExecutor = defaultExecutor;
this.addExecutable(ConditionKey.SWITCH_DEFAULT_KEY, defaultExecutor);
}
}

View File

@ -12,9 +12,8 @@ import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* 串行器
@ -22,23 +21,16 @@ import java.util.List;
*/
public class ThenCondition extends Condition {
/**
* 前置处理Condition
*/
private final List<PreCondition> preConditionList = new ArrayList<>();
/**
* 后置处理Condition
*/
private final List<FinallyCondition> finallyConditionList = new ArrayList<>();
@Override
public ConditionTypeEnum getConditionType() {
return ConditionTypeEnum.TYPE_THEN;
}
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
List<PreCondition> preConditionList = this.getPreConditionList();
List<FinallyCondition> finallyConditionList = this.getFinallyConditionList();
try{
for (PreCondition preCondition : preConditionList){
preCondition.setCurrChainId(this.getCurrChainId());
@ -74,11 +66,27 @@ public class ThenCondition extends Condition {
@Override
public void addExecutable(Executable executable) {
if (executable instanceof PreCondition){
preConditionList.add((PreCondition) executable);
this.addPreCondition((PreCondition) executable);
}else if (executable instanceof FinallyCondition){
finallyConditionList.add((FinallyCondition) executable);
this.addFinallyCondition((FinallyCondition) executable);
}else{
super.addExecutable(executable);
}
}
public List<PreCondition> getPreConditionList() {
return this.getExecutableList(ConditionKey.PRE_KEY).stream().map(executable -> (PreCondition) executable).collect(Collectors.toList());
}
public void addPreCondition(PreCondition preCondition){
this.addExecutable(ConditionKey.PRE_KEY, preCondition);
}
public List<FinallyCondition> getFinallyConditionList() {
return this.getExecutableList(ConditionKey.FINALLY_KEY).stream().map(executable -> (FinallyCondition) executable).collect(Collectors.toList());
}
public void addFinallyCondition(FinallyCondition finallyCondition){
this.addExecutable(ConditionKey.FINALLY_KEY, finallyCondition);
}
}

View File

@ -51,7 +51,7 @@ public class WhenCondition extends Condition {
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
executeAsyncCondition(slotIndex);
}

View File

@ -18,19 +18,26 @@ import com.yomahub.liteflow.util.LiteFlowProxyUtil;
*/
public class WhileCondition extends LoopCondition{
private Node whileNode;
@Override
public void execute(Integer slotIndex) throws Exception {
public void executeCondition(Integer slotIndex) throws Exception {
Slot slot = DataBus.getSlot(slotIndex);
Node whileNode = this.getWhileNode();
if (ObjectUtil.isNull(whileNode)){
String errorInfo = StrUtil.format("[{}]:no while-node found", slot.getRequestId());
throw new NoWhileNodeException(errorInfo);
}
//先去判断isAccess方法如果isAccess方法都返回false整个WHILE表达式不执行
if (!this.getWhileNode().isAccess(slotIndex)){
return;
}
//获得要循环的可执行对象
Executable executableItem = this.getDoExecutor();
//获取Break节点
Node breakNode = this.getBreakNode();
//循环执行
int index = 0;
while(getWhileResult(slotIndex)){
@ -42,7 +49,7 @@ public class WhileCondition extends LoopCondition{
breakNode.setCurrChainId(this.getCurrChainId());
setLoopIndex(breakNode, index);
breakNode.execute(slotIndex);
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(breakNode.getInstance().getClass());
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
if (isBreak){
break;
@ -54,10 +61,11 @@ public class WhileCondition extends LoopCondition{
private boolean getWhileResult(Integer slotIndex) throws Exception{
Slot slot = DataBus.getSlot(slotIndex);
Node whileNode = this.getWhileNode();
//执行while组件
whileNode.setCurrChainId(this.getCurrChainId());
whileNode.execute(slotIndex);
Class<?> originalWhileClass = LiteFlowProxyUtil.getUserClass(this.whileNode.getInstance().getClass());
Class<?> originalWhileClass = LiteFlowProxyUtil.getUserClass(whileNode.getInstance().getClass());
return slot.getWhileResult(originalWhileClass.getName());
}
@ -67,10 +75,10 @@ public class WhileCondition extends LoopCondition{
}
public Node getWhileNode() {
return whileNode;
return (Node) this.getExecutableOne(ConditionKey.WHILE_KEY);
}
public void setWhileNode(Node whileNode) {
this.whileNode = whileNode;
this.addExecutable(ConditionKey.WHILE_KEY, whileNode);
}
}

View File

@ -100,4 +100,8 @@ public class MonitorBus {
public void closeScheduler(){
this.printLogScheduler.shutdown();
}
public ConcurrentHashMap<String, BoundedPriorityBlockingQueue<CompStatistics>> getStatisticsMap() {
return statisticsMap;
}
}

View File

@ -0,0 +1,94 @@
package com.yomahub.liteflow.monitor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.watch.SimpleWatcher;
import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.watchers.DelayWatcher;
import cn.hutool.core.lang.Singleton;
import com.yomahub.liteflow.core.FlowExecutorHolder;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.monitor.FileAlterationListener;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* 规则文件监听器
*
* @author tangkc
*/
public class MonitorFile {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final Set<String> PATH_SET = new HashSet<>();
public static MonitorFile getInstance() {
return Singleton.get(MonitorFile.class);
}
/**
* 添加监听文件路径
*
* @param path 文件路径
*/
public void addMonitorFilePath(String path) {
if (FileUtil.isFile(path)){
String parentFolder = FileUtil.getParent(path, 1);
PATH_SET.add(parentFolder);
}else{
PATH_SET.add(path);
}
}
/**
* 添加监听文件路径
*
* @param filePaths 文件路径
*/
public void addMonitorFilePaths(List<String> filePaths) {
filePaths.forEach(this::addMonitorFilePath);
}
/**
* 创建文件监听
*/
public void create() throws Exception{
for (String path : PATH_SET) {
long interval = TimeUnit.MILLISECONDS.toMillis(2);
//不使用过滤器
FileAlterationObserver observer = new FileAlterationObserver(new File(path));
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileChange(File file) {
logger.info("file modify,filePath={}", file.getAbsolutePath());
FlowExecutorHolder.loadInstance().reloadRule();
}
@Override
public void onFileDelete(File file) {
logger.info("file delete,filePath={}", file.getAbsolutePath());
FlowExecutorHolder.loadInstance().reloadRule();
}
});
//创建文件变化监听器
FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
// 开始监控
monitor.start();
}
}
}

View File

@ -6,6 +6,7 @@ import java.util.List;
/**
* 基于本地的json方式EL表达式解析器
*
* @author Bryan.Zhang
* @since 2.8.0
*/

View File

@ -44,6 +44,7 @@ public class ParserHelper {
String script = nodePropBean.getScript();
String type = nodePropBean.getType();
String file = nodePropBean.getFile();
String language = nodePropBean.getLanguage();
//clazz有值的基本都不是脚本节点
//脚本节点都必须配置type
@ -80,6 +81,7 @@ public class ParserHelper {
.setType(nodeTypeEnum)
.setScript(script)
.setFile(file)
.setLanguage(language)
.build();
}
@ -98,7 +100,7 @@ public class ParserHelper {
// 当存在<nodes>节点定义时解析node节点
if (ObjectUtil.isNotNull(nodesElement)) {
List<Element> nodeList = nodesElement.elements(NODE);
String id, name, clazz, type, script, file;
String id, name, clazz, type, script, file, language;
for (Element e : nodeList) {
id = e.attributeValue(ID);
name = e.attributeValue(NAME);
@ -106,6 +108,7 @@ public class ParserHelper {
type = e.attributeValue(TYPE);
script = e.getText();
file = e.attributeValue(FILE);
language = e.attributeValue(LANGUAGE);
// 构建 node
NodePropBean nodePropBean = new NodePropBean()
@ -114,7 +117,8 @@ public class ParserHelper {
.setClazz(clazz)
.setScript(script)
.setType(type)
.setFile(file);
.setFile(file)
.setLanguage(language);
ParserHelper.buildNode(nodePropBean);
}

View File

@ -94,6 +94,17 @@ public class LiteflowConfig {
//替补组件class路径
private String substituteCmpClass;
// 规则文件/脚本文件变更监听
private Boolean enableMonitorFile = Boolean.FALSE;
public Boolean getEnableMonitorFile() {
return enableMonitorFile;
}
public void setEnableMonitorFile(Boolean enableMonitorFile) {
this.enableMonitorFile = enableMonitorFile;
}
public Boolean getEnable() {
if (ObjectUtil.isNull(enable)) {
return Boolean.TRUE;

View File

@ -1,5 +1,7 @@
package com.yomahub.liteflow.script;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
/**
* 脚本执行器接口
* @author Bryan.Zhang
@ -14,4 +16,6 @@ public interface ScriptExecutor {
Object execute(ScriptExecuteWrap wrap) throws Exception;
void cleanCache();
ScriptTypeEnum scriptType();
}

View File

@ -1,9 +1,15 @@
package com.yomahub.liteflow.script;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.exception.ScriptSpiException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.function.Consumer;
/**
* 脚本执行器工厂类
@ -14,7 +20,9 @@ public class ScriptExecutorFactory {
private static ScriptExecutorFactory scriptExecutorFactory;
private ScriptExecutor scriptExecutor;
private final Map<String,ScriptExecutor> scriptExecutorMap = new HashMap<>();
private final String NONE_LANGUAGE = "none";
public static ScriptExecutorFactory loadInstance(){
if (ObjectUtil.isNull(scriptExecutorFactory)){
@ -23,17 +31,43 @@ public class ScriptExecutorFactory {
return scriptExecutorFactory;
}
public ScriptExecutor getScriptExecutor(){
if (ObjectUtil.isNull(scriptExecutor)){
public ScriptExecutor getScriptExecutor(String language){
if (StrUtil.isBlank(language)){
language = NONE_LANGUAGE;
}
if (!scriptExecutorMap.containsKey(language)){
ServiceLoader<ScriptExecutor> loader = ServiceLoader.load(ScriptExecutor.class);
if (loader.iterator().hasNext()){
scriptExecutor = loader.iterator().next().init();
return scriptExecutor;
}else{
throw new ScriptSpiException("script spi component failed to load");
ScriptExecutor scriptExecutor;
Iterator<ScriptExecutor> it = loader.iterator();
while(it.hasNext()){
scriptExecutor = it.next().init();
if (language.equals(NONE_LANGUAGE)){
scriptExecutorMap.put(language, scriptExecutor);
break;
}else{
ScriptTypeEnum scriptType = ScriptTypeEnum.getEnumByDisplayName(language);
if (ObjectUtil.isNull(scriptType)){
throw new ScriptSpiException("script language config error");
}
if (scriptType.equals(scriptExecutor.scriptType())){
scriptExecutorMap.put(language, scriptExecutor);
break;
}
}
}
}
return scriptExecutor;
if (scriptExecutorMap.containsKey(language)){
return scriptExecutorMap.get(language);
}else{
throw new ScriptSpiException("script spi component failed to load");
}
}
public void cleanScriptCache(){
this.scriptExecutorMap.forEach((key, value) -> value.cleanCache());
}
}

View File

@ -3,6 +3,8 @@ package com.yomahub.liteflow.script.jsr223;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.context.ContextBean;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.script.ScriptBeanManager;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
@ -16,6 +18,7 @@ import org.slf4j.LoggerFactory;
import javax.script.*;
import java.util.Map;
import java.util.Optional;
/**
* JSR223 script engine的统一实现抽象类
@ -33,12 +36,10 @@ public abstract class JSR223ScriptExecutor implements ScriptExecutor {
@Override
public ScriptExecutor init() {
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
scriptEngine = scriptEngineManager.getEngineByName(scriptEngineName());
scriptEngine = scriptEngineManager.getEngineByName(this.scriptType().getEngineName());
return this;
}
protected abstract String scriptEngineName();
protected String convertScript(String script){
return script;
}
@ -71,7 +72,13 @@ public abstract class JSR223ScriptExecutor implements ScriptExecutor {
//比如你的自定义上下文为AbcContext那么key就为:abcContext
//这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法而不是参数所以脚本语言的绑定表里也是放多个上下文
DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
String key = StrUtil.lowerFirst(o.getClass().getSimpleName());
ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(),ContextBean.class);
String key;
if(contextBean !=null && contextBean.value().trim().length()>0){
key = contextBean.value();
}else{
key = StrUtil.lowerFirst(o.getClass().getSimpleName());
}
bindings.put(key, o);
});

View File

@ -339,6 +339,10 @@ public class Slot{
putMetaDataMap(EXCEPTION, e);
}
public void removeException(){
metaDataMap.remove(EXCEPTION);
}
public Exception getSubException(String chainId) {
return (Exception) this.metaDataMap.get(SUB_EXCEPTION_PREFIX + chainId);
}

View File

@ -2,7 +2,23 @@ package com.yomahub.liteflow.spi;
import java.util.List;
public interface PathContentParser extends SpiPriority{
public interface PathContentParser extends SpiPriority {
/**
* 解析路径下的文件内容
*
* @param pathList 文件路径支持 classpath 路径和 file 绝对路径spring 环境支持 PathMatchingResourcePatternResolver 规则
* @return 返回文件内容
* @throws Exception ex
*/
List<String> parseContent(List<String> pathList) throws Exception;
/**
* 获取文件路径的绝对路径
*
* @param pathList 文件路径支持 classpath 路径和 file 绝对路径spring 环境支持 PathMatchingResourcePatternResolver 规则
* @return 返回文件绝对路径
* @throws Exception ex
*/
List<String> getFileAbsolutePath(List<String> pathList) throws Exception;
}

View File

@ -2,7 +2,10 @@ package com.yomahub.liteflow.spi.local;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.io.resource.FileResource;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.exception.ConfigErrorException;
import com.yomahub.liteflow.spi.PathContentParser;
@ -18,14 +21,14 @@ public class LocalPathContentParser implements PathContentParser {
@Override
public List<String> parseContent(List<String> pathList) throws Exception {
if(CollectionUtil.isEmpty(pathList)){
if (CollectionUtil.isEmpty(pathList)) {
throw new ConfigErrorException("rule source must not be null");
}
List<String> contentList = new ArrayList<>();
for(String path : pathList){
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)){
for (String path : pathList) {
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
path = FILE_URL_PREFIX + path;
} else {
if (!path.startsWith(CLASSPATH_URL_PREFIX)) {
@ -33,7 +36,7 @@ public class LocalPathContentParser implements PathContentParser {
}
}
String content = ResourceUtil.readUtf8Str(path);
if (StrUtil.isNotBlank(content)){
if (StrUtil.isNotBlank(content)) {
contentList.add(content);
}
}
@ -41,6 +44,33 @@ public class LocalPathContentParser implements PathContentParser {
return contentList;
}
@Override
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
if (CollectionUtil.isEmpty(pathList)) {
throw new ConfigErrorException("rule source must not be null");
}
List<String> result = new ArrayList<>();
for (String path : pathList) {
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
path = FILE_URL_PREFIX + path;
result.add(new FileResource(path).getFile().getAbsolutePath());
} else {
if (!path.startsWith(CLASSPATH_URL_PREFIX)) {
path = CLASSPATH_URL_PREFIX + path;
// 这里会有自定义解析器
if(ClassLoaderUtil.isPresent(path)){
result.add(new ClassPathResource(path).getAbsolutePath());
}
}
}
}
return result;
}
@Override
public int priority() {
return 2;

View File

@ -26,8 +26,11 @@ public class LOGOPrinter {
str.append(" | |___ | | | | | |__|_____| _| | |__| |_| |\\ V V / \n");
str.append(" |_____|___| |_| |_____| |_| |_____\\___/ \\_/\\_/ \n\n");
str.append(" Version: " + VERSION_NO + "\n");
str.append(" 轻量且强大的规则引擎框架。\n");
str.append(" 新一代轻量且强大的规则引擎编排框架。\n");
str.append(" 基于开源社区文化,社区驱动型开源框架。\n");
str.append(" Small but powerful rules engine.\n");
str.append(" 官网地址https://liteflow.yomahub.com/\n");
str.append(" wechatbryan_31\n");
str.append("================================================================================================\n");
LOG.info(str.toString());
}

View File

@ -2,9 +2,11 @@ package com.yomahub.liteflow.parser.sql.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
@ -22,250 +24,250 @@ import java.util.Objects;
*/
public class JDBCHelper {
private static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?";
private static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?";
private static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} WHERE {}=?";
private static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} WHERE {}=?";
private static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?";
private static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?";
private static final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private static final String NODE_XML_PATTERN = "<nodes>{}</nodes>";
private static final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private static final String NODE_XML_PATTERN = "<nodes>{}</nodes>";
private static final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>";
private static final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>";
private static final Integer FETCH_SIZE_MAX = 1000;
private static final String NODE_ITEM_XML_PATTERN = "<node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node>";
private static final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}{}</flow>";
private static final Integer FETCH_SIZE_MAX = 1000;
private SQLParserVO sqlParserVO;
private SQLParserVO sqlParserVO;
private static JDBCHelper INSTANCE;
private static JDBCHelper INSTANCE;
/**
* 初始化 INSTANCE
*/
public static void init(SQLParserVO sqlParserVO) {
try {
INSTANCE = new JDBCHelper();
Class.forName(sqlParserVO.getDriverClassName());
INSTANCE.setSqlParserVO(sqlParserVO);
} catch (ClassNotFoundException e) {
throw new ELSQLException(e.getMessage());
}
}
/**
* 初始化 INSTANCE
*/
public static void init(SQLParserVO sqlParserVO) {
try {
INSTANCE = new JDBCHelper();
Class.forName(sqlParserVO.getDriverClassName());
INSTANCE.setSqlParserVO(sqlParserVO);
} catch (ClassNotFoundException e) {
throw new ELSQLException(e.getMessage());
}
}
/**
* 获取 INSTANCE
*/
public static JDBCHelper getInstance() {
return INSTANCE;
}
/**
* 获取 INSTANCE
*/
public static JDBCHelper getInstance() {
return INSTANCE;
}
/**
* 获取链接
*/
public Connection getConn() {
Connection connection;
try {
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
return connection;
}
/**
* 获取链接
*/
public Connection getConn() {
Connection connection;
try {
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
return connection;
}
/**
* 获取 ElData 数据内容
*/
public String getContent() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
/**
* 获取 ElData 数据内容
*/
public String getContent() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String chainTableName = sqlParserVO.getChainTableName();
String elDataField = sqlParserVO.getElDataField();
String chainNameField = sqlParserVO.getChainNameField();
String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
String applicationName = sqlParserVO.getApplicationName();
String chainTableName = sqlParserVO.getChainTableName();
String elDataField = sqlParserVO.getElDataField();
String chainNameField = sqlParserVO.getChainNameField();
String chainApplicationNameField = sqlParserVO.getChainApplicationNameField();
String applicationName = sqlParserVO.getApplicationName();
if(StrUtil.isBlank(chainTableName)){
throw new ELSQLException("You did not define the chainTableName property");
}
if (StrUtil.isBlank(chainTableName)) {
throw new ELSQLException("You did not define the chainTableName property");
}
if(StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)){
throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property");
}
if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)) {
throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property");
}
String sqlCmd = StrUtil.format(SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField);
String sqlCmd = StrUtil.format(SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField);
List<String> result = new ArrayList<>();
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
stmt.setString(1, applicationName);
rs = stmt.executeQuery();
List<String> result = new ArrayList<>();
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
stmt.setString(1, applicationName);
rs = stmt.executeQuery();
while (rs.next()) {
String elData = getStringFromResultSet(rs, elDataField);
String chainName = getStringFromResultSet(rs, chainNameField);
while (rs.next()) {
String elData = getStringFromResultSet(rs, elDataField);
String chainName = getStringFromResultSet(rs, chainNameField);
result.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, elData));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
result.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
String chainsContent = CollUtil.join(result, StrUtil.EMPTY);
String chainsContent = CollUtil.join(result, StrUtil.EMPTY);
String nodesContent;
if (hasScriptData()){
nodesContent = getScriptNodes();
}else{
nodesContent = StrUtil.EMPTY;
}
String nodesContent;
if (hasScriptData()) {
nodesContent = getScriptNodes();
} else {
nodesContent = StrUtil.EMPTY;
}
return StrUtil.format(XML_PATTERN, nodesContent, chainsContent);
}
return StrUtil.format(XML_PATTERN, nodesContent, chainsContent);
}
public String getScriptNodes() {
List<String> result = new ArrayList<>();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
public String getScriptNodes() {
List<String> result = new ArrayList<>();
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String scriptTableName = sqlParserVO.getScriptTableName();
String scriptIdField = sqlParserVO.getScriptIdField();
String scriptDataField = sqlParserVO.getScriptDataField();
String scriptNameField = sqlParserVO.getScriptNameField();
String scriptTypeField = sqlParserVO.getScriptTypeField();
String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
String applicationName = sqlParserVO.getApplicationName();
String scriptTableName = sqlParserVO.getScriptTableName();
String scriptIdField = sqlParserVO.getScriptIdField();
String scriptDataField = sqlParserVO.getScriptDataField();
String scriptNameField = sqlParserVO.getScriptNameField();
String scriptTypeField = sqlParserVO.getScriptTypeField();
String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField();
String applicationName = sqlParserVO.getApplicationName();
if(StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)){
throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property");
}
if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) {
throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property");
}
String sqlCmd = StrUtil.format(
SCRIPT_SQL_PATTERN,
scriptIdField,
scriptDataField,
scriptNameField,
scriptTypeField,
scriptTableName,
scriptApplicationNameField
);
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
stmt.setString(1, applicationName);
rs = stmt.executeQuery();
String sqlCmd = StrUtil.format(
SCRIPT_SQL_PATTERN,
scriptIdField,
scriptDataField,
scriptNameField,
scriptTypeField,
scriptTableName,
scriptApplicationNameField
);
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
stmt.setString(1, applicationName);
rs = stmt.executeQuery();
while (rs.next()) {
String id = getStringFromResultSet(rs, scriptIdField);
String data = getStringFromResultSet(rs, scriptDataField);
String name = getStringFromResultSet(rs, scriptNameField);
String type = getStringFromResultSet(rs, scriptTypeField);
while (rs.next()) {
String id = getStringFromResultSet(rs, scriptIdField);
String data = getStringFromResultSet(rs, scriptDataField);
String name = getStringFromResultSet(rs, scriptNameField);
String type = getStringFromResultSet(rs, scriptTypeField);
NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type);
if (Objects.isNull(nodeTypeEnum)){
throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type));
}
NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type);
if (Objects.isNull(nodeTypeEnum)) {
throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type));
}
if (!nodeTypeEnum.isScript()) {
throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type));
}
if (!nodeTypeEnum.isScript()) {
throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type));
}
result.add(StrUtil.format(NODE_ITEM_XML_PATTERN, id, name, type, data));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY));
}
result.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, data));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY));
}
/**
* 关闭连接
*
* @param conn conn
* @param stmt stmt
* @param rs rs
*/
private void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
// 关闭连接
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
// 关闭 statement
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
//关闭结果集
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
}
/**
* 关闭连接
*
* @param conn conn
* @param stmt stmt
* @param rs rs
*/
private void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
// 关闭连接
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
// 关闭 statement
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
//关闭结果集
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
}
private boolean hasScriptData(){
if (StrUtil.isBlank(sqlParserVO.getScriptTableName())){
return false;
}
private boolean hasScriptData() {
if (StrUtil.isBlank(sqlParserVO.getScriptTableName())) {
return false;
}
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String sqlCmd = StrUtil.format(SCRIPT_SQL_CHECK_PATTERN,
sqlParserVO.getScriptTableName(),
sqlParserVO.getScriptApplicationNameField());
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(1);
stmt.setString(1, sqlParserVO.getApplicationName());
rs = stmt.executeQuery();
return rs.next();
} catch (Exception e) {
return false;
} finally {
// 关闭连接
close(conn, stmt, rs);
}
}
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String sqlCmd = StrUtil.format(SCRIPT_SQL_CHECK_PATTERN,
sqlParserVO.getScriptTableName(),
sqlParserVO.getScriptApplicationNameField());
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(1);
stmt.setString(1, sqlParserVO.getApplicationName());
rs = stmt.executeQuery();
return rs.next();
} catch (Exception e) {
return false;
} finally {
// 关闭连接
close(conn, stmt, rs);
}
}
//#region get set method
private String getStringFromResultSet(ResultSet rs, String field) throws SQLException {
String data = rs.getString(field);
if (StrUtil.isBlank(data)) {
throw new ELSQLException(StrUtil.format("exist {} field value is empty", field));
}
return data;
}
//#region get set method
private String getStringFromResultSet(ResultSet rs, String field) throws SQLException {
String data = rs.getString(field);
if (StrUtil.isBlank(data)) {
throw new ELSQLException(StrUtil.format("exist {} field value is empty", field));
}
return data;
}
private SQLParserVO getSqlParserVO() {
return sqlParserVO;
}
private SQLParserVO getSqlParserVO() {
return sqlParserVO;
}
private void setSqlParserVO(SQLParserVO sqlParserVO) {
this.sqlParserVO = sqlParserVO;
}
private void setSqlParserVO(SQLParserVO sqlParserVO) {
this.sqlParserVO = sqlParserVO;
}
}

View File

@ -3,6 +3,9 @@ package com.yomahub.liteflow.script.graaljs;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.context.ContextBean;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.ScriptBeanManager;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.script.ScriptExecutor;
@ -57,7 +60,13 @@ public class GraalJavaScriptExecutor implements ScriptExecutor {
//比如你的自定义上下文为AbcContext那么key就为:abcContext
//这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法而不是参数所以脚本语言的绑定表里也是放多个上下文
DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
String key = StrUtil.lowerFirst(o.getClass().getSimpleName());
ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(),ContextBean.class);
String key;
if(contextBean !=null && contextBean.value().trim().length()>0){
key = contextBean.value();
}else{
key = StrUtil.lowerFirst(o.getClass().getSimpleName());
}
bindings.putMember(key, o);
});
@ -103,4 +112,9 @@ public class GraalJavaScriptExecutor implements ScriptExecutor {
public void cleanCache() {
scriptMap.clear();
}
@Override
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.JS;
}
}

View File

@ -1,5 +1,6 @@
package com.yomahub.liteflow.script.groovy;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
/**
@ -10,7 +11,7 @@ import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
public class GroovyScriptExecutor extends JSR223ScriptExecutor {
@Override
protected String scriptEngineName() {
return "groovy";
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.GROOVY;
}
}

View File

@ -1,6 +1,7 @@
package com.yomahub.liteflow.script.javascript;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
/**
* JavaScript脚本语言的执行器实现
@ -9,13 +10,13 @@ import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
*/
public class JavaScriptExecutor extends JSR223ScriptExecutor {
@Override
protected String scriptEngineName() {
return "javascript";
}
@Override
protected String convertScript(String script) {
return StrUtil.format("function process(){{}} process();",script);
}
@Override
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.JS;
}
}

View File

@ -2,6 +2,7 @@ package com.yomahub.liteflow.script.lua;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
import java.util.Arrays;
@ -15,8 +16,8 @@ import java.util.stream.Collectors;
*/
public class LuaScriptExecutor extends JSR223ScriptExecutor {
@Override
protected String scriptEngineName() {
return "luaj";
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.LUA;
}
@Override

View File

@ -2,6 +2,7 @@ package com.yomahub.liteflow.script.python;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
import java.util.Arrays;
import java.util.List;
@ -15,8 +16,8 @@ import java.util.stream.Collectors;
public class PythonScriptExecutor extends JSR223ScriptExecutor {
@Override
protected String scriptEngineName() {
return "python";
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.PYTHON;
}
@Override

View File

@ -8,6 +8,9 @@ import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressLoader;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.context.ContextBean;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.ScriptBeanManager;
import com.yomahub.liteflow.script.ScriptExecuteWrap;
import com.yomahub.liteflow.slot.DataBus;
@ -69,7 +72,13 @@ public class QLExpressScriptExecutor implements ScriptExecutor {
//比如你的自定义上下文为AbcContext那么key就为:abcContext
//这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法而不是参数所以脚本语言的绑定表里也是放多个上下文
DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
String key = StrUtil.lowerFirst(o.getClass().getSimpleName());
ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(),ContextBean.class);
String key;
if(contextBean !=null && contextBean.value().trim().length()>0){
key = contextBean.value();
}else{
key = StrUtil.lowerFirst(o.getClass().getSimpleName());
}
context.put(key, o);
});
@ -108,4 +117,9 @@ public class QLExpressScriptExecutor implements ScriptExecutor {
expressRunner.clearExpressCache();
ReflectUtil.setFieldValue(expressRunner,"loader",new ExpressLoader(expressRunner));
}
@Override
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.QLEXPRESS;
}
}

View File

@ -3,6 +3,7 @@ package com.yomahub.liteflow.spi.solon;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.exception.ConfigErrorException;
@ -10,17 +11,42 @@ import com.yomahub.liteflow.spi.PathContentParser;
import org.noear.solon.Utils;
import java.io.File;
import java.net.URI;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SolonPathContentParser implements PathContentParser {
@Override
public List<String> parseContent(List<String> pathList) throws Exception {
if(CollectionUtil.isEmpty(pathList)){
List<URL> allResource = getUrls(pathList);
//转换成内容List
List<String> contentList = new ArrayList<>();
for (URL resource : allResource) {
String content = IoUtil.read(resource.openStream(), CharsetUtil.CHARSET_UTF_8);
if (StrUtil.isNotBlank(content)) {
contentList.add(content);
}
}
return contentList;
}
@Override
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
List<URL> allResource = getUrls(pathList);
return StreamUtil.of(allResource)
.map(URL::getPath)
.filter(FileUtil::isFile)
.collect(Collectors.toList());
}
private static List<URL> getUrls(List<String> pathList) throws MalformedURLException {
if (CollectionUtil.isEmpty(pathList)) {
throw new ConfigErrorException("rule source must not be null");
}
@ -34,27 +60,19 @@ public class SolonPathContentParser implements PathContentParser {
path = path.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
}
allResource.add(Utils.getResource(path));
if (Utils.getResource(path) != null) {
allResource.add(Utils.getResource(path));
}
}
}
//如果有多个资源检查资源都是同一个类型如果出现不同类型的配置则抛出错误提示
Set<String> fileTypeSet = new HashSet<>();
allResource.forEach(resource -> fileTypeSet.add(FileUtil.extName(resource.getPath())));
if (fileTypeSet.size() != 1) {
if (fileTypeSet.size() > 1) {
throw new ConfigErrorException("config error,please use the same type of configuration");
}
//转换成内容List
List<String> contentList = new ArrayList<>();
for (URL resource : allResource) {
String content = IoUtil.read(resource.openStream(), CharsetUtil.CHARSET_UTF_8);
if (StrUtil.isNotBlank(content)){
contentList.add(content);
}
}
return contentList;
return allResource;
}
@Override

View File

@ -70,6 +70,17 @@ public class LiteflowProperty {
//替补组件的class路径
private String substituteCmpClass;
// 规则文件/脚本文件变更监听
private Boolean enableMonitorFile;
public Boolean getEnableMonitorFile() {
return enableMonitorFile;
}
public void setEnableMonitorFile(Boolean enableMonitorFile) {
this.enableMonitorFile = enableMonitorFile;
}
public boolean isEnable() {
return enable;
}

View File

@ -47,6 +47,7 @@ public class LiteflowPropertyAutoConfiguration {
liteflowConfig.setMainExecutorClass(property.getMainExecutorClass());
liteflowConfig.setPrintExecutionLog(property.isPrintExecutionLog());
liteflowConfig.setSubstituteCmpClass(property.getSubstituteCmpClass());
liteflowConfig.setEnableMonitorFile(property.getEnableMonitorFile());
return liteflowConfig;
}
}

View File

@ -158,6 +158,13 @@
"description": "Set period time to print monitor log.",
"sourceType": "com.yomahub.liteflow.springboot.LiteflowMonitorProperty",
"defaultValue": 300000
},
{
"name": "liteflow.enable-monitor-file",
"type": "java.lang.Boolean",
"description": "Set file change monitoring.",
"sourceType": "com.yomahub.liteflow.springboot.LiteflowMonitorProperty",
"defaultValue": false
}
]
}

View File

@ -18,3 +18,4 @@ liteflow.monitor.enable-log=false
liteflow.monitor.queue-limit=200
liteflow.monitor.delay=300000
liteflow.monitor.period=300000
liteflow.enable-monitor-file=false

View File

@ -0,0 +1,2 @@
com.yomahub.liteflow.springboot.config.LiteflowPropertyAutoConfiguration
com.yomahub.liteflow.springboot.config.LiteflowMainAutoConfiguration

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.stream.StreamUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
@ -13,15 +14,49 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.ResourceUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SpringPathContentParser implements PathContentParser {
@Override
public List<String> parseContent(List<String> pathList) throws Exception {
if(CollectionUtil.isEmpty(pathList)){
List<Resource> allResource = getResources(pathList);
//转换成内容List
List<String> contentList = new ArrayList<>();
for (Resource resource : allResource) {
String content = IoUtil.read(resource.getInputStream(), CharsetUtil.CHARSET_UTF_8);
if (StrUtil.isNotBlank(content)) {
contentList.add(content);
}
}
return contentList;
}
@Override
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
List<Resource> allResource = getResources(pathList);
return StreamUtil.of(allResource)
// 过滤非 file 类型 Resource
.filter(Resource::isFile)
.map(r -> {
try {
return r.getFile().getAbsolutePath();
} catch (IOException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
}
private List<Resource> getResources(List<String> pathList) throws IOException {
if (CollectionUtil.isEmpty(pathList)) {
throw new ConfigErrorException("rule source must not be null");
}
@ -30,12 +65,12 @@ public class SpringPathContentParser implements PathContentParser {
String locationPattern;
//如果path是绝对路径且这个文件存在时我们认为这是一个本地文件路径而并非classpath路径
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)){
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
locationPattern = ResourceUtils.FILE_URL_PREFIX + path;
} else {
if (!path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX) && !path.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) {
locationPattern = ResourceUtils.CLASSPATH_URL_PREFIX + path;
}else{
} else {
locationPattern = path;
}
}
@ -53,19 +88,10 @@ public class SpringPathContentParser implements PathContentParser {
if (fileTypeSet.size() > 1) {
throw new ConfigErrorException("config error,please use the same type of configuration");
}
//转换成内容List
List<String> contentList = new ArrayList<>();
for (Resource resource : allResource) {
String content = IoUtil.read(resource.getInputStream(), CharsetUtil.CHARSET_UTF_8);
if (StrUtil.isNotBlank(content)){
contentList.add(content);
}
}
return contentList;
return allResource;
}
@Override
public int priority() {
return 1;

View File

@ -0,0 +1,41 @@
package com.yomahub.liteflow.test.monitorFile;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.CharsetUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.io.File;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
@SpringBootTest(classes = MonitorFileELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.monitorFile.cmp"})
public class MonitorFileELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testMonitor() throws Exception{
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
String content = FileUtil.readUtf8String(absolutePath);
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
Thread.sleep(2500);
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,28 @@
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import java.util.Random;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void process(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,2 @@
liteflow.rule-source=monitorFile/flow.el.xml
liteflow.enable-monitor-file=true

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>

View File

@ -0,0 +1,40 @@
package com.yomahub.liteflow.test.monitorFile;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.CharsetUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.io.File;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
@SpringBootTest(classes = MonitorFileELDeclSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.monitorFile.cmp"})
public class MonitorFileELDeclSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testMonitor() throws Exception{
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
String content = FileUtil.readUtf8String(absolutePath);
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
Thread.sleep(2500);
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,24 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("a")
public class ACmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,25 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("b")
public class BCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,25 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("c")
public class CCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

View File

@ -46,7 +46,7 @@ public class RefreshRuleELDeclSpringbootTest extends BaseTest {
public void testRefresh2() throws Exception{
new Thread(() -> {
try {
Thread.sleep(3000L);
Thread.sleep(2000L);
String content = ResourceUtil.readUtf8Str("classpath: /refreshRule/flow_update.el.xml");
FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, content);
} catch (Exception e) {

View File

@ -0,0 +1,2 @@
liteflow.rule-source=monitorFile/flow.el.xml
liteflow.enable-monitor-file=true

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>

View File

@ -0,0 +1,40 @@
package com.yomahub.liteflow.test.monitorFile;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.CharsetUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.core.FlowExecutorHolder;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
public class LiteflowMonitorFileTest extends BaseTest {
private static FlowExecutor flowExecutor;
@BeforeClass
public static void init() {
LiteflowConfig config = new LiteflowConfig();
config.setRuleSource("monitorFile/flow.el.xml");
config.setEnableMonitorFile(true);
flowExecutor = FlowExecutorHolder.loadInstance(config);
}
@Test
public void testMonitor() throws InterruptedException {
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
String content = FileUtil.readUtf8String(absolutePath);
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
FileUtil.writeString(newContent, new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
Thread.sleep(2500);
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,18 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,19 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,19 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitorFile.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="a" class="com.yomahub.liteflow.test.multipleType.cmp.ACmp"/>
<node id="b" class="com.yomahub.liteflow.test.multipleType.cmp.BCmp"/>
<node id="c" class="com.yomahub.liteflow.test.multipleType.cmp.CCmp"/>
</nodes>
<chain name="chain1">
THEN(a, b, c);
</chain>
</flow>

View File

@ -0,0 +1,55 @@
package com.yomahub.liteflow.test.script.graaljs.contextbean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.script.graaljs.contextbean.bean.CheckContext;
import com.yomahub.liteflow.test.script.graaljs.contextbean.bean.Order2Context;
import com.yomahub.liteflow.test.script.graaljs.contextbean.bean.OrderContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/contextbean/application.properties")
@SpringBootTest(classes = LiteFlowScriptContextbeanGraaljsTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.graaljs.contextbean.cmp","com.yomahub.liteflow.test.script.graaljs.contextbean.bean"})
public class LiteFlowScriptContextbeanGraaljsTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testContextBean1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", OrderContext.class, CheckContext.class, Order2Context.class);
Assert.assertTrue(response.isSuccess());
OrderContext orderContext = response.getContextBean(OrderContext.class);
CheckContext checkContext = response.getContextBean(CheckContext.class);
Order2Context order2Context = response.getContextBean(Order2Context.class);
Assert.assertEquals("order1", orderContext.getOrderNo());
Assert.assertEquals("d", checkContext.getSign());
Assert.assertEquals("order2", order2Context.getOrderNo());
}
@Test
public void testContextBean2() throws Exception{
OrderContext orderContext = new OrderContext();
orderContext.setOrderNo("order1");
CheckContext checkContext = new CheckContext();
checkContext.setSign("sign1");
Order2Context orderContext2 = new Order2Context();
orderContext2.setOrderNo("order2");
LiteflowResponse response = flowExecutor.execute2Resp("chain2", null, orderContext, checkContext, orderContext2);
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,27 @@
package com.yomahub.liteflow.test.script.graaljs.contextbean.bean;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean
public class CheckContext {
private String sign;
private int randomId;
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public int getRandomId() {
return randomId;
}
public void setRandomId(int randomId) {
this.randomId = randomId;
}
}

View File

@ -0,0 +1,36 @@
package com.yomahub.liteflow.test.script.graaljs.contextbean.bean;
import java.util.Date;
public class Order2Context {
private String orderNo;
private int orderType;
private Date createTime;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public int getOrderType() {
return orderType;
}
public void setOrderType(int orderType) {
this.orderType = orderType;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,39 @@
package com.yomahub.liteflow.test.script.graaljs.contextbean.bean;
import com.yomahub.liteflow.context.ContextBean;
import java.util.Date;
@ContextBean("order")
public class OrderContext {
private String orderNo;
private int orderType;
private Date createTime;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public int getOrderType() {
return orderType;
}
public void setOrderType(int orderType) {
this.orderType = orderType;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.graaljs.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.graaljs.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.graaljs.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=contextbean/flow.xml

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="d" type="script" language="js">
<![CDATA[
order.setOrderNo("order1")
checkContext.setSign(_meta.get("nodeId"))
order2Context.setOrderNo("order2")
]]>
</node>
<node id="e" type="script" language="js">
<![CDATA[
var orderNo = order.getOrderNo()
var sign = checkContext.getSign()
var orderNo2 = order2Context.getOrderNo()
]]>
</node>
</nodes>
<chain name="chain1">
THEN(a,b,c,d);
</chain>
<chain name="chain2">
THEN(a,b,c,e);
</chain>
</flow>

View File

@ -0,0 +1,55 @@
package com.yomahub.liteflow.test.script.groovy.contextbean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.script.groovy.contextbean.bean.CheckContext;
import com.yomahub.liteflow.test.script.groovy.contextbean.bean.Order2Context;
import com.yomahub.liteflow.test.script.groovy.contextbean.bean.OrderContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/contextbean/application.properties")
@SpringBootTest(classes = LiteFlowScriptContextbeanGroovyELTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.groovy.contextbean.cmp","com.yomahub.liteflow.test.script.groovy.contextbean.bean"})
public class LiteFlowScriptContextbeanGroovyELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testContextBean1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", OrderContext.class, CheckContext.class, Order2Context.class);
Assert.assertTrue(response.isSuccess());
OrderContext orderContext = response.getContextBean(OrderContext.class);
CheckContext checkContext = response.getContextBean(CheckContext.class);
Order2Context order2Context = response.getContextBean(Order2Context.class);
Assert.assertEquals("order1", orderContext.getOrderNo());
Assert.assertEquals("sign1", checkContext.getSign());
Assert.assertEquals("order2", order2Context.getOrderNo());
}
@Test
public void testContextBean2() throws Exception{
OrderContext orderContext = new OrderContext();
orderContext.setOrderNo("order1");
CheckContext checkContext = new CheckContext();
checkContext.setSign("sign1");
Order2Context orderContext2 = new Order2Context();
orderContext2.setOrderNo("order2");
LiteflowResponse response = flowExecutor.execute2Resp("chain2", null, orderContext, checkContext, orderContext2);
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,27 @@
package com.yomahub.liteflow.test.script.groovy.contextbean.bean;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean
public class CheckContext {
private String sign;
private int randomId;
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public int getRandomId() {
return randomId;
}
public void setRandomId(int randomId) {
this.randomId = randomId;
}
}

View File

@ -0,0 +1,36 @@
package com.yomahub.liteflow.test.script.groovy.contextbean.bean;
import java.util.Date;
public class Order2Context {
private String orderNo;
private int orderType;
private Date createTime;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public int getOrderType() {
return orderType;
}
public void setOrderType(int orderType) {
this.orderType = orderType;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,39 @@
package com.yomahub.liteflow.test.script.groovy.contextbean.bean;
import com.yomahub.liteflow.context.ContextBean;
import java.util.Date;
@ContextBean("order")
public class OrderContext {
private String orderNo;
private int orderType;
private Date createTime;
public String getOrderNo() {
return orderNo;
}
public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
}
public int getOrderType() {
return orderType;
}
public void setOrderType(int orderType) {
this.orderType = orderType;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.groovy.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.groovy.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.groovy.contextbean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=contextbean/flow.xml

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="d" type="script" language="groovy">
<![CDATA[
order.setOrderNo("order1")
checkContext.setSign("sign1")
order2Context.setOrderNo("order2")
]]>
</node>
<node id="e" type="script" language="groovy">
<![CDATA[
def orderNo = order.getOrderNo()
println orderNo
def sign = checkContext.getSign()
println sign
def orderNo2 = order2Context.getOrderNo()
println orderNo2
]]>
</node>
</nodes>
<chain name="chain1">
THEN(a,b,c,d);
</chain>
<chain name="chain2">
THEN(a,b,c,e);
</chain>
</flow>

View File

@ -0,0 +1,55 @@
package com.yomahub.liteflow.test.script.javascript.contextbean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.script.javascript.contextbean.bean.CheckContext;
import com.yomahub.liteflow.test.script.javascript.contextbean.bean.Order2Context;
import com.yomahub.liteflow.test.script.javascript.contextbean.bean.OrderContext;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/contextbean/application.properties")
@SpringBootTest(classes = LiteFlowScriptContextbeanJavaScriptTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.javascript.contextbean.cmp","com.yomahub.liteflow.test.script.javascript.contextbean.bean"})
public class LiteFlowScriptContextbeanJavaScriptTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testContextBean1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", OrderContext.class, CheckContext.class, Order2Context.class);
Assert.assertTrue(response.isSuccess());
OrderContext orderContext = response.getContextBean(OrderContext.class);
CheckContext checkContext = response.getContextBean(CheckContext.class);
Order2Context order2Context = response.getContextBean(Order2Context.class);
Assert.assertEquals("order1", orderContext.getOrderNo());
Assert.assertEquals("sign1", checkContext.getSign());
Assert.assertEquals("order2", order2Context.getOrderNo());
}
@Test
public void testContextBean2() throws Exception{
OrderContext orderContext = new OrderContext();
orderContext.setOrderNo("order1");
CheckContext checkContext = new CheckContext();
checkContext.setSign("sign1");
Order2Context orderContext2 = new Order2Context();
orderContext2.setOrderNo("order2");
LiteflowResponse response = flowExecutor.execute2Resp("chain2", null, orderContext, checkContext, orderContext2);
Assert.assertTrue(response.isSuccess());
}
}

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