From e3a56242f9263edbd480bffcd803ad6fe8df6aef Mon Sep 17 00:00:00 2001 From: bryan31 Date: Fri, 14 Jan 2022 20:20:46 +0800 Subject: [PATCH] =?UTF-8?q?feature=20#I4GS07=20=E4=BB=A3=E7=A0=81=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E7=BB=84=E4=BB=B6=E8=A3=85=E9=85=8D=E7=9A=84=E7=89=B9?= =?UTF-8?q?=E6=80=A7=20enhancement=20#I4QWJK=20=E9=87=8D=E6=9E=84parser?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E8=A7=A3=E5=86=B3=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=86=97=E4=BD=99=E9=97=AE=E9=A2=98=20feature=20#I4QW?= =?UTF-8?q?H7=20=E6=94=AF=E6=8C=81=E5=BE=AA=E7=8E=AF=E4=BE=9D=E8=B5=96=20e?= =?UTF-8?q?nhancement=20#I4CNO1=20=E4=B8=8D=E4=BD=BF=E7=94=A8spring?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../builder/LiteFlowChainBuilder.java | 55 +++- .../builder/LiteFlowConditionBuilder.java | 6 +- .../liteflow/builder/LiteFlowNodeBuilder.java | 50 +++- .../builder/LiteFlowWhenConditionBuilder.java | 28 ++- .../yomahub/liteflow/entity/flow/Chain.java | 10 +- .../liteflow/entity/flow/Condition.java | 7 +- .../yomahub/liteflow/entity/flow/Node.java | 13 +- .../liteflow/enums/ConditionTypeEnum.java | 9 + .../EmptyConditionValueException.java | 21 ++ .../exception/NodeBuildException.java | 21 ++ .../NotSupportConditionException.java | 21 ++ .../com/yomahub/liteflow/flow/FlowBus.java | 8 +- .../yomahub/liteflow/parser/FlowParser.java | 37 --- .../liteflow/parser/JsonFlowParser.java | 237 +++++++----------- .../liteflow/parser/XmlFlowParser.java | 183 +++++--------- .../DeadLoopChainSpringbootTest.java | 9 +- 16 files changed, 391 insertions(+), 324 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/exception/EmptyConditionValueException.java create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeBuildException.java create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/exception/NotSupportConditionException.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowChainBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowChainBuilder.java index 3fec66fec..80af26990 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowChainBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowChainBuilder.java @@ -1,10 +1,12 @@ package com.yomahub.liteflow.builder; -import com.yomahub.liteflow.entity.flow.Chain; -import com.yomahub.liteflow.entity.flow.Condition; +import cn.hutool.core.collection.CollectionUtil; +import com.yomahub.liteflow.entity.flow.*; +import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.flow.FlowBus; import java.util.ArrayList; +import java.util.List; /** * Chain基于代码形式的组装器 @@ -13,7 +15,7 @@ import java.util.ArrayList; */ public class LiteFlowChainBuilder { - private final Chain chain; + private Chain chain; public static LiteFlowChainBuilder createChain(){ return new LiteFlowChainBuilder(); @@ -21,20 +23,61 @@ public class LiteFlowChainBuilder { public LiteFlowChainBuilder(){ chain = new Chain(); - chain.setConditionList(new ArrayList<>()); } public LiteFlowChainBuilder setChainName(String chainName){ - this.chain.setChainName(chainName); + if (FlowBus.containChain(chainName)){ + this.chain = FlowBus.getChain(chainName); + }else{ + this.chain.setChainName(chainName); + } return this; } public LiteFlowChainBuilder setCondition(Condition condition){ - this.chain.getConditionList().add(condition); + //这里把condition组装进conditionList, + buildConditions(condition); return this; } public void build(){ FlowBus.addChain(this.chain); } + + private void buildConditions(Condition condition) { + //这里进行合并逻辑 + //对于then来说,相邻的2个then会合并成一个condition + //对于when来说,相同组的when会合并成一个condition,不同组的when还是会拆开 + if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE)) { + this.chain.getConditionList().add(new PreCondition(condition)); + } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN)) { + if (this.chain.getConditionList().size() >= 1 && + CollectionUtil.getLast(this.chain.getConditionList()) instanceof ThenCondition) { + CollectionUtil.getLast(this.chain.getConditionList()).getNodeList().addAll(condition.getNodeList()); + } else { + this.chain.getConditionList().add(new ThenCondition(condition)); + } + } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_WHEN)) { + if (this.chain.getConditionList().size() > 1 && + CollectionUtil.getLast(this.chain.getConditionList()) instanceof WhenCondition && + CollectionUtil.getLast(this.chain.getConditionList()).getGroup().equals(condition.getGroup())) { + CollectionUtil.getLast(this.chain.getConditionList()).getNodeList().addAll(condition.getNodeList()); + } else { + this.chain.getConditionList().add(new WhenCondition(condition)); + } + } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)) { + this.chain.getConditionList().add(new FinallyCondition(condition)); + } + + //每一次build之后,对conditionList进行排序,pre最前面,finally最后 + //这里为什么要排序,因为在声明的时候,哪怕有人不把pre放最前,finally放最后,但最终也要确保是正确的顺序 + CollectionUtil.sort(this.chain.getConditionList(), (o1, o2) -> { + if (o1.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o2.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){ + return -1; + } else if (o2.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o1.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){ + return 1; + } + return 0; + }); + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowConditionBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowConditionBuilder.java index f2f1df80d..02fa64608 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowConditionBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowConditionBuilder.java @@ -22,6 +22,10 @@ public class LiteFlowConditionBuilder { protected Condition condition; + public static LiteFlowConditionBuilder createCondition(ConditionTypeEnum conditionType){ + return new LiteFlowConditionBuilder(conditionType); + } + public static LiteFlowConditionBuilder createThenCondition(){ return new LiteFlowConditionBuilder(ConditionTypeEnum.TYPE_THEN); } @@ -40,7 +44,7 @@ public class LiteFlowConditionBuilder { public LiteFlowConditionBuilder(ConditionTypeEnum conditionType){ this.condition = new Condition(); - this.condition.setConditionType(conditionType.getType()); + this.condition.setConditionType(conditionType); this.condition.setNodeList(new ArrayList<>()); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java index 29ce95da3..7501e2dab 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java @@ -1,14 +1,21 @@ package com.yomahub.liteflow.builder; -import com.yomahub.liteflow.entity.flow.Chain; +import cn.hutool.core.io.resource.ResourceUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.entity.flow.Node; import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.exception.NodeBuildException; +import com.yomahub.liteflow.flow.FlowBus; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class LiteFlowNodeBuilder { + private final Logger LOG = LoggerFactory.getLogger(this.getClass()); + private final Node node; - public static LiteFlowNodeBuilder createNode(){ + public static LiteFlowNodeBuilder createNode() { return new LiteFlowNodeBuilder(); } @@ -16,19 +23,52 @@ public class LiteFlowNodeBuilder { this.node = new Node(); } - public LiteFlowNodeBuilder setId(String nodeId){ + public LiteFlowNodeBuilder setId(String nodeId) { this.node.setId(nodeId); return this; } - public LiteFlowNodeBuilder setName(String name){ + public LiteFlowNodeBuilder setName(String name) { this.node.setName(name); return this; } - public LiteFlowNodeBuilder setType(NodeTypeEnum type){ + public LiteFlowNodeBuilder setClazz(String clazz) { + this.node.setClazz(clazz); + return this; + } + + public LiteFlowNodeBuilder setType(NodeTypeEnum type) { this.node.setType(type); return this; } + public LiteFlowNodeBuilder setScript(String script) { + this.node.setScript(script); + return this; + } + + public LiteFlowNodeBuilder setFile(String filePath) { + if (StrUtil.isBlank(filePath)){ + return this; + } + String script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", filePath)); + return setScript(script); + } + + public void build() { + try { + if (this.node.getType().equals(NodeTypeEnum.COMMON)) { + FlowBus.addCommonNode(this.node.getId(), this.node.getName(), this.node.getClazz()); + } else if (this.node.getType().equals(NodeTypeEnum.SCRIPT)){ + FlowBus.addCommonScriptNode(this.node.getId(), this.node.getName(), this.node.getScript()); + } else if (this.node.getType().equals(NodeTypeEnum.COND_SCRIPT)){ + FlowBus.addCondScriptNode(this.node.getId(), this.node.getName(), this.node.getScript()); + } + } catch (Exception e) { + String errMsg = StrUtil.format("An exception occurred while building the node[{}]", this.node.getId()); + LOG.error(errMsg, e); + throw new NodeBuildException(errMsg); + } + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowWhenConditionBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowWhenConditionBuilder.java index 38f104891..c61a1dda5 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowWhenConditionBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowWhenConditionBuilder.java @@ -1,5 +1,7 @@ package com.yomahub.liteflow.builder; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.common.LocalDefaultFlowConstant; import com.yomahub.liteflow.enums.ConditionTypeEnum; /** @@ -14,18 +16,36 @@ public class LiteFlowWhenConditionBuilder extends LiteFlowConditionBuilder{ super(conditionType); } - public LiteFlowConditionBuilder setErrorResume(boolean errorResume){ + public LiteFlowWhenConditionBuilder setErrorResume(boolean errorResume){ this.condition.setErrorResume(errorResume); return this; } - public LiteFlowConditionBuilder setGroup(String group){ - this.condition.setGroup(group); + public LiteFlowWhenConditionBuilder setErrorResume(String errorResume){ + if (StrUtil.isBlank(errorResume)){ + return this; + } + return setErrorResume(Boolean.parseBoolean(errorResume)); + } + + public LiteFlowWhenConditionBuilder setGroup(String group){ + if (StrUtil.isBlank(group)){ + this.condition.setGroup(LocalDefaultFlowConstant.DEFAULT); + }else{ + this.condition.setGroup(group); + } return this; } - public LiteFlowConditionBuilder setAny(boolean any){ + public LiteFlowWhenConditionBuilder setAny(boolean any){ this.condition.setAny(any); return this; } + + public LiteFlowWhenConditionBuilder setAny(String any){ + if (StrUtil.isBlank(any)){ + return this; + } + return setAny(Boolean.parseBoolean(any)); + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java index 77c097b5a..2c85a4623 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Chain.java @@ -40,12 +40,14 @@ public class Chain implements Executable { private String chainName; - private List conditionList; - - public Chain(){ + private List conditionList = new ArrayList<>(); + public Chain(String chainName){ + this.chainName = chainName; } + public Chain(){} + public Chain(String chainName, List conditionList) { this.chainName = chainName; this.conditionList = conditionList; @@ -94,7 +96,7 @@ public class Chain implements Executable { public void executeFinally(Integer slotIndex) throws Exception { //先把finally的节点过滤出来 List finallyConditionList = conditionList.stream().filter(condition -> - condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())).collect(Collectors.toList()); + condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)).collect(Collectors.toList()); for (Condition finallyCondition : finallyConditionList){ for(Executable executableItem : finallyCondition.getNodeList()){ executableItem.execute(slotIndex); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Condition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Condition.java index cbe1e4ec1..6dbbeb23e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Condition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Condition.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.entity.flow; import com.yomahub.liteflow.common.LocalDefaultFlowConstant; +import com.yomahub.liteflow.enums.ConditionTypeEnum; import java.util.List; @@ -18,7 +19,7 @@ import java.util.List; public class Condition { //condition 类型 参数:ConditionTypeEnum 包含:then when - private String conditionType; + private ConditionTypeEnum conditionType; private List nodeList; @@ -61,11 +62,11 @@ public class Condition { this.group = group; } - public String getConditionType() { + public ConditionTypeEnum getConditionType() { return conditionType; } - public void setConditionType(String conditionType) { + public void setConditionType(ConditionTypeEnum conditionType) { this.conditionType = conditionType; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Node.java b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Node.java index 14a5c0cac..1c7e48c99 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Node.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/entity/flow/Node.java @@ -37,7 +37,7 @@ public class Node implements Executable,Cloneable{ private String name; - private String tag; + private String clazz; private NodeTypeEnum type; @@ -45,6 +45,8 @@ public class Node implements Executable,Cloneable{ private NodeComponent instance; + private String tag; + private final Map condNodeMap = new HashMap<>(); public Node(){ @@ -56,6 +58,7 @@ public class Node implements Executable,Cloneable{ this.name = instance.getName(); this.instance = instance; this.type = instance.getType(); + this.clazz = instance.getClass().getName(); } public String getId() { @@ -204,4 +207,12 @@ public class Node implements Executable,Cloneable{ public void setScript(String script) { this.script = script; } + + public String getClazz() { + return clazz; + } + + public void setClazz(String clazz) { + this.clazz = clazz; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java index a8560b527..dfb29fe14 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ConditionTypeEnum.java @@ -29,4 +29,13 @@ public enum ConditionTypeEnum { public void setName(String name) { this.name = name; } + + public static ConditionTypeEnum getEnumByCode(String code) { + for (ConditionTypeEnum e : ConditionTypeEnum.values()) { + if (e.getType().equals(code)) { + return e; + } + } + return null; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/EmptyConditionValueException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/EmptyConditionValueException.java new file mode 100644 index 000000000..0331091e4 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/EmptyConditionValueException.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.exception; + + +public class EmptyConditionValueException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** 异常信息 */ + private String message; + + public EmptyConditionValueException(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeBuildException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeBuildException.java new file mode 100644 index 000000000..d3f397a49 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NodeBuildException.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.exception; + + +public class NodeBuildException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** 异常信息 */ + private String message; + + public NodeBuildException(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NotSupportConditionException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NotSupportConditionException.java new file mode 100644 index 000000000..4a7d4f27f --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/NotSupportConditionException.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.exception; + + +public class NotSupportConditionException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** 异常信息 */ + private String message; + + public NotSupportConditionException(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index 0baf109c2..288c5e412 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -18,6 +18,7 @@ import com.yomahub.liteflow.core.ScriptComponent; import com.yomahub.liteflow.core.ScriptCondComponent; import com.yomahub.liteflow.entity.data.DataBus; import com.yomahub.liteflow.entity.flow.Chain; +import com.yomahub.liteflow.entity.flow.Condition; import com.yomahub.liteflow.entity.flow.Node; import com.yomahub.liteflow.enums.FlowParserTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; @@ -38,6 +39,7 @@ import org.slf4j.LoggerFactory; import org.springframework.util.SerializationUtils; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -60,7 +62,11 @@ public class FlowBus { } public static void addChain(Chain chain) { - chainMap.put(chain.getChainName(), chain); + if (chainMap.containsKey(chain.getChainName()) && CollectionUtil.isEmpty(chain.getConditionList())){ + chainMap.get(chain.getChainName()).setConditionList(chain.getConditionList()); + }else{ + chainMap.put(chain.getChainName(), chain); + } } public static boolean containChain(String chainId) { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/FlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/FlowParser.java index b1087ddb7..1c74692e4 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/FlowParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/FlowParser.java @@ -27,43 +27,6 @@ public abstract class FlowParser { public abstract void parse(List contentList) throws Exception; - protected void buildConditions(List conditionList, Condition condition) { - //这里进行合并逻辑 - //对于then来说,相邻的2个then会合并成一个condition - //对于when来说,相同组的when会合并成一个condition,不同组的when还是会拆开 - if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType())) { - conditionList.add(new PreCondition(condition)); - } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN.getType())) { - if (conditionList.size() >= 1 && - CollectionUtil.getLast(conditionList) instanceof ThenCondition) { - CollectionUtil.getLast(conditionList).getNodeList().addAll(condition.getNodeList()); - } else { - conditionList.add(new ThenCondition(condition)); - } - } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_WHEN.getType())) { - if (conditionList.size() > 1 && - CollectionUtil.getLast(conditionList) instanceof WhenCondition && - CollectionUtil.getLast(conditionList).getGroup().equals(condition.getGroup())) { - CollectionUtil.getLast(conditionList).getNodeList().addAll(condition.getNodeList()); - } else { - conditionList.add(new WhenCondition(condition)); - } - } else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())) { - conditionList.add(new FinallyCondition(condition)); - } - - //每一次build之后,对conditionList进行排序,pre最前面,finally最后 - //这里为什么要排序,因为在声明的时候,哪怕有人不把pre放最前,finally放最后,但最终也要确保是正确的顺序 - CollectionUtil.sort(conditionList, (o1, o2) -> { - if (o1.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType()) || o2.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())){ - return -1; - } else if (o2.getConditionType().equals(ConditionTypeEnum.TYPE_PRE.getType()) || o1.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY.getType())){ - return 1; - } - return 0; - }); - } - /** * 根据配置的ruleSource查找匹配的资源 */ diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java index bc995fa72..f0c09f34f 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/JsonFlowParser.java @@ -8,18 +8,26 @@ 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.builder.LiteFlowChainBuilder; +import com.yomahub.liteflow.builder.LiteFlowConditionBuilder; +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.common.LocalDefaultFlowConstant; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.entity.flow.*; +import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.exception.EmptyConditionValueException; import com.yomahub.liteflow.exception.ExecutableItemNotFoundException; import com.yomahub.liteflow.exception.NodeTypeNotSupportException; +import com.yomahub.liteflow.exception.NotSupportConditionException; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.spring.ComponentScanner; +import org.dom4j.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; +import java.util.function.Consumer; /** * Json格式解析器 @@ -53,171 +61,120 @@ public abstract class JsonFlowParser extends FlowParser { //json格式,解析过程 public void parseJsonObject(List flowJsonObjectList) throws Exception { - try { - for (Map.Entry componentEntry : ComponentScanner.nodeComponentMap.entrySet()) { - if (!FlowBus.containNode(componentEntry.getKey())) { - FlowBus.addSpringScanNode(componentEntry.getKey(), componentEntry.getValue()); - } + for (Map.Entry componentEntry : ComponentScanner.nodeComponentMap.entrySet()) { + if (!FlowBus.containNode(componentEntry.getKey())) { + FlowBus.addSpringScanNode(componentEntry.getKey(), componentEntry.getValue()); } + } - for (JSONObject flowJsonObject : flowJsonObjectList) { - // 当存在节点定义时,解析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"); - file = nodeObject.getString("file"); + //先在元数据里放上chain + //先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 + //这样就不用去像之前的版本那样回归调用 + //同时也解决了不能循环依赖的问题 + flowJsonObjectList.forEach(jsonObject -> { + // 解析chain节点 + JSONArray chainArray = jsonObject.getJSONObject("flow").getJSONArray("chain"); - //初始化type - if (StrUtil.isBlank(type)){ - type = NodeTypeEnum.COMMON.getCode(); - } - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (ObjectUtil.isNull(nodeTypeEnum)){ - throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type)); - } + //先在元数据里放上chain + chainArray.forEach(o -> { + JSONObject innerJsonObject = (JSONObject)o; + FlowBus.addChain(new Chain(innerJsonObject.getString("name"))); + }); + }); - //这里区分是普通java节点还是脚本节点 - //如果是脚本节点,又区分是普通脚本节点,还是条件脚本节点 - if (nodeTypeEnum.equals(NodeTypeEnum.COMMON) && StrUtil.isNotBlank(clazz)){ - FlowBus.addCommonNode(id, name, clazz); - }else if(nodeTypeEnum.equals(NodeTypeEnum.SCRIPT) || nodeTypeEnum.equals(NodeTypeEnum.COND_SCRIPT)){ - //如果file字段不为空,则优先从file里面读取脚本文本 - if (StrUtil.isNotBlank(file)){ - script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file)); - }else{ - script = nodeObject.getString("value"); - } + for (JSONObject flowJsonObject : flowJsonObjectList) { + // 当存在节点定义时,解析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"); - //根据节点类型把脚本添加到元数据里 - if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){ - FlowBus.addCommonScriptNode(id, name, script); - }else { - FlowBus.addCondScriptNode(id, name, script); - } - } + //初始化type + if (StrUtil.isBlank(type)){ + type = NodeTypeEnum.COMMON.getCode(); } - } - // 解析chain节点 - JSONArray chainArray = flowJsonObject.getJSONObject("flow").getJSONArray("chain"); - for (int i = 0; i < chainArray.size(); i++) { - JSONObject jsonObject = chainArray.getJSONObject(i); - parseOneChain(jsonObject, flowJsonObjectList); + //检查nodeType是不是规定的类型 + NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); + if (ObjectUtil.isNull(nodeTypeEnum)){ + throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type)); + } + + //进行node的build过程 + LiteFlowNodeBuilder.createNode().setId(id) + .setName(name) + .setClazz(clazz) + .setType(nodeTypeEnum) + .setScript(script) + .setFile(file) + .build(); } } - } catch (Exception e) { - LOG.error("JsonFlowParser parser exception", e); - throw e; + + //解析每一个chain + JSONArray chainArray = flowJsonObject.getJSONObject("flow").getJSONArray("chain"); + chainArray.forEach(o -> { + JSONObject jsonObject = (JSONObject)o; + parseOneChain(jsonObject); + }); } } /** * 解析一个chain的过程 */ - private void parseOneChain(JSONObject chainObject, List flowJsonObjectList) throws Exception { - String condArrayStr; - String[] condArray; - List chainNodeList; - List conditionList = new ArrayList<>(); + private void parseOneChain(JSONObject chainObject){ + String condValueStr; + ConditionTypeEnum conditionType; String group; String errorResume; - Condition condition; String any; + + //构建chainBuilder String chainName = chainObject.getString("name"); - JSONArray conditionArray = chainObject.getJSONArray("condition"); - for (Object o : conditionArray) { + LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName); + + for (Object o : chainObject.getJSONArray("condition")) { JSONObject condObject = (JSONObject) o; - String condType = condObject.getString("type"); - condArrayStr = condObject.getString("value"); - group = condObject.getString("group"); + conditionType = ConditionTypeEnum.getEnumByCode(condObject.getString("type")); + condValueStr = condObject.getString("value"); errorResume = condObject.getString("errorResume"); + group = condObject.getString("group"); any = condObject.getString("any"); - if (StrUtil.isBlank(condType) || StrUtil.isBlank(condArrayStr)) { - continue; - } - if (StrUtil.isBlank(group)) { - group = LocalDefaultFlowConstant.DEFAULT; - } - if (StrUtil.isBlank(errorResume)) { - errorResume = Boolean.FALSE.toString(); - } - if (StrUtil.isBlank(any)){ - any = Boolean.FALSE.toString(); - } - condition = new Condition(); - chainNodeList = new ArrayList<>(); - condArray = condArrayStr.split(","); - RegexEntity regexEntity; - String itemExpression; - RegexNodeEntity item; - //这里解析的规则,优先按照node去解析,再按照chain去解析 - for (int i = 0; i < condArray.length; i++) { - itemExpression = condArray[i].trim(); - regexEntity = RegexEntity.parse(itemExpression); - item = regexEntity.getItem(); - if (FlowBus.containNode(item.getId())) { - Node node = FlowBus.copyNode(item.getId()); - node.setTag(regexEntity.getItem().getTag()); - chainNodeList.add(node); - //这里判断是不是条件节点,条件节点会含有realItem,也就是括号里的node - if (regexEntity.getRealItemArray() != null) { - for (RegexNodeEntity realItem : regexEntity.getRealItemArray()) { - if (FlowBus.containNode(realItem.getId())) { - Node condNode = FlowBus.copyNode(realItem.getId()); - condNode.setTag(realItem.getTag()); - node.setCondNode(condNode.getId(), condNode); - } else if (hasChain(flowJsonObjectList, realItem.getId())) { - Chain chain = FlowBus.getChain(realItem.getId()); - node.setCondNode(chain.getChainName(), chain); - } else{ - String errorMsg = StrUtil.format("executable node[{}] is not found!", realItem.getId()); - throw new ExecutableItemNotFoundException(errorMsg); - } - } - } - } else if (hasChain(flowJsonObjectList, item.getId())) { - Chain chain = FlowBus.getChain(item.getId()); - chainNodeList.add(chain); - } else { - String errorMsg = StrUtil.format("executable node[{}] is not found!", regexEntity.getItem().getId()); - throw new ExecutableItemNotFoundException(errorMsg); - } - } - condition.setErrorResume(Boolean.parseBoolean(errorResume)); - condition.setGroup(group); - condition.setAny(any.equals(Boolean.TRUE.toString())); - condition.setConditionType(condType); - condition.setNodeList(chainNodeList); - //这里把condition组装进conditionList,根据参数有些condition要和conditionList里面的某些进行合并操作 - super.buildConditions(conditionList, condition); - } - FlowBus.addChain(new Chain(chainName, conditionList)); - } + if (ObjectUtil.isNull(conditionType)){ + throw new NotSupportConditionException("ConditionType is not supported"); + } - /** - * 判断在这个FlowBus元数据里是否含有这个chain - * 因为chain和node都是可执行器,在一个规则文件上,有可能是node,有可能是chain - */ - private boolean hasChain(List flowJsonObjectList, String chainName) throws Exception { - for (JSONObject jsonObject : flowJsonObjectList) { - JSONArray chainArray = jsonObject.getJSONObject("flow").getJSONArray("chain"); - for (int i = 0; i < chainArray.size(); i++) { - JSONObject chainObject = chainArray.getJSONObject(i); - if (chainObject.getString("name").equals(chainName) && !FlowBus.containChain(chainName)) { - parseOneChain(chainObject, flowJsonObjectList); - return true; - } else if (FlowBus.containChain(chainName)) { - return true; - } + if (StrUtil.isBlank(condValueStr)) { + throw new EmptyConditionValueException("Condition value cannot be empty"); + } + + + //如果是when类型的话,有特殊化参数要设置,只针对于when的 + if (conditionType.equals(ConditionTypeEnum.TYPE_WHEN)){ + chainBuilder.setCondition( + LiteFlowConditionBuilder.createWhenCondition() + .setErrorResume(errorResume) + .setGroup(group) + .setAny(any) + .setValue(condValueStr) + .build() + ).build(); + }else{ + chainBuilder.setCondition( + LiteFlowConditionBuilder.createCondition(conditionType) + .setValue(condValueStr) + .build() + ).build(); } } - return false; } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java index 264ec6e57..fa5bd4def 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/XmlFlowParser.java @@ -5,17 +5,18 @@ import cn.hutool.core.collection.ListUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.builder.LiteFlowChainBuilder; +import com.yomahub.liteflow.builder.LiteFlowConditionBuilder; +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.common.LocalDefaultFlowConstant; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.entity.flow.Chain; import com.yomahub.liteflow.entity.flow.Condition; import com.yomahub.liteflow.entity.flow.Executable; import com.yomahub.liteflow.entity.flow.Node; +import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; -import com.yomahub.liteflow.exception.CyclicDependencyException; -import com.yomahub.liteflow.exception.ExecutableItemNotFoundException; -import com.yomahub.liteflow.exception.NodeTypeNotSupportException; -import com.yomahub.liteflow.exception.ParseException; +import com.yomahub.liteflow.exception.*; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.spring.ComponentScanner; import org.dom4j.Document; @@ -28,10 +29,10 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; +import java.util.function.Consumer; /** * xml形式的解析器 - * * @author Bryan.Zhang */ public abstract class XmlFlowParser extends FlowParser { @@ -63,6 +64,18 @@ public abstract class XmlFlowParser extends FlowParser { } } + //先在元数据里放上chain + //先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 + //这样就不用去像之前的版本那样回归调用 + //同时也解决了不能循环依赖的问题 + documentList.forEach(document -> { + // 解析chain节点 + List chainList = document.getRootElement().elements("chain"); + + //先在元数据里放上chain + chainList.forEach(e -> FlowBus.addChain(new Chain(e.attributeValue("name")))); + }); + for (Document document : documentList) { Element rootElement = document.getRootElement(); Element nodesElement = rootElement.element("nodes"); @@ -75,152 +88,84 @@ public abstract class XmlFlowParser extends FlowParser { name = e.attributeValue("name"); clazz = e.attributeValue("class"); type = e.attributeValue("type"); + script = e.getTextTrim(); file = e.attributeValue("file"); //初始化type if (StrUtil.isBlank(type)){ type = NodeTypeEnum.COMMON.getCode(); } + + //检查nodeType是不是规定的类型 NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); if (ObjectUtil.isNull(nodeTypeEnum)){ throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type)); } - //这里区分是普通java节点还是脚本节点 - //如果是脚本节点,又区分是普通脚本节点,还是条件脚本节点 - if (nodeTypeEnum.equals(NodeTypeEnum.COMMON) && StrUtil.isNotBlank(clazz)){ - FlowBus.addCommonNode(id, name, clazz); - }else if(nodeTypeEnum.equals(NodeTypeEnum.SCRIPT) || nodeTypeEnum.equals(NodeTypeEnum.COND_SCRIPT)){ - //如果file字段不为空,则优先从file里面读取脚本文本 - if (StrUtil.isNotBlank(file)){ - script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", file)); - }else{ - script = e.getTextTrim(); - } - - //根据节点类型把脚本添加到元数据里 - if (nodeTypeEnum.equals(NodeTypeEnum.SCRIPT)){ - FlowBus.addCommonScriptNode(id, name, script); - }else { - FlowBus.addCondScriptNode(id, name, script); - } - } + //进行node的build过程 + LiteFlowNodeBuilder.createNode().setId(id) + .setName(name) + .setClazz(clazz) + .setType(nodeTypeEnum) + .setScript(script) + .setFile(file) + .build(); } } - // 解析chain节点 + //解析每一个chain List chainList = rootElement.elements("chain"); - for (Element e : chainList) { - parseOneChain(e, documentList); - } + chainList.forEach(this::parseOneChain); } } /** * 解析一个chain的过程 */ - private void parseOneChain(Element e, List documentList) throws Exception { - String condArrayStr; - String[] condArray; + private void parseOneChain(Element e) { + String condValueStr; String group; String errorResume; String any; - Condition condition; - Element condE; - List chainNodeList; - List conditionList = new ArrayList<>(); + ConditionTypeEnum conditionType; + //构建chainBuilder String chainName = e.attributeValue("name"); + LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName); + for (Iterator it = e.elementIterator(); it.hasNext(); ) { - condE = it.next(); - condArrayStr = condE.attributeValue("value"); + Element condE = it.next(); + conditionType = ConditionTypeEnum.getEnumByCode(condE.getName()); + condValueStr = condE.attributeValue("value"); errorResume = condE.attributeValue("errorResume"); group = condE.attributeValue("group"); any = condE.attributeValue("any"); - if (StrUtil.isBlank(condArrayStr)) { - continue; - } - if (StrUtil.isBlank(group)) { - group = LocalDefaultFlowConstant.DEFAULT; - } - if (StrUtil.isBlank(errorResume)) { - errorResume = Boolean.FALSE.toString(); - } - if (StrUtil.isBlank(any)){ - any = Boolean.FALSE.toString(); - } - condition = new Condition(); - chainNodeList = new ArrayList<>(); - condArray = condArrayStr.split(","); - RegexEntity regexEntity; - String itemExpression; - RegexNodeEntity item; - //这里解析的规则,优先按照node去解析,再按照chain去解析 - for (String s : condArray) { - itemExpression = s.trim(); - regexEntity = RegexEntity.parse(itemExpression); - item = regexEntity.getItem(); - if (FlowBus.containNode(item.getId())) { - Node node = FlowBus.copyNode(item.getId()); - node.setTag(regexEntity.getItem().getTag()); - chainNodeList.add(node); - //这里判断是不是条件节点,条件节点会含有realItem,也就是括号里的node - if (regexEntity.getRealItemArray() != null) { - for (RegexNodeEntity realItem : regexEntity.getRealItemArray()) { - if (FlowBus.containNode(realItem.getId())) { - Node condNode = FlowBus.copyNode(realItem.getId()); - condNode.setTag(realItem.getTag()); - node.setCondNode(condNode.getId(), condNode); - } else if (hasChain(documentList, realItem.getId())) { - Chain chain = FlowBus.getChain(realItem.getId()); - node.setCondNode(chain.getChainName(), chain); - } else{ - String errorMsg = StrUtil.format("executable node[{}] is not found!", realItem.getId()); - throw new ExecutableItemNotFoundException(errorMsg); - } - } - } - } else if (hasChain(documentList, item.getId())) { - Chain chain = FlowBus.getChain(item.getId()); - chainNodeList.add(chain); - } else { - String errorMsg = StrUtil.format("executable node[{}] is not found!", regexEntity.getItem().getId()); - throw new ExecutableItemNotFoundException(errorMsg); - } - } - condition.setErrorResume(Boolean.parseBoolean(errorResume)); - condition.setGroup(group); - condition.setAny(any.equals(Boolean.TRUE.toString())); - condition.setConditionType(condE.getName()); - condition.setNodeList(chainNodeList); - //这里把condition组装进conditionList,根据参数有些condition要和conditionList里面的某些进行合并操作 - super.buildConditions(conditionList, condition); - } - FlowBus.addChain(new Chain(chainName, conditionList)); - } - - //判断在这个FlowBus元数据里是否含有这个chain - //因为chain和node都是可执行器,在一个规则文件上,有可能是node,有可能是chain - @SuppressWarnings("unchecked") - private boolean hasChain(List documentList, String chainName) throws Exception { - try{ - for (Document document : documentList) { - List chainList = document.getRootElement().elements("chain"); - for (Element ce : chainList) { - String ceName = ce.attributeValue("name"); - if (ceName.equals(chainName)) { - if (!FlowBus.containChain(chainName)) { - parseOneChain(ce, documentList); - } - return true; - } - } + if (ObjectUtil.isNull(conditionType)){ + throw new NotSupportConditionException("ConditionType is not supported"); + } + + if (StrUtil.isBlank(condValueStr)) { + throw new EmptyConditionValueException("Condition value cannot be empty"); + } + + //如果是when类型的话,有特殊化参数要设置,只针对于when的 + if (conditionType.equals(ConditionTypeEnum.TYPE_WHEN)){ + chainBuilder.setCondition( + LiteFlowConditionBuilder.createWhenCondition() + .setErrorResume(errorResume) + .setGroup(group) + .setAny(any) + .setValue(condValueStr) + .build() + ).build(); + }else{ + chainBuilder.setCondition( + LiteFlowConditionBuilder.createCondition(conditionType) + .setValue(condValueStr) + .build() + ).build(); } - return false; - }catch (StackOverflowError e){ - LOG.error("a cyclic dependency occurs in chain", e); - throw new CyclicDependencyException("a cyclic dependency occurs in chain"); } } } diff --git a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/deadLoopChain/DeadLoopChainSpringbootTest.java b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/deadLoopChain/DeadLoopChainSpringbootTest.java index 3049d1bc3..3e8eccb09 100644 --- a/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/deadLoopChain/DeadLoopChainSpringbootTest.java +++ b/liteflow-testcase-springboot/src/test/java/com/yomahub/liteflow/test/deadLoopChain/DeadLoopChainSpringbootTest.java @@ -22,11 +22,11 @@ import javax.annotation.Resource; * @author Bryan.Zhang * @since 2.5.10 */ -@RunWith(SpringRunner.class) +/*@RunWith(SpringRunner.class) @TestPropertySource(value = "classpath:/deadLoopChain/application.properties") @SpringBootTest(classes = DeadLoopChainSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({"com.yomahub.liteflow.test.deadLoopChain.cmp"}) +@ComponentScan({"com.yomahub.liteflow.test.deadLoopChain.cmp"})*/ public class DeadLoopChainSpringbootTest extends BaseTest { @Resource @@ -34,7 +34,10 @@ public class DeadLoopChainSpringbootTest extends BaseTest { //死循环问题解析时自动发现,抛错 //为了写测试用例,才配置了liteflow.parse-on-start=false参数,实际上应用不用配置延迟加载参数 - @Test(expected = CyclicDependencyException.class) + //自从2.6.8之后,支持循环依赖,但是用户必须在组件里自己判断退出的条件,否则会报栈溢出 + //所以这个测试用例暂时不打开 + //为什么不删除呢?是因为如果用户不自己判断退出的条件。会报出栈溢出。以后希望liteflow自己能抛出相关的错。而不是抛出JDK的异常。所以暂时留着。 + //@Test(expected = CyclicDependencyException.class) public void testDeadLoopChain() { LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); }