Merge branch 'dev' of https://gitee.com/dromara/liteFlow
# Conflicts: # liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java
This commit is contained in:
commit
723ea21db0
|
@ -16,170 +16,171 @@ import java.util.function.Predicate;
|
|||
|
||||
/**
|
||||
* 节点类型枚举
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.0
|
||||
*/
|
||||
public enum NodeTypeEnum {
|
||||
|
||||
COMMON("common","普通", false, NodeComponent.class),
|
||||
COMMON("common", "普通", false, NodeComponent.class),
|
||||
|
||||
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
|
||||
SWITCH("switch", "选择", false, NodeSwitchComponent.class),
|
||||
|
||||
IF("if", "条件", false, NodeIfComponent.class),
|
||||
IF("if", "条件", false, NodeIfComponent.class),
|
||||
|
||||
FOR("for","循环次数", false, NodeForComponent.class),
|
||||
FOR("for", "循环次数", false, NodeForComponent.class),
|
||||
|
||||
WHILE("while", "循环条件", false, NodeWhileComponent.class),
|
||||
WHILE("while", "循环条件", false, NodeWhileComponent.class),
|
||||
|
||||
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
|
||||
SCRIPT("script","脚本", true, ScriptCommonComponent.class),
|
||||
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
|
||||
|
||||
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
|
||||
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
|
||||
|
||||
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
|
||||
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
|
||||
|
||||
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
|
||||
IF_SCRIPT("if_script", "条件脚本", true, ScriptIfComponent.class),
|
||||
|
||||
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
|
||||
FOR_SCRIPT("for_script", "循环次数脚本", true, ScriptForComponent.class),
|
||||
|
||||
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class)
|
||||
;
|
||||
WHILE_SCRIPT("while_script", "循环条件脚本", true, ScriptWhileComponent.class),
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
|
||||
BREAK_SCRIPT("break_script", "循环跳出脚本", true, ScriptBreakComponent.class);
|
||||
|
||||
private String code;
|
||||
private String name;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NodeTypeEnum.class);
|
||||
|
||||
private boolean isScript;
|
||||
private String code;
|
||||
private String name;
|
||||
|
||||
private Class<? extends NodeComponent> mappingClazz;
|
||||
private boolean isScript;
|
||||
|
||||
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.isScript = isScript;
|
||||
this.mappingClazz = mappingClazz;
|
||||
}
|
||||
private Class<? extends NodeComponent> mappingClazz;
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
NodeTypeEnum(String code, String name, boolean isScript, Class<? extends NodeComponent> mappingClazz) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
this.isScript = isScript;
|
||||
this.mappingClazz = mappingClazz;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isScript() {
|
||||
return isScript;
|
||||
}
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setScript(boolean script) {
|
||||
isScript = script;
|
||||
}
|
||||
public boolean isScript() {
|
||||
return isScript;
|
||||
}
|
||||
|
||||
public Class<? extends NodeComponent> getMappingClazz() {
|
||||
return mappingClazz;
|
||||
}
|
||||
public void setScript(boolean script) {
|
||||
isScript = script;
|
||||
}
|
||||
|
||||
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
|
||||
this.mappingClazz = mappingClazz;
|
||||
}
|
||||
public Class<? extends NodeComponent> getMappingClazz() {
|
||||
return mappingClazz;
|
||||
}
|
||||
|
||||
public static NodeTypeEnum getEnumByCode(String code) {
|
||||
for (NodeTypeEnum e : NodeTypeEnum.values()) {
|
||||
if (e.getCode().equals(code)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public void setMappingClazz(Class<? extends NodeComponent> mappingClazz) {
|
||||
this.mappingClazz = mappingClazz;
|
||||
}
|
||||
|
||||
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz){
|
||||
Class<?> superClazz = clazz;
|
||||
while(true){
|
||||
superClazz = superClazz.getSuperclass();
|
||||
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")){
|
||||
break;
|
||||
}
|
||||
if(superClazz.equals(Object.class)){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static NodeTypeEnum getEnumByCode(String code) {
|
||||
for (NodeTypeEnum e : NodeTypeEnum.values()) {
|
||||
if (e.getCode().equals(code)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
for (NodeTypeEnum e : NodeTypeEnum.values()) {
|
||||
if (e.getMappingClazz().equals(superClazz)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static NodeTypeEnum guessTypeBySuperClazz(Class<?> clazz) {
|
||||
Class<?> superClazz = clazz;
|
||||
while (true) {
|
||||
superClazz = superClazz.getSuperclass();
|
||||
if (superClazz.getPackage().getName().startsWith("com.yomahub.liteflow.core")) {
|
||||
break;
|
||||
}
|
||||
if (superClazz.equals(Object.class)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static NodeTypeEnum guessType(Class<?> clazz){
|
||||
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
|
||||
if (nodeType == null){
|
||||
//尝试从类声明处进行推断
|
||||
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
|
||||
if (liteflowCmpDefine != null){
|
||||
//类声明方式中@LiteflowMethod是无需设置nodeId的
|
||||
//但是如果设置了,那么核心逻辑其实是取类上定义的id的
|
||||
//这种可以运行,但是理解起来不大好理解,所以给出提示,建议不要这么做
|
||||
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
|
||||
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
|
||||
if (liteflowMethod != null){
|
||||
return StrUtil.isNotBlank(liteflowMethod.nodeId());
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
for (NodeTypeEnum e : NodeTypeEnum.values()) {
|
||||
if (e.getMappingClazz().equals(superClazz)) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mixDefined){
|
||||
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
|
||||
clazz.getName());
|
||||
}
|
||||
public static NodeTypeEnum guessType(Class<?> clazz) {
|
||||
NodeTypeEnum nodeType = guessTypeBySuperClazz(clazz);
|
||||
if (nodeType == null) {
|
||||
//尝试从类声明处进行推断
|
||||
LiteflowCmpDefine liteflowCmpDefine = clazz.getAnnotation(LiteflowCmpDefine.class);
|
||||
if (liteflowCmpDefine != null) {
|
||||
//类声明方式中@LiteflowMethod是无需设置nodeId的
|
||||
//但是如果设置了,那么核心逻辑其实是取类上定义的id的
|
||||
//这种可以运行,但是理解起来不大好理解,所以给出提示,建议不要这么做
|
||||
boolean mixDefined = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
|
||||
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
|
||||
if (liteflowMethod != null) {
|
||||
return StrUtil.isNotBlank(liteflowMethod.nodeId());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (mixDefined) {
|
||||
LOG.warn("[[[WARNING!!!]]]The @liteflowMethod in the class[{}] defined by @liteflowCmpDefine should not configure the nodeId again!",
|
||||
clazz.getName());
|
||||
}
|
||||
|
||||
|
||||
//在返回之前,还要对方法级别的@LiteflowMethod进行检查,如果存在方法上的类型与类上的不一致时,给予警告信息
|
||||
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
|
||||
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
|
||||
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
|
||||
if (liteflowMethod != null){
|
||||
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())){
|
||||
differenceTypeMethod.set(method);
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
});
|
||||
//在返回之前,还要对方法级别的@LiteflowMethod进行检查,如果存在方法上的类型与类上的不一致时,给予警告信息
|
||||
AtomicReference<Method> differenceTypeMethod = new AtomicReference<>();
|
||||
boolean hasDifferenceNodeType = Arrays.stream(clazz.getDeclaredMethods()).anyMatch(method -> {
|
||||
LiteflowMethod liteflowMethod = AnnotationUtil.getAnnotation(method, LiteflowMethod.class);
|
||||
if (liteflowMethod != null) {
|
||||
if (!liteflowMethod.nodeType().equals(liteflowCmpDefine.value())) {
|
||||
differenceTypeMethod.set(method);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
//表示存在不一样的类型
|
||||
if (hasDifferenceNodeType){
|
||||
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
|
||||
clazz.getName(), differenceTypeMethod.get().getName());
|
||||
}
|
||||
//表示存在不一样的类型
|
||||
if (hasDifferenceNodeType) {
|
||||
LOG.warn("[[[WARNING!!!]]]The nodeType in @liteflowCmpDefine declared on the class[{}] does not match the nodeType in @liteflowMethod declared on the method[{}]!",
|
||||
clazz.getName(), differenceTypeMethod.get().getName());
|
||||
}
|
||||
|
||||
return liteflowCmpDefine.value();
|
||||
}
|
||||
return liteflowCmpDefine.value();
|
||||
}
|
||||
|
||||
//再尝试声明式组件这部分的推断
|
||||
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
|
||||
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
|
||||
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
|
||||
//再尝试声明式组件这部分的推断
|
||||
LiteflowMethod liteflowMethod = Arrays.stream(clazz.getDeclaredMethods()).map(
|
||||
method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class)
|
||||
).filter(Objects::nonNull).filter(lfMethod -> lfMethod.value().isMainMethod()).findFirst().orElse(null);
|
||||
|
||||
if (liteflowMethod != null) {
|
||||
nodeType = liteflowMethod.nodeType();
|
||||
}
|
||||
}
|
||||
return nodeType;
|
||||
}
|
||||
if (liteflowMethod != null) {
|
||||
nodeType = liteflowMethod.nodeType();
|
||||
}
|
||||
}
|
||||
return nodeType;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ public abstract class BaseJsonFlowParser implements FlowParser {
|
|||
JsonNode flowJsonNode = JsonUtil.parseObject(content);
|
||||
jsonObjectList.add(flowJsonNode);
|
||||
}
|
||||
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
|
||||
ParserHelper.parseNodeJson(jsonObjectList);
|
||||
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, this::parseOneChain);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -36,8 +36,8 @@ public abstract class BaseXmlFlowParser implements FlowParser {
|
|||
documentList.add(document);
|
||||
}
|
||||
|
||||
Consumer<Element> parseOneChainConsumer = this::parseOneChain;
|
||||
ParserHelper.parseDocument(documentList, CHAIN_NAME_SET, parseOneChainConsumer);
|
||||
ParserHelper.parseNodeDocument(documentList);
|
||||
ParserHelper.parseChainDocument(documentList, CHAIN_NAME_SET, this::parseOneChain);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,7 +39,8 @@ public abstract class BaseYmlFlowParser implements FlowParser {
|
|||
}
|
||||
|
||||
Consumer<JsonNode> parseOneChainConsumer = this::parseOneChain;
|
||||
ParserHelper.parseJsonNode(jsonObjectList, CHAIN_NAME_SET,parseOneChainConsumer);
|
||||
ParserHelper.parseNodeJson(jsonObjectList);
|
||||
ParserHelper.parseChainJson(jsonObjectList, CHAIN_NAME_SET, parseOneChainConsumer);
|
||||
}
|
||||
|
||||
protected JsonNode convertToJson(String yamlString) {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package com.yomahub.liteflow.parser.helper;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.yomahub.liteflow.annotation.*;
|
||||
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
|
||||
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||
import com.yomahub.liteflow.builder.prop.NodePropBean;
|
||||
|
@ -87,35 +85,13 @@ public class ParserHelper {
|
|||
|
||||
/**
|
||||
* xml 形式的主要解析过程
|
||||
*
|
||||
* @param documentList documentList
|
||||
* @param chainNameSet 用于去重
|
||||
* @param parseOneChainConsumer parseOneChain 函数
|
||||
*/
|
||||
public static void parseDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer) {
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
documentList.forEach(document -> {
|
||||
// 解析chain节点
|
||||
List<Element> chainList = document.getRootElement().elements(CHAIN);
|
||||
|
||||
//先在元数据里放上chain
|
||||
chainList.forEach(e -> {
|
||||
//校验加载的 chainName 是否有重复的
|
||||
//TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
|
||||
if (!chainNameSet.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(chainName);
|
||||
});
|
||||
});
|
||||
// 清空
|
||||
chainNameSet.clear();
|
||||
|
||||
/**
|
||||
* xml 形式的主要解析过程
|
||||
* @param documentList documentList
|
||||
*/
|
||||
public static void parseNodeDocument(List<Document> documentList) {
|
||||
for (Document document : documentList) {
|
||||
Element rootElement = document.getRootElement();
|
||||
Element nodesElement = rootElement.element(NODES);
|
||||
|
@ -143,38 +119,67 @@ public class ParserHelper {
|
|||
ParserHelper.buildNode(nodePropBean);
|
||||
}
|
||||
}
|
||||
|
||||
//解析每一个chain
|
||||
List<Element> chainList = rootElement.elements(CHAIN);
|
||||
chainList.forEach(parseOneChainConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void parseJsonNode(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer) {
|
||||
|
||||
public static void parseDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer) {
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
flowJsonObjectList.forEach(jsonObject -> {
|
||||
documentList.forEach(document -> {
|
||||
// 解析chain节点
|
||||
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
|
||||
List<Element> chainList = document.getRootElement().elements(CHAIN);
|
||||
|
||||
//先在元数据里放上chain
|
||||
while (iterator.hasNext()) {
|
||||
JsonNode innerJsonObject = iterator.next();
|
||||
chainList.forEach(e -> {
|
||||
//校验加载的 chainName 是否有重复的
|
||||
// TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
//String chainName = innerJsonObject.get(NAME).textValue();
|
||||
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
|
||||
//TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
|
||||
if (!chainNameSet.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(chainName);
|
||||
}
|
||||
});
|
||||
});
|
||||
// 清空
|
||||
chainNameSet.clear();
|
||||
}
|
||||
|
||||
public static void parseChainDocument(List<Document> documentList, Set<String> chainNameSet, Consumer<Element> parseOneChainConsumer){
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
documentList.forEach(document -> {
|
||||
// 解析chain节点
|
||||
List<Element> chainList = document.getRootElement().elements(CHAIN);
|
||||
|
||||
//先在元数据里放上chain
|
||||
chainList.forEach(e -> {
|
||||
//校验加载的 chainName 是否有重复的
|
||||
//TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
|
||||
if (!chainNameSet.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(chainName);
|
||||
});
|
||||
});
|
||||
// 清空
|
||||
chainNameSet.clear();
|
||||
|
||||
//解析每一个chain
|
||||
for (Document document : documentList) {
|
||||
Element rootElement = document.getRootElement();
|
||||
List<Element> chainList = rootElement.elements(CHAIN);
|
||||
chainList.forEach(parseOneChainConsumer);
|
||||
}
|
||||
}
|
||||
|
||||
public static void parseNodeJson(List<JsonNode> flowJsonObjectList) {
|
||||
for (JsonNode flowJsonNode : flowJsonObjectList) {
|
||||
// 当存在<nodes>节点定义时,解析node节点
|
||||
if (flowJsonNode.get(FLOW).has(NODES)) {
|
||||
|
@ -201,7 +206,61 @@ public class ParserHelper {
|
|||
ParserHelper.buildNode(nodePropBean);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void parseJsonNode(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer) {
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
flowJsonObjectList.forEach(jsonObject -> {
|
||||
// 解析chain节点
|
||||
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
|
||||
//先在元数据里放上chain
|
||||
while (iterator.hasNext()) {
|
||||
JsonNode innerJsonObject = iterator.next();
|
||||
//校验加载的 chainName 是否有重复的
|
||||
// TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
//String chainName = innerJsonObject.get(NAME).textValue();
|
||||
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
|
||||
if (!chainNameSet.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(chainName);
|
||||
}
|
||||
});
|
||||
// 清空
|
||||
chainNameSet.clear();
|
||||
|
||||
}
|
||||
|
||||
public static void parseChainJson(List<JsonNode> flowJsonObjectList, Set<String> chainNameSet, Consumer<JsonNode> parseOneChainConsumer){
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
flowJsonObjectList.forEach(jsonObject -> {
|
||||
// 解析chain节点
|
||||
Iterator<JsonNode> iterator = jsonObject.get(FLOW).get(CHAIN).elements();
|
||||
//先在元数据里放上chain
|
||||
while (iterator.hasNext()) {
|
||||
JsonNode innerJsonObject = iterator.next();
|
||||
//校验加载的 chainName 是否有重复的
|
||||
// TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
String chainName = Optional.ofNullable(innerJsonObject.get(ID)).orElse(innerJsonObject.get(NAME)).textValue();
|
||||
if (!chainNameSet.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(chainName);
|
||||
}
|
||||
});
|
||||
// 清空
|
||||
chainNameSet.clear();
|
||||
|
||||
for (JsonNode flowJsonNode : flowJsonObjectList) {
|
||||
//解析每一个chain
|
||||
Iterator<JsonNode> chainIterator = flowJsonNode.get(FLOW).get(CHAIN).elements();
|
||||
while (chainIterator.hasNext()) {
|
||||
|
@ -218,9 +277,9 @@ public class ParserHelper {
|
|||
*/
|
||||
public static void parseOneChainEl(JsonNode chainNode) {
|
||||
//构建chainBuilder
|
||||
String chainName = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
|
||||
String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
|
||||
String el = chainNode.get(VALUE).textValue();
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
|
||||
chainELBuilder.setEL(el).build();
|
||||
}
|
||||
|
||||
|
@ -231,10 +290,10 @@ public class ParserHelper {
|
|||
*/
|
||||
public static void parseOneChainEl(Element e) {
|
||||
//构建chainBuilder
|
||||
String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
|
||||
String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
|
||||
String text = e.getText();
|
||||
String el = RegexUtil.removeComments(text);
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId);
|
||||
chainELBuilder.setEL(el).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -12,4 +12,4 @@
|
|||
language (groovy|js) #IMPLIED
|
||||
>
|
||||
<!ATTLIST chain
|
||||
name CDATA #REQUIRED>
|
||||
name CDATA>
|
|
@ -30,7 +30,9 @@ public class EtcdParserHelper {
|
|||
this.etcdParserVO = etcdParserVO;
|
||||
|
||||
try{
|
||||
this.etcdClient = ContextAwareHolder.loadContextAware().getBean(EtcdClient.class);
|
||||
try{
|
||||
this.etcdClient = ContextAwareHolder.loadContextAware().getBean(EtcdClient.class);
|
||||
}catch (Exception ignored){}
|
||||
if (this.etcdClient == null) {
|
||||
Client client = Client.builder()
|
||||
.endpoints(etcdParserVO.getConnectStr().split(","))
|
||||
|
|
|
@ -32,7 +32,9 @@ public class NacosParserHelper {
|
|||
public NacosParserHelper(NacosParserVO nacosParserVO) {
|
||||
this.nacosParserVO = nacosParserVO;
|
||||
try{
|
||||
this.configService = ContextAwareHolder.loadContextAware().getBean(NacosConfigService.class);
|
||||
try{
|
||||
this.configService = ContextAwareHolder.loadContextAware().getBean(NacosConfigService.class);
|
||||
}catch (Exception ignored){}
|
||||
if (this.configService == null){
|
||||
Properties properties = new Properties();
|
||||
properties.put(PropertyKeyConst.SERVER_ADDR, nacosParserVO.getServerAddr());
|
||||
|
|
|
@ -13,7 +13,6 @@ import com.yomahub.liteflow.property.LiteflowConfig;
|
|||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
import com.yomahub.liteflow.util.JsonUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
|
@ -35,9 +34,9 @@ public class SQLXmlELParser extends ClassXmlFlowELParser {
|
|||
|
||||
try {
|
||||
SQLParserVO sqlParserVO = null;
|
||||
if(MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))){
|
||||
if (MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))) {
|
||||
sqlParserVO = BeanUtil.toBean(liteflowConfig.getRuleSourceExtDataMap(), SQLParserVO.class, CopyOptions.create());
|
||||
}else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())){
|
||||
} else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) {
|
||||
sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
|
||||
}
|
||||
if (Objects.isNull(sqlParserVO)) {
|
||||
|
@ -49,7 +48,6 @@ public class SQLXmlELParser extends ClassXmlFlowELParser {
|
|||
|
||||
// 初始化 JDBCHelper
|
||||
JDBCHelper.init(sqlParserVO);
|
||||
|
||||
} catch (ELSQLException elsqlException) {
|
||||
throw elsqlException;
|
||||
} catch (Exception ex) {
|
||||
|
@ -60,10 +58,9 @@ public class SQLXmlELParser extends ClassXmlFlowELParser {
|
|||
|
||||
@Override
|
||||
public String parseCustom() {
|
||||
return JDBCHelper.getInstance().getElDataContent();
|
||||
return JDBCHelper.getInstance().getContent();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查配置文件并设置默认值
|
||||
*
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package com.yomahub.liteflow.parser.sql.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.StrFormatter;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
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;
|
||||
|
@ -13,6 +12,7 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* jdbc 工具类
|
||||
|
@ -22,10 +22,15 @@ import java.util.List;
|
|||
*/
|
||||
public class JDBCHelper {
|
||||
|
||||
private static final String SQL_PATTERN = "SELECT {},{} FROM {} ";
|
||||
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_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?";
|
||||
|
||||
private static final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
|
||||
private static final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}</flow>";
|
||||
private static final String NODE_XML_PATTERN = "<nodes><node id=\"{}\" name=\"{}\" type=\"{}\"><![CDATA[{}]]></node></nodes>";
|
||||
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;
|
||||
|
@ -56,7 +61,7 @@ public class JDBCHelper {
|
|||
* 获取链接
|
||||
*/
|
||||
public Connection getConn() {
|
||||
Connection connection = null;
|
||||
Connection connection;
|
||||
try {
|
||||
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
|
||||
} catch (SQLException e) {
|
||||
|
@ -68,15 +73,26 @@ public class JDBCHelper {
|
|||
/**
|
||||
* 获取 ElData 数据内容
|
||||
*/
|
||||
public String getElDataContent() {
|
||||
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 tableName = sqlParserVO.getTableName();
|
||||
String sqlCmd = StrFormatter.format(SQL_PATTERN, chainNameField, elDataField, tableName);
|
||||
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(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);
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
|
@ -84,19 +100,14 @@ public class JDBCHelper {
|
|||
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 = rs.getString(elDataField);
|
||||
if (StrUtil.isBlank(elData)) {
|
||||
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
|
||||
}
|
||||
String chainName = rs.getString(chainNameField);
|
||||
if (StrUtil.isBlank(elData)) {
|
||||
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
|
||||
}
|
||||
String elData = getStringFromResultSet(rs, elDataField);
|
||||
String chainName = getStringFromResultSet(rs, chainNameField);
|
||||
|
||||
result.add(StrFormatter.format(CHAIN_XML_PATTERN, chainName, elData));
|
||||
result.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, elData));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ELSQLException(e.getMessage());
|
||||
|
@ -105,8 +116,77 @@ public class JDBCHelper {
|
|||
close(conn, stmt, rs);
|
||||
}
|
||||
|
||||
String chains = CollUtil.join(result, StrUtil.CRLF);
|
||||
return StrFormatter.format(XML_PATTERN, chains);
|
||||
String chainsContent = CollUtil.join(result, StrUtil.EMPTY);
|
||||
|
||||
String nodesContent;
|
||||
if (hasScriptData()){
|
||||
nodesContent = getScriptNodes();
|
||||
}else{
|
||||
nodesContent = StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
return StrUtil.format(XML_PATTERN, nodesContent, chainsContent);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (!nodeTypeEnum.isScript()) {
|
||||
throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type));
|
||||
}
|
||||
|
||||
result.add(StrUtil.format(NODE_XML_PATTERN, id, name, type, data));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ELSQLException(e.getMessage());
|
||||
} finally {
|
||||
// 关闭连接
|
||||
close(conn, stmt, rs);
|
||||
}
|
||||
return CollUtil.join(result, StrUtil.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,8 +223,42 @@ public class JDBCHelper {
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//#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;
|
||||
}
|
||||
|
@ -152,5 +266,4 @@ public class JDBCHelper {
|
|||
private void setSqlParserVO(SQLParserVO sqlParserVO) {
|
||||
this.sqlParserVO = sqlParserVO;
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
|
|
@ -29,9 +29,19 @@ public class SQLParserVO {
|
|||
private String password;
|
||||
|
||||
/**
|
||||
* 表名
|
||||
* 应用名
|
||||
*/
|
||||
private String tableName = "el_table";
|
||||
private String applicationName;
|
||||
|
||||
/**
|
||||
* chain表名
|
||||
*/
|
||||
private String chainTableName;
|
||||
|
||||
/**
|
||||
* chain表里的应用名字段
|
||||
*/
|
||||
private String chainApplicationNameField = "application_name";
|
||||
|
||||
/**
|
||||
* chainName
|
||||
|
@ -43,6 +53,36 @@ public class SQLParserVO {
|
|||
*/
|
||||
private String elDataField = "el_data";
|
||||
|
||||
/**
|
||||
* 脚本 node 表名
|
||||
*/
|
||||
private String scriptTableName;
|
||||
|
||||
/**
|
||||
* script表里的应用名字段
|
||||
*/
|
||||
private String scriptApplicationNameField = "application_name";
|
||||
|
||||
/**
|
||||
* 脚本 node id 字段
|
||||
*/
|
||||
private String scriptIdField = "script_id";
|
||||
|
||||
/**
|
||||
* 脚本 node name 字段
|
||||
*/
|
||||
private String scriptNameField = "script_name";
|
||||
|
||||
/**
|
||||
* 脚本 node data 字段
|
||||
*/
|
||||
private String scriptDataField = "script_data";
|
||||
|
||||
/**
|
||||
* 脚本 node type 字段
|
||||
*/
|
||||
private String scriptTypeField = "script_type";
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
@ -75,12 +115,28 @@ public class SQLParserVO {
|
|||
this.password = password;
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
public String getApplicationName() {
|
||||
return applicationName;
|
||||
}
|
||||
|
||||
public void setTableName(String tableName) {
|
||||
this.tableName = tableName;
|
||||
public void setApplicationName(String applicationName) {
|
||||
this.applicationName = applicationName;
|
||||
}
|
||||
|
||||
public String getChainTableName() {
|
||||
return chainTableName;
|
||||
}
|
||||
|
||||
public void setChainTableName(String chainTableName) {
|
||||
this.chainTableName = chainTableName;
|
||||
}
|
||||
|
||||
public String getChainApplicationNameField() {
|
||||
return chainApplicationNameField;
|
||||
}
|
||||
|
||||
public void setChainApplicationNameField(String chainApplicationNameField) {
|
||||
this.chainApplicationNameField = chainApplicationNameField;
|
||||
}
|
||||
|
||||
public String getChainNameField() {
|
||||
|
@ -98,4 +154,52 @@ public class SQLParserVO {
|
|||
public void setElDataField(String elDataField) {
|
||||
this.elDataField = elDataField;
|
||||
}
|
||||
|
||||
public String getScriptTableName() {
|
||||
return scriptTableName;
|
||||
}
|
||||
|
||||
public void setScriptTableName(String scriptTableName) {
|
||||
this.scriptTableName = scriptTableName;
|
||||
}
|
||||
|
||||
public String getScriptApplicationNameField() {
|
||||
return scriptApplicationNameField;
|
||||
}
|
||||
|
||||
public void setScriptApplicationNameField(String scriptApplicationNameField) {
|
||||
this.scriptApplicationNameField = scriptApplicationNameField;
|
||||
}
|
||||
|
||||
public String getScriptIdField() {
|
||||
return scriptIdField;
|
||||
}
|
||||
|
||||
public void setScriptIdField(String scriptIdField) {
|
||||
this.scriptIdField = scriptIdField;
|
||||
}
|
||||
|
||||
public String getScriptNameField() {
|
||||
return scriptNameField;
|
||||
}
|
||||
|
||||
public void setScriptNameField(String scriptNameField) {
|
||||
this.scriptNameField = scriptNameField;
|
||||
}
|
||||
|
||||
public String getScriptDataField() {
|
||||
return scriptDataField;
|
||||
}
|
||||
|
||||
public void setScriptDataField(String scriptDataField) {
|
||||
this.scriptDataField = scriptDataField;
|
||||
}
|
||||
|
||||
public String getScriptTypeField() {
|
||||
return scriptTypeField;
|
||||
}
|
||||
|
||||
public void setScriptTypeField(String scriptTypeField) {
|
||||
this.scriptTypeField = scriptTypeField;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package com.yomahub.liteflow.test.script.groovy.scriptOrder;
|
||||
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.enums.FlowParserTypeEnum;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* 测试springboot下的groovy脚本组件,基于xml配置
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.0
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/scriptOrder/application.properties")
|
||||
@SpringBootTest(classes = LiteflowScriptOrderGroovyELTest.class)
|
||||
@EnableAutoConfiguration
|
||||
public class LiteflowScriptOrderGroovyELTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//测试普通脚本节点
|
||||
@Test
|
||||
public void testScript1() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
liteflow.rule-source=scriptOrder/flow1.xml,scriptOrder/flow2.xml
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<nodes>
|
||||
<node id="s1" language="groovy" type="if_script">
|
||||
<![CDATA[
|
||||
System.out.println("Groovy 脚本1 被调用。")
|
||||
return false
|
||||
]]>
|
||||
</node>
|
||||
<node id="s2" language="groovy" type="script">
|
||||
<![CDATA[
|
||||
System.out.println("Groovy 脚本2 被调用。")
|
||||
]]>
|
||||
</node>
|
||||
<node id="s3" language="groovy" type="script">
|
||||
<![CDATA[
|
||||
System.out.println("Groovy 脚本3 被调用。")
|
||||
]]>
|
||||
</node>
|
||||
</nodes>
|
||||
|
||||
<chain name="chain1">
|
||||
// IF(s1, s2, s3);
|
||||
IF(s1, s2, s4);
|
||||
</chain>
|
||||
</flow>
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<nodes>
|
||||
<node id="s4" language="groovy" type="script">
|
||||
<![CDATA[
|
||||
System.out.println("Groovy 脚本4 被调用。")
|
||||
]]>
|
||||
</node>
|
||||
</nodes>
|
||||
</flow>
|
|
@ -27,11 +27,11 @@
|
|||
|
||||
<node id="s2" name="选择脚本" type="switch_script" language="groovy">
|
||||
<![CDATA[
|
||||
count = defaultContext.getData("count");
|
||||
count = defaultContext.getData("count")
|
||||
if(count > 100){
|
||||
return "a";
|
||||
return "a"
|
||||
}else{
|
||||
return "b";
|
||||
return "b"
|
||||
}
|
||||
]]>
|
||||
</node>
|
||||
|
|
|
@ -49,5 +49,13 @@
|
|||
<version>${h2.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.yomahub</groupId>
|
||||
<artifactId>liteflow-script-groovy</artifactId>
|
||||
<version>${revision}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
|
@ -33,35 +33,64 @@ import java.sql.Statement;
|
|||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.sql.cmp"})
|
||||
public class SQLWithXmlELSpringbootTest extends BaseTest {
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testSQLWithXml() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>b==>c", response.getExecuteStepStr());
|
||||
@Test
|
||||
public void testSQLWithXml() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>b==>c", response.getExecuteStepStr());
|
||||
|
||||
// 修改数据库
|
||||
changeData();
|
||||
// 修改数据库
|
||||
changeData();
|
||||
|
||||
// 重新加载规则
|
||||
flowExecutor.reloadRule();
|
||||
Assert.assertEquals("a==>c==>b", flowExecutor.execute2Resp("chain1", "arg").getExecuteStepStr());
|
||||
}
|
||||
// 重新加载规则
|
||||
flowExecutor.reloadRule();
|
||||
Assert.assertEquals("a==>c==>b", flowExecutor.execute2Resp("chain1", "arg").getExecuteStepStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据库数据
|
||||
*/
|
||||
private void changeData() {
|
||||
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
|
||||
SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
|
||||
Connection connection = null;
|
||||
try {
|
||||
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
|
||||
Statement statement = connection.createStatement();
|
||||
statement.executeUpdate("UPDATE EL_TABLE SET EL_DATA='THEN(a, c, b);' WHERE chain_name='chain1'");
|
||||
} catch (SQLException e) {
|
||||
throw new ELSQLException(e.getMessage());
|
||||
}
|
||||
}
|
||||
@Test
|
||||
public void testSQLWithScriptXml() {
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
Assert.assertEquals("x0[if 脚本]==>a==>b", response.getExecuteStepStrWithoutTime());
|
||||
|
||||
// 修改数据库
|
||||
changeScriptData();
|
||||
// 重新加载规则
|
||||
flowExecutor.reloadRule();
|
||||
Assert.assertEquals("x0[if 脚本]", flowExecutor.execute2Resp("chain3", "arg").getExecuteStepStr());
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据库数据
|
||||
*/
|
||||
private void changeData() {
|
||||
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
|
||||
SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
|
||||
Statement statement = connection.createStatement();
|
||||
statement.executeUpdate("UPDATE EL_TABLE SET EL_DATA='THEN(a, c, b);' WHERE chain_name='chain1'");
|
||||
} catch (SQLException e) {
|
||||
throw new ELSQLException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改数据库数据
|
||||
*/
|
||||
private void changeScriptData() {
|
||||
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
|
||||
SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
|
||||
Connection connection;
|
||||
try {
|
||||
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
|
||||
Statement statement = connection.createStatement();
|
||||
statement.executeUpdate("UPDATE SCRIPT_NODE_TABLE SET SCRIPT_NODE_DATA='return false;' WHERE SCRIPT_NODE_ID='x0'");
|
||||
} catch (SQLException e) {
|
||||
throw new ELSQLException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
liteflow.rule-source-ext-data={"url":"jdbc:h2:mem:test_db;MODE=MySQL","driverClassName":"org.h2.Driver","username":"root","password":"123456","tableName":"EL_TABLE","chainNameField":"chain_name","elDataField":"EL_DATA"}
|
||||
liteflow.rule-source-ext-data={\
|
||||
"url":"jdbc:h2:mem:test_db;MODE=MySQL",\
|
||||
"driverClassName":"org.h2.Driver",\
|
||||
"username":"root",\
|
||||
"password":"123456",\
|
||||
"applicationName":"demo",\
|
||||
"chainTableName":"EL_TABLE",\
|
||||
"chainApplicationNameField":"application_name",\
|
||||
"chainNameField":"chain_name",\
|
||||
"elDataField":"EL_DATA",\
|
||||
"scriptTableName":"script_node_table",\
|
||||
"scriptApplicationNameField":"application_name",\
|
||||
"scriptIdField":"script_node_id",\
|
||||
"scriptNameField":"script_node_name",\
|
||||
"scriptDataField":"script_node_data",\
|
||||
"scriptTypeField":"script_node_type"\
|
||||
}
|
||||
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.datasource.url=jdbc:h2:mem:test_db;MODE=MySQL
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
DELETE FROM EL_TABLE;
|
||||
|
||||
INSERT INTO EL_TABLE (CHAIN_NAME,EL_DATA) values ('chain1','THEN(a, b, c);');
|
||||
INSERT INTO EL_TABLE (CHAIN_NAME,EL_DATA) values ('chain2','THEN(a, b, c);');
|
||||
INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain1','THEN(a, b, c);');
|
||||
INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain2','THEN(a, b, c);');
|
||||
INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain3','IF(x0, THEN(a, b));');
|
||||
|
||||
DELETE FROM SCRIPT_NODE_TABLE;
|
||||
|
||||
INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA) values ('demo','x0','if 脚本','if_script','return true');
|
||||
INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA) values ('demo','x1','if 脚本','if_script','return false');
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
create table `EL_TABLE` (
|
||||
`id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`chain_name` varchar(32) NOT NULL,
|
||||
`el_data` varchar(1024) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ;
|
||||
create table `EL_TABLE`
|
||||
(
|
||||
`id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`application_name` varchar(32) NOT NULL,
|
||||
`chain_name` varchar(32) NOT NULL,
|
||||
`el_data` varchar(1024) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
||||
|
||||
create table `script_node_table`
|
||||
(
|
||||
`id` bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
`application_name` varchar(32) NOT NULL,
|
||||
`script_node_id` varchar(32) NOT NULL,
|
||||
`script_node_name` varchar(32) NOT NULL,
|
||||
`script_node_type` varchar(32) NOT NULL,
|
||||
`script_node_data` varchar(1024) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
);
|
2
pom.xml
2
pom.xml
|
@ -39,7 +39,7 @@
|
|||
</scm>
|
||||
|
||||
<properties>
|
||||
<revision>2.9.1</revision>
|
||||
<revision>2.9.3</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
|
|
Loading…
Reference in New Issue