feature #I3CTY2 规则支持json和yml

This commit is contained in:
tonnyguo
2021-03-26 15:58:57 +08:00
parent 32026989e1
commit 4794792cfb
11 changed files with 412 additions and 29 deletions

View File

@@ -62,6 +62,10 @@
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>

View File

@@ -16,6 +16,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Lists;
import com.yomahub.liteflow.exception.ConfigErrorException;
import com.yomahub.liteflow.parser.*;
import com.yomahub.liteflow.property.LiteflowConfig;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
@@ -29,9 +30,6 @@ import com.yomahub.liteflow.exception.ChainNotFoundException;
import com.yomahub.liteflow.exception.FlowExecutorNotInitException;
import com.yomahub.liteflow.exception.NoAvailableSlotException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.parser.LocalXmlFlowParser;
import com.yomahub.liteflow.parser.XmlFlowParser;
import com.yomahub.liteflow.parser.ZookeeperXmlFlowParser;
/**
* 流程规则主要执行器类
@@ -53,13 +51,18 @@ public class FlowExecutor {
List<String> rulePath = Lists.newArrayList(liteflowConfig.getRuleSource().split(",|;"));
XmlFlowParser parser = null;
// XmlFlowParser parser = null;
FlowParser parser = null;
for(String path : rulePath){
try {
if(isLocalConfig(path)) { //判断是否是本地的xml文件
parser = new LocalXmlFlowParser();
}else if(isZKConfig(path)){ //判断是否是zk配置
} else if(isLocalJsonConfig(path)) {
parser = new LocalJsonFlowParser();
} else if(isLocalYmlConfig(path)) {
parser = new LocalYmlFlowParser();
} else if(isZKConfig(path)){ //判断是否是zk配置
if(StringUtils.isNotBlank(zkNode)) {
parser = new ZookeeperXmlFlowParser(zkNode);
}else {
@@ -90,6 +93,18 @@ public class FlowExecutor {
return m.find();
}
private boolean isLocalJsonConfig(String path) {
Pattern p = Pattern.compile("^[\\w_\\-\\@\\/]+\\.json$");
Matcher m = p.matcher(path);
return m.find();
}
private boolean isLocalYmlConfig(String path) {
Pattern p = Pattern.compile("^[\\w_\\-\\@\\/]+\\.yml$");
Matcher m = p.matcher(path);
return m.find();
}
private boolean isClassConfig(String path) {
Pattern p = Pattern.compile("^\\w+(\\.\\w+)*$");
Matcher m = p.matcher(path);

View File

@@ -0,0 +1,38 @@
package com.yomahub.liteflow.parser;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: guodongqing
* @Date: 2021/3/25 4:47 下午
*/
public abstract class FlowParser {
public abstract void parseMain(String path) throws Exception;
public abstract void parse(String content) throws Exception ;
//条件节点的正则解析
public static RegexEntity parseNodeStr(String str) {
List<String> list = new ArrayList<String>();
Pattern p = Pattern.compile("[^\\)\\(]+");
Matcher m = p.matcher(str);
while(m.find()){
list.add(m.group());
}
RegexEntity regexEntity = new RegexEntity();
regexEntity.setItem(list.get(0).trim());
if(list.size() > 1){
String[] realNodeArray = list.get(1).split("\\|");
for (int i = 0; i < realNodeArray.length; i++) {
realNodeArray[i] = realNodeArray[i].trim();
}
regexEntity.setRealItemArray(realNodeArray);
}
return regexEntity;
}
}

View File

