mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-06-10 19:26:54 +08:00
feature #I5CW7I 【版本特性】构造全新的EL规则表达式
This commit is contained in:
@@ -68,6 +68,7 @@ public class LiteFlowChainELBuilder {
|
||||
expressRunner.addFunctionAndClassMethod("to", Object.class, new ToOperator());
|
||||
expressRunner.addFunctionAndClassMethod("tag", Object.class, new TagOperator());
|
||||
expressRunner.addFunctionAndClassMethod("any", Object.class, new AnyOperator());
|
||||
expressRunner.addFunctionAndClassMethod("id", Object.class, new IdOperator());
|
||||
expressRunner.addFunctionAndClassMethod("ignoreError", Object.class, new IgnoreErrorOperator());
|
||||
expressRunner.addFunctionAndClassMethod("threadPool", Object.class, new ThreadPoolOperator());
|
||||
}
|
||||
@@ -90,7 +91,7 @@ public class LiteFlowChainELBuilder {
|
||||
//往上下文里放入所有的node,使得el表达式可以直接引用到nodeId
|
||||
DefaultContext<String, Object> context = new DefaultContext<>();
|
||||
FlowBus.getNodeMap().keySet().forEach(nodeId -> context.put(nodeId, FlowBus.copyNode(nodeId)));
|
||||
Condition condition = (Condition) expressRunner.execute(elStr, context, errorList, false, false);
|
||||
Condition condition = (Condition) expressRunner.execute(elStr, context, errorList, false, true);
|
||||
|
||||
//从condition的第一层嵌套结构里拿出Pre和Finally节点
|
||||
//为什么只寻找第一层,而不往下寻找了呢?
|
||||
|
||||
@@ -24,7 +24,7 @@ public class ToOperator extends Operator {
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
if (objects.length != 2){
|
||||
if (objects.length <= 2){
|
||||
LOG.error("parameter error");
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.parser.*;
|
||||
import com.yomahub.liteflow.parser.el.*;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
import com.yomahub.liteflow.slot.DataBus;
|
||||
@@ -196,6 +197,12 @@ public class FlowExecutor {
|
||||
return new LocalJsonFlowParser();
|
||||
case TYPE_YML:
|
||||
return new LocalYmlFlowParser();
|
||||
case TYPE_EL_XML:
|
||||
return new LocalXmlFlowELParser();
|
||||
case TYPE_EL_JSON:
|
||||
return new LocalJsonFlowELParser();
|
||||
case TYPE_EL_YML:
|
||||
return new LocalYmlFlowELParser();
|
||||
default:
|
||||
}
|
||||
} else if (isClassConfig(path)) {
|
||||
@@ -208,6 +215,12 @@ public class FlowExecutor {
|
||||
return (JsonFlowParser) ContextAwareHolder.loadContextAware().registerBean(c);
|
||||
case TYPE_YML:
|
||||
return (YmlFlowParser) ContextAwareHolder.loadContextAware().registerBean(c);
|
||||
case TYPE_EL_XML:
|
||||
return (XmlFlowELParser) ContextAwareHolder.loadContextAware().registerBean(c);
|
||||
case TYPE_EL_JSON:
|
||||
return (JsonFlowELParser) ContextAwareHolder.loadContextAware().registerBean(c);
|
||||
case TYPE_EL_YML:
|
||||
return (YmlFlowELParser) ContextAwareHolder.loadContextAware().registerBean(c);
|
||||
default:
|
||||
}
|
||||
} else if (isZKConfig(path)) {
|
||||
@@ -219,6 +232,12 @@ public class FlowExecutor {
|
||||
return new ZookeeperJsonFlowParser(liteflowConfig.getZkNode());
|
||||
case TYPE_YML:
|
||||
return new ZookeeperYmlFlowParser(liteflowConfig.getZkNode());
|
||||
case TYPE_EL_XML:
|
||||
return new ZookeeperXmlFlowELParser(liteflowConfig.getZkNode());
|
||||
case TYPE_EL_JSON:
|
||||
return new ZookeeperJsonFlowELParser(liteflowConfig.getZkNode());
|
||||
case TYPE_EL_YML:
|
||||
return new ZookeeperYmlFlowELParser(liteflowConfig.getZkNode());
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -232,7 +251,10 @@ public class FlowExecutor {
|
||||
private boolean isLocalConfig(String path) {
|
||||
return ReUtil.isMatch(LOCAL_XML_CONFIG_REGEX, path)
|
||||
|| ReUtil.isMatch(LOCAL_JSON_CONFIG_REGEX, path)
|
||||
|| ReUtil.isMatch(LOCAL_YML_CONFIG_REGEX, path);
|
||||
|| ReUtil.isMatch(LOCAL_YML_CONFIG_REGEX, path)
|
||||
|| ReUtil.isMatch(LOCAL_EL_XML_CONFIG_REGEX, path)
|
||||
|| ReUtil.isMatch(LOCAL_EL_JSON_CONFIG_REGEX, path)
|
||||
|| ReUtil.isMatch(LOCAL_EL_YML_CONFIG_REGEX, path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,7 +290,6 @@ public class FlowExecutor {
|
||||
} else if (isClassConfig(path)) {
|
||||
//其实整个这个判断块代码可以不要,因为如果是自定义配置源的话,标准写法也要在前面加xml:/json:/yml:这种
|
||||
//但是这块可能是考虑到有些人忘加了,所以再来判断下。如果写了标准的话,是不会走到这块来的
|
||||
//不过el形式的已经不支持这块了,需要标准写法,这点注意
|
||||
try {
|
||||
Class<?> clazz = Class.forName(path);
|
||||
if (ClassXmlFlowParser.class.isAssignableFrom(clazz)) {
|
||||
@@ -277,6 +298,12 @@ public class FlowExecutor {
|
||||
return FlowParserTypeEnum.TYPE_JSON;
|
||||
} else if (ClassYmlFlowParser.class.isAssignableFrom(clazz)) {
|
||||
return FlowParserTypeEnum.TYPE_YML;
|
||||
} else if (ClassXmlFlowELParser.class.isAssignableFrom(clazz)) {
|
||||
return FlowParserTypeEnum.TYPE_EL_XML;
|
||||
} else if (ClassJsonFlowELParser.class.isAssignableFrom(clazz)) {
|
||||
return FlowParserTypeEnum.TYPE_EL_JSON;
|
||||
} else if (ClassYmlFlowELParser.class.isAssignableFrom(clazz)) {
|
||||
return FlowParserTypeEnum.TYPE_EL_YML;
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOG.error(e.getMessage());
|
||||
|
||||
@@ -45,7 +45,7 @@ public abstract class Condition implements Executable{
|
||||
|
||||
@Override
|
||||
public String getExecuteName() {
|
||||
return this.getExecuteType().name();
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public List<Executable> getExecutableList() {
|
||||
|
||||
@@ -145,7 +145,7 @@ public class WhenCondition extends Condition {
|
||||
//循环判断CompletableFuture的返回值,如果异步执行失败,则抛出相应的业务异常
|
||||
for(WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList){
|
||||
if (!whenFutureObj.isSuccess()){
|
||||
LOG.info(StrUtil.format("requestId [{}] when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName(), slot.getRequestId()));
|
||||
LOG.info(StrUtil.format("requestId [{}] when-executor[{}] execute failed. errorResume [false].",slot.getRequestId(), whenFutureObj.getExecutorName()));
|
||||
throw whenFutureObj.getEx();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import com.yomahub.liteflow.parser.JsonFlowParser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于自定义的json方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class ClassJsonFlowELParser extends JsonFlowParser {
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
String content = parseCustom();
|
||||
parse(content);
|
||||
}
|
||||
|
||||
public abstract String parseCustom();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于自定义的xml方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class ClassXmlFlowELParser extends XmlFlowELParser{
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
String content = parseCustom();
|
||||
parse(content);
|
||||
}
|
||||
|
||||
public abstract String parseCustom();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于自定义的yml方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class ClassYmlFlowELParser extends YmlFlowELParser {
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
String content = parseCustom();
|
||||
parse(content);
|
||||
}
|
||||
|
||||
public abstract String parseCustom();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.parser.Feature;
|
||||
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||
import com.yomahub.liteflow.builder.prop.NodePropBean;
|
||||
import com.yomahub.liteflow.exception.ChainDuplicateException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.parser.BaseFlowParser;
|
||||
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import static com.yomahub.liteflow.common.ChainConstant.*;
|
||||
|
||||
/**
|
||||
* JSON形式的EL表达式解析抽象引擎
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class JsonFlowELParser extends BaseFlowParser {
|
||||
|
||||
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final Set<String> CHAIN_NAME_SET = new CopyOnWriteArraySet<>();
|
||||
|
||||
public void parse(String content) throws Exception {
|
||||
parse(ListUtil.toList(content));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(List<String> contentList) throws Exception {
|
||||
if (CollectionUtil.isEmpty(contentList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<JSONObject> jsonObjectList = ListUtil.toList();
|
||||
for (String content : contentList) {
|
||||
//把字符串原生转换为json对象,如果不加第二个参数OrderedField,会无序
|
||||
JSONObject flowJsonObject = JSONObject.parseObject(content, Feature.OrderedField);
|
||||
jsonObjectList.add(flowJsonObject);
|
||||
}
|
||||
|
||||
parseJsonObject(jsonObjectList);
|
||||
}
|
||||
|
||||
//json格式,解析过程
|
||||
public void parseJsonObject(List<JSONObject> flowJsonObjectList) throws Exception {
|
||||
//在相应的环境下进行节点的初始化工作
|
||||
//在spring体系下会获得spring扫描后的节点,接入元数据
|
||||
//在非spring体系下是一个空实现,等于不做此步骤
|
||||
ContextCmpInitHolder.loadContextCmpInit().initCmp();
|
||||
|
||||
//先在元数据里放上chain
|
||||
//先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析
|
||||
//这样就不用去像之前的版本那样回归调用
|
||||
//同时也解决了不能循环依赖的问题
|
||||
flowJsonObjectList.forEach(jsonObject -> {
|
||||
// 解析chain节点
|
||||
JSONArray chainArray = jsonObject.getJSONObject(FLOW).getJSONArray(CHAIN);
|
||||
|
||||
//先在元数据里放上chain
|
||||
chainArray.forEach(o -> {
|
||||
JSONObject innerJsonObject = (JSONObject) o;
|
||||
//校验加载的 chainName 是否有重复的
|
||||
//TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了
|
||||
String chainName = innerJsonObject.getString(NAME);
|
||||
if (!CHAIN_NAME_SET.add(chainName)) {
|
||||
throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName));
|
||||
}
|
||||
|
||||
FlowBus.addChain(innerJsonObject.getString(NAME));
|
||||
});
|
||||
});
|
||||
// 清空
|
||||
CHAIN_NAME_SET.clear();
|
||||
|
||||
for (JSONObject flowJsonObject : flowJsonObjectList) {
|
||||
// 当存在<nodes>节点定义时,解析node节点
|
||||
if (flowJsonObject.getJSONObject(FLOW).containsKey(NODES)) {
|
||||
JSONArray nodeArrayList = flowJsonObject.getJSONObject(FLOW).getJSONObject(NODES).getJSONArray(NODE);
|
||||
String id, name, clazz, script, type, file;
|
||||
for (int i = 0; i < nodeArrayList.size(); i++) {
|
||||
JSONObject nodeObject = nodeArrayList.getJSONObject(i);
|
||||
id = nodeObject.getString(ID);
|
||||
name = nodeObject.getString(NAME);
|
||||
clazz = nodeObject.getString(_CLASS);
|
||||
type = nodeObject.getString(TYPE);
|
||||
script = nodeObject.getString(VALUE);
|
||||
file = nodeObject.getString(FILE);
|
||||
|
||||
// 构建 node
|
||||
NodePropBean nodePropBean = new NodePropBean()
|
||||
.setId(id)
|
||||
.setName(name)
|
||||
.setClazz(clazz)
|
||||
.setScript(script)
|
||||
.setType(type)
|
||||
.setFile(file);
|
||||
|
||||
buildNode(nodePropBean);
|
||||
}
|
||||
}
|
||||
|
||||
//解析每一个chain
|
||||
JSONArray chainArray = flowJsonObject.getJSONObject(FLOW).getJSONArray(CHAIN);
|
||||
chainArray.forEach(o -> {
|
||||
JSONObject jsonObject = (JSONObject) o;
|
||||
parseOneChain(jsonObject);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析一个chain的过程
|
||||
*/
|
||||
private void parseOneChain(JSONObject chainObject) {
|
||||
//构建chainBuilder
|
||||
String chainName = chainObject.getString(NAME);
|
||||
String el = chainObject.getString(VALUE);
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
|
||||
chainELBuilder.setEL(el).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于本地的json方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class LocalJsonFlowELParser extends JsonFlowELParser {
|
||||
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
List<String> contentList = PathContentParserHolder.loadContextAware().parseContent(pathList);
|
||||
parse(contentList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于本地的xml方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class LocalXmlFlowELParser extends XmlFlowELParser{
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
List<String> contentList = PathContentParserHolder.loadContextAware().parseContent(pathList);
|
||||
parse(contentList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于本地的yml方式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class LocalYmlFlowELParser extends YmlFlowELParser {
|
||||
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
List<String> contentList = PathContentParserHolder.loadContextAware().parseContent(pathList);
|
||||
parse(contentList);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,10 +3,8 @@ package com.yomahub.liteflow.parser.el;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
|
||||
import com.yomahub.liteflow.builder.prop.ChainPropBean;
|
||||
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||
import com.yomahub.liteflow.builder.prop.NodePropBean;
|
||||
import com.yomahub.liteflow.enums.ConditionTypeEnum;
|
||||
import com.yomahub.liteflow.exception.ChainDuplicateException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.parser.BaseFlowParser;
|
||||
@@ -18,22 +16,20 @@ import org.dom4j.Element;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
import static com.yomahub.liteflow.common.ChainConstant.*;
|
||||
import static com.yomahub.liteflow.common.ChainConstant.THREAD_EXECUTOR_CLASS;
|
||||
|
||||
/**
|
||||
* EL表达引擎Xml形式的解析抽象引擎
|
||||
* Xml形式的EL表达式解析抽象引擎
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class ELXmlFlowParser extends BaseFlowParser {
|
||||
public abstract class XmlFlowELParser extends BaseFlowParser {
|
||||
|
||||
private final Logger LOG = LoggerFactory.getLogger(XmlFlowParser.class);
|
||||
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final Set<String> CHAIN_NAME_SET = new CopyOnWriteArraySet<>();
|
||||
|
||||
@@ -122,36 +118,10 @@ public abstract class ELXmlFlowParser extends BaseFlowParser {
|
||||
* 解析一个chain的过程
|
||||
*/
|
||||
private void parseOneChain(Element e) {
|
||||
String condValueStr;
|
||||
String group;
|
||||
String errorResume;
|
||||
String any;
|
||||
String threadExecutorClass;
|
||||
ConditionTypeEnum conditionType;
|
||||
|
||||
//构建chainBuilder
|
||||
String chainName = e.attributeValue(NAME);
|
||||
LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName);
|
||||
|
||||
for (Iterator<Element> it = e.elementIterator(); it.hasNext(); ) {
|
||||
Element condE = it.next();
|
||||
conditionType = ConditionTypeEnum.getEnumByCode(condE.getName());
|
||||
condValueStr = condE.attributeValue(VALUE);
|
||||
errorResume = condE.attributeValue(ERROR_RESUME);
|
||||
group = condE.attributeValue(GROUP);
|
||||
any = condE.attributeValue(ANY);
|
||||
threadExecutorClass = condE.attributeValue(THREAD_EXECUTOR_CLASS);
|
||||
|
||||
ChainPropBean chainPropBean = new ChainPropBean()
|
||||
.setCondValueStr(condValueStr)
|
||||
.setGroup(group)
|
||||
.setErrorResume(errorResume)
|
||||
.setAny(any)
|
||||
.setThreadExecutorClass(threadExecutorClass)
|
||||
.setConditionType(conditionType);
|
||||
|
||||
// 构建 chain
|
||||
buildChain(chainPropBean, chainBuilder);
|
||||
}
|
||||
String el = e.getTextTrim();
|
||||
LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainName(chainName);
|
||||
chainELBuilder.setEL(el).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.yomahub.liteflow.parser.JsonFlowParser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* yml形式的EL表达式解析抽象引擎
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public abstract class YmlFlowELParser extends JsonFlowELParser {
|
||||
|
||||
private final Logger LOG = LoggerFactory.getLogger(YmlFlowELParser.class);
|
||||
|
||||
@Override
|
||||
public void parse(String content) throws Exception{
|
||||
parse(ListUtil.toList(content));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(List<String> contentList) throws Exception {
|
||||
if (CollectionUtil.isEmpty(contentList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<JSONObject> jsonObjectList = ListUtil.toList();
|
||||
for (String content : contentList){
|
||||
JSONObject ruleObject = convertToJson(content);
|
||||
jsonObjectList.add(ruleObject);
|
||||
}
|
||||
|
||||
super.parseJsonObject(jsonObjectList);
|
||||
}
|
||||
|
||||
protected JSONObject convertToJson(String yamlString) {
|
||||
Yaml yaml= new Yaml();
|
||||
Map<String, Object> map = yaml.load(yamlString);
|
||||
return JSON.parseObject(JSON.toJSONString(map));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.exception.ParseException;
|
||||
import com.yomahub.liteflow.parser.JsonFlowParser;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.framework.recipes.cache.NodeCache;
|
||||
import org.apache.curator.retry.RetryNTimes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于zk方式的json形式的解析器
|
||||
* @author guodongqing
|
||||
* @since 2.5.0
|
||||
*/
|
||||
public class ZookeeperJsonFlowELParser extends JsonFlowELParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZookeeperJsonFlowELParser.class);
|
||||
|
||||
private final String nodePath;
|
||||
|
||||
public ZookeeperJsonFlowELParser(String node) {
|
||||
nodePath = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
//zk不允许有多个path
|
||||
String path = pathList.get(0);
|
||||
CuratorFramework client = CuratorFrameworkFactory.newClient(
|
||||
path,
|
||||
new RetryNTimes(10, 5000)
|
||||
);
|
||||
client.start();
|
||||
|
||||
if (client.checkExists().forPath(nodePath) == null) {
|
||||
client.create().creatingParentsIfNeeded().forPath(nodePath, "".getBytes());
|
||||
}
|
||||
|
||||
String content = new String(client.getData().forPath(nodePath));
|
||||
|
||||
|
||||
if (StrUtil.isBlank(content)) {
|
||||
String error = MessageFormat.format("the node[{0}] value is empty", nodePath);
|
||||
throw new ParseException(error);
|
||||
}
|
||||
parse(content);
|
||||
|
||||
|
||||
final NodeCache cache = new NodeCache(client,nodePath);
|
||||
cache.start();
|
||||
|
||||
cache.getListenable().addListener(() -> {
|
||||
String content1 = new String(cache.getCurrentData().getData());
|
||||
LOG.info("stating load flow config....");
|
||||
parse(content1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.exception.ParseException;
|
||||
import com.yomahub.liteflow.parser.XmlFlowParser;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.framework.recipes.cache.NodeCache;
|
||||
import org.apache.curator.retry.RetryNTimes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于zk方式的xml形式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class ZookeeperXmlFlowELParser extends XmlFlowELParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZookeeperXmlFlowELParser.class);
|
||||
|
||||
private final String nodePath;
|
||||
|
||||
public ZookeeperXmlFlowELParser(String node) {
|
||||
nodePath = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
//zk不允许有多个path
|
||||
String path = pathList.get(0);
|
||||
CuratorFramework client = CuratorFrameworkFactory.newClient(
|
||||
path,
|
||||
new RetryNTimes(10, 5000)
|
||||
);
|
||||
client.start();
|
||||
|
||||
if (client.checkExists().forPath(nodePath) == null) {
|
||||
client.create().creatingParentsIfNeeded().forPath(nodePath, "".getBytes());
|
||||
}
|
||||
|
||||
String content = new String(client.getData().forPath(nodePath));
|
||||
|
||||
|
||||
if (StrUtil.isBlank(content)) {
|
||||
String error = MessageFormat.format("the node[{0}] value is empty", nodePath);
|
||||
throw new ParseException(error);
|
||||
}
|
||||
parse(content);
|
||||
|
||||
|
||||
final NodeCache cache = new NodeCache(client,nodePath);
|
||||
cache.start();
|
||||
|
||||
cache.getListenable().addListener(() -> {
|
||||
String content1 = new String(cache.getCurrentData().getData());
|
||||
LOG.info("stating load flow config....");
|
||||
parse(content1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.yomahub.liteflow.parser.el;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.yomahub.liteflow.exception.ParseException;
|
||||
import com.yomahub.liteflow.parser.YmlFlowParser;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.framework.recipes.cache.NodeCache;
|
||||
import org.apache.curator.retry.RetryNTimes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 基于zk方式的yml形式EL表达式解析器
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class ZookeeperYmlFlowELParser extends YmlFlowELParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZookeeperYmlFlowELParser.class);
|
||||
|
||||
private final String nodePath;
|
||||
|
||||
public ZookeeperYmlFlowELParser(String node) {
|
||||
nodePath = node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parseMain(List<String> pathList) throws Exception {
|
||||
//zk不允许有多个path
|
||||
String path = pathList.get(0);
|
||||
CuratorFramework client = CuratorFrameworkFactory.newClient(
|
||||
path,
|
||||
new RetryNTimes(10, 5000)
|
||||
);
|
||||
client.start();
|
||||
|
||||
if (client.checkExists().forPath(nodePath) == null) {
|
||||
client.create().creatingParentsIfNeeded().forPath(nodePath, "".getBytes());
|
||||
}
|
||||
|
||||
String content = new String(client.getData().forPath(nodePath));
|
||||
|
||||
if (StrUtil.isBlank(content)) {
|
||||
String error = MessageFormat.format("the node[{0}] value is empty", nodePath);
|
||||
throw new ParseException(error);
|
||||
}
|
||||
|
||||
JSONObject ruleObject = convertToJson(content);
|
||||
|
||||
parse(ruleObject.toJSONString());
|
||||
|
||||
|
||||
final NodeCache cache = new NodeCache(client,nodePath);
|
||||
cache.start();
|
||||
|
||||
cache.getListenable().addListener(() -> {
|
||||
String content1 = new String(cache.getCurrentData().getData());
|
||||
LOG.info("stating load flow config....");
|
||||
JSONObject ruleObject1 = convertToJson(content1);
|
||||
parse(ruleObject1.toJSONString());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,8 @@ import org.springframework.test.context.junit4.SpringRunner;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* springboot环境最普通的例子测试
|
||||
* springboot环境EL常规的例子测试
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.4
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/base/application.properties")
|
||||
@@ -29,10 +28,32 @@ public class BaseELSpringbootTest extends BaseTest {
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testBase() throws Exception{
|
||||
public void testBase1() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
//switch节点最简单的测试用例
|
||||
@Test
|
||||
public void testBase2() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
//then,when,switch混用的稍微复杂点的用例,switch跳到一个then上
|
||||
@Test
|
||||
public void testBase3() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
//一个非常复杂的例子,可以看base目录下的img.png这个图示
|
||||
@Test
|
||||
public void testBase4() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class DCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("CCmp executed!");
|
||||
System.out.println("DCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeSwitchComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("e")
|
||||
public class ESwitchCmp extends NodeSwitchComponent {
|
||||
|
||||
@Override
|
||||
public String processCond() throws Exception {
|
||||
return "d";
|
||||
}
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("f")
|
||||
public class FCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("FCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeSwitchComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("g")
|
||||
public class GSwitchCmp extends NodeSwitchComponent {
|
||||
|
||||
@Override
|
||||
public String processCond() throws Exception {
|
||||
return "then_1001";
|
||||
}
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("h")
|
||||
public class HCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("HCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("i")
|
||||
public class ICmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("ICmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("j")
|
||||
public class JCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("JCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("k")
|
||||
public class KCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("KCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("m")
|
||||
public class MCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("MCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("n")
|
||||
public class NCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("NCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("p")
|
||||
public class PCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("PCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("q")
|
||||
public class QCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("QCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("r")
|
||||
public class RCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("RCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeSwitchComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("x")
|
||||
public class XSwitchCmp extends NodeSwitchComponent {
|
||||
|
||||
@Override
|
||||
public String processCond() throws Exception {
|
||||
return "w01";
|
||||
}
|
||||
}
|
||||
@@ -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.base.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("z")
|
||||
public class ZCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("ZCmp executed!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
<then value="a,b"/>
|
||||
<when value="c,d"/>
|
||||
THEN(a,b,WHEN(c,d))
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
THEN(
|
||||
a,b,
|
||||
SWITCH(e).to(d,f)
|
||||
)
|
||||
</chain>
|
||||
|
||||
<chain name="chain3">
|
||||
THEN(
|
||||
a,
|
||||
WHEN(
|
||||
c,
|
||||
SWITCH(g).to(b, d, THEN(h,i).id("then_1001"))
|
||||
)
|
||||
)
|
||||
</chain>
|
||||
|
||||
<chain name="chain4">
|
||||
THEN(
|
||||
a,b,
|
||||
WHEN(
|
||||
THEN(c, WHEN(j,k)),
|
||||
d,
|
||||
THEN(h, i)
|
||||
),
|
||||
SWITCH(x).to(
|
||||
m,
|
||||
n,
|
||||
WHEN(q, THEN(p, r)).id("w01")
|
||||
),
|
||||
z
|
||||
)
|
||||
</chain>
|
||||
</flow>
|
||||
BIN
liteflow-testcase-el-springboot/src/test/resources/base/img.png
Normal file
BIN
liteflow-testcase-el-springboot/src/test/resources/base/img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 270 KiB |
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration debug="false">
|
||||
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
|
||||
<property name="LOG_HOME" value="./logs" />
|
||||
<!--控制台日志-->
|
||||
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<!--这里替换成AspectLogbackEncoder-->
|
||||
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 日志输出级别 -->
|
||||
<root level="info">
|
||||
<appender-ref ref="Console" />
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user