Merge branch 'refs/heads/dev'

This commit is contained in:
everywhere.z 2024-05-31 10:55:39 +08:00
commit d4e464eb1a
146 changed files with 3786 additions and 202 deletions

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Documented
@Inherited
public @interface LiteflowFact {
String value();
}

View File

@ -136,8 +136,7 @@ public class LiteFlowChainELBuilder {
public LiteFlowChainELBuilder setRoute(String routeEl){ public LiteFlowChainELBuilder setRoute(String routeEl){
if (StrUtil.isBlank(routeEl)) { if (StrUtil.isBlank(routeEl)) {
String errMsg = StrUtil.format("You have defined the label <route> but there is no el in the chain route[{}].", chain.getChainId()); return this;
throw new FlowSystemException(errMsg);
} }
List<String> errorList = new ArrayList<>(); List<String> errorList = new ArrayList<>();
try { try {
@ -233,6 +232,14 @@ public class LiteFlowChainELBuilder {
} }
} }
public LiteFlowChainELBuilder setNamespace(String nameSpace){
if (StrUtil.isBlank(nameSpace)) {
nameSpace = ChainConstant.DEFAULT_NAMESPACE;
}
this.chain.setNamespace(nameSpace);
return this;
}
/** /**
* EL表达式校验 * EL表达式校验
* @param elStr EL表达式 * @param elStr EL表达式

View File

@ -32,6 +32,10 @@ public interface ChainConstant {
String LANGUAGE = "language"; String LANGUAGE = "language";
String NAMESPACE = "namespace";
String DEFAULT_NAMESPACE = "default";
String VALUE = "value"; String VALUE = "value";
String ANY = "any"; String ANY = "any";

View File

@ -13,6 +13,7 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Tuple; import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*; import cn.hutool.core.util.*;
import com.yomahub.liteflow.common.ChainConstant;
import com.yomahub.liteflow.enums.ChainExecuteModeEnum; import com.yomahub.liteflow.enums.ChainExecuteModeEnum;
import com.yomahub.liteflow.enums.InnerChainTypeEnum; import com.yomahub.liteflow.enums.InnerChainTypeEnum;
import com.yomahub.liteflow.enums.ParseModeEnum; import com.yomahub.liteflow.enums.ParseModeEnum;
@ -136,8 +137,8 @@ public class FlowExecutor {
// 查找对应的解析器 // 查找对应的解析器
parser = FlowParserProvider.lookup(path); parser = FlowParserProvider.lookup(path);
parserNameSet.add(parser.getClass().getName()); parserNameSet.add(parser.getClass().getName());
// 替换掉前缀标识xml:/json:保留剩下的完整地址 // 替换掉前缀标识xml:/json:保留剩下的完整地址并统一路径格式
path = ReUtil.replaceAll(path, PREFIX_FORMAT_CONFIG_REGEX, ""); path = ReUtil.replaceAll(path, PREFIX_FORMAT_CONFIG_REGEX, "").replace("\\", "/");
rulePathList.add(path); rulePathList.add(path);
// 支持多类型的配置文件分别解析 // 支持多类型的配置文件分别解析
@ -283,7 +284,11 @@ public class FlowExecutor {
} }
public List<LiteflowResponse> executeRouteChain(Object param, Class<?>... contextBeanClazzArray){ public List<LiteflowResponse> executeRouteChain(Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(param, null, contextBeanClazzArray, null); return this.executeWithRoute(null, param, null, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Class<?>... contextBeanClazzArray){
return this.executeWithRoute(namespace, param, null, contextBeanClazzArray, null);
} }
public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) { public LiteflowResponse execute2Resp(String chainId, Object param, Object... contextBeanArray) {
@ -291,23 +296,35 @@ public class FlowExecutor {
} }
public List<LiteflowResponse> executeRouteChain(Object param, Object... contextBeanArray){ public List<LiteflowResponse> executeRouteChain(Object param, Object... contextBeanArray){
return this.executeWithRoute(param, null, null, contextBeanArray); return this.executeWithRoute(null, param, null, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChain(String namespace, Object param, Object... contextBeanArray){
return this.executeWithRoute(namespace, param, null, null, contextBeanArray);
} }
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) { public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null); return this.execute2Resp(chainId, param, requestId, contextBeanClazzArray, null);
} }
public List<LiteflowResponse> executeRouteChainWithRid(String chainId, Object param, String requestId, Class<?>... contextBeanClazzArray) { public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(param, requestId, contextBeanClazzArray, null); return this.executeWithRoute(null, param, requestId, contextBeanClazzArray, null);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Class<?>... contextBeanClazzArray) {
return this.executeWithRoute(namespace, param, requestId, contextBeanClazzArray, null);
} }
public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) { public LiteflowResponse execute2RespWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) {
return this.execute2Resp(chainId, param, requestId, null, contextBeanArray); return this.execute2Resp(chainId, param, requestId, null, contextBeanArray);
} }
public List<LiteflowResponse> executeRouteChainWithRid(String chainId, Object param, String requestId, Object... contextBeanArray) { public List<LiteflowResponse> executeRouteChainWithRid(Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(param, requestId, null, contextBeanArray); return this.executeWithRoute(null, param, requestId, null, contextBeanArray);
}
public List<LiteflowResponse> executeRouteChainWithRid(String namespace, Object param, String requestId, Object... contextBeanArray) {
return this.executeWithRoute(namespace, param, requestId, null, contextBeanArray);
} }
// 调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入 // 调用一个流程并返回Future<LiteflowResponse>允许多上下文的传入
@ -353,8 +370,8 @@ public class FlowExecutor {
return LiteflowResponse.newMainResponse(slot); return LiteflowResponse.newMainResponse(slot);
} }
private List<LiteflowResponse> executeWithRoute(Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){ private List<LiteflowResponse> executeWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
List<Slot> slotList = doExecuteWithRoute(param, requestId, contextBeanClazzArray, contextBeanArray); List<Slot> slotList = doExecuteWithRoute(namespace, param, requestId, contextBeanClazzArray, contextBeanArray);
return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList()); return slotList.stream().map(LiteflowResponse::newMainResponse).collect(Collectors.toList());
} }
@ -522,15 +539,23 @@ public class FlowExecutor {
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath); MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
} }
private List<Slot> doExecuteWithRoute(Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){ private List<Slot> doExecuteWithRoute(String namespace, Object param, String requestId, Class<?>[] contextBeanClazzArray, Object[] contextBeanArray){
if (FlowBus.needInit()) { if (FlowBus.needInit()) {
init(true); init(true);
} }
List<Chain> routeChainList = FlowBus.getChainMap().values().stream().filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList()); if (StrUtil.isBlank(namespace)){
namespace = ChainConstant.DEFAULT_NAMESPACE;
}
String finalNamespace = namespace;
List<Chain> routeChainList = FlowBus.getChainMap().values().stream()
.filter(chain -> chain.getNamespace().equals(finalNamespace))
.filter(chain -> chain.getRouteItem() != null).collect(Collectors.toList());
if (CollUtil.isEmpty(routeChainList)){ if (CollUtil.isEmpty(routeChainList)){
throw new RouteChainNotFoundException("cannot find any route chain"); String errorMsg = StrUtil.format("no route found for namespace[{}]", finalNamespace);
throw new RouteChainNotFoundException(errorMsg);
} }
String finalRequestId; String finalRequestId;
@ -601,7 +626,7 @@ public class FlowExecutor {
} }
}).filter(Objects::nonNull).collect(Collectors.toList()); }).filter(Objects::nonNull).collect(Collectors.toList());
LOG.info("There are {} chains that matched the route.", resultSlotList.size()); LOG.info("chain namespace:[{}], total size:[{}], matched size:[{}]", namespace, routeChainList.size(), resultSlotList.size());
return resultSlotList; return resultSlotList;
} }

View File

@ -1,17 +1,24 @@
package com.yomahub.liteflow.core.proxy; package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.exceptions.InvocationTargetRuntimeException; import cn.hutool.core.exceptions.InvocationTargetRuntimeException;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.InstructionSet;
import com.yomahub.liteflow.annotation.LiteflowMethod; import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry; import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException; import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException;
import com.yomahub.liteflow.exception.LiteFlowException; import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.exception.ParameterFactException;
import com.yomahub.liteflow.exception.ProxyException; import com.yomahub.liteflow.exception.ProxyException;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.util.SerialsUtil; import com.yomahub.liteflow.util.SerialsUtil;
import net.bytebuddy.ByteBuddy; import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
@ -21,7 +28,12 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -112,6 +124,10 @@ public class DeclComponentProxy {
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if (currentMethodWrapBean == null){
throw new ProxyException("currentMethodWrapBean is null");
}
// 如果被代理的对象里有此标注标的方法则调用此被代理的对象里的方法如果没有则调用父类里的方法 // 如果被代理的对象里有此标注标的方法则调用此被代理的对象里的方法如果没有则调用父类里的方法
// 进行检查检查被代理的bean里是否第一个参数为NodeComponent这个类型的 // 进行检查检查被代理的bean里是否第一个参数为NodeComponent这个类型的
boolean checkFlag = currentMethodWrapBean.getMethod().getParameterTypes().length > 0 boolean checkFlag = currentMethodWrapBean.getMethod().getParameterTypes().length > 0
@ -124,13 +140,13 @@ public class DeclComponentProxy {
throw new ComponentMethodDefineErrorException(errMsg); throw new ComponentMethodDefineErrorException(errMsg);
} }
// 这里是针对于参数的处理
// 首先需要保证第一个参数是NodeComponent
// 其次需要针对于@LiteflowFact做处理
try { try {
if (args != null && args.length > 0){ Object[] realArgs = loadMethodParameter(proxy, currentMethodWrapBean);
Object[] wrapArgs = ArrayUtil.insert(args, 0, proxy); return ReflectUtil.invoke(declWarpBean.getRawBean(), currentMethodWrapBean.getMethod(), realArgs);
return ReflectUtil.invoke(declWarpBean.getRawBean(), currentMethodWrapBean.getMethod(), wrapArgs);
}else{
return ReflectUtil.invoke(declWarpBean.getRawBean(), currentMethodWrapBean.getMethod(), proxy);
}
}catch (InvocationTargetRuntimeException e) { }catch (InvocationTargetRuntimeException e) {
InvocationTargetException targetEx = (InvocationTargetException) e.getCause(); InvocationTargetException targetEx = (InvocationTargetException) e.getCause();
throw targetEx.getTargetException(); throw targetEx.getTargetException();
@ -138,4 +154,55 @@ public class DeclComponentProxy {
} }
} }
private final ExpressRunner expressRunner = new ExpressRunner();
private Object[] loadMethodParameter(Object proxy, MethodWrapBean methodWrapBean){
NodeComponent thisNodeComponent = (NodeComponent) proxy;
return methodWrapBean.getParameterWrapBeanList().stream().map(parameterWrapBean -> {
// 如果参数是NodeComponent那就返回proxy本身
if (parameterWrapBean.getParameterType().isAssignableFrom(NodeComponent.class)) {
return proxy;
}
// 如果没有@LiteflowFact标注那么不处理直接赋值null
if (parameterWrapBean.getFact() == null) {
return null;
}
// 把上下文数据转换成map形式的key为别名value为上下文
Map<String, Object> contextMap = DataBus.getSlot(thisNodeComponent.getSlotIndex()).getContextBeanList().stream().collect(
Collectors.toMap(tuple -> tuple.get(0), tuple -> tuple.get(1))
);
List<String> errorList = new ArrayList<>();
Object result = null;
// 根据表达式去上下文里搜索相匹配的数据
for(Map.Entry<String, Object> entry : contextMap.entrySet()){
try{
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(entry.getKey() + "." + parameterWrapBean.getFact().value());
DefaultContext<String, Object> context = new DefaultContext<>();
context.put(entry.getKey(), entry.getValue());
result = expressRunner.execute(instructionSet, context, errorList, false, false);
if (result != null){
break;
}
}catch (Exception ignore){}
}
if (result == null){
try{
// 如果没有搜到那么尝试推断表达式是指定的上下文按照指定上下文的方式去再获取
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache("contextMap." + parameterWrapBean.getFact().value());
DefaultContext<String, Object> context = new DefaultContext<>();
context.put("contextMap", contextMap);
result = expressRunner.execute(instructionSet, context, errorList, false, false);
}catch (Exception ignore){}
}
return result;
}).toArray();
}
} }

View File

@ -4,6 +4,7 @@ import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry; import com.yomahub.liteflow.annotation.LiteflowRetry;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.List;
/** /**
* LiteflowMethod的包装类 * LiteflowMethod的包装类
@ -18,10 +19,13 @@ public class MethodWrapBean {
private LiteflowRetry liteflowRetry; private LiteflowRetry liteflowRetry;
public MethodWrapBean(Method method, LiteflowMethod liteflowMethod, LiteflowRetry liteflowRetry) { private List<ParameterWrapBean> parameterWrapBeanList;
public MethodWrapBean(Method method, LiteflowMethod liteflowMethod, LiteflowRetry liteflowRetry, List<ParameterWrapBean> parameterWrapBeanList) {
this.method = method; this.method = method;
this.liteflowMethod = liteflowMethod; this.liteflowMethod = liteflowMethod;
this.liteflowRetry = liteflowRetry; this.liteflowRetry = liteflowRetry;
this.parameterWrapBeanList = parameterWrapBeanList;
} }
public Method getMethod() { public Method getMethod() {
@ -47,4 +51,12 @@ public class MethodWrapBean {
public void setLiteflowRetry(LiteflowRetry liteflowRetry) { public void setLiteflowRetry(LiteflowRetry liteflowRetry) {
this.liteflowRetry = liteflowRetry; this.liteflowRetry = liteflowRetry;
} }
public List<ParameterWrapBean> getParameterWrapBeanList() {
return parameterWrapBeanList;
}
public void setParameterWrapBeanList(List<ParameterWrapBean> parameterWrapBeanList) {
this.parameterWrapBeanList = parameterWrapBeanList;
}
} }

View File

@ -0,0 +1,47 @@
package com.yomahub.liteflow.core.proxy;
import com.yomahub.liteflow.annotation.LiteflowFact;
/**
* 声明式的包装类
* @author Bryan.Zhang
* @since 2.12.1
*/
public class ParameterWrapBean {
private Class<?> parameterType;
private LiteflowFact fact;
private int index;
public ParameterWrapBean(Class<?> parameterType, LiteflowFact fact, int index) {
this.parameterType = parameterType;
this.fact = fact;
this.index = index;
}
public Class<?> getParameterType() {
return parameterType;
}
public void setParameterType(Class<?> parameterType) {
this.parameterType = parameterType;
}
public LiteflowFact getFact() {
return fact;
}
public void setFact(LiteflowFact fact) {
this.fact = fact;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}

View File

@ -2,13 +2,15 @@ package com.yomahub.liteflow.enums;
public enum ScriptTypeEnum { public enum ScriptTypeEnum {
CUSTOM("custom", "custom"),
GROOVY("groovy", "groovy"), GROOVY("groovy", "groovy"),
QLEXPRESS("qlexpress", "qlexpress"), QLEXPRESS("qlexpress", "qlexpress"),
JS("javascript", "js"), JS("javascript", "js"),
PYTHON("python", "python"), PYTHON("python", "python"),
LUA("luaj", "lua"), LUA("luaj", "lua"),
AVIATOR("AviatorScript", "aviator"), AVIATOR("AviatorScript", "aviator"),
JAVA("java", "java"); JAVA("java", "java"),
KOTLIN("kotlin", "kotlin");
private String engineName; private String engineName;

View File

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

View File

@ -40,6 +40,8 @@ public class Chain implements Executable{
private boolean isCompiled = true; private boolean isCompiled = true;
private String namespace;
public Chain(String chainName) { public Chain(String chainName) {
this.chainId = chainName; this.chainId = chainName;
} }
@ -195,4 +197,12 @@ public class Chain implements Executable{
public void setCompiled(boolean compiled) { public void setCompiled(boolean compiled) {
isCompiled = compiled; isCompiled = compiled;
} }
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
} }

View File

@ -15,29 +15,6 @@ import java.util.List;
*/ */
public class NodeConvertHelper { public class NodeConvertHelper {
/*script节点的修改/添加*/
public static void changeScriptNode(NodeSimpleVO nodeSimpleVO, String newValue) {
// 有语言类型
if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) {
LiteFlowNodeBuilder.createScriptNode()
.setId(nodeSimpleVO.getNodeId())
.setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
.setName(nodeSimpleVO.getName())
.setScript(newValue)
.setLanguage(nodeSimpleVO.getLanguage())
.build();
}
// 没有语言类型
else {
LiteFlowNodeBuilder.createScriptNode()
.setId(nodeSimpleVO.getNodeId())
.setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType()))
.setName(nodeSimpleVO.getName())
.setScript(newValue)
.build();
}
}
public static NodeSimpleVO convert(String scriptKey){ public static NodeSimpleVO convert(String scriptKey){
// 不需要去理解这串正则就是一个匹配冒号的 // 不需要去理解这串正则就是一个匹配冒号的
// 一定得是a:b或是a:b:c...这种完整类型的字符串的 // 一定得是a:b或是a:b:c...这种完整类型的字符串的

View File

@ -1,5 +1,6 @@
package com.yomahub.liteflow.parser.helper; package com.yomahub.liteflow.parser.helper;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
@ -24,6 +25,7 @@ import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import static com.yomahub.liteflow.common.ChainConstant.*; import static com.yomahub.liteflow.common.ChainConstant.*;
@ -205,7 +207,7 @@ public class ParserHelper {
// 当存在<nodes>节点定义时解析node节点 // 当存在<nodes>节点定义时解析node节点
if (flowJsonNode.get(FLOW).has(NODES)) { if (flowJsonNode.get(FLOW).has(NODES)) {
Iterator<JsonNode> nodeIterator = flowJsonNode.get(FLOW).get(NODES).get(NODE).elements(); Iterator<JsonNode> nodeIterator = flowJsonNode.get(FLOW).get(NODES).get(NODE).elements();
String id, name, clazz, script, type, file; String id, name, clazz, script, type, file, language;
while ((nodeIterator.hasNext())) { while ((nodeIterator.hasNext())) {
JsonNode nodeObject = nodeIterator.next(); JsonNode nodeObject = nodeIterator.next();
id = nodeObject.get(ID).textValue(); id = nodeObject.get(ID).textValue();
@ -214,6 +216,7 @@ public class ParserHelper {
type = nodeObject.hasNonNull(TYPE) ? nodeObject.get(TYPE).textValue() : null; type = nodeObject.hasNonNull(TYPE) ? nodeObject.get(TYPE).textValue() : null;
script = nodeObject.hasNonNull(VALUE) ? nodeObject.get(VALUE).textValue() : ""; script = nodeObject.hasNonNull(VALUE) ? nodeObject.get(VALUE).textValue() : "";
file = nodeObject.hasNonNull(FILE) ? nodeObject.get(FILE).textValue() : ""; file = nodeObject.hasNonNull(FILE) ? nodeObject.get(FILE).textValue() : "";
language = nodeObject.hasNonNull(LANGUAGE) ? nodeObject.get(LANGUAGE).textValue() : "";
// 如果是禁用的就不编译了 // 如果是禁用的就不编译了
if (!getEnableByJsonNode(nodeObject)) { if (!getEnableByJsonNode(nodeObject)) {
@ -226,7 +229,8 @@ public class ParserHelper {
.setClazz(clazz) .setClazz(clazz)
.setScript(script) .setScript(script)
.setType(type) .setType(type)
.setFile(file); .setFile(file)
.setLanguage(language);
ParserHelper.buildNode(nodePropBean); ParserHelper.buildNode(nodePropBean);
} }
@ -308,9 +312,11 @@ public class ParserHelper {
// 构建chainBuilder // 构建chainBuilder
String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue();
String namespace = chainNode.get(NAMESPACE) == null? DEFAULT_NAMESPACE : chainNode.get(NAMESPACE).textValue();
JsonNode routeJsonNode = chainNode.get(ROUTE); JsonNode routeJsonNode = chainNode.get(ROUTE);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace);
// 如果有route这个标签说明是决策表chain // 如果有route这个标签说明是决策表chain
// 决策表链路必须有route和body这两个标签 // 决策表链路必须有route和body这两个标签
@ -337,9 +343,11 @@ public class ParserHelper {
// 构建chainBuilder // 构建chainBuilder
String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME));
String namespace = StrUtil.blankToDefault(e.attributeValue(NAMESPACE), DEFAULT_NAMESPACE);
Element routeElement = e.element(ROUTE); Element routeElement = e.element(ROUTE);
LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId).setNamespace(namespace);
// 如果有route这个标签说明是决策表chain // 如果有route这个标签说明是决策表chain
// 决策表链路必须有route和body这两个标签 // 决策表链路必须有route和body这两个标签
@ -362,6 +370,7 @@ public class ParserHelper {
} }
} }
builder.build(); builder.build();
} }

View File

@ -3,6 +3,7 @@ package com.yomahub.liteflow.util;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.yomahub.liteflow.exception.JsonProcessException; import com.yomahub.liteflow.exception.JsonProcessException;
@ -27,6 +28,7 @@ public class JsonUtil {
static { static {
objectMapper.setTimeZone(TimeZone.getDefault()); objectMapper.setTimeZone(TimeZone.getDefault());
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
} }
public static String toJsonString(Object object) { public static String toJsonString(Object object) {
@ -71,5 +73,4 @@ public class JsonUtil {
throw new JsonProcessException(errMsg); throw new JsonProcessException(errMsg);
} }
} }
} }

View File

@ -13,10 +13,13 @@
type (script|boolean_script|switch_script|for_script) #IMPLIED type (script|boolean_script|switch_script|for_script) #IMPLIED
class CDATA #IMPLIED class CDATA #IMPLIED
file CDATA #IMPLIED file CDATA #IMPLIED
language (qlexpress|groovy|js|python|lua|aviator|java) #IMPLIED language (qlexpress|groovy|js|python|lua|aviator|java|kotlin) #IMPLIED
enable (true|false) #IMPLIED
> >
<!ATTLIST chain <!ATTLIST chain
id CDATA #IMPLIED id CDATA #IMPLIED
name CDATA #IMPLIED name CDATA #IMPLIED
extends CDATA #IMPLIED extends CDATA #IMPLIED
enable (true|false) #IMPLIED
namespace CDATA #IMPLIED
> >

View File

@ -16,7 +16,7 @@ public class SqlReadConstant {
public static final String SCRIPT_SQL_PATTERN = "SELECT * FROM {} WHERE {}=?"; public static final String SCRIPT_SQL_PATTERN = "SELECT * FROM {} WHERE {}=?";
public static final String CHAIN_XML_PATTERN = "<chain name=\"{}\"><![CDATA[{}]]></chain>"; public static final String CHAIN_XML_PATTERN = "<chain id=\"{}\" namespace=\"{}\"><route><![CDATA[{}]]></route><body><![CDATA[{}]]></body></chain>";
public static final String NODE_XML_PATTERN = "<nodes>{}</nodes>"; public static final String NODE_XML_PATTERN = "<nodes>{}</nodes>";

View File

@ -11,6 +11,8 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* sql 轮询任务抽象类维护公共方法 * sql 轮询任务抽象类维护公共方法
@ -19,11 +21,11 @@ import java.util.Set;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public abstract class AbstractSqlReadPollTask implements SqlReadPollTask { public abstract class AbstractSqlReadPollTask<T> implements SqlReadPollTask<T> {
private final Map<String/*唯一键*/, String/*data-xml的sha1值*/> DATA_SHA_MAP = new HashMap<>(); private final Map<String/*唯一键*/, String/*data-xml的sha1值*/> DATA_SHA_MAP = new HashMap<>();
private final SqlRead read; private final SqlRead<T> read;
public AbstractSqlReadPollTask(SqlRead read) { public AbstractSqlReadPollTask(SqlRead<T> read) {
this.read = read; this.read = read;
if (!read.type().equals(type())) { if (!read.type().equals(type())) {
@ -33,35 +35,34 @@ public abstract class AbstractSqlReadPollTask implements SqlReadPollTask {
@Override @Override
public void execute() { public void execute() {
Map<String/*唯一键*/, String/*data-xml*/> newData = read.read(); List<T> dataList = read.read();
// 新增或者更新的元素 // 新增或者更新的元素
Map<String, String> saveElementMap = new HashMap<>(); List<T> saveElementList = new ArrayList<>();
// 删除的元素 // 删除的元素
List<String> deleteElementIds = new ArrayList<>(); List<String> deleteElementIds;
for (Map.Entry<String, String> entry : newData.entrySet()) { for (T data : dataList) {
String id = entry.getKey(); String id = getKey(data);
String element = entry.getValue(); String newSHA = getNeedSha1Value(data);
String newSHA = DigestUtil.sha1Hex(element);
// 新增 // 新增
// 如果封装的SHAMap中不存在该chain, 表示该元素为新增 // 如果封装的SHAMap中不存在该chain, 表示该元素为新增
if (!DATA_SHA_MAP.containsKey(id)) { if (!DATA_SHA_MAP.containsKey(id)) {
saveElementMap.put(id, element); saveElementList.add(data);
DATA_SHA_MAP.put(id, newSHA); DATA_SHA_MAP.put(id, newSHA);
} }
// 修改 // 修改
// SHA值发生变化,表示该元素的值已被修改,重新拉取变化的chain // SHA值发生变化,表示该元素的值已被修改,重新拉取变化的chain
else if (!StrUtil.equals(newSHA, DATA_SHA_MAP.get(id))) { else if (!StrUtil.equals(newSHA, DATA_SHA_MAP.get(id))) {
saveElementMap.put(id, element); saveElementList.add(data);
DATA_SHA_MAP.put(id, newSHA); DATA_SHA_MAP.put(id, newSHA);
} }
} }
Set<String> oldIdList = DATA_SHA_MAP.keySet(); // 旧的 id 列表 Set<String> oldIdList = DATA_SHA_MAP.keySet(); // 旧的 id 列表
Set<String> newIdList = newData.keySet(); // 新的 id 列表 Set<String> newIdList = dataList.stream().map(this::getKey).collect(Collectors.toSet()); // 新的 id 列表
// 计算单差集 // 计算单差集
// 计算集合的单差集即只返回oldIdList中有但是newIdList中没有的元素例如 // 计算集合的单差集即只返回oldIdList中有但是newIdList中没有的元素例如
// subtractToList([1,2,3,4],[2,3,4,5]) - [1] // subtractToList([1,2,3,4],[2,3,4,5]) - [1]
@ -71,8 +72,8 @@ public abstract class AbstractSqlReadPollTask implements SqlReadPollTask {
DATA_SHA_MAP.remove(id); DATA_SHA_MAP.remove(id);
} }
if (CollUtil.isNotEmpty(saveElementMap)) { if (CollUtil.isNotEmpty(saveElementList)) {
doSave(saveElementMap); doSave(saveElementList);
} }
if (CollUtil.isNotEmpty(deleteElementIds)) { if (CollUtil.isNotEmpty(deleteElementIds)) {
@ -81,20 +82,34 @@ public abstract class AbstractSqlReadPollTask implements SqlReadPollTask {
} }
@Override @Override
public void initData(Map<String/*唯一键*/, String/*data-xml的数据*/> dataMap) { public void initData(List<T> dataList) {
DATA_SHA_MAP.putAll(shaMapValue(dataMap)); DATA_SHA_MAP.putAll(shaValue(dataList));
} }
public abstract void doSave(Map<String, String> saveElementMap); public abstract void doSave(List<T> saveElementList);
public abstract void doDelete(List<String> deleteElementId); public abstract void doDelete(List<String> deleteElementId);
private Map<String/*唯一键*/, String/*data-xml的sha1值*/> shaMapValue(Map<String, String> dataMap) { private Map<String/*唯一键*/, String/*data-xml的sha1值*/> shaValue(List<T> dataList) {
Map<String, String> result = new HashMap<>(); Map<String, String> result = new HashMap<>();
dataMap.forEach((k, v) -> { dataList.forEach(t -> result.put(getKey(t), DigestUtil.sha1Hex(getNeedSha1Value(t))));
result.put(k, DigestUtil.sha1Hex(v));
});
return result; return result;
} }
private String getNeedSha1Value(T data) {
if (StrUtil.isBlank(getExtValue(data))) {
return DigestUtil.sha1Hex(getValue(data));
}else{
return DigestUtil.sha1Hex(getValue(data) + "|||" + getExtValue(data));
}
}
// 如果是chain那就是返回chain的id如果是script那就返回script的id
protected abstract String getKey(T t);
// 如果是chain那就返回EL如果是script那就返回脚本数据
protected abstract String getValue(T t);
// 如果是chain那就返回route el如果是script这个不返回因为script没有扩展value
protected abstract String getExtValue(T t);
} }

View File

@ -2,6 +2,7 @@ package com.yomahub.liteflow.parser.sql.polling;
import com.yomahub.liteflow.parser.constant.ReadType; import com.yomahub.liteflow.parser.constant.ReadType;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -11,7 +12,7 @@ import java.util.Map;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public interface SqlReadPollTask { public interface SqlReadPollTask<T> {
/** /**
* 执行 * 执行
@ -21,9 +22,8 @@ public interface SqlReadPollTask {
/** /**
* 初始化数据 * 初始化数据
* *
* @param dataMap 数据
*/ */
void initData(Map<String/*唯一键*/, String/*data-xml的数据*/> dataMap); void initData(List<T> dataList);
/** /**
* 类型 * 类型

View File

@ -1,13 +1,16 @@
package com.yomahub.liteflow.parser.sql.polling.impl; package com.yomahub.liteflow.parser.sql.polling.impl;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.parser.constant.ReadType; import com.yomahub.liteflow.parser.constant.ReadType;
import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask; import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask;
import com.yomahub.liteflow.parser.sql.read.SqlRead; import com.yomahub.liteflow.parser.sql.read.SqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.ChainVO;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
/** /**
* chain 读取任务 * chain 读取任务
@ -16,20 +19,20 @@ import java.util.Map;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public class ChainReadPollTask extends AbstractSqlReadPollTask { public class ChainReadPollTask extends AbstractSqlReadPollTask<ChainVO> {
public ChainReadPollTask(SqlRead read) { public ChainReadPollTask(SqlRead<ChainVO> read) {
super(read); super(read);
} }
@Override @Override
public void doSave(Map<String, String> saveElementMap) { public void doSave(List<ChainVO> saveElementList) {
for (Map.Entry<String, String> entry : saveElementMap.entrySet()) { saveElementList.forEach(chainVO ->
String chainName = entry.getKey(); LiteFlowChainELBuilder.createChain().setChainId(chainVO.getChainId())
String newData = entry.getValue(); .setRoute(chainVO.getRoute())
.setNamespace(chainVO.getNamespace())
LiteFlowChainELBuilder.createChain().setChainId(chainName).setEL(newData).build(); .setEL(chainVO.getBody())
} .build());
} }
@Override @Override
@ -39,6 +42,21 @@ public class ChainReadPollTask extends AbstractSqlReadPollTask {
} }
} }
@Override
protected String getKey(ChainVO chainVO) {
return chainVO.getChainId();
}
@Override
protected String getValue(ChainVO chainVO) {
return chainVO.getBody();
}
@Override
protected String getExtValue(ChainVO chainVO) {
return chainVO.getRoute();
}
@Override @Override
public ReadType type() { public ReadType type() {
return ReadType.CHAIN; return ReadType.CHAIN;

View File

@ -1,13 +1,18 @@
package com.yomahub.liteflow.parser.sql.polling.impl; package com.yomahub.liteflow.parser.sql.polling.impl;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.parser.constant.ReadType; import com.yomahub.liteflow.parser.constant.ReadType;
import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.helper.NodeConvertHelper;
import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask; import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask;
import com.yomahub.liteflow.parser.sql.read.SqlRead; import com.yomahub.liteflow.parser.sql.read.SqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.ScriptVO;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer;
/** /**
* 脚本轮询任务 * 脚本轮询任务
@ -16,32 +21,45 @@ import java.util.Map;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public class ScriptReadPollTask extends AbstractSqlReadPollTask { public class ScriptReadPollTask extends AbstractSqlReadPollTask<ScriptVO> {
public ScriptReadPollTask(SqlRead read) { public ScriptReadPollTask(SqlRead<ScriptVO> read) {
super(read); super(read);
} }
@Override @Override
public void doSave(Map<String, String> saveElementMap) { public void doSave(List<ScriptVO> saveElementList) {
for (Map.Entry<String, String> entry : saveElementMap.entrySet()) { saveElementList.forEach(scriptVO -> LiteFlowNodeBuilder.createScriptNode()
String scriptKey = entry.getKey(); .setId(scriptVO.getNodeId())
String newData = entry.getValue(); .setType(NodeTypeEnum.getEnumByCode(scriptVO.getType()))
.setName(scriptVO.getName())
NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(scriptKey); .setScript(scriptVO.getScript())
NodeConvertHelper.changeScriptNode(scriptVO, newData); .setLanguage(scriptVO.getLanguage())
} .build());
} }
@Override @Override
public void doDelete(List<String> deleteElementId) { public void doDelete(List<String> deleteElementId) {
for (String id : deleteElementId) { for (String id : deleteElementId) {
NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(id);
// 删除script // 删除script
FlowBus.unloadScriptNode(scriptVO.getNodeId()); FlowBus.unloadScriptNode(id);
} }
} }
@Override
protected String getKey(ScriptVO scriptVO) {
return scriptVO.getNodeId();
}
@Override
protected String getValue(ScriptVO scriptVO) {
return scriptVO.getScript();
}
@Override
protected String getExtValue(ScriptVO scriptVO) {
return StrUtil.EMPTY;
}
@Override @Override
public ReadType type() { public ReadType type() {
return ReadType.SCRIPT; return ReadType.SCRIPT;

View File

@ -9,7 +9,9 @@ import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import java.sql.*; import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -20,7 +22,7 @@ import java.util.Map;
* @author Bryan.Zhang * @author Bryan.Zhang
* @since 2.11.1 * @since 2.11.1
*/ */
public abstract class AbstractSqlRead implements SqlRead { public abstract class AbstractSqlRead<T> implements SqlRead<T> {
public final SQLParserVO config; public final SQLParserVO config;
private static LFLog LOG = LFLoggerManager.getLogger(AbstractSqlRead.class); private static LFLog LOG = LFLoggerManager.getLogger(AbstractSqlRead.class);
@ -29,10 +31,10 @@ public abstract class AbstractSqlRead implements SqlRead {
} }
@Override @Override
public Map<String/*规则唯一键*/, String/*规则内容*/> read() { public List<T> read() {
// 如果不需要读取直接返回 // 如果不需要读取直接返回
if (!needRead()) { if (!needRead()) {
return new HashMap<>(); return new ArrayList<>();
} }
checkConfig(); checkConfig();
@ -40,7 +42,7 @@ public abstract class AbstractSqlRead implements SqlRead {
// 如果允许就打印 sql 语句 // 如果允许就打印 sql 语句
logSqlIfEnable(sqlCmd); logSqlIfEnable(sqlCmd);
Map<String/*规则唯一键*/, String/*规则*/> result = new HashMap<>(); List<T> result = new ArrayList<>();
Connection conn = null; Connection conn = null;
PreparedStatement stmt = null; PreparedStatement stmt = null;
ResultSet rs = null; ResultSet rs = null;
@ -54,9 +56,6 @@ public abstract class AbstractSqlRead implements SqlRead {
rs = stmt.executeQuery(); rs = stmt.executeQuery();
while (rs.next()) { while (rs.next()) {
String xml = buildXmlElement(rs);
String uniqueKey = buildXmlElementUniqueKey(rs);
if (hasEnableFiled()){ if (hasEnableFiled()){
boolean enable = getEnableFiledValue(rs); boolean enable = getEnableFiledValue(rs);
// 如果停用直接跳过 // 如果停用直接跳过
@ -64,7 +63,7 @@ public abstract class AbstractSqlRead implements SqlRead {
continue; continue;
} }
} }
result.put(uniqueKey, xml); result.add(parse(rs));
} }
} catch (Exception e) { } catch (Exception e) {
throw new ELSQLException(e.getMessage()); throw new ELSQLException(e.getMessage());
@ -76,6 +75,8 @@ public abstract class AbstractSqlRead implements SqlRead {
return result; return result;
} }
protected abstract T parse(ResultSet rs) throws SQLException;
/** /**
* 是否包含启停字段 * 是否包含启停字段
*/ */
@ -88,10 +89,6 @@ public abstract class AbstractSqlRead implements SqlRead {
public abstract String buildQuerySql(); public abstract String buildQuerySql();
public abstract String buildXmlElement(ResultSet rs) throws SQLException;
public abstract String buildXmlElementUniqueKey(ResultSet rs) throws SQLException;
public abstract void checkConfig(); public abstract void checkConfig();
/** /**

View File

@ -2,6 +2,7 @@ package com.yomahub.liteflow.parser.sql.read;
import com.yomahub.liteflow.parser.constant.ReadType; import com.yomahub.liteflow.parser.constant.ReadType;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
@ -11,14 +12,14 @@ import java.util.Map;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public interface SqlRead { public interface SqlRead<T> {
/** /**
* 读取 * 读取
* *
* @return 返回读取到的数据 * @return 返回读取到的数据
*/ */
Map<String/*规则唯一键*/, String/*规则内容*/> read(); List<T> read();
/** /**
* 类型 * 类型

View File

@ -19,8 +19,8 @@ import java.util.Map;
* @since 2.11.1 * @since 2.11.1
*/ */
public class SqlReadFactory { public class SqlReadFactory {
private static final Map<ReadType, SqlRead> READ_MAP = new HashMap<>(); private static final Map<ReadType, SqlRead<?>> READ_MAP = new HashMap<>();
private static final Map<ReadType, SqlReadPollTask> POLL_TASK_MAP = new HashMap<>(); private static final Map<ReadType, SqlReadPollTask<?>> POLL_TASK_MAP = new HashMap<>();
public static void registerRead(SQLParserVO config) { public static void registerRead(SQLParserVO config) {
READ_MAP.put(ReadType.CHAIN, new ChainRead(config)); READ_MAP.put(ReadType.CHAIN, new ChainRead(config));
@ -28,19 +28,21 @@ public class SqlReadFactory {
} }
public static void registerSqlReadPollTask(ReadType readType) { public static void registerSqlReadPollTask(ReadType readType) {
SqlRead sqlRead = getSqlRead(readType); SqlRead<?> sqlRead = getSqlRead(readType);
if (ReadType.CHAIN.equals(readType)) { if (ReadType.CHAIN.equals(readType)) {
POLL_TASK_MAP.put(ReadType.CHAIN, new ChainReadPollTask(sqlRead)); POLL_TASK_MAP.put(ReadType.CHAIN, new ChainReadPollTask((ChainRead)sqlRead));
} else if (ReadType.SCRIPT.equals(readType)) { } else if (ReadType.SCRIPT.equals(readType)) {
POLL_TASK_MAP.put(ReadType.SCRIPT, new ScriptReadPollTask(sqlRead)); POLL_TASK_MAP.put(ReadType.SCRIPT, new ScriptReadPollTask((ScriptRead)sqlRead));
} }
} }
public static SqlRead getSqlRead(ReadType readType) { @SuppressWarnings("unchecked")
return READ_MAP.get(readType); public static <T> SqlRead<T> getSqlRead(ReadType readType) {
return (SqlRead<T>)READ_MAP.get(readType);
} }
public static SqlReadPollTask getSqlReadPollTask(ReadType readType) { @SuppressWarnings("unchecked")
return POLL_TASK_MAP.get(readType); public static <T> SqlReadPollTask<T> getSqlReadPollTask(ReadType readType) {
return (SqlReadPollTask<T>)POLL_TASK_MAP.get(readType);
} }
} }

View File

@ -5,6 +5,7 @@ import com.yomahub.liteflow.parser.constant.ReadType;
import com.yomahub.liteflow.parser.constant.SqlReadConstant; import com.yomahub.liteflow.parser.constant.SqlReadConstant;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException; import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.ChainVO;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -17,12 +18,26 @@ import java.sql.SQLException;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public class ChainRead extends AbstractSqlRead { public class ChainRead extends AbstractSqlRead<ChainVO> {
public ChainRead(SQLParserVO config) { public ChainRead(SQLParserVO config) {
super(config); super(config);
} }
@Override
protected ChainVO parse(ResultSet rs) throws SQLException {
ChainVO chainVO = new ChainVO();
chainVO.setChainId(getStringFromRsWithCheck(rs, super.config.getChainNameField()));
chainVO.setBody(getStringFromRsWithCheck(rs, super.config.getElDataField()));
if (StrUtil.isNotBlank(super.config.getNamespaceField())){
chainVO.setNamespace(getStringFromRs(rs, super.config.getNamespaceField()));
}
if (StrUtil.isNotBlank(super.config.getRouteField())){
chainVO.setRoute(getStringFromRs(rs, super.config.getRouteField()));
}
return chainVO;
}
@Override @Override
public boolean hasEnableFiled() { public boolean hasEnableFiled() {
String chainEnableField = super.config.getChainEnableField(); String chainEnableField = super.config.getChainEnableField();
@ -70,20 +85,6 @@ public class ChainRead extends AbstractSqlRead {
} }
} }
@Override
public String buildXmlElement(ResultSet rs) throws SQLException {
String elDataField = super.config.getElDataField();
return getStringFromRs(rs, elDataField);
}
@Override
public String buildXmlElementUniqueKey(ResultSet rs) throws SQLException {
String chainNameField = super.config.getChainNameField();
return getStringFromRsWithCheck(rs, chainNameField);
}
@Override @Override
public ReadType type() { public ReadType type() {
return ReadType.CHAIN; return ReadType.CHAIN;

View File

@ -8,6 +8,7 @@ import com.yomahub.liteflow.parser.constant.ReadType;
import com.yomahub.liteflow.parser.constant.SqlReadConstant; import com.yomahub.liteflow.parser.constant.SqlReadConstant;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException; import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead;
import com.yomahub.liteflow.parser.sql.read.vo.ScriptVO;
import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil; import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
@ -24,12 +25,23 @@ import java.util.Objects;
* @author houxinyu * @author houxinyu
* @since 2.11.1 * @since 2.11.1
*/ */
public class ScriptRead extends AbstractSqlRead { public class ScriptRead extends AbstractSqlRead<ScriptVO> {
public ScriptRead(SQLParserVO config) { public ScriptRead(SQLParserVO config) {
super(config); super(config);
} }
@Override
protected ScriptVO parse(ResultSet rs) throws SQLException {
ScriptVO scriptVO = new ScriptVO();
scriptVO.setNodeId(getStringFromRsWithCheck(rs, super.config.getScriptIdField()));
scriptVO.setName(getStringFromRs(rs, super.config.getScriptNameField()));
scriptVO.setType(getStringFromRsWithCheck(rs, super.config.getScriptTypeField()));
scriptVO.setLanguage(getStringFromRs(rs, super.config.getScriptLanguageField()));
scriptVO.setScript(getStringFromRsWithCheck(rs, super.config.getScriptDataField()));
return scriptVO;
}
@Override @Override
public boolean hasEnableFiled() { public boolean hasEnableFiled() {
String scriptEnableField = super.config.getScriptEnableField(); String scriptEnableField = super.config.getScriptEnableField();
@ -80,46 +92,6 @@ public class ScriptRead extends AbstractSqlRead {
} }
} }
@Override
public String buildXmlElement(ResultSet rs) throws SQLException {
String scriptDataField = super.config.getScriptDataField();
return getStringFromRs(rs, scriptDataField);
}
@Override
public String buildXmlElementUniqueKey(ResultSet rs) throws SQLException {
String scriptIdField = super.config.getScriptIdField();
String scriptNameField = super.config.getScriptNameField();
String scriptTypeField = super.config.getScriptTypeField();
String scriptLanguageField = super.config.getScriptLanguageField();
String id = getStringFromRsWithCheck(rs, scriptIdField);
String name = getStringFromRsWithCheck(rs, scriptNameField);
String type = getStringFromRsWithCheck(rs, scriptTypeField);
String language = withLanguage() ? getStringFromRs(rs, scriptLanguageField) : null;
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 (withLanguage() && !ScriptTypeEnum.checkScriptType(language)) {
throw new ELSQLException(StrUtil.format("The language value[{}] is invalid", language));
}
List<String> keys = CollUtil.newArrayList(id, type, name);
if (StrUtil.isNotBlank(language)) {
keys.add(language);
}
return StrUtil.join(StrUtil.COLON, keys);
}
@Override @Override
public boolean needRead() { public boolean needRead() {
if (StrUtil.isBlank(super.config.getScriptTableName())) { if (StrUtil.isBlank(super.config.getScriptTableName())) {

View File

@ -0,0 +1,44 @@
package com.yomahub.liteflow.parser.sql.read.vo;
public class ChainVO {
private String chainId;
private String route;
private String namespace;
private String body;
public String getChainId() {
return chainId;
}
public void setChainId(String chainId) {
this.chainId = chainId;
}
public String getRoute() {
return route;
}
public void setRoute(String route) {
this.route = route;
}
public String getNamespace() {
return namespace;
}
public void setNamespace(String namespace) {
this.namespace = namespace;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}

View File

@ -0,0 +1,64 @@
package com.yomahub.liteflow.parser.sql.read.vo;
public class ScriptVO {
private String nodeId;
private String type;
private String name;
private String language;
private Boolean enable;
private String script;
public String getNodeId() {
return nodeId;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
}

View File

@ -11,9 +11,12 @@ import com.yomahub.liteflow.parser.constant.ReadType;
import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.helper.NodeConvertHelper;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException; import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.polling.SqlReadPollTask;
import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead;
import com.yomahub.liteflow.parser.sql.read.SqlRead; import com.yomahub.liteflow.parser.sql.read.SqlRead;
import com.yomahub.liteflow.parser.sql.read.SqlReadFactory; import com.yomahub.liteflow.parser.sql.read.SqlReadFactory;
import com.yomahub.liteflow.parser.sql.read.vo.ChainVO;
import com.yomahub.liteflow.parser.sql.read.vo.ScriptVO;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -87,30 +90,30 @@ public class JDBCHelper {
* @return 数据内容 * @return 数据内容
*/ */
public String getContent() { public String getContent() {
SqlRead chainRead = SqlReadFactory.getSqlRead(ReadType.CHAIN); SqlRead<ChainVO> chainRead = SqlReadFactory.getSqlRead(ReadType.CHAIN);
SqlRead scriptRead = SqlReadFactory.getSqlRead(ReadType.SCRIPT); SqlRead<ScriptVO> scriptRead = SqlReadFactory.getSqlRead(ReadType.SCRIPT);
// 获取 chain 数据 // 获取 chain 数据
Map<String, String> chainMap = chainRead.read(); List<ChainVO> chainVOList = chainRead.read();
List<String> chainList = new ArrayList<>(); List<String> chainList = new ArrayList<>();
chainMap.entrySet().stream() chainVOList.forEach(
.filter(entry -> StrUtil.isNotBlank(entry.getValue())) chainVO -> chainList.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainVO.getChainId()), StrUtil.emptyIfNull(chainVO.getNamespace()), StrUtil.emptyIfNull(chainVO.getRoute()), chainVO.getBody()))
.forEach( );
entry -> chainList.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(entry.getKey()), entry.getValue()))
);
String chainsContent = CollUtil.join(chainList, StrUtil.EMPTY); String chainsContent = CollUtil.join(chainList, StrUtil.EMPTY);
// 获取脚本数据 // 获取脚本数据
Map<String, String> scriptMap = scriptRead.read(); List<ScriptVO> scriptVOList = scriptRead.read();
List<String> scriptList = new ArrayList<>(); List<String> scriptList = new ArrayList<>();
scriptMap.forEach((scriptKey, elData) -> {
NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(scriptKey); scriptVOList.forEach(scriptVO -> {
String id = scriptVO.getNodeId(); String id = scriptVO.getNodeId();
String name = scriptVO.getName(); String name = scriptVO.getName();
String type = scriptVO.getType(); String type = scriptVO.getType();
String language = scriptVO.getLanguage(); String language = scriptVO.getLanguage();
String elData = scriptVO.getScript();
if (StringUtils.isNotBlank(scriptVO.getLanguage())) { if (StringUtils.isNotBlank(scriptVO.getLanguage())) {
scriptList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, language, elData)); scriptList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, language, elData));
@ -118,11 +121,14 @@ public class JDBCHelper {
scriptList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, elData)); scriptList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, elData));
} }
}); });
String nodesContent = StrUtil.format(NODE_XML_PATTERN, CollUtil.join(scriptList, StrUtil.EMPTY)); String nodesContent = StrUtil.format(NODE_XML_PATTERN, CollUtil.join(scriptList, StrUtil.EMPTY));
// 初始化轮询任务 // 初始化轮询任务
SqlReadFactory.getSqlReadPollTask(ReadType.CHAIN).initData(chainMap); SqlReadPollTask<ChainVO> sqlReadPollTask4Chain = SqlReadFactory.getSqlReadPollTask(ReadType.CHAIN);
SqlReadFactory.getSqlReadPollTask(ReadType.SCRIPT).initData(scriptMap); sqlReadPollTask4Chain.initData(chainVOList);
SqlReadPollTask<ScriptVO> sqlReadPollTask4Script = SqlReadFactory.getSqlReadPollTask(ReadType.SCRIPT);
sqlReadPollTask4Script.initData(scriptVOList);
return StrUtil.format(XML_PATTERN, nodesContent, chainsContent); return StrUtil.format(XML_PATTERN, nodesContent, chainsContent);
} }

View File

@ -56,6 +56,16 @@ public class SQLParserVO {
*/ */
private String elDataField = "el_data"; private String elDataField = "el_data";
/**
* 决策路由字段
*/
private String routeField;
/**
* 命名空间字段
*/
private String namespaceField;
/** /**
* 是否启动某一条chain * 是否启动某一条chain
*/ */
@ -310,4 +320,20 @@ public class SQLParserVO {
public boolean hasEnableField() { public boolean hasEnableField() {
return StrUtil.isNotBlank(chainEnableField) || StrUtil.isNotBlank(scriptEnableField); return StrUtil.isNotBlank(chainEnableField) || StrUtil.isNotBlank(scriptEnableField);
} }
public String getRouteField() {
return routeField;
}
public void setRouteField(String routeField) {
this.routeField = routeField;
}
public String getNamespaceField() {
return namespaceField;
}
public void setNamespaceField(String namespaceField) {
this.namespaceField = namespaceField;
}
} }

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-script-plugin</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-script-kotlin</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-core</artifactId>
<version>${revision}</version>
<optional>true</optional>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,15 @@
package com.yomahub.liteflow.script.kotlin;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.jsr223.JSR223ScriptExecutor;
/**
* Kotlin脚本执行器
* @author DaleLee
*/
public class KotlinScriptExecutor extends JSR223ScriptExecutor {
@Override
public ScriptTypeEnum scriptType() {
return ScriptTypeEnum.KOTLIN;
}
}

View File

@ -0,0 +1,2 @@
# Kotlin的实现
com.yomahub.liteflow.script.kotlin.KotlinScriptExecutor

View File

@ -23,6 +23,7 @@
<module>liteflow-script-lua</module> <module>liteflow-script-lua</module>
<module>liteflow-script-aviator</module> <module>liteflow-script-aviator</module>
<module>liteflow-script-java</module> <module>liteflow-script-java</module>
<module>liteflow-script-kotlin</module>
</modules> </modules>
</project> </project>

View File

@ -8,6 +8,7 @@ import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.exception.ConfigErrorException; import com.yomahub.liteflow.exception.ConfigErrorException;
import com.yomahub.liteflow.spi.PathContentParser; import com.yomahub.liteflow.spi.PathContentParser;
import com.yomahub.liteflow.util.PathMatchUtil;
import org.noear.solon.Utils; import org.noear.solon.Utils;
import java.io.File; import java.io.File;
@ -47,9 +48,10 @@ public class SolonPathContentParser implements PathContentParser {
if (CollectionUtil.isEmpty(pathList)) { if (CollectionUtil.isEmpty(pathList)) {
throw new ConfigErrorException("rule source must not be null"); throw new ConfigErrorException("rule source must not be null");
} }
List<String> absolutePathList = PathMatchUtil.searchAbsolutePath(pathList);
List<URL> allResource = new ArrayList<>(); List<URL> allResource = new ArrayList<>();
for (String path : pathList) {
for (String path : absolutePathList) {
// 如果 path 是绝对路径且这个文件存在时我们认为这是一个本地文件路径而并非classpath路径 // 如果 path 是绝对路径且这个文件存在时我们认为这是一个本地文件路径而并非classpath路径
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) { if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
allResource.add(new File(path).toURI().toURL()); allResource.add(new File(path).toURI().toURL());

View File

@ -1,24 +1,27 @@
package com.yomahub.liteflow.spi.spring; package com.yomahub.liteflow.spi.spring;
import cn.hutool.core.annotation.AnnotationUtil; import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine; import com.yomahub.liteflow.annotation.*;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.annotation.util.AnnoUtil; import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.core.proxy.DeclWarpBean; import com.yomahub.liteflow.core.proxy.DeclWarpBean;
import com.yomahub.liteflow.core.proxy.MethodWrapBean; import com.yomahub.liteflow.core.proxy.MethodWrapBean;
import com.yomahub.liteflow.core.proxy.ParameterWrapBean;
import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.CmpDefinitionException; import com.yomahub.liteflow.exception.CmpDefinitionException;
import com.yomahub.liteflow.spi.DeclComponentParser; import com.yomahub.liteflow.spi.DeclComponentParser;
import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.lang.reflect.Parameter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
/** /**
* Spring环境声明式组件解析器实现 * Spring环境声明式组件解析器实现
@ -73,7 +76,16 @@ public class SpringDeclComponentParser implements DeclComponentParser {
nodeType = liteflowMethod.nodeType(); nodeType = liteflowMethod.nodeType();
} }
return new DeclInfo(currNodeId, currNodeName, nodeType, method.getDeclaringClass(), new MethodWrapBean(method, liteflowMethod, liteflowRetry));
Parameter[] parameters = method.getParameters();
List<ParameterWrapBean> parameterList = IntStream.range(0, parameters.length).boxed().map(index -> {
Parameter parameter = parameters[index];
return new ParameterWrapBean(parameter.getType(), AnnotationUtil.getAnnotation(parameter, LiteflowFact.class), index);
}).collect(Collectors.toList());
return new DeclInfo(currNodeId, currNodeName, nodeType, method.getDeclaringClass(), new MethodWrapBean(method, liteflowMethod, liteflowRetry, parameterList));
}).filter(declInfo -> StrUtil.isNotBlank(declInfo.getNodeId())).collect(Collectors.groupingBy(DeclInfo::getNodeId)); }).filter(declInfo -> StrUtil.isNotBlank(declInfo.getNodeId())).collect(Collectors.groupingBy(DeclInfo::getNodeId));
return definitionMap.entrySet().stream().map(entry -> { return definitionMap.entrySet().stream().map(entry -> {

View File

@ -13,6 +13,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -63,6 +64,29 @@ public class AbsoluteConfigPathELDeclMultiSpringbootTest extends BaseTest {
}); });
} }
@Test
@EnabledIf("isWindows")
public void testAbsPath() throws Exception{
Assertions.assertTrue(() -> {
LiteflowConfig config = LiteflowConfigGetter.get();
config.setRuleSource(StrUtil.format("{}\\sub\\**\\*.xml",rootDir));
flowExecutor.reloadRule();
return flowExecutor.execute2Resp("chain1", "arg").isSuccess();
});
}
public static boolean isWindows() {
try {
String osName = System.getProperty("os.name");
if (osName.isEmpty()) return false;
else {
return osName.contains("Windows");
}
} catch (Exception e) {
return false;
}
}
@BeforeAll @BeforeAll
public static void createFiles() { public static void createFiles() {
rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath()); rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath());

View File

@ -0,0 +1,97 @@
package com.yomahub.liteflow.test.processFact;
import cn.hutool.core.date.DateUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.processFact.context.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
/**
* process方法上的fact映射参数测试
* @author Bryan.Zhang
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/processFact/application.properties")
@SpringBootTest(classes = ProcessFactELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.processFact.cmp"})
public class ProcessFactELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 最基本的情况
@Test
public void testFact1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", createContext());
Assertions.assertTrue(response.isSuccess());
TestContext context = response.getContextBean(TestContext.class);
Assertions.assertEquals("jack", context.getUser().getName());
}
// 多上下文自动搜寻
@Test
public void testFact2() throws Exception {
TestContext testContext = createContext();
Demo1Context demo1Context = new Demo1Context();
demo1Context.setData1("xxxx");
demo1Context.setData2(99);
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg", demo1Context, testContext);
Assertions.assertTrue(response.isSuccess());
TestContext context = response.getContextBean(TestContext.class);
Assertions.assertEquals(20, context.getUser().getCompany().getHeadCount());
}
// 多上下文都有user指定上下文中的user
@Test
public void testFact3() throws Exception {
TestContext testContext = createContext();
Demo2Context demo2Context = createDemo2Context();
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg", testContext, demo2Context);
Assertions.assertTrue(response.isSuccess());
Demo2Context context = response.getContextBean(Demo2Context.class);
Assertions.assertEquals("rose", context.getUser().getName());
}
// 多上下文都有user指定上下文中的user
@Test
public void testFact4() throws Exception {
Demo3Context demo3Context = createDemo3Context();
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg", demo3Context);
Assertions.assertTrue(response.isSuccess());
Demo3Context context = response.getContextBean(Demo3Context.class);
Assertions.assertEquals("jelly", context.getUser().getName());
}
private TestContext createContext(){
Company company = new Company("XXX有限公司", "黄河路34号303室", 400);
User user = new User("张三", 18, DateUtil.parseDate("1990-08-20"), company);
return new TestContext(user, "this is data");
}
private Demo2Context createDemo2Context(){
Company company = new Company("XXX有限公司", "和平路12号101室", 600);
User user = new User("李四", 28, DateUtil.parseDate("1990-06-01"), company);
return new Demo2Context("xxx", user);
}
private Demo3Context createDemo3Context(){
Company company = new Company("XXX有限公司", "和平路12号101室", 600);
User user = new User("王五", 28, DateUtil.parseDate("1990-06-01"), company);
return new Demo3Context("xxx", user);
}
}

View File

@ -0,0 +1,43 @@
package com.yomahub.liteflow.test.processFact.cmp;
import cn.hutool.core.collection.ConcurrentHashSet;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.processFact.context.Company;
import com.yomahub.liteflow.test.processFact.context.User;
import org.springframework.stereotype.Component;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON, nodeId = "a")
public void processA(NodeComponent bindCmp,
@LiteflowFact("user") User user,
@LiteflowFact("user.company.address") String address) {
user.setName("jack");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON, nodeId = "b")
public void processB(NodeComponent bindCmp,
@LiteflowFact("user.company") Company company,
@LiteflowFact("data2") Integer data) {
company.setHeadCount(20);
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON, nodeId = "c")
public void processC(NodeComponent bindCmp,
@LiteflowFact("demo2Context.user") User user) {
user.setName("rose");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON, nodeId = "d")
public void processD(NodeComponent bindCmp,
@LiteflowFact("ctx.user") User user) {
user.setName("jelly");
}
}

View File

@ -0,0 +1,40 @@
package com.yomahub.liteflow.test.processFact.context;
public class Company {
private String name;
private String address;
private int headCount;
public Company(String name, String address, int headCount) {
this.name = name;
this.address = address;
this.headCount = headCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getHeadCount() {
return headCount;
}
public void setHeadCount(int headCount) {
this.headCount = headCount;
}
}

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.processFact.context;
public class Demo1Context {
private String data1;
private Integer data2;
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public Integer getData2() {
return data2;
}
public void setData2(Integer data2) {
this.data2 = data2;
}
}

View File

@ -0,0 +1,29 @@
package com.yomahub.liteflow.test.processFact.context;
public class Demo2Context {
private String data1;
private User user;
public Demo2Context(String data1, User user) {
this.data1 = data1;
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@ -0,0 +1,32 @@
package com.yomahub.liteflow.test.processFact.context;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean("ctx")
public class Demo3Context {
private String data1;
private User user;
public Demo3Context(String data1, User user) {
this.data1 = data1;
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@ -0,0 +1,29 @@
package com.yomahub.liteflow.test.processFact.context;
public class TestContext {
private User user;
private String data1;
public TestContext(User user, String data1) {
this.user = user;
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
}

View File

@ -0,0 +1,53 @@
package com.yomahub.liteflow.test.processFact.context;
import java.util.Date;
public class User {
private String name;
private int age;
private Date birthday;
private Company company;
public User(String name, int age, Date birthday, Company company) {
this.name = name;
this.age = age;
this.birthday = birthday;
this.company = company;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}

View File

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

View File

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

View File

@ -15,6 +15,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -65,6 +66,29 @@ public class AbsoluteConfigPathELDeclSpringbootTest extends BaseTest {
}); });
} }
@Test
@EnabledIf("isWindows")
public void testAbsPath() throws Exception{
Assertions.assertTrue(() -> {
LiteflowConfig config = LiteflowConfigGetter.get();
config.setRuleSource(StrUtil.format("{}\\sub\\**\\*.xml",rootDir));
flowExecutor.reloadRule();
return flowExecutor.execute2Resp("chain1", "arg").isSuccess();
});
}
public static boolean isWindows() {
try {
String osName = System.getProperty("os.name");
if (osName.isEmpty()) return false;
else {
return osName.contains("Windows");
}
} catch (Exception e) {
return false;
}
}
@BeforeAll @BeforeAll
public static void createFiles() { public static void createFiles() {
rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath()); rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath());

View File

@ -0,0 +1,97 @@
package com.yomahub.liteflow.test.processFact;
import cn.hutool.core.date.DateUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.processFact.context.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
/**
* process方法上的fact映射参数测试
* @author Bryan.Zhang
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/processFact/application.properties")
@SpringBootTest(classes = ProcessFactELDeclSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.processFact.cmp"})
public class ProcessFactELDeclSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 最基本的情况
@Test
public void testFact1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", createContext());
Assertions.assertTrue(response.isSuccess());
TestContext context = response.getContextBean(TestContext.class);
Assertions.assertEquals("jack", context.getUser().getName());
}
// 多上下文自动搜寻
@Test
public void testFact2() throws Exception {
TestContext testContext = createContext();
Demo1Context demo1Context = new Demo1Context();
demo1Context.setData1("xxxx");
demo1Context.setData2(99);
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg", demo1Context, testContext);
Assertions.assertTrue(response.isSuccess());
TestContext context = response.getContextBean(TestContext.class);
Assertions.assertEquals(20, context.getUser().getCompany().getHeadCount());
}
// 多上下文都有user指定上下文中的user
@Test
public void testFact3() throws Exception {
TestContext testContext = createContext();
Demo2Context demo2Context = createDemo2Context();
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg", testContext, demo2Context);
Assertions.assertTrue(response.isSuccess());
Demo2Context context = response.getContextBean(Demo2Context.class);
Assertions.assertEquals("rose", context.getUser().getName());
}
// 多上下文都有user指定上下文中的user
@Test
public void testFact4() throws Exception {
Demo3Context demo3Context = createDemo3Context();
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg", demo3Context);
Assertions.assertTrue(response.isSuccess());
Demo3Context context = response.getContextBean(Demo3Context.class);
Assertions.assertEquals("jelly", context.getUser().getName());
}
private TestContext createContext(){
Company company = new Company("XXX有限公司", "黄河路34号303室", 400);
User user = new User("张三", 18, DateUtil.parseDate("1990-08-20"), company);
return new TestContext(user, "this is data");
}
private Demo2Context createDemo2Context(){
Company company = new Company("XXX有限公司", "和平路12号101室", 600);
User user = new User("李四", 28, DateUtil.parseDate("1990-06-01"), company);
return new Demo2Context("xxx", user);
}
private Demo3Context createDemo3Context(){
Company company = new Company("XXX有限公司", "和平路12号101室", 600);
User user = new User("王五", 28, DateUtil.parseDate("1990-06-01"), company);
return new Demo3Context("xxx", user);
}
}

View File

@ -0,0 +1,28 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.processFact.cmp;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.processFact.context.User;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON)
public void process(NodeComponent bindCmp,
@LiteflowFact("user") User user,
@LiteflowFact("user.company.address") String address) {
user.setName("jack");
}
}

View File

@ -0,0 +1,28 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.processFact.cmp;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.processFact.context.Company;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON)
public void process(NodeComponent bindCmp,
@LiteflowFact("user.company") Company company,
@LiteflowFact("data2") Integer data) {
company.setHeadCount(20);
}
}

View File

@ -0,0 +1,27 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.processFact.cmp;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.processFact.context.User;
import org.springframework.stereotype.Component;
@Component("c")
public class CCmp {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON)
public void process(NodeComponent bindCmp,
@LiteflowFact("demo2Context.user") User user) {
user.setName("rose");
}
}

View File

@ -0,0 +1,27 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.processFact.cmp;
import com.yomahub.liteflow.annotation.LiteflowFact;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.test.processFact.context.User;
import org.springframework.stereotype.Component;
@Component("d")
public class DCmp {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeType = NodeTypeEnum.COMMON)
public void process(NodeComponent bindCmp,
@LiteflowFact("ctx.user") User user) {
user.setName("jelly");
}
}

View File

@ -0,0 +1,40 @@
package com.yomahub.liteflow.test.processFact.context;
public class Company {
private String name;
private String address;
private int headCount;
public Company(String name, String address, int headCount) {
this.name = name;
this.address = address;
this.headCount = headCount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getHeadCount() {
return headCount;
}
public void setHeadCount(int headCount) {
this.headCount = headCount;
}
}

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.processFact.context;
public class Demo1Context {
private String data1;
private Integer data2;
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public Integer getData2() {
return data2;
}
public void setData2(Integer data2) {
this.data2 = data2;
}
}

View File

@ -0,0 +1,29 @@
package com.yomahub.liteflow.test.processFact.context;
public class Demo2Context {
private String data1;
private User user;
public Demo2Context(String data1, User user) {
this.data1 = data1;
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@ -0,0 +1,32 @@
package com.yomahub.liteflow.test.processFact.context;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean("ctx")
public class Demo3Context {
private String data1;
private User user;
public Demo3Context(String data1, User user) {
this.data1 = data1;
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}

View File

@ -0,0 +1,29 @@
package com.yomahub.liteflow.test.processFact.context;
public class TestContext {
private User user;
private String data1;
public TestContext(User user, String data1) {
this.user = user;
this.data1 = data1;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
}

View File

@ -0,0 +1,53 @@
package com.yomahub.liteflow.test.processFact.context;
import java.util.Date;
public class User {
private String name;
private int age;
private Date birthday;
private Company company;
public User(String name, int age, Date birthday, Company company) {
this.name = name;
this.age = age;
this.birthday = birthday;
this.company = company;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}

View File

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

View File

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

View File

@ -14,6 +14,7 @@ import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;
import java.util.Objects; import java.util.Objects;
@ -50,6 +51,29 @@ public class AbsoluteConfigPathTest extends BaseTest {
}); });
} }
@Test
@EnabledIf("isWindows")
public void testAbsPath() throws Exception{
Assertions.assertTrue(() -> {
LiteflowConfig config = new LiteflowConfig();
config.setRuleSource(StrUtil.format("{}\\sub\\**\\*.xml",rootDir));
flowExecutor = FlowExecutorHolder.loadInstance(config);
return flowExecutor.execute2Resp("chain1", "arg").isSuccess();
});
}
public static boolean isWindows() {
try {
String osName = System.getProperty("os.name");
if (osName.isEmpty()) return false;
else {
return osName.contains("Windows");
}
} catch (Exception e) {
return false;
}
}
@BeforeAll @BeforeAll
public static void createFiles() { public static void createFiles() {
rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath()); rootDir = FileUtil.getAbsolutePath(ResourceUtil.getResource("").getPath());

View File

@ -0,0 +1,66 @@
package com.yomahub.liteflow.test.namespace;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.NoMatchedRouteChainException;
import com.yomahub.liteflow.exception.RouteChainNotFoundException;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
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 javax.annotation.Resource;
import java.util.List;
/**
* springboot环境EL常规的例子测试
*
* @author Bryan.Zhang
*/
@TestPropertySource(value = "classpath:/namespace/application.properties")
@SpringBootTest(classes = RouteSpringbootNamespaceTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.namespace.cmp" })
public class RouteSpringbootNamespaceTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// n1 space中的两个链路都能匹配
@Test
public void testNamespaceRoute1() throws Exception {
List<LiteflowResponse> responseList = flowExecutor.executeRouteChain("n1", 15, DefaultContext.class);
LiteflowResponse response1 = responseList.stream().filter(
liteflowResponse -> liteflowResponse.getChainId().equals("r_chain1")
).findFirst().orElse(null);
assert response1 != null;
Assertions.assertTrue(response1.isSuccess());
Assertions.assertEquals("b==>a", response1.getExecuteStepStr());
LiteflowResponse response2 = responseList.stream().filter(
liteflowResponse -> liteflowResponse.getChainId().equals("r_chain2")
).findFirst().orElse(null);
assert response2 != null;
Assertions.assertTrue(response2.isSuccess());
Assertions.assertEquals("a==>b", response2.getExecuteStepStr());
}
// n1这个namespace中没有规则被匹配上
@Test
public void testNamespaceRoute2() throws Exception {
Assertions.assertThrows(NoMatchedRouteChainException.class, () -> flowExecutor.executeRouteChain("n1", 8, DefaultContext.class));
}
// 没有n3这个namespace
@Test
public void testNamespaceRoute3() throws Exception {
Assertions.assertThrows(RouteChainNotFoundException.class, () -> flowExecutor.executeRouteChain("n3", 8, DefaultContext.class));
}
}

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.namespace.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.namespace.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,13 @@
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeBooleanComponent;
@LiteflowComponent("r1")
public class R1 extends NodeBooleanComponent {
@Override
public boolean processBoolean() throws Exception {
int testInt = this.getRequestData();
return testInt >= 10 && testInt <= 20;
}
}

View File

@ -0,0 +1,13 @@
package com.yomahub.liteflow.test.namespace.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeBooleanComponent;
@LiteflowComponent("r2")
public class R2 extends NodeBooleanComponent {
@Override
public boolean processBoolean() throws Exception {
int testInt = this.getRequestData();
return testInt > 100;
}
}

View File

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

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
<flow>
<chain name="r_chain1" namespace="n1">
<route>
r1
</route>
<body>
THEN(b,a);
</body>
</chain>
<chain name="r_chain2" namespace="n1">
<route>
OR(r1,r2)
</route>
<body>
THEN(a,b);
</body>
</chain>
<chain name="r_chain3" namespace="n2">
<route>
r2
</route>
<body>
THEN(b,b);
</body>
</chain>
</flow>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-testcase-el</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-testcase-el-script-kotlin-springboot</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-kotlin</artifactId>
<version>${revision}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.script;
import com.yomahub.liteflow.core.FlowInitHook;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner;
import com.yomahub.liteflow.spring.ComponentScanner;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.junit.jupiter.api.AfterAll;
public class BaseTest {
@AfterAll
public static void cleanScanCache() {
ComponentScanner.cleanCache();
FlowBus.cleanCache();
ExecutorHelper.loadInstance().clearExecutorServiceMap();
SpiFactoryCleaner.clean();
LiteflowConfigGetter.clean();
FlowInitHook.cleanHook();
FlowBus.clearStat();
}
}

View File

@ -0,0 +1,36 @@
package com.yomahub.liteflow.test.script.kotlin.cmpdata;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/cmpdata/application.properties")
@SpringBootTest(classes = LiteFlowCmpDataKotlinScriptELTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.script.kotlin.cmpdata.cmp" })
public class LiteFlowCmpDataKotlinScriptELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testCmpData1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response.getFirstContextBean();
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("1995-10-01", context.getData("s1"));
}
}

View File

@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.script.kotlin.cmpdata.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(this.getCmpData(String.class));
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,91 @@
package com.yomahub.liteflow.test.script.kotlin.common;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
/**
* Kotlin 脚本测试
*
* @author DaleLee
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/common/application.properties")
@SpringBootTest(classes = LiteFlowKotlinScriptCommonELTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.kotlin.common.cmp"})
public class LiteFlowKotlinScriptCommonELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testCommonScript1() {
LiteflowResponse response = flowExecutor.execute2Resp("testCommonScript1", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals(Integer.valueOf(5), context.getData("s1"));
}
@Test
public void testForScript1() {
DefaultContext context = new DefaultContext();
context.setData("k1", 1);
context.setData("k2", 2);
LiteflowResponse response = flowExecutor.execute2Resp("testForScript1", "arg", context);
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s2==>a==>a==>a", response.getExecuteStepStr());
}
@Test
public void testBooleanScript1() {
LiteflowResponse response = flowExecutor.execute2Resp("testBooleanScript1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s3==>b", response.getExecuteStepStr());
}
@Test
public void testBooleanScript2() {
LiteflowResponse response = flowExecutor.execute2Resp("testBooleanScript2", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s4", response.getExecuteStepStr());
}
@Test
public void testBooleanScript3() {
DefaultContext context = new DefaultContext();
context.setData("count", 2);
LiteflowResponse response = flowExecutor.execute2Resp("testBooleanScript3", "arg", context);
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s3==>b==>s6==>s3==>b==>s6==>s3==>b==>s6",response.getExecuteStepStr());
}
@Test
public void testSwitchScript1() {
DefaultContext context = new DefaultContext();
context.setData("id", "c");
LiteflowResponse response = flowExecutor.execute2Resp("testSwitchScript1", "arg", context);
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("s5==>c", response.getExecuteStepStr());
}
@Test
public void testFileScript1() {
LiteflowResponse response = flowExecutor.execute2Resp("testFileScript1", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals(Integer.valueOf(6), context.getData("s7"));
}
}

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.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.common.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,57 @@
package com.yomahub.liteflow.test.script.kotlin.contextbean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.script.BaseTest;
import com.yomahub.liteflow.test.script.kotlin.contextbean.bean.CheckContext;
import com.yomahub.liteflow.test.script.kotlin.contextbean.bean.Order2Context;
import com.yomahub.liteflow.test.script.kotlin.contextbean.bean.OrderContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/contextbean/application.properties")
@SpringBootTest(classes = LiteFlowScriptContextbeanKotlinELTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.script.kotlin.contextbean.cmp",
"com.yomahub.liteflow.test.script.kotlin.contextbean.bean" })
public class LiteFlowScriptContextbeanKotlinELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testContextBean1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", OrderContext.class, CheckContext.class,
Order2Context.class);
Assertions.assertTrue(response.isSuccess());
OrderContext orderContext = response.getContextBean(OrderContext.class);
CheckContext checkContext = response.getContextBean(CheckContext.class);
Order2Context order2Context = response.getContextBean(Order2Context.class);
Assertions.assertEquals("order1", orderContext.getOrderNo());
Assertions.assertEquals("sign1", checkContext.getSign());
Assertions.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);
Assertions.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,28 @@
package com.yomahub.liteflow.test.script.kotlin.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,37 @@
package com.yomahub.liteflow.test.script.kotlin.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,40 @@
package com.yomahub.liteflow.test.script.kotlin.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,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.kotlin.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.kotlin.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.kotlin.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,37 @@
package com.yomahub.liteflow.test.script.kotlin.meta;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/meta/application.properties")
@SpringBootTest(classes = LiteflowKotlinScriptMetaELTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.script.kotlin.meta.cmp"})
public class LiteflowKotlinScriptMetaELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testMeta() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response.getFirstContextBean();
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("chain1", context.getData("currChainId"));
Assertions.assertEquals("arg", context.getData("requestData"));
Assertions.assertEquals("s1", context.getData("nodeId"));
}
}

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.kotlin.meta.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.meta.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.meta.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,48 @@
package com.yomahub.liteflow.test.script.kotlin.refresh;
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.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/refresh/application.properties")
@SpringBootTest(classes = LiteflowKotlinScriptRefreshELTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.script.kotlin.refresh.cmp" })
public class LiteflowKotlinScriptRefreshELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 测试脚本的热重载
@Test
public void testRefresh1() throws Exception {
// 根据配置加载的应该是flow.xml执行原来的规则
LiteflowResponse responseOld = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(responseOld.isSuccess());
Assertions.assertEquals("d==>s1[选择脚本]==>a", responseOld.getExecuteStepStr());
// 更改规则重新加载更改的规则内容从flow_update.xml里读取这里只是为了模拟下获取新的内容不一定是从文件中读取
String newContent = ResourceUtil.readUtf8Str("classpath: /refresh/flow_update.xml");
// 进行刷新
FlowBus.refreshFlowMetaData(FlowParserTypeEnum.TYPE_EL_XML, newContent);
// 重新执行chain2这个链路结果会变
LiteflowResponse responseNew = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(responseNew.isSuccess());
Assertions.assertEquals("d==>s1[选择脚本_改]==>b==>s2[普通脚本_新增]", responseNew.getExecuteStepStr());
}
}

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.kotlin.refresh.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.refresh.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("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.kotlin.refresh.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

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.script.kotlin.refresh.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DefaultContext;
@LiteflowComponent("d")
public class DCmp extends NodeComponent {
@Override
public void process() {
DefaultContext context = this.getFirstContextBean();
context.setData("count", 198);
System.out.println("DCmp executed!");
}
}

View File

@ -0,0 +1,114 @@
package com.yomahub.liteflow.test.script.kotlin.remove;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.script.ScriptExecutor;
import com.yomahub.liteflow.script.ScriptExecutorFactory;
import com.yomahub.liteflow.script.exception.ScriptLoadException;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
import java.util.List;
/**
* 测试脚本的卸载和重载功能
*
* @author DaleLee
*/
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/remove/application.properties")
@SpringBootTest(classes = LiteFlowKotlinScriptRemoveELTest.class)
@EnableAutoConfiguration
public class LiteFlowKotlinScriptRemoveELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
private ScriptExecutor scriptExecutor = ScriptExecutorFactory.loadInstance()
.getScriptExecutor(ScriptTypeEnum.KOTLIN.getDisplayName());
// 仅卸载脚本
@Test
public void testUnload() {
flowExecutor.reloadRule();
// 获取节点id
List<String> nodeIds = scriptExecutor.getNodeIds();
Assertions.assertEquals(2, nodeIds.size());
Assertions.assertTrue(nodeIds.contains("s1"));
Assertions.assertTrue(nodeIds.contains("s2"));
// 保证脚本可以正常运行
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals(Integer.valueOf(6), context.getData("s1"));
// 卸载脚本
scriptExecutor.unLoad("s1");
response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals(ScriptLoadException.class, response.getCause().getClass());
Assertions.assertEquals("script for node[s1] is not loaded", response.getMessage());
// 脚本已卸载
Assertions.assertFalse(scriptExecutor.getNodeIds().contains("s1"));
}
// 卸载节点和脚本
@Test
public void testRemove() {
flowExecutor.reloadRule();
// 保证脚本可以正常运行
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals(Integer.valueOf(5), context.getData("s2"));
// 卸载节点
FlowBus.unloadScriptNode("s2");
// chain 报脚本加载错误
response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertEquals(ScriptLoadException.class, response.getCause().getClass());
// chian 会找不到节点
Assertions.assertThrows(ELParseException.class,
() -> LiteFlowChainELBuilder.createChain()
.setChainId("chain3")
.setEL("THEN(s2)")
.build());
// 节点已卸载
Assertions.assertFalse(FlowBus.containNode("s2"));
// 脚本已卸载
Assertions.assertFalse(scriptExecutor.getNodeIds().contains("s2"));
}
// 重载脚本
@Test
public void testReloadScript() {
flowExecutor.reloadRule();
String script = "(bindings[\"defaultContext\"] as? DefaultContext)?.setData(\"s1\", \"abc\")";
FlowBus.reloadScript("s1", script);
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response.getFirstContextBean();
// 执行结果变更
Assertions.assertEquals("abc", context.getData("s1"));
// 脚本变更
Assertions.assertEquals(FlowBus.getNode("s1").getScript(), script);
}
}

View File

@ -0,0 +1,103 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.ScriptBeanMethodInvokeException;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.script.ScriptBeanManager;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.script.BaseTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
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.junit.jupiter.SpringExtension;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/scriptbean/application.properties")
@SpringBootTest(classes = LiteFlowScriptScriptbeanKotlinELTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.script.kotlin.scriptbean.cmp",
"com.yomahub.liteflow.test.script.kotlin.scriptbean.bean" })
public class LiteFlowScriptScriptbeanKotlinELTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testScriptBean1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals("hello", context.getData("demo"));
}
@Test
public void testScriptBean2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals("hello,kobe", context.getData("demo"));
}
// 测试scriptBean includeMethodName配置包含情况下
@Test
public void testScriptBean3() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals("hello,kobe", context.getData("demo"));
}
// 测试scriptBean includeMethodName配置不包含情况下
@Test
public void testScriptBean4() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass());
}
// 测试scriptBean excludeMethodName配置不包含情况下
@Test
public void testScriptBean5() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals("hello,kobe", context.getData("demo"));
}
// 测试scriptBean excludeMethodName配置包含情况下
@Test
public void testScriptBean6() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass());
}
// 测试在ScriptBeanManager里放入上下文实现自定义脚本引用名称
@Test
public void testScriptBean7() throws Exception {
Map<String, String> map = new HashMap<>();
ScriptBeanManager.addScriptBean("abcCx", map);
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg", map);
Assertions.assertTrue(response.isSuccess());
Map<String, String> context = response.getFirstContextBean();
Assertions.assertEquals("hello", context.get("demo"));
}
//测试用构造方法的方式注入bean的场景
@Test
public void testScriptBean8() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
DefaultContext context = response.getFirstContextBean();
Assertions.assertEquals("hello,jordan", context.getData("demo"));
}
}

View File

@ -0,0 +1,23 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean.bean;
import com.yomahub.liteflow.script.annotation.ScriptBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@ScriptBean("demo")
public class DemoBean1 {
@Resource
private DemoBean2 demoBean2;
public String getDemoStr1() {
return "hello";
}
public String getDemoStr2(String name) {
return demoBean2.getDemoStr2(name);
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean.bean;
import org.springframework.stereotype.Component;
@Component
public class DemoBean2 {
public String getDemoStr2(String name) {
return "hello," + name;
}
}

View File

@ -0,0 +1,22 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean.bean;
import com.yomahub.liteflow.script.annotation.ScriptBean;
import org.springframework.stereotype.Component;
@Component
@ScriptBean(name = "demo3", includeMethodName = { "test1", "test2" })
public class DemoBean3 {
public String test1(String name) {
return "hello," + name;
}
public String test2(String name) {
return "hello," + name;
}
public String test3(String name) {
return "hello," + name;
}
}

View File

@ -0,0 +1,22 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean.bean;
import com.yomahub.liteflow.script.annotation.ScriptBean;
import org.springframework.stereotype.Component;
@Component
@ScriptBean(name = "demo4", excludeMethodName = { "test2", "test3" })
public class DemoBean4 {
public String test1(String name) {
return "hello," + name;
}
public String test2(String name) {
return "hello," + name;
}
public String test3(String name) {
return "hello," + name;
}
}

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.script.kotlin.scriptbean.bean;
import com.yomahub.liteflow.script.annotation.ScriptBean;
import org.springframework.stereotype.Component;
@Component
@ScriptBean("demo5")
public class DemoBean5 {
private final DemoBean2 demoBean2;
public DemoBean5(DemoBean2 demoBean2) {
this.demoBean2 = demoBean2;
}
public String getDemoStr1() {
return "hello";
}
public String getDemoStr2(String name) {
return demoBean2.getDemoStr2(name);
}
}

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.kotlin.scriptbean.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.kotlin.scriptbean.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!");
}
}

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