@@ -0,0 +1,175 @@
package com.yomahub.liteflow.parser;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.entity.flow.*;
import com.yomahub.liteflow.exception.ExecutableItemNotFoundException;
import com.yomahub.liteflow.exception.ParseException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.spring.ComponentScaner;
import com.yomahub.liteflow.util.SpringAware;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/**
* Json格式解析器
* @Author: guodongqing
* @Date: 2021-03-25 16:40:00
*/
public abstract class JsonFlowParser extends FlowParser{
private final Logger LOG = LoggerFactory.getLogger(JsonFlowParser.class);
@Override
public void parse(String content) throws Exception {
//把字符串原生转换为json对象如果不加第二个参数OrderedField会无序
JSONObject flowJsonObject = JSONObject.parseObject(content, Feature.OrderedField);
parse(flowJsonObject);
}
/**
* json格式解析过程
* @param flowJsonObject
* @throws Exception
*/
public void parse(JSONObject flowJsonObject) throws Exception {
try {
//判断是以spring方式注册节点还是以json方式注册
if(ComponentScaner.nodeComponentMap.isEmpty()){
JSONArray nodeArrayList = flowJsonObject.getJSONObject("nodes").getJSONArray("node");
String id;
String clazz;
Node node;
NodeComponent component;
Class<NodeComponent> nodeComponentClass;
for(int i = 0; i< nodeArrayList.size(); i++) {
JSONObject nodeObject = nodeArrayList.getJSONObject(i);
node = new Node();
id = nodeObject.getString("id");
clazz = nodeObject.getString("class");
node.setId(id);
node.setClazz(clazz);
nodeComponentClass = (Class<NodeComponent>)Class.forName(clazz);
component = SpringAware.registerOrGet(nodeComponentClass);
if (component == null) {
LOG.error("couldn't find component class [{}] ", clazz);
throw new ParseException("cannot parse flow json");
}
component.setNodeId(id);
node.setInstance(component);
FlowBus.addNode(id, node);
}
} else {
for(Map.Entry<String, NodeComponent> componentEntry : ComponentScaner.nodeComponentMap.entrySet()){
if(!FlowBus.containNode(componentEntry.getKey())){
FlowBus.addNode(componentEntry.getKey(), new Node(componentEntry.getKey(), componentEntry.getValue().getClass().getName(), componentEntry.getValue()));
}
}
}
// 解析chain节点
JSONArray chainList = flowJsonObject.getJSONObject("flow").getJSONArray("chain");
Map<String, JSONObject> chainMap = new HashMap<>();
for(int i=0; i<chainList.size(); i++) {
JSONObject chainObject = chainList.getJSONObject(i);
if(chainObject.containsKey("name") && StringUtils.isNotBlank(chainObject.getString("name"))) {
chainMap.put(chainObject.getString("name"), chainObject);
}
}
for(Map.Entry<String, JSONObject> chainEntry : chainMap.entrySet()) {
parseOneChain(chainEntry.getValue(), chainMap);
}
} catch (Exception e) {
LOG.error("JsonFlowParser parser exception", e);
throw e;
}
}
/**
* 解析一个chain的过程
* @param chainObject
* @param chainMap
* @throws Exception
*/
private void parseOneChain(JSONObject chainObject, Map<String, JSONObject> chainMap) throws Exception{
String condArrayStr;
String[] condArray;
List<Executable> chainNodeList;
List<Condition> conditionList;
String chainName = chainObject.getString("name");
JSONArray chainTopoArray = chainObject.getJSONArray("topo");
conditionList = new ArrayList<>();
for(Iterator<Object> iterator = chainTopoArray.iterator(); iterator.hasNext();) {
JSONObject condObject = (JSONObject) iterator.next();
String condType = condObject.getString("type");
condArrayStr = condObject.getString("value");
if (StringUtils.isBlank(condType) || StringUtils.isBlank(condArrayStr)) {
continue;
}
chainNodeList = new ArrayList<>();
condArray = condArrayStr.split(",");
RegexEntity regexEntity;
String itemExpression;
String item;
//这里解析的规则优先按照node去解析再按照chain去解析
for (int i = 0; i < condArray.length; i++) {
itemExpression = condArray[i].trim();
regexEntity = parseNodeStr(itemExpression);
item = regexEntity.getItem();
if (FlowBus.containNode(item)) {
Node node = FlowBus.getNode(item);
chainNodeList.add(node);
//这里判断是不是条件节点条件节点会含有realItem也就是括号里的node
if (regexEntity.getRealItemArray() != null) {
for (String key : regexEntity.getRealItemArray()) {
if (FlowBus.containNode(key)) {
Node condNode = FlowBus.getNode(key);
node.setCondNode(condNode.getId(), condNode);
} else if (hasChain(chainMap, key)) {
Chain chain = FlowBus.getChain(key);
node.setCondNode(chain.getChainName(), chain);
}
}
}
} else if(hasChain(chainMap,item)){
Chain chain = FlowBus.getChain(item);
chainNodeList.add(chain);
}
else {
String errorMsg = StrUtil.format("executable node[{}] is not found!", regexEntity.getItem());
throw new ExecutableItemNotFoundException(errorMsg);
}
}
if (condType.equals("then")) {
conditionList.add(new ThenCondition(chainNodeList));
} else if (condType.equals("when")) {
conditionList.add(new WhenCondition(chainNodeList));
}
FlowBus.addChain(chainName, new Chain(chainName,conditionList));
}
}
/**
* 判断在这个FlowBus元数据里是否含有这个chain
* 因为chain和node都是可执行器在一个规则文件上有可能是node有可能是chain
* @param chainMap
* @param chainName
* @return
*/
private boolean hasChain(Map<String, JSONObject> chainMap, String chainName) throws Exception {
if(chainMap.containsKey(chainName) && !FlowBus.containChain(chainName)) {
parseOneChain(chainMap.get(chainName), chainMap);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
package com.yomahub.liteflow.parser;
import cn.hutool.core.io.FileUtil;
/**
* @Author: guodongqing
* @Date: 2021/3/26 12:26 下午
*/
public class LocalJsonFlowParser extends JsonFlowParser{
@Override
public void parseMain(String rulePath) throws Exception {
String ruleContent = FileUtil.readUtf8String(rulePath);
parse(ruleContent);
}
}

View File

@@ -0,0 +1,29 @@
package com.yomahub.liteflow.parser;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSONObject;
import org.yaml.snakeyaml.Yaml;
import java.util.Map;
/**
* Yaml格式转换
* @Author: guodongqing
* @Date: 2021/3/26 12:56 下午
*/
public class LocalYmlFlowParser extends JsonFlowParser{
@Override
public void parseMain(String rulePath) throws Exception {
String ruleContent = FileUtil.readUtf8String(rulePath);
JSONObject ruleObject = convertToJson(ruleContent);
parse(ruleObject.toJSONString());
}
private JSONObject convertToJson(String yamlString) {
Yaml yaml= new Yaml();
Map<String, Object> map = yaml.load(yamlString);
JSONObject jsonObject = new JSONObject(map);
return jsonObject;
}
}

View File

@@ -27,12 +27,10 @@ import com.yomahub.liteflow.spring.ComponentScaner;
* xml形式的解析器
* @author Bryan.Zhang
*/
public abstract class XmlFlowParser {
public abstract class XmlFlowParser extends FlowParser{
private final Logger LOG = LoggerFactory.getLogger(XmlFlowParser.class);
public abstract void parseMain(String path) throws Exception;
public void parse(String content) throws Exception {
Document document = DocumentHelper.parseText(content);
parse(document);
@@ -161,24 +159,4 @@ public abstract class XmlFlowParser {
}
return false;
}
//条件节点的正则解析
public static RegexEntity parseNodeStr(String str) {
List<String> list = new ArrayList<String>();
Pattern p = Pattern.compile("[^\\)\\(]+");
Matcher m = p.matcher(str);
while(m.find()){
list.add(m.group());
}
RegexEntity regexEntity = new RegexEntity();
regexEntity.setItem(list.get(0).trim());
if(list.size() > 1){
String[] realNodeArray = list.get(1).split("\\|");
for (int i = 0; i < realNodeArray.length; i++) {
realNodeArray[i] = realNodeArray[i].trim();
}
regexEntity.setRealItemArray(realNodeArray);
}
return regexEntity;
}
}

View File

@@ -1,4 +1,5 @@
liteflow.rule-source=config/flow.xml
#liteflow.rule-source=config/flow.yml
#liteflow.slot-size=2048
liteflow.when-max-wait-seconds=20
liteflow.monitor.enable-log=true

View File

@@ -0,0 +1,75 @@
{
"flow": {
"nodes": {
"node": [
{
"id": "a",
"class": "com.yomahub.liteflow.test.component.AComponent"
},
{
"id": "b",
"class": "com.yomahub.liteflow.test.component.BComponent"
},
{
"id": "c",
"class": "com.yomahub.liteflow.test.component.CComponent"
},
{
"id": "d",
"class": "com.yomahub.liteflow.test.component.DComponent"
},
{
"id": "e",
"class": "com.yomahub.liteflow.test.component.EComponent"
},
{
"id": "f",
"class": "com.yomahub.liteflow.test.component.FComponent"
},
{
"id": "g",
"class": "com.yomahub.liteflow.test.component.GComponent"
},
{
"id": "cond",
"class": "com.yomahub.liteflow.test.component.CondComponent"
}
]
},
"chain": [
{
"name": "chain1",
"topo": [
{"type": "then", "value": "a,cond(b|d)"},
{"type": "then", "value": "e,f,g"}
]
},
{
"name": "chain2",
"topo": [
{"type": "then", "value": "a,c"},
{"type": "when", "value": "b,d,e,f,g"},
{"type": "then", "value": "c"}
]
},
{
"name": "chain3",
"topo": [
{"type": "then", "value": "a,c,strategy1,g"}
]
},
{
"name": "strategy1",
"topo": [
{"type": "then", "value": "m(m1|m2|strategy2)"}
]
},
{
"name": "strategy2",
"topo": [
{"type": "then", "value": "q,p(p1|p2)"}
]
}
]
}
}

View File

@@ -0,0 +1,46 @@
flow:
nodes:
node:
- id: a
class: com.yomahub.liteflow.test.component.AComponent
- id: b
class: com.yomahub.liteflow.test.component.BComponent
- id: c
class: com.yomahub.liteflow.test.component.CComponent
- id: d
class: com.yomahub.liteflow.test.component.DComponent
- id: e
class: com.yomahub.liteflow.test.component.EComponent
- id: f
class: com.yomahub.liteflow.test.component.FComponent
- id: g
class: com.yomahub.liteflow.test.component.GComponent
- id: cond
class: com.yomahub.liteflow.test.component.CondComponent
chain:
- name: chain1
topo:
- type: then
value: 'a,cond(b|d)'
- type: then
value: 'e,f,g'
- name: chain2
topo:
- type: then
value: 'a,c'
- type: when
value: 'b,d,e,f,g'
- type: then
value: 'c'
- name: chain3
topo:
- type: then
value: 'a,c,strategy1,g'
- name: strategy1
topo:
- type: then
value: m(m1|m2|strategy2)
- name: strategy2
topo:
- type: then
value: 'q,p(p1|p2)'

View File

@@ -50,7 +50,8 @@
<log4j.version>1.2.17</log4j.version>
<log4j-slf4j.version>1.7.5</log4j-slf4j.version>
<slf4j.version>1.7.13</slf4j.version>
<fastjson.version>1.2.25</fastjson.version>
<fastjson.version>1.2.70</fastjson.version>
<snakeyaml.version>1.19</snakeyaml.version>
<dom4j.version>1.6.1</dom4j.version>
<curator.version>2.12.0</curator.version>
<junit.version>4.12</junit.version>
@@ -109,6 +110,11 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>