From 972ce3f0f75a1f9224a1af9c7b6a8717dfbfd39b Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sun, 14 Jan 2024 19:45:15 +0800 Subject: [PATCH 1/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/common/ChainConstant.java | 2 + .../liteflow/parser/helper/ParserHelper.java | 730 +++++++++--------- .../liteflow/test/parser/JsonParserTest.java | 8 + .../liteflow/test/parser/XmlParserTest.java | 8 + .../liteflow/test/parser/YmlParserTest.java | 7 + .../src/test/resources/parser/flow.el.json | 5 + .../src/test/resources/parser/flow.el.xml | 3 + .../src/test/resources/parser/flow.el.yml | 3 + 8 files changed, 421 insertions(+), 345 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java b/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java index 884b8b157..105c8b755 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/common/ChainConstant.java @@ -24,6 +24,8 @@ public interface ChainConstant { String NAME = "name"; + String ENABLE = "enable"; + String LANGUAGE = "language"; String VALUE = "value"; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java index 49ab1bf63..e3d8f055e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java @@ -30,18 +30,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; -import static com.yomahub.liteflow.common.ChainConstant.CHAIN; -import static com.yomahub.liteflow.common.ChainConstant.EXTENDS; -import static com.yomahub.liteflow.common.ChainConstant.FILE; -import static com.yomahub.liteflow.common.ChainConstant.FLOW; -import static com.yomahub.liteflow.common.ChainConstant.ID; -import static com.yomahub.liteflow.common.ChainConstant.LANGUAGE; -import static com.yomahub.liteflow.common.ChainConstant.NAME; -import static com.yomahub.liteflow.common.ChainConstant.NODE; -import static com.yomahub.liteflow.common.ChainConstant.NODES; -import static com.yomahub.liteflow.common.ChainConstant.TYPE; -import static com.yomahub.liteflow.common.ChainConstant.VALUE; -import static com.yomahub.liteflow.common.ChainConstant._CLASS; +import static com.yomahub.liteflow.common.ChainConstant.*; /** * Parser 通用 Helper @@ -51,363 +40,414 @@ import static com.yomahub.liteflow.common.ChainConstant._CLASS; */ public class ParserHelper { - /** - * 私有化构造器 - */ - private ParserHelper() { - } + /** + * 私有化构造器 + */ + private ParserHelper() { + } - /** - * 构建 node - * @param nodePropBean 构建 node 的中间属性 - */ - public static void buildNode(NodePropBean nodePropBean) { - String id = nodePropBean.getId(); - String name = nodePropBean.getName(); - String clazz = nodePropBean.getClazz(); - String script = nodePropBean.getScript(); - String type = nodePropBean.getType(); - String file = nodePropBean.getFile(); - String language = nodePropBean.getLanguage(); + /** + * 构建 node + * + * @param nodePropBean 构建 node 的中间属性 + */ + public static void buildNode(NodePropBean nodePropBean) { + String id = nodePropBean.getId(); + String name = nodePropBean.getName(); + String clazz = nodePropBean.getClazz(); + String script = nodePropBean.getScript(); + String type = nodePropBean.getType(); + String file = nodePropBean.getFile(); + String language = nodePropBean.getLanguage(); - // clazz有值的,基本都不是脚本节点 - // 脚本节点,都必须配置type - // 非脚本节点的先尝试自动推断类型 - if (StrUtil.isNotBlank(clazz)) { - try { - // 先尝试从继承的类型中推断 - Class c = Class.forName(clazz); - NodeTypeEnum nodeType = NodeTypeEnum.guessType(c); - if (nodeType != null) { - type = nodeType.getCode(); - } - } - catch (Exception e) { - throw new NodeClassNotFoundException(StrUtil.format("cannot find the node[{}]", clazz)); - } - } + // clazz有值的,基本都不是脚本节点 + // 脚本节点,都必须配置type + // 非脚本节点的先尝试自动推断类型 + if (StrUtil.isNotBlank(clazz)) { + try { + // 先尝试从继承的类型中推断 + Class c = Class.forName(clazz); + NodeTypeEnum nodeType = NodeTypeEnum.guessType(c); + if (nodeType != null) { + type = nodeType.getCode(); + } + } catch (Exception e) { + throw new NodeClassNotFoundException(StrUtil.format("cannot find the node[{}]", clazz)); + } + } - // 因为脚本节点是必须设置type的,所以到这里type就全都有了,所以进行二次检查 - if (StrUtil.isBlank(type)) { - throw new NodeTypeCanNotGuessException(StrUtil.format("cannot guess the type of node[{}]", clazz)); - } + // 因为脚本节点是必须设置type的,所以到这里type就全都有了,所以进行二次检查 + if (StrUtil.isBlank(type)) { + throw new NodeTypeCanNotGuessException(StrUtil.format("cannot guess the type of node[{}]", clazz)); + } - // 检查nodeType是不是规定的类型 - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (ObjectUtil.isNull(nodeTypeEnum)) { - throw new NodeTypeNotSupportException(StrUtil.format("type [{}] is not support", type)); - } + // 检查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) - .setLanguage(language) - .build(); - } + // 进行node的build过程 + LiteFlowNodeBuilder.createNode() + .setId(id) + .setName(name) + .setClazz(clazz) + .setType(nodeTypeEnum) + .setScript(script) + .setFile(file) + .setLanguage(language) + .build(); + } - /** - * xml 形式的主要解析过程 - * @param documentList documentList - */ - /** - * xml 形式的主要解析过程 - * @param documentList documentList - */ - public static void parseNodeDocument(List documentList) { - for (Document document : documentList) { - Element rootElement = document.getRootElement(); - Element nodesElement = rootElement.element(NODES); - // 当存在节点定义时,解析node节点 - if (ObjectUtil.isNotNull(nodesElement)) { - List nodeList = nodesElement.elements(NODE); - String id, name, clazz, type, script, file, language; - for (Element e : nodeList) { - id = e.attributeValue(ID); - name = e.attributeValue(NAME); - clazz = e.attributeValue(_CLASS); - type = e.attributeValue(TYPE); - script = e.getText(); - file = e.attributeValue(FILE); - language = e.attributeValue(LANGUAGE); + /** + * xml 形式的主要解析过程 + * @param documentList documentList + */ + /** + * xml 形式的主要解析过程 + * + * @param documentList documentList + */ + public static void parseNodeDocument(List documentList) { + for (Document document : documentList) { + Element rootElement = document.getRootElement(); + Element nodesElement = rootElement.element(NODES); + // 当存在节点定义时,解析node节点 + if (ObjectUtil.isNotNull(nodesElement)) { + List nodeList = nodesElement.elements(NODE); + String id, name, clazz, type, script, file, language; + for (Element e : nodeList) { + id = e.attributeValue(ID); + name = e.attributeValue(NAME); + clazz = e.attributeValue(_CLASS); + type = e.attributeValue(TYPE); + script = e.getText(); + file = e.attributeValue(FILE); + language = e.attributeValue(LANGUAGE); - // 构建 node - NodePropBean nodePropBean = new NodePropBean().setId(id) - .setName(name) - .setClazz(clazz) - .setScript(script) - .setType(type) - .setFile(file) - .setLanguage(language); + // 如果是禁用的,就不编译了 + if (!getEnableByElement(e)) { + continue; + } - ParserHelper.buildNode(nodePropBean); - } - } - } - } + // 构建 node + NodePropBean nodePropBean = new NodePropBean().setId(id) + .setName(name) + .setClazz(clazz) + .setScript(script) + .setType(type) + .setFile(file) + .setLanguage(language); - public static void parseChainDocument(List documentList, Set chainNameSet, - Consumer parseOneChainConsumer) { - //用于存放抽象chain的map - Map abstratChainMap = new HashMap<>(); - //用于存放已经解析过的实现chain - Set implChainSet = new HashSet<>(); - // 先在元数据里放上chain - // 先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 - // 这样就不用去像之前的版本那样回归调用 - // 同时也解决了不能循环依赖的问题 - documentList.forEach(document -> { - // 解析chain节点 - List chainList = document.getRootElement().elements(CHAIN); + ParserHelper.buildNode(nodePropBean); + } + } + } + } - // 先在元数据里放上chain - chainList.forEach(e -> { - // 校验加载的 chainName 是否有重复的 - // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 - String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); - // 检查 chainName - checkChainId(chainName, e.getText()); - if (!chainNameSet.add(chainName)) { - throw new ChainDuplicateException(StrUtil.format("[chain name duplicate] chainName={}", chainName)); - } + public static void parseChainDocument(List documentList, Set chainNameSet, + Consumer parseOneChainConsumer) { + //用于存放抽象chain的map + Map abstratChainMap = new HashMap<>(); + //用于存放已经解析过的实现chain + Set implChainSet = new HashSet<>(); + // 先在元数据里放上chain + // 先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 + // 这样就不用去像之前的版本那样回归调用 + // 同时也解决了不能循环依赖的问题 + documentList.forEach(document -> { + // 解析chain节点 + List chainList = document.getRootElement().elements(CHAIN); - FlowBus.addChain(chainName); - if(ElRegexUtil.isAbstractChain(e.getText())){ - abstratChainMap.put(chainName,e); - //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain - Chain chain = FlowBus.getChain(chainName); - chain.getConditionList().add(new AbstractCondition()); - } - }); - }); - // 清空 - chainNameSet.clear(); + // 先在元数据里放上chain + for (Element e : chainList) { + // 校验加载的 chainName 是否有重复的 + // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 + String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); + // 检查 chainName + checkChainId(chainName, e.getText()); + if (!chainNameSet.add(chainName)) { + throw new ChainDuplicateException(StrUtil.format("[chain name duplicate] chainName={}", chainName)); + } + // 如果是禁用,就不解析了 + if (!getEnableByElement(e)) { + continue; + } - // 解析每一个chain - for (Document document : documentList) { - Element rootElement = document.getRootElement(); - List chainList = rootElement.elements(CHAIN); - for(Element chain:chainList){ - //首先需要对继承自抽象Chain的chain进行字符串替换 - parseImplChain(abstratChainMap, implChainSet, chain); - //如果一个chain不为抽象chain,则进行解析 - String chainName = Optional.ofNullable(chain.attributeValue(ID)).orElse(chain.attributeValue(NAME)); - if(!abstratChainMap.containsKey(chainName)){ - parseOneChainConsumer.accept(chain); - } - } - } - } + FlowBus.addChain(chainName); + if (ElRegexUtil.isAbstractChain(e.getText())) { + abstratChainMap.put(chainName, e); + //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain + Chain chain = FlowBus.getChain(chainName); + chain.getConditionList().add(new AbstractCondition()); + } + } + }); + // 清空 + chainNameSet.clear(); - public static void parseNodeJson(List flowJsonObjectList) { - for (JsonNode flowJsonNode : flowJsonObjectList) { - // 当存在节点定义时,解析node节点 - if (flowJsonNode.get(FLOW).has(NODES)) { - Iterator nodeIterator = flowJsonNode.get(FLOW).get(NODES).get(NODE).elements(); - String id, name, clazz, script, type, file; - while ((nodeIterator.hasNext())) { - JsonNode nodeObject = nodeIterator.next(); - id = nodeObject.get(ID).textValue(); - name = nodeObject.hasNonNull(NAME) ? nodeObject.get(NAME).textValue() : ""; - clazz = nodeObject.hasNonNull(_CLASS) ? nodeObject.get(_CLASS).textValue() : ""; - ; - type = nodeObject.hasNonNull(TYPE) ? nodeObject.get(TYPE).textValue() : null; - script = nodeObject.hasNonNull(VALUE) ? nodeObject.get(VALUE).textValue() : ""; - file = nodeObject.hasNonNull(FILE) ? nodeObject.get(FILE).textValue() : ""; + // 解析每一个chain + for (Document document : documentList) { + Element rootElement = document.getRootElement(); + List chainList = rootElement.elements(CHAIN); + for (Element chain : chainList) { + // 如果是禁用,就不解析了 + if (!getEnableByElement(chain)) { + continue; + } - // 构建 node - NodePropBean nodePropBean = new NodePropBean().setId(id) - .setName(name) - .setClazz(clazz) - .setScript(script) - .setType(type) - .setFile(file); + //首先需要对继承自抽象Chain的chain进行字符串替换 + parseImplChain(abstratChainMap, implChainSet, chain); + //如果一个chain不为抽象chain,则进行解析 + String chainName = Optional.ofNullable(chain.attributeValue(ID)).orElse(chain.attributeValue(NAME)); + if (!abstratChainMap.containsKey(chainName)) { + parseOneChainConsumer.accept(chain); + } + } + } + } - ParserHelper.buildNode(nodePropBean); - } - } - } - } + public static void parseNodeJson(List flowJsonObjectList) { + for (JsonNode flowJsonNode : flowJsonObjectList) { + // 当存在节点定义时,解析node节点 + if (flowJsonNode.get(FLOW).has(NODES)) { + Iterator nodeIterator = flowJsonNode.get(FLOW).get(NODES).get(NODE).elements(); + String id, name, clazz, script, type, file; + while ((nodeIterator.hasNext())) { + JsonNode nodeObject = nodeIterator.next(); + id = nodeObject.get(ID).textValue(); + name = nodeObject.hasNonNull(NAME) ? nodeObject.get(NAME).textValue() : ""; + clazz = nodeObject.hasNonNull(_CLASS) ? nodeObject.get(_CLASS).textValue() : ""; + type = nodeObject.hasNonNull(TYPE) ? nodeObject.get(TYPE).textValue() : null; + script = nodeObject.hasNonNull(VALUE) ? nodeObject.get(VALUE).textValue() : ""; + file = nodeObject.hasNonNull(FILE) ? nodeObject.get(FILE).textValue() : ""; - public static void parseChainJson(List flowJsonObjectList, Set chainNameSet, - Consumer parseOneChainConsumer) { - //用于存放抽象chain的map - Map abstratChainMap = new HashMap<>(); - //用于存放已经解析过的实现chain - Set implChainSet = new HashSet<>(); - // 先在元数据里放上chain - // 先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 - // 这样就不用去像之前的版本那样回归调用 - // 同时也解决了不能循环依赖的问题 - flowJsonObjectList.forEach(jsonObject -> { - // 解析chain节点 - Iterator iterator = jsonObject.get(FLOW).get(CHAIN).elements(); - // 先在元数据里放上chain - while (iterator.hasNext()) { - JsonNode innerJsonObject = iterator.next(); - // 校验加载的 chainName 是否有重复的 - // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 - JsonNode chainNameJsonNode = Optional.ofNullable(innerJsonObject.get(ID)) - .orElse(innerJsonObject.get(NAME)); - String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); - // 检查 chainName - checkChainId(chainName, innerJsonObject.toPrettyString()); - if (!chainNameSet.add(chainName)) { - throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName)); - } + // 如果是禁用的,就不编译了 + if (!getEnableByJsonNode(nodeObject)) { + continue; + } - FlowBus.addChain(chainName); - if(ElRegexUtil.isAbstractChain(innerJsonObject.get(VALUE).textValue())){ - abstratChainMap.put(chainName,innerJsonObject); - //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain - Chain chain = FlowBus.getChain(chainName); - chain.getConditionList().add(new AbstractCondition()); - } - } - }); - // 清空 - chainNameSet.clear(); + // 构建 node + NodePropBean nodePropBean = new NodePropBean().setId(id) + .setName(name) + .setClazz(clazz) + .setScript(script) + .setType(type) + .setFile(file); - for (JsonNode flowJsonNode : flowJsonObjectList) { - // 解析每一个chain - Iterator chainIterator = flowJsonNode.get(FLOW).get(CHAIN).elements(); - while (chainIterator.hasNext()) { - JsonNode chainNode = chainIterator.next(); - //首先需要对继承自抽象Chain的chain进行字符串替换 - parseImplChain(abstratChainMap, implChainSet, chainNode); - //如果一个chain不为抽象chain,则进行解析 - JsonNode chainNameJsonNode = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)); - String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); - if(!abstratChainMap.containsKey(chainName)){ - parseOneChainConsumer.accept(chainNode); - } - } - } - } + ParserHelper.buildNode(nodePropBean); + } + } + } + } - /** - * 解析一个chain的过程 - * @param chainNode chain 节点 - */ - public static void parseOneChainEl(JsonNode chainNode) { - // 构建chainBuilder - String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); - String el = chainNode.get(VALUE).textValue(); - LiteFlowChainELBuilder.createChain() - .setChainId(chainId) - .setEL(el) - .build(); - } + public static void parseChainJson(List flowJsonObjectList, Set chainNameSet, + Consumer parseOneChainConsumer) { + //用于存放抽象chain的map + Map abstratChainMap = new HashMap<>(); + //用于存放已经解析过的实现chain + Set implChainSet = new HashSet<>(); + // 先在元数据里放上chain + // 先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 + // 这样就不用去像之前的版本那样回归调用 + // 同时也解决了不能循环依赖的问题 + flowJsonObjectList.forEach(jsonObject -> { + // 解析chain节点 + Iterator iterator = jsonObject.get(FLOW).get(CHAIN).elements(); + // 先在元数据里放上chain + while (iterator.hasNext()) { + JsonNode innerJsonObject = iterator.next(); + // 校验加载的 chainName 是否有重复的 + // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 + JsonNode chainNameJsonNode = Optional.ofNullable(innerJsonObject.get(ID)) + .orElse(innerJsonObject.get(NAME)); + String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); + // 检查 chainName + checkChainId(chainName, innerJsonObject.toPrettyString()); + if (!chainNameSet.add(chainName)) { + throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName)); + } - /** - * 解析一个chain的过程 - * @param e chain 节点 - */ - public static void parseOneChainEl(Element e) { - // 构建chainBuilder - String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); - String text = e.getText(); - String el = ElRegexUtil.removeComments(text); - LiteFlowChainELBuilder.createChain() - .setChainId(chainId) - .setEL(el) - .build(); - } + // 如果是禁用,就不解析了 + if (!getEnableByJsonNode(innerJsonObject)) { + continue; + } - /** - * 检查 chainId - * @param chainId chainId - * @param elData elData - */ - private static void checkChainId(String chainId, String elData) { - if (StrUtil.isBlank(chainId)) { - throw new ParseException("missing chain id in expression \r\n" + elData); - } - } + FlowBus.addChain(chainName); + if (ElRegexUtil.isAbstractChain(innerJsonObject.get(VALUE).textValue())) { + abstratChainMap.put(chainName, innerJsonObject); + //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain + Chain chain = FlowBus.getChain(chainName); + chain.getConditionList().add(new AbstractCondition()); + } + } + }); + // 清空 + chainNameSet.clear(); - /** - * 解析一个带继承关系的Chain,xml格式 - * @param chain 实现Chain - * @param abstratChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain - */ - private static void parseImplChain(Map abstratChainMap, Set implChainSet, Element chain) { - if(ObjectUtil.isNotNull(chain.attributeValue(EXTENDS))){ - String baseChainId = chain.attributeValue(EXTENDS); - Element baseChain = abstratChainMap.get(baseChainId); - if(baseChain!=null) { - internalParseImplChain(baseChain,chain,abstratChainMap,implChainSet); - }else{ - throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); - } - } - } + for (JsonNode flowJsonNode : flowJsonObjectList) { + // 解析每一个chain + Iterator chainIterator = flowJsonNode.get(FLOW).get(CHAIN).elements(); + while (chainIterator.hasNext()) { + JsonNode chainNode = chainIterator.next(); + // 如果是禁用,就不解析了 + if (!getEnableByJsonNode(chainNode)) { + continue; + } - /** - * 解析一个带继承关系的Chain,json格式 - * @param chainNode 实现Chain - * @param abstratChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain - */ - private static void parseImplChain(Map abstratChainMap, Set implChainSet, JsonNode chainNode) { - if(chainNode.hasNonNull(EXTENDS)){ - String baseChainId = chainNode.get(EXTENDS).textValue(); - JsonNode baseChain= abstratChainMap.get(baseChainId); - if(baseChain!=null) { - internalParseImplChain(baseChain,chainNode,abstratChainMap,implChainSet); - }else{ - throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); - } - } - } + //首先需要对继承自抽象Chain的chain进行字符串替换 + parseImplChain(abstratChainMap, implChainSet, chainNode); + //如果一个chain不为抽象chain,则进行解析 + JsonNode chainNameJsonNode = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)); + String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); + if (!abstratChainMap.containsKey(chainName)) { + parseOneChainConsumer.accept(chainNode); + } + } + } + } - /** - * 解析一个继承自baseChain的implChain,xml格式 - * @param baseChain 父Chain - * @param implChain 实现Chain - * @param abstractChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain - */ - private static void internalParseImplChain(JsonNode baseChain,JsonNode implChain,Map abstractChainMap,Set implChainSet) { - //如果已经解析过了,就不再解析 - if(implChainSet.contains(implChain)) return; - //如果baseChainId也是继承自其他的chain,需要递归解析 - parseImplChain(abstractChainMap, implChainSet, baseChain); - //否则根据baseChainId解析implChainId - String implChainEl = implChain.get(VALUE).textValue(); - String baseChainEl = baseChain.get(VALUE).textValue(); - //替换baseChainId中的implChainId - // 使用正则表达式匹配占位符并替换 - String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl,implChainEl); - ObjectNode objectNode = (ObjectNode) implChain; - objectNode.put(VALUE,parsedEl); - implChainSet.add(implChain); - } + /** + * 解析一个chain的过程 + * + * @param chainNode chain 节点 + */ + public static void parseOneChainEl(JsonNode chainNode) { + // 构建chainBuilder + String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); + String el = chainNode.get(VALUE).textValue(); + LiteFlowChainELBuilder.createChain() + .setChainId(chainId) + .setEL(el) + .build(); + } - /** - * 解析一个继承自baseChain的implChain,json格式 - * @param baseChain 父Chain - * @param implChain 实现Chain - * @param abstractChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain - */ - private static void internalParseImplChain(Element baseChain,Element implChain,Map abstractChainMap,Set implChainSet) { - //如果已经解析过了,就不再解析 - if(implChainSet.contains(implChain)) return; - //如果baseChainId也是继承自其他的chain,需要递归解析 - parseImplChain(abstractChainMap, implChainSet, baseChain); - //否则根据baseChainId解析implChainId - String implChainEl = implChain.getText(); - String baseChainEl = baseChain.getText(); - //替换baseChainId中的implChainId - // 使用正则表达式匹配占位符并替换 - String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl,implChainEl); - implChain.setText(parsedEl); - implChainSet.add(implChain); - } + /** + * 解析一个chain的过程 + * + * @param e chain 节点 + */ + public static void parseOneChainEl(Element e) { + // 构建chainBuilder + String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); + String text = e.getText(); + String el = ElRegexUtil.removeComments(text); + LiteFlowChainELBuilder.createChain() + .setChainId(chainId) + .setEL(el) + .build(); + } + /** + * 检查 chainId + * + * @param chainId chainId + * @param elData elData + */ + private static void checkChainId(String chainId, String elData) { + if (StrUtil.isBlank(chainId)) { + throw new ParseException("missing chain id in expression \r\n" + elData); + } + } + + /** + * 解析一个带继承关系的Chain,xml格式 + * + * @param chain 实现Chain + * @param abstratChainMap 所有的抽象Chain + * @param implChainSet 已经解析过的实现Chain + */ + private static void parseImplChain(Map abstratChainMap, Set implChainSet, Element chain) { + if (ObjectUtil.isNotNull(chain.attributeValue(EXTENDS))) { + String baseChainId = chain.attributeValue(EXTENDS); + Element baseChain = abstratChainMap.get(baseChainId); + if (baseChain != null) { + internalParseImplChain(baseChain, chain, abstratChainMap, implChainSet); + } else { + throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); + } + } + } + + /** + * 解析一个带继承关系的Chain,json格式 + * + * @param chainNode 实现Chain + * @param abstratChainMap 所有的抽象Chain + * @param implChainSet 已经解析过的实现Chain + */ + private static void parseImplChain(Map abstratChainMap, Set implChainSet, JsonNode chainNode) { + if (chainNode.hasNonNull(EXTENDS)) { + String baseChainId = chainNode.get(EXTENDS).textValue(); + JsonNode baseChain = abstratChainMap.get(baseChainId); + if (baseChain != null) { + internalParseImplChain(baseChain, chainNode, abstratChainMap, implChainSet); + } else { + throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); + } + } + } + + /** + * 解析一个继承自baseChain的implChain,xml格式 + * + * @param baseChain 父Chain + * @param implChain 实现Chain + * @param abstractChainMap 所有的抽象Chain + * @param implChainSet 已经解析过的实现Chain + */ + private static void internalParseImplChain(JsonNode baseChain, JsonNode implChain, Map abstractChainMap, Set implChainSet) { + //如果已经解析过了,就不再解析 + if (implChainSet.contains(implChain)) return; + //如果baseChainId也是继承自其他的chain,需要递归解析 + parseImplChain(abstractChainMap, implChainSet, baseChain); + //否则根据baseChainId解析implChainId + String implChainEl = implChain.get(VALUE).textValue(); + String baseChainEl = baseChain.get(VALUE).textValue(); + //替换baseChainId中的implChainId + // 使用正则表达式匹配占位符并替换 + String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl, implChainEl); + ObjectNode objectNode = (ObjectNode) implChain; + objectNode.put(VALUE, parsedEl); + implChainSet.add(implChain); + } + + /** + * 解析一个继承自baseChain的implChain,json格式 + * + * @param baseChain 父Chain + * @param implChain 实现Chain + * @param abstractChainMap 所有的抽象Chain + * @param implChainSet 已经解析过的实现Chain + */ + private static void internalParseImplChain(Element baseChain, Element implChain, Map abstractChainMap, Set implChainSet) { + //如果已经解析过了,就不再解析 + if (implChainSet.contains(implChain)) return; + //如果baseChainId也是继承自其他的chain,需要递归解析 + parseImplChain(abstractChainMap, implChainSet, baseChain); + //否则根据baseChainId解析implChainId + String implChainEl = implChain.getText(); + String baseChainEl = baseChain.getText(); + //替换baseChainId中的implChainId + // 使用正则表达式匹配占位符并替换 + String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl, implChainEl); + implChain.setText(parsedEl); + implChainSet.add(implChain); + } + + private static Boolean getEnableByElement(Element element) { + String enableStr = element.attributeValue(ENABLE); + if (StrUtil.isBlank(enableStr)) { + return true; + } + return Boolean.TRUE.toString().equalsIgnoreCase(enableStr); + } + + private static Boolean getEnableByJsonNode(JsonNode nodeObject) { + String enableStr = nodeObject.hasNonNull(ENABLE) ? nodeObject.get(ENABLE).textValue() : ""; + if (StrUtil.isBlank(enableStr)) { + return true; + } + return Boolean.TRUE.toString().equalsIgnoreCase(enableStr); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/JsonParserTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/JsonParserTest.java index 21410c9e8..ddc5c0af9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/JsonParserTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/JsonParserTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.parser; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.ChainNotFoundException; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.test.BaseTest; @@ -33,4 +34,11 @@ public class JsonParserTest extends BaseTest { Assertions.assertTrue(response.isSuccess()); } + @Test + public void testJsonDisableParser() { + Assertions.assertThrows(ChainNotFoundException.class,()->{ + throw flowExecutor.execute2Resp("chain3", "arg").getCause(); + }); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/XmlParserTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/XmlParserTest.java index ab58e5b08..480fc9532 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/XmlParserTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/XmlParserTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.parser; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.ChainNotFoundException; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.test.BaseTest; @@ -33,4 +34,11 @@ public class XmlParserTest extends BaseTest { Assertions.assertTrue(response.isSuccess()); } + @Test + public void testXmlDisableParser() { + Assertions.assertThrows(ChainNotFoundException.class,()->{ + throw flowExecutor.execute2Resp("chain3", "arg").getCause(); + }); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/YmlParserTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/YmlParserTest.java index 43aa68ed4..95e8f7e03 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/YmlParserTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/parser/YmlParserTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.parser; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.ChainNotFoundException; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.test.BaseTest; @@ -33,4 +34,10 @@ public class YmlParserTest extends BaseTest { Assertions.assertTrue(response.isSuccess()); } + @Test + public void testYmlDisableParser() { + Assertions.assertThrows(ChainNotFoundException.class,()->{ + throw flowExecutor.execute2Resp("chain3", "arg").getCause(); + }); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.json b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.json index ffb87b5e3..63b8e68ac 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.json +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.json @@ -40,6 +40,11 @@ { "name": "chain1", "value": "THEN(a,c,WHEN(b,d,SWITCH(e).to(f,g)), chain2);" + }, + { + "name": "chain3", + "enable": "false", + "value": "THEN(a,c,WHEN(b,d,SWITCH(e).to(f,g)), chain2);" } ] } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.xml index 1e3bc2989..3d74b642f 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.xml @@ -17,4 +17,7 @@ THEN(c, g, f); + + THEN(c, g, f); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.yml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.yml index d84d0acb9..6bd9ab81e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.yml +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/parser/flow.el.yml @@ -20,3 +20,6 @@ flow: value: "THEN(a, c, WHEN(b, d, SWITCH(e).to(f, g)), chain2);" - name: chain2 value: "THEN(c, g, f);" + - name: chain3 + value: "THEN(c, g, f);" + enable: false From d15eeb98559217d04452e8cd43b5c5175786d0f3 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sun, 14 Jan 2024 19:56:52 +0800 Subject: [PATCH 2/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91,=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20sql=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/sql/read/AbstractSqlRead.java | 22 +++++++++++++++---- .../parser/sql/read/impl/ChainRead.java | 20 ++++++++++++----- .../parser/sql/read/impl/ScriptRead.java | 20 ++++++++++++----- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java index fc0ac3e96..610c7079c 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java @@ -49,16 +49,20 @@ public abstract class AbstractSqlRead implements SqlRead { // 设置游标拉取数量 stmt.setFetchSize(SqlReadConstant.FETCH_SIZE_MAX); stmt.setString(1, config.getApplicationName()); - ParameterMetaData parameterMetaData = stmt.getParameterMetaData(); - if (parameterMetaData.getParameterCount() == 2) { - stmt.setBoolean(2, true); - } + rs = stmt.executeQuery(); while (rs.next()) { String xml = buildXmlElement(rs); String uniqueKey = buildXmlElementUniqueKey(rs); + if (hasEnableFiled()){ + boolean enable = getEnableFiledValue(rs); + // 如果停用,直接跳过 + if (!enable){ + continue; + } + } result.put(uniqueKey, xml); } } catch (Exception e) { @@ -71,6 +75,16 @@ public abstract class AbstractSqlRead implements SqlRead { return result; } + /** + * 是否包含启停字段 + */ + public abstract boolean hasEnableFiled(); + + /** + * 获取启停字段对应的字段值 + */ + public abstract boolean getEnableFiledValue(ResultSet rs) throws SQLException; + public abstract String buildQuerySql(); public abstract String buildXmlElement(ResultSet rs) throws SQLException; diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java index d2b9defc0..4bbd1d3cf 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java @@ -23,6 +23,20 @@ public class ChainRead extends AbstractSqlRead { super(config); } + @Override + public boolean hasEnableFiled() { + String chainEnableField = super.config.getChainEnableField(); + return StrUtil.isNotBlank(chainEnableField); + } + + @Override + public boolean getEnableFiledValue(ResultSet rs) throws SQLException { + String chainEnableField = super.config.getChainEnableField(); + byte enable = rs.getByte(chainEnableField); + + return enable == 1; + } + @Override public String buildQuerySql() { String chainTableName = super.config.getChainTableName(); @@ -30,7 +44,7 @@ public class ChainRead extends AbstractSqlRead { String chainNameField = super.config.getChainNameField(); String chainApplicationNameField = super.config.getChainApplicationNameField(); String applicationName = super.config.getApplicationName(); - String chainEnableField = super.config.getChainEnableField(); + if (StrUtil.isBlank(chainTableName)) { throw new ELSQLException("You did not define the chainTableName property"); @@ -43,10 +57,6 @@ public class ChainRead extends AbstractSqlRead { String sqlCmd = StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, chainApplicationNameField); - if (StrUtil.isNotBlank(chainEnableField)){ - sqlCmd = StrUtil.format("{} {}", sqlCmd, StrUtil.format(SqlReadConstant.SQL_ENABLE_PATTERN, chainEnableField)); - } - return sqlCmd; } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java index 6c1253b8b..d55b142f4 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java @@ -30,6 +30,20 @@ public class ScriptRead extends AbstractSqlRead { super(config); } + @Override + public boolean hasEnableFiled() { + String scriptEnableField = super.config.getScriptEnableField(); + return StrUtil.isNotBlank(scriptEnableField); + } + + @Override + public boolean getEnableFiledValue(ResultSet rs) throws SQLException { + String scriptEnableField = super.config.getScriptEnableField(); + byte enable = rs.getByte(scriptEnableField); + + return enable == 1; + } + @Override public String buildQuerySql() { String scriptLanguageField = super.config.getScriptLanguageField(); @@ -40,7 +54,7 @@ public class ScriptRead extends AbstractSqlRead { String scriptTypeField = super.config.getScriptTypeField(); String scriptApplicationNameField = super.config.getScriptApplicationNameField(); String applicationName = super.config.getApplicationName(); - String scriptEnableField = super.config.getScriptEnableField(); + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); @@ -73,10 +87,6 @@ public class ScriptRead extends AbstractSqlRead { ); } - if (StrUtil.isNotBlank(scriptEnableField)){ - sqlCmd = StrUtil.format("{} {}", sqlCmd, StrUtil.format(SqlReadConstant.SQL_ENABLE_PATTERN, scriptEnableField)); - } - return sqlCmd; } From 443b1b51bcd8437eddd9a68bc3849a0d5c403e08 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sun, 14 Jan 2024 22:56:03 +0800 Subject: [PATCH 3/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91,=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20apollo=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/helper/NodeConvertHelper.java | 24 ++++ .../liteflow/util/RuleParsePluginUtil.java | 108 +++++++++++++++++ .../parser/apollo/util/ApolloParseHelper.java | 109 +++++------------- .../apollo/ApolloWithXmlELSpringbootTest.java | 64 +++++----- 4 files changed, 199 insertions(+), 106 deletions(-) create mode 100644 liteflow-core/src/main/java/com/yomahub/liteflow/util/RuleParsePluginUtil.java diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/NodeConvertHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/NodeConvertHelper.java index b4309eaa9..7b5d9105b 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/NodeConvertHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/NodeConvertHelper.java @@ -59,6 +59,10 @@ public class NodeConvertHelper { nodeSimpleVO.setLanguage(matchItemList.get(3)); } + if (matchItemList.size() > 4) { + nodeSimpleVO.setEnable(Boolean.TRUE.toString().equalsIgnoreCase(matchItemList.get(4))); + } + return nodeSimpleVO; } @@ -73,6 +77,10 @@ public class NodeConvertHelper { private String language; + private Boolean enable = Boolean.TRUE; + + private String script; + public String getNodeId() { return nodeId; } @@ -104,5 +112,21 @@ public class NodeConvertHelper { public void setLanguage(String language) { this.language = language; } + + public Boolean getEnable() { + return enable; + } + + public void setEnable(Boolean enable) { + this.enable = enable; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } } } \ No newline at end of file diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/RuleParsePluginUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/RuleParsePluginUtil.java new file mode 100644 index 000000000..96f661163 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/RuleParsePluginUtil.java @@ -0,0 +1,108 @@ +package com.yomahub.liteflow.util; + +import cn.hutool.core.lang.Pair; +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; + +/** + * 插件通用工具类 + * + * @author gaibu + */ +public class RuleParsePluginUtil { + + private static final String CHAIN_XML_PATTERN = "{}"; + private static final String NODE_ITEM_XML_PATTERN = ""; + private static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = ""; + + public static ChainDto parseChainKey(String chainKey) { + String[] chainProp = chainKey.split(StrPool.COLON); + if (chainProp.length == 2) { + return new ChainDto(chainProp[0], chainProp[1]); + } + return new ChainDto(chainKey); + } + + public static String toScriptXml(NodeConvertHelper.NodeSimpleVO simpleVO) { + if (StrUtil.isNotBlank(simpleVO.getLanguage())) { + return StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, + simpleVO.getNodeId(), + simpleVO.getName(), + simpleVO.getType(), + simpleVO.getLanguage(), + simpleVO.getEnable(), + simpleVO.getScript() + ); + } else { + return StrUtil.format(NODE_ITEM_XML_PATTERN, + simpleVO.getNodeId(), + simpleVO.getName(), + simpleVO.getType(), + simpleVO.getEnable(), + simpleVO.getScript() + ); + } + } + + public static Pair parseIdKey(String idKey) { + String[] idProp = idKey.split(StrPool.COLON); + if (idProp.length == 2) { + String id = idProp[0]; + String enableStr = idProp[1]; + return new Pair<>(Boolean.TRUE.toString().equalsIgnoreCase(enableStr), id); + } + return new Pair<>(Boolean.TRUE, idKey); + } + + public static class ChainDto { + /** + * chain id + */ + private String id; + + /** + * chain 启用状态,默认启用 + */ + private String enable = Boolean.TRUE.toString(); + + public boolean isDisable() { + return !isEnable(); + } + + public boolean isEnable() { + return Boolean.TRUE.toString().equalsIgnoreCase(enable); + } + + public ChainDto(String chainId) { + ChainDto chainDto = new ChainDto(chainId, null); + this.enable = chainDto.getEnable(); + this.id = chainDto.getId(); + } + + public ChainDto(String chainId, String enable) { + this.id = chainId; + if (StrUtil.isBlank(enable)) { + this.enable = Boolean.TRUE.toString(); + return; + } + if (Boolean.TRUE.toString().equalsIgnoreCase(enable)) { + this.enable = Boolean.TRUE.toString(); + return; + } + this.enable = Boolean.FALSE.toString(); + } + + public String getId() { + return id; + } + + public String getEnable() { + return enable; + } + + public String toElXml(String elContent) { + return StrUtil.format(CHAIN_XML_PATTERN, id, enable, elContent); + } + } +} diff --git a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java index cfb981469..197a0dc1a 100644 --- a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java @@ -2,8 +2,8 @@ package com.yomahub.liteflow.parser.apollo.util; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import com.ctrip.framework.apollo.Config; import com.ctrip.framework.apollo.ConfigService; @@ -17,6 +17,7 @@ import com.yomahub.liteflow.parser.apollo.exception.ApolloException; import com.yomahub.liteflow.parser.apollo.vo.ApolloParserConfigVO; import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,12 +36,8 @@ public class ApolloParseHelper { private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class); - private final String CHAIN_XML_PATTERN = "{}"; - private final String NODE_XML_PATTERN = "{}"; - private final String NODE_ITEM_XML_PATTERN = ""; - private final String XML_PATTERN = "{}{}"; private final ApolloParserConfigVO apolloParserConfigVO; @@ -81,7 +78,7 @@ public class ApolloParseHelper { // 1. handle chain Set propertyNames = chainConfig.getPropertyNames(); List chainItemContentList = propertyNames.stream() - .map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) + .map(item -> RuleParsePluginUtil.parseChainKey(item).toElXml(chainConfig.getProperty(item, StrUtil.EMPTY))) .collect(Collectors.toList()); // merge all chain content String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); @@ -95,8 +92,7 @@ public class ApolloParseHelper { List scriptItemContentList = scriptNamespaces.stream() .map(item -> convert(item, scriptConfig.getProperty(item, StrUtil.EMPTY))) .filter(Objects::nonNull) - .map(item -> StrUtil.format(NODE_ITEM_XML_PATTERN, item.getNodeId(), item.getName(), item.getType(), - item.getScript())) + .map(RuleParsePluginUtil::toScriptXml) .collect(Collectors.toList()); scriptAllContent = StrUtil.format(NODE_XML_PATTERN, @@ -118,16 +114,25 @@ public class ApolloParseHelper { ConfigChange configChange = changeEvent.getChange(changeKey); String newValue = configChange.getNewValue(); PropertyChangeType changeType = configChange.getChangeType(); + Pair pair = RuleParsePluginUtil.parseIdKey(changeKey); + String id = pair.getValue(); switch (changeType) { case ADDED: case MODIFIED: LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, newValue); - LiteFlowChainELBuilder.createChain().setChainId(changeKey).setEL(newValue).build(); + // 如果是启用,就正常更新 + if (pair.getKey()) { + LiteFlowChainELBuilder.createChain().setChainId(id).setEL(newValue).build(); + } + // 如果是禁用,就删除 + else { + FlowBus.removeChain(id); + } break; case DELETED: LOG.info("starting reload flow config... delete key={}", changeKey); - FlowBus.removeChain(changeKey); + FlowBus.removeChain(id); break; default: } @@ -142,7 +147,7 @@ public class ApolloParseHelper { if (DELETED.equals(changeType)) { newValue = null; } - NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue); if (Objects.isNull(nodeSimpleVO)) { // key不符合规范的时候,直接忽略 LOG.error("key={} is not a valid node config, ignore it", changeKey); @@ -154,12 +159,19 @@ public class ApolloParseHelper { LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, newValue); - LiteFlowNodeBuilder.createScriptNode() - .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) - .setName(nodeSimpleVO.getName()) - .setScript(nodeSimpleVO.getScript()) - .build(); + // 启用就正常更新 + if (nodeSimpleVO.getEnable()) { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(nodeSimpleVO.getScript()) + .build(); + } + // 禁用就删除 + else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + } break; case DELETED: LOG.info("starting reload flow config... delete key={}", changeKey); @@ -171,72 +183,13 @@ public class ApolloParseHelper { } } - private NodeSimpleVO convert(String key, String value) { - // 不需要去理解这串正则,就是一个匹配冒号的 - // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 - List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key); - if (CollUtil.isEmpty(matchItemList)) { - return null; - } - - NodeSimpleVO nodeSimpleVO = new NodeSimpleVO(); - if (matchItemList.size() > 1) { - nodeSimpleVO.setNodeId(matchItemList.get(0)); - nodeSimpleVO.setType(matchItemList.get(1)); - } - - if (matchItemList.size() > 2) { - nodeSimpleVO.setName(matchItemList.get(2)); - } - + private NodeConvertHelper.NodeSimpleVO convert(String key, String value) { + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(key); // set script nodeSimpleVO.setScript(value); return nodeSimpleVO; } - private static class NodeSimpleVO { - - private String nodeId; - - private String type; - - private String name = StrUtil.EMPTY; - - private String script; - - public String getNodeId() { - return nodeId; - } - - public void setNodeId(String nodeId) { - this.nodeId = nodeId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/apollo/ApolloWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/apollo/ApolloWithXmlELSpringbootTest.java index d41153647..886f922f7 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/apollo/ApolloWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-apollo-springboot/src/test/java/com/yomahub/liteflow/test/apollo/ApolloWithXmlELSpringbootTest.java @@ -3,10 +3,10 @@ package com.yomahub.liteflow.test.apollo; import com.ctrip.framework.apollo.Config; import com.google.common.collect.Sets; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ChainNotFoundException; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.LiteflowResponse; import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -16,13 +16,12 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.event.annotation.AfterTestClass; -import org.springframework.test.context.event.annotation.BeforeTestClass; import org.springframework.test.context.junit.jupiter.SpringExtension; + import javax.annotation.Resource; import java.util.Set; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.when; /** * @Description: @@ -33,35 +32,44 @@ import static org.mockito.Mockito.*; @TestPropertySource(value = "classpath:/apollo/application-xml.properties") @SpringBootTest(classes = ApolloWithXmlELSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.apollo.cmp" }) +@ComponentScan({"com.yomahub.liteflow.test.apollo.cmp"}) public class ApolloWithXmlELSpringbootTest { - @MockBean(name = "chainConfig") - private Config chainConfig; + @MockBean(name = "chainConfig") + private Config chainConfig; - @MockBean(name = "scriptConfig") - private Config scriptConfig; + @MockBean(name = "scriptConfig") + private Config scriptConfig; - @Resource - private FlowExecutor flowExecutor; + @Resource + private FlowExecutor flowExecutor; - @BeforeEach - public void setUp() { - MockitoAnnotations.initMocks(this); - } - @Test - public void testApolloWithXml1() { - Set chainNameList = Sets.newHashSet("chain1"); - Set scriptNodeValueList = Sets.newHashSet("s1:script:脚本s1"); - when(chainConfig.getPropertyNames()).thenReturn(chainNameList); - when(scriptConfig.getPropertyNames()).thenReturn(scriptNodeValueList); + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + } - String chain1Data = "THEN(a, b, c, s1);"; - String scriptNodeValue = "defaultContext.setData(\"test\",\"hello\");"; - when(chainConfig.getProperty(anyString(), anyString())).thenReturn(chain1Data); - when(scriptConfig.getProperty(anyString(), anyString())).thenReturn(scriptNodeValue); + @Test + public void testApolloWithXml1() { + Set chainNameList = Sets.newHashSet("chain1", "chain2:false"); + Set scriptNodeValueList = Sets.newHashSet("s1:script:脚本s1", "s2:script:脚本s1:groovy:false"); + when(chainConfig.getPropertyNames()).thenReturn(chainNameList); + when(scriptConfig.getPropertyNames()).thenReturn(scriptNodeValueList); - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStrWithoutTime()); - } + when(chainConfig.getProperty("chain1", "")).thenReturn("THEN(a, b, c, s1);"); + when(chainConfig.getProperty("chain2:false", "")).thenReturn("THEN(a, b, c, s1);"); + when(scriptConfig.getProperty("s1:script:脚本s1", "")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + when(scriptConfig.getProperty("s2:script:脚本s1:groovy:false", "")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStrWithoutTime()); + + // 测试 chain 停用 + Assertions.assertThrows(ChainNotFoundException.class, () -> { + throw flowExecutor.execute2Resp("chain2", "arg").getCause(); + }); + + // 测试 script 停用 + Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s2")); + } } From 5ec298a3d02c52f430360d886be43892b274bfb3 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sat, 20 Jan 2024 21:43:55 +0800 Subject: [PATCH 4/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91,=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20etcd=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/etcd/util/EtcdParserHelper.java | 313 ++++++++---------- .../etcd/EtcdWithXmlELSpringbootTest.java | 20 +- 2 files changed, 153 insertions(+), 180 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java index 04504f17e..f58036d5f 100644 --- a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java @@ -1,10 +1,9 @@ package com.yomahub.liteflow.parser.etcd.util; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; @@ -13,7 +12,9 @@ import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.parser.etcd.EtcdClient; import com.yomahub.liteflow.parser.etcd.exception.EtcdException; import com.yomahub.liteflow.parser.etcd.vo.EtcdParserVO; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import io.etcd.jetcd.ByteSequence; import io.etcd.jetcd.Client; import io.etcd.jetcd.ClientBuilder; @@ -31,200 +32,162 @@ import java.util.stream.Collectors; */ public class EtcdParserHelper { - private static final Logger LOG = LoggerFactory.getLogger(EtcdParserHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(EtcdParserHelper.class); - private final String CHAIN_XML_PATTERN = "{}"; + private final String CHAIN_XML_PATTERN = "{}"; - private final String NODE_XML_PATTERN = "{}"; + private final String NODE_XML_PATTERN = "{}"; - private final String NODE_ITEM_XML_PATTERN = ""; + private final String NODE_ITEM_XML_PATTERN = ""; - private final String XML_PATTERN = "{}{}"; + private final String XML_PATTERN = "{}{}"; - private static final String SEPARATOR = "/"; + private static final String SEPARATOR = "/"; - private final EtcdParserVO etcdParserVO; + private final EtcdParserVO etcdParserVO; - private EtcdClient client; + private EtcdClient client; - public EtcdParserHelper(EtcdParserVO etcdParserVO) { - this.etcdParserVO = etcdParserVO; + public EtcdParserHelper(EtcdParserVO etcdParserVO) { + this.etcdParserVO = etcdParserVO; - try { - try { - this.client = ContextAwareHolder.loadContextAware().getBean(EtcdClient.class); - } - catch (Exception ignored) { - } - if (this.client == null) { - ClientBuilder clientBuilder = Client.builder().endpoints(etcdParserVO.getEndpoints().split(",")); - if (StrUtil.isNotBlank(etcdParserVO.getNamespace())) { - clientBuilder.namespace(ByteSequence.from(etcdParserVO.getNamespace(), CharsetUtil.CHARSET_UTF_8)); - } - if (StrUtil.isAllNotBlank(etcdParserVO.getUser(), etcdParserVO.getPassword())) { - clientBuilder.user(ByteSequence.from(etcdParserVO.getUser(), CharsetUtil.CHARSET_UTF_8)); - clientBuilder.password(ByteSequence.from(etcdParserVO.getPassword(), CharsetUtil.CHARSET_UTF_8)); - } - this.client = new EtcdClient(clientBuilder.build()); - } - } - catch (Exception e) { - throw new EtcdException(e.getMessage()); - } - } + try { + try { + this.client = ContextAwareHolder.loadContextAware().getBean(EtcdClient.class); + } catch (Exception ignored) { + } + if (this.client == null) { + ClientBuilder clientBuilder = Client.builder().endpoints(etcdParserVO.getEndpoints().split(",")); + if (StrUtil.isNotBlank(etcdParserVO.getNamespace())) { + clientBuilder.namespace(ByteSequence.from(etcdParserVO.getNamespace(), CharsetUtil.CHARSET_UTF_8)); + } + if (StrUtil.isAllNotBlank(etcdParserVO.getUser(), etcdParserVO.getPassword())) { + clientBuilder.user(ByteSequence.from(etcdParserVO.getUser(), CharsetUtil.CHARSET_UTF_8)); + clientBuilder.password(ByteSequence.from(etcdParserVO.getPassword(), CharsetUtil.CHARSET_UTF_8)); + } + this.client = new EtcdClient(clientBuilder.build()); + } + } catch (Exception e) { + throw new EtcdException(e.getMessage()); + } + } - public String getContent() { - try { - // 检查chainPath路径下有没有子节点 - List chainNameList = client.getChildrenKeys(etcdParserVO.getChainPath(), SEPARATOR); + public String getContent() { + try { + // 检查chainPath路径下有没有子节点 + List chainNameList = client.getChildrenKeys(etcdParserVO.getChainPath(), SEPARATOR); - // 获取chainPath路径下的所有子节点内容List - List chainItemContentList = new ArrayList<>(); - for (String chainName : chainNameList) { - String chainData = client.get(StrUtil.format("{}/{}", etcdParserVO.getChainPath(), chainName)); - if (StrUtil.isNotBlank(chainData)) { - chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, chainData)); - } - } - // 合并成所有chain的xml内容 - String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); + // 获取chainPath路径下的所有子节点内容List + List chainItemContentList = new ArrayList<>(); + for (String chainName : chainNameList) { + RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainName); + String chainData = client.get(StrUtil.format("{}/{}", etcdParserVO.getChainPath(), chainName)); + if (StrUtil.isNotBlank(chainData) && chainDto.isEnable()) { + chainItemContentList.add(chainDto.toElXml(chainData)); + } + } + // 合并成所有chain的xml内容 + String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); - // 检查是否有脚本内容,如果有,进行脚本内容的获取 - String scriptAllContent = StrUtil.EMPTY; - if (hasScript()) { - List scriptNodeValueList = client.getChildrenKeys(etcdParserVO.getScriptPath(), SEPARATOR) - .stream() - .filter(StrUtil::isNotBlank) - .collect(Collectors.toList()); + // 检查是否有脚本内容,如果有,进行脚本内容的获取 + String scriptAllContent = StrUtil.EMPTY; + if (hasScript()) { + List scriptNodeValueList = client.getChildrenKeys(etcdParserVO.getScriptPath(), SEPARATOR) + .stream() + .filter(StrUtil::isNotBlank) + .collect(Collectors.toList()); - List scriptItemContentList = new ArrayList<>(); - for (String scriptNodeValue : scriptNodeValueList) { - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); - if (Objects.isNull(nodeSimpleVO)) { - throw new EtcdException( - StrUtil.format("The name of the etcd node is invalid:{}", scriptNodeValue)); - } - String scriptData = client - .get(StrUtil.format("{}/{}", etcdParserVO.getScriptPath(), scriptNodeValue)); + List scriptItemContentList = new ArrayList<>(); + for (String scriptNodeValue : scriptNodeValueList) { + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + if (Objects.isNull(nodeSimpleVO)) { + throw new EtcdException( + StrUtil.format("The name of the etcd node is invalid:{}", scriptNodeValue)); + } + String scriptData = client + .get(StrUtil.format("{}/{}", etcdParserVO.getScriptPath(), scriptNodeValue)); - scriptItemContentList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, nodeSimpleVO.getNodeId(), - nodeSimpleVO.getName(), nodeSimpleVO.getType(), scriptData)); - } + nodeSimpleVO.setScript(scriptData); + scriptItemContentList.add(RuleParsePluginUtil.toScriptXml(nodeSimpleVO)); + } - scriptAllContent = StrUtil.format(NODE_XML_PATTERN, - CollUtil.join(scriptItemContentList, StrUtil.EMPTY)); - } - return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); - } - catch (Exception e) { - throw new EtcdException(e.getMessage()); - } - } + scriptAllContent = StrUtil.format(NODE_XML_PATTERN, + CollUtil.join(scriptItemContentList, StrUtil.EMPTY)); + } - public boolean hasScript() { - // 没有配置scriptPath - if (StrUtil.isBlank(etcdParserVO.getScriptPath())) { - return false; - } + return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); + } catch (Exception e) { + throw new EtcdException(e.getMessage()); + } + } - try { - // 存在这个节点,但是子节点不存在 - List chainNameList = client.getChildrenKeys(etcdParserVO.getScriptPath(), SEPARATOR); - return !CollUtil.isEmpty(chainNameList); - } - catch (Exception e) { - return false; - } - } + public boolean hasScript() { + // 没有配置scriptPath + if (StrUtil.isBlank(etcdParserVO.getScriptPath())) { + return false; + } - /** - * 监听 etcd 节点 - */ - public void listen() { - this.client.watchChildChange(this.etcdParserVO.getChainPath(), (updatePath, updateValue) -> { - LOG.info("starting reload flow config... update path={} value={},", updatePath, updateValue); - String chainName = FileNameUtil.getName(updatePath); - LiteFlowChainELBuilder.createChain().setChainId(chainName).setEL(updateValue).build(); - }, (deletePath) -> { - LOG.info("starting reload flow config... delete path={}", deletePath); - String chainName = FileNameUtil.getName(deletePath); - FlowBus.removeChain(chainName); - }); + try { + // 存在这个节点,但是子节点不存在 + List chainNameList = client.getChildrenKeys(etcdParserVO.getScriptPath(), SEPARATOR); + return !CollUtil.isEmpty(chainNameList); + } catch (Exception e) { + return false; + } + } - if (StrUtil.isNotBlank(this.etcdParserVO.getScriptPath())) { - this.client.watchChildChange(this.etcdParserVO.getScriptPath(), (updatePath, updateValue) -> { - LOG.info("starting reload flow config... update path={} value={}", updatePath, updateValue); - String scriptNodeValue = FileNameUtil.getName(updatePath); - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); - LiteFlowNodeBuilder.createScriptNode() - .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.type)) - .setName(nodeSimpleVO.getName()) - .setScript(updateValue) - .build(); - }, (deletePath) -> { - LOG.info("starting reload flow config... delete path={}", deletePath); - String scriptNodeValue = FileNameUtil.getName(deletePath); - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); - FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); - }); - } - } + /** + * 监听 etcd 节点 + */ + public void listen() { + this.client.watchChildChange(this.etcdParserVO.getChainPath(), (updatePath, updateValue) -> { + LOG.info("starting reload flow config... update path={} value={},", updatePath, updateValue); + String changeKey = FileNameUtil.getName(updatePath); + Pair pair = RuleParsePluginUtil.parseIdKey(changeKey); + Boolean enable = pair.getKey(); + String id = pair.getValue(); + // 如果是启用,就正常更新 + if (pair.getKey()) { + LiteFlowChainELBuilder.createChain().setChainId(id).setEL(updateValue).build(); + } + // 如果是禁用,就删除 + else { + FlowBus.removeChain(id); + } + }, (deletePath) -> { + LOG.info("starting reload flow config... delete path={}", deletePath); + String chainName = FileNameUtil.getName(deletePath); + Pair pair = RuleParsePluginUtil.parseIdKey(chainName); + FlowBus.removeChain(pair.getValue()); + }); - public NodeSimpleVO convert(String str) { - // 不需要去理解这串正则,就是一个匹配冒号的 - // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 - List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", str); - if (CollUtil.isEmpty(matchItemList)) { - return null; - } + if (StrUtil.isNotBlank(this.etcdParserVO.getScriptPath())) { + this.client.watchChildChange(this.etcdParserVO.getScriptPath(), (updatePath, updateValue) -> { + LOG.info("starting reload flow config... update path={} value={}", updatePath, updateValue); + String scriptNodeValue = FileNameUtil.getName(updatePath); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + // 启用就正常更新 + if (nodeSimpleVO.getEnable()) { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(nodeSimpleVO.getScript()) + .build(); + } + // 禁用就删除 + else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + } + }, (deletePath) -> { + LOG.info("starting reload flow config... delete path={}", deletePath); + String scriptNodeValue = FileNameUtil.getName(deletePath); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + }); + } + } - NodeSimpleVO nodeSimpleVO = new NodeSimpleVO(); - if (matchItemList.size() > 1) { - nodeSimpleVO.setNodeId(matchItemList.get(0)); - nodeSimpleVO.setType(matchItemList.get(1)); - } - - if (matchItemList.size() > 2) { - nodeSimpleVO.setName(matchItemList.get(2)); - } - - return nodeSimpleVO; - } - - private static class NodeSimpleVO { - - private String nodeId; - - private String type; - - private String name = ""; - - public String getNodeId() { - return nodeId; - } - - public void setNodeId(String nodeId) { - this.nodeId = nodeId; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java index 4c6ec29e8..4dd7e13b1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.etcd; import com.google.common.collect.Lists; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ChainNotFoundException; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.parser.etcd.EtcdClient; @@ -55,20 +56,29 @@ public class EtcdWithXmlELSpringbootTest extends BaseTest { @Test public void testEtcdNodeWithXml1() throws Exception { - List chainNameList = Lists.newArrayList("chain1"); - List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1"); + List chainNameList = Lists.newArrayList("chain1","chain2:false"); + List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1", "s2:script:脚本s1:groovy:false"); when(etcdClient.getChildrenKeys(anyString(), anyString())).thenReturn(chainNameList) .thenReturn(scriptNodeValueList); - String chain1Data = "THEN(a, b, c, s1);"; - String scriptNodeValue = "defaultContext.setData(\"test\",\"hello\");"; - when(etcdClient.get(anyString())).thenReturn(chain1Data).thenReturn(scriptNodeValue); + when(etcdClient.get("chain1")).thenReturn("THEN(a, b, c, s1);"); + when(etcdClient.get("chain2:false")).thenReturn("THEN(a, b, c, s1);"); + when(etcdClient.get("s1:script:脚本s1")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + when(etcdClient.get("s2:script:脚本s1:groovy:false")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); DefaultContext context = response.getFirstContextBean(); Assertions.assertTrue(response.isSuccess()); Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStr()); Assertions.assertEquals("hello", context.getData("test")); + + // 测试 chain 停用 + Assertions.assertThrows(ChainNotFoundException.class, () -> { + throw flowExecutor.execute2Resp("chain2", "arg").getCause(); + }); + + // 测试 script 停用 + Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s2")); } @Test From 20c4aca0c3ca0ebbdc7911eedb9383b9e6ed9656 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sat, 20 Jan 2024 21:57:55 +0800 Subject: [PATCH 5/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91,=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20zk=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/builder/LiteFlowNodeBuilder.java | 4 +- .../parser/apollo/util/ApolloParseHelper.java | 1 + .../parser/etcd/util/EtcdParserHelper.java | 1 + .../parser/zk/util/ZkParserHelper.java | 315 +++++++++--------- .../ZkNodeWithXmlELSpringbootTest.java | 18 + 5 files changed, 177 insertions(+), 162 deletions(-) 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 afb817c92..932cd6b62 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 @@ -151,7 +151,9 @@ public class LiteFlowNodeBuilder { } public LiteFlowNodeBuilder setLanguage(String language) { - this.node.setLanguage(language); + if (StrUtil.isNotBlank(language)){ + this.node.setLanguage(language); + } return this; } diff --git a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java index 197a0dc1a..af965e6da 100644 --- a/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-apollo/src/main/java/com/yomahub/liteflow/parser/apollo/util/ApolloParseHelper.java @@ -166,6 +166,7 @@ public class ApolloParseHelper { .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) .setName(nodeSimpleVO.getName()) .setScript(nodeSimpleVO.getScript()) + .setLanguage(nodeSimpleVO.getLanguage()) .build(); } // 禁用就删除 diff --git a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java index f58036d5f..9749f9a59 100644 --- a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java @@ -174,6 +174,7 @@ public class EtcdParserHelper { .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) .setName(nodeSimpleVO.getName()) .setScript(nodeSimpleVO.getScript()) + .setLanguage(nodeSimpleVO.getLanguage()) .build(); } // 禁用就删除 diff --git a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java index a9811533c..53b6348f2 100644 --- a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java @@ -1,9 +1,9 @@ package com.yomahub.liteflow.parser.zk.util; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.io.file.FileNameUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; @@ -12,6 +12,7 @@ import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.zk.exception.ZkException; import com.yomahub.liteflow.parser.zk.vo.ZkParserVO; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.recipes.cache.CuratorCache; @@ -26,186 +27,178 @@ import java.util.Objects; public class ZkParserHelper { - private static final Logger LOG = LoggerFactory.getLogger(ZkParserHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(ZkParserHelper.class); - private final ZkParserVO zkParserVO; + private final ZkParserVO zkParserVO; - private final CuratorFramework client; + private final CuratorFramework client; - private final String CHAIN_XML_PATTERN = "{}"; + private final String CHAIN_XML_PATTERN = "{}"; - private final String NODE_XML_PATTERN = "{}"; + private final String NODE_XML_PATTERN = "{}"; - private final String NODE_ITEM_XML_PATTERN = ""; + private final String NODE_ITEM_XML_PATTERN = ""; - private final String NODE_ITEM_XML_WITH_LANGUAGE_PATTERN = ""; + private final String NODE_ITEM_XML_WITH_LANGUAGE_PATTERN = ""; - private final String XML_PATTERN = "{}{}"; + private final String XML_PATTERN = "{}{}"; - public ZkParserHelper(ZkParserVO zkParserVO) { - this.zkParserVO = zkParserVO; + public ZkParserHelper(ZkParserVO zkParserVO) { + this.zkParserVO = zkParserVO; - try { - CuratorFramework client = CuratorFrameworkFactory.newClient(zkParserVO.getConnectStr(), - new RetryNTimes(10, 5000)); - client.start(); + try { + CuratorFramework client = CuratorFrameworkFactory.newClient(zkParserVO.getConnectStr(), + new RetryNTimes(10, 5000)); + client.start(); - this.client = client; - } - catch (Exception e) { - throw new ZkException(e.getMessage()); - } - } + this.client = client; + } catch (Exception e) { + throw new ZkException(e.getMessage()); + } + } - public String getContent() { - try { - // 检查zk上有没有chainPath节点 - if (client.checkExists().forPath(zkParserVO.getChainPath()) == null) { - throw new ZkException(StrUtil.format("zk node[{}] is not exist", zkParserVO.getChainPath())); - } + public String getContent() { + try { + // 检查zk上有没有chainPath节点 + if (client.checkExists().forPath(zkParserVO.getChainPath()) == null) { + throw new ZkException(StrUtil.format("zk node[{}] is not exist", zkParserVO.getChainPath())); + } - // 检查chainPath路径下有没有子节点 - List chainNameList = client.getChildren().forPath(zkParserVO.getChainPath()); - // 获取chainPath路径下的所有子节点内容List - List chainItemContentList = new ArrayList<>(); - for (String chainName : chainNameList) { - String chainData = new String( - client.getData().forPath(StrUtil.format("{}/{}", zkParserVO.getChainPath(), chainName))); - if (StrUtil.isBlank(chainData)){ - continue; - } - chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, chainData)); - } - // 合并成所有chain的xml内容 - String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); + // 检查chainPath路径下有没有子节点 + List chainNameList = client.getChildren().forPath(zkParserVO.getChainPath()); + // 获取chainPath路径下的所有子节点内容List + List chainItemContentList = new ArrayList<>(); + for (String chainName : chainNameList) { + RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainName); + String chainData = new String( + client.getData().forPath(StrUtil.format("{}/{}", zkParserVO.getChainPath(), chainName))); + if (StrUtil.isNotBlank(chainData) && chainDto.isEnable()) { + chainItemContentList.add(chainDto.toElXml(chainData)); + } + } + // 合并成所有chain的xml内容 + String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); - // 检查是否有脚本内容,如果有,进行脚本内容的获取 - String scriptAllContent = StrUtil.EMPTY; - if (hasScript()) { - List scriptNodeValueList = client.getChildren().forPath(zkParserVO.getScriptPath()); + // 检查是否有脚本内容,如果有,进行脚本内容的获取 + String scriptAllContent = StrUtil.EMPTY; + if (hasScript()) { + List scriptNodeValueList = client.getChildren().forPath(zkParserVO.getScriptPath()); - List scriptItemContentList = new ArrayList<>(); - for (String scriptNodeValue : scriptNodeValueList) { - NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); - if (Objects.isNull(nodeSimpleVO)) { - throw new ZkException(StrUtil.format("The name of the zk node is invalid:{}", scriptNodeValue)); - } - String scriptData = new String(client.getData() - .forPath(StrUtil.format("{}/{}", zkParserVO.getScriptPath(), scriptNodeValue))); + List scriptItemContentList = new ArrayList<>(); + for (String scriptNodeValue : scriptNodeValueList) { + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + if (Objects.isNull(nodeSimpleVO)) { + throw new ZkException(StrUtil.format("The name of the zk node is invalid:{}", scriptNodeValue)); + } + String scriptData = new String(client.getData() + .forPath(StrUtil.format("{}/{}", zkParserVO.getScriptPath(), scriptNodeValue))); - // 有语言类型 - if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_XML_WITH_LANGUAGE_PATTERN, - nodeSimpleVO.getNodeId(), nodeSimpleVO.getName(), nodeSimpleVO.getType(), - nodeSimpleVO.getLanguage(), scriptData)); - } - // 没有语言类型 - else { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, nodeSimpleVO.getNodeId(), - nodeSimpleVO.getName(), nodeSimpleVO.getType(), scriptData)); - } - } + nodeSimpleVO.setScript(scriptData); + scriptItemContentList.add(RuleParsePluginUtil.toScriptXml(nodeSimpleVO)); + } - scriptAllContent = StrUtil.format(NODE_XML_PATTERN, - CollUtil.join(scriptItemContentList, StrUtil.EMPTY)); - } + scriptAllContent = StrUtil.format(NODE_XML_PATTERN, + CollUtil.join(scriptItemContentList, StrUtil.EMPTY)); + } - return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); - } - catch (Exception e) { - throw new ZkException(e.getMessage()); - } - } + return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); + } catch (Exception e) { + throw new ZkException(e.getMessage()); + } + } - public boolean hasScript() { - // 没有配置scriptPath - if (StrUtil.isBlank(zkParserVO.getScriptPath())) { - return false; - } + public boolean hasScript() { + // 没有配置scriptPath + if (StrUtil.isBlank(zkParserVO.getScriptPath())) { + return false; + } - try { - // 配置了,但是不存在这个节点 - if (client.checkExists().forPath(zkParserVO.getScriptPath()) == null) { - return false; - } + try { + // 配置了,但是不存在这个节点 + if (client.checkExists().forPath(zkParserVO.getScriptPath()) == null) { + return false; + } - // 存在这个节点,但是子节点不存在 - List chainNameList = client.getChildren().forPath(zkParserVO.getScriptPath()); - return !CollUtil.isEmpty(chainNameList); - } - catch (Exception e) { - return false; - } - } + // 存在这个节点,但是子节点不存在 + List chainNameList = client.getChildren().forPath(zkParserVO.getScriptPath()); + return !CollUtil.isEmpty(chainNameList); + } catch (Exception e) { + return false; + } + } - /** - * 监听 zk 节点 - */ - public void listenZkNode() { - // 监听chain - CuratorCache cache1 = CuratorCache.build(client, zkParserVO.getChainPath()); - cache1.start(); - cache1.listenable().addListener((type, oldData, data) -> { - String path = data.getPath(); - String value = new String(data.getData()); - if (StrUtil.isBlank(value)) { - return; - } - if (ListUtil.toList(CuratorCacheListener.Type.NODE_CREATED, CuratorCacheListener.Type.NODE_CHANGED) - .contains(type)) { - LOG.info("starting reload flow config... {} path={} value={},", type.name(), path, value); - String chainName = FileNameUtil.getName(path); - LiteFlowChainELBuilder.createChain().setChainId(chainName).setEL(value).build(); - } - else if (CuratorCacheListener.Type.NODE_DELETED.equals(type)) { - LOG.info("starting reload flow config... delete path={}", path); - String chainName = FileNameUtil.getName(path); - FlowBus.removeChain(chainName); - } - }); + /** + * 监听 zk 节点 + */ + public void listenZkNode() { + // 监听chain + CuratorCache cache1 = CuratorCache.build(client, zkParserVO.getChainPath()); + cache1.start(); + cache1.listenable().addListener((type, oldData, data) -> { + String path = data.getPath(); + String value = new String(data.getData()); + if (StrUtil.isBlank(value)) { + return; + } + if (ListUtil.toList(CuratorCacheListener.Type.NODE_CREATED, CuratorCacheListener.Type.NODE_CHANGED) + .contains(type)) { + LOG.info("starting reload flow config... {} path={} value={},", type.name(), path, value); + String chainName = FileNameUtil.getName(path); + Pair pair = RuleParsePluginUtil.parseIdKey(chainName); + String id = pair.getValue(); + // 如果是启用,就正常更新 + if (pair.getKey()) { + LiteFlowChainELBuilder.createChain().setChainId(id).setEL(value).build(); + } + // 如果是禁用,就删除 + else { + FlowBus.removeChain(id); + } + } else if (CuratorCacheListener.Type.NODE_DELETED.equals(type)) { + LOG.info("starting reload flow config... delete path={}", path); + String chainName = FileNameUtil.getName(path); + Pair pair = RuleParsePluginUtil.parseIdKey(chainName); + FlowBus.removeChain(pair.getValue()); + } + }); - if (StrUtil.isNotBlank(zkParserVO.getScriptPath())) { - // 监听script - CuratorCache cache2 = CuratorCache.build(client, zkParserVO.getScriptPath()); - cache2.start(); - cache2.listenable().addListener((type, oldData, data) -> { - String path = data.getPath(); - String value = new String(data.getData()); - if (StrUtil.isBlank(value)) { - return; - } - if (ListUtil.toList(CuratorCacheListener.Type.NODE_CREATED, CuratorCacheListener.Type.NODE_CHANGED) - .contains(type)) { - LOG.info("starting reload flow config... {} path={} value={},", type.name(), path, value); - String scriptNodeValue = FileNameUtil.getName(path); - NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); - // 有语言类型 - if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { - LiteFlowNodeBuilder.createScriptNode() - .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) - .setName(nodeSimpleVO.getName()) - .setScript(value) - .setLanguage(nodeSimpleVO.getLanguage()) - .build(); - } - // 没有语言类型 - else { - LiteFlowNodeBuilder.createScriptNode() - .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) - .setName(nodeSimpleVO.getName()) - .setScript(value) - .build(); - } - } - else if (CuratorCacheListener.Type.NODE_DELETED.equals(type)) { - LOG.info("starting reload flow config... delete path={}", path); - String scriptNodeValue = FileNameUtil.getName(path); - NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); - FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); - } - }); - } - } + if (StrUtil.isNotBlank(zkParserVO.getScriptPath())) { + // 监听script + CuratorCache cache2 = CuratorCache.build(client, zkParserVO.getScriptPath()); + cache2.start(); + cache2.listenable().addListener((type, oldData, data) -> { + String path = data.getPath(); + String value = new String(data.getData()); + if (StrUtil.isBlank(value)) { + return; + } + if (ListUtil.toList(CuratorCacheListener.Type.NODE_CREATED, CuratorCacheListener.Type.NODE_CHANGED) + .contains(type)) { + LOG.info("starting reload flow config... {} path={} value={},", type.name(), path, value); + String scriptNodeValue = FileNameUtil.getName(path); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + + // 启用就正常更新 + if (nodeSimpleVO.getEnable()) { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(nodeSimpleVO.getScript()) + .setLanguage(nodeSimpleVO.getLanguage()) + .build(); + } + // 禁用就删除 + else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + } + } else if (CuratorCacheListener.Type.NODE_DELETED.equals(type)) { + LOG.info("starting reload flow config... delete path={}", path); + String scriptNodeValue = FileNameUtil.getName(path); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + } + }); + } + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/zookeeper/ZkNodeWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/zookeeper/ZkNodeWithXmlELSpringbootTest.java index c04891253..0ade361f7 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/zookeeper/ZkNodeWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-zk-springboot/src/test/java/com/yomahub/liteflow/test/zookeeper/ZkNodeWithXmlELSpringbootTest.java @@ -1,6 +1,8 @@ package com.yomahub.liteflow.test.zookeeper; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ChainNotFoundException; +import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; @@ -66,6 +68,10 @@ public class ZkNodeWithXmlELSpringbootTest extends BaseTest { zkClient.createPersistent(chain2Path, true); zkClient.writeData(chain2Path, "THEN(a, b, c, s3);"); + String chain3Path = ZK_CHAIN_PATH + "/chain3:false"; + zkClient.createPersistent(chain3Path, true); + zkClient.writeData(chain3Path, "THEN(a, b, c, s3);"); + String script1Path = ZK_SCRIPT_PATH + "/s1:script:脚本s1:groovy"; zkClient.createPersistent(script1Path, true); zkClient.writeData(script1Path, "defaultContext.setData(\"test\",\"hello\");"); @@ -77,6 +83,10 @@ public class ZkNodeWithXmlELSpringbootTest extends BaseTest { String script3Path = ZK_SCRIPT_PATH + "/s3:script:脚本s3"; zkClient.createPersistent(script3Path, true); zkClient.writeData(script3Path, "defaultContext.setData(\"test\",\"hello\");"); + + String script4Path = ZK_SCRIPT_PATH + "/s4:script:脚本s3:groovy:false"; + zkClient.createPersistent(script4Path, true); + zkClient.writeData(script4Path, "defaultContext.setData(\"test\",\"hello\");"); } @Test @@ -94,6 +104,14 @@ public class ZkNodeWithXmlELSpringbootTest extends BaseTest { DefaultContext context = response.getFirstContextBean(); Assertions.assertTrue(response.isSuccess()); Assertions.assertEquals("hello", context.getData("test")); + + // 测试 chain 停用 + Assertions.assertThrows(ChainNotFoundException.class, () -> { + throw flowExecutor.execute2Resp("chain3", "arg").getCause(); + }); + + // 测试 script 停用 + Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s4")); } @AfterAll From d39de336d46a22140d110964a3edd22eee75afae Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sat, 20 Jan 2024 22:37:51 +0800 Subject: [PATCH 6/8] =?UTF-8?q?enhancement=20#I61D1N=20=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20enable=20=E9=80=BB=E8=BE=91,=E5=AE=8C?= =?UTF-8?q?=E6=88=90=20redis=20=E6=94=B9=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../parser/etcd/util/EtcdParserHelper.java | 6 +- .../redis/mode/polling/ChainPollingTask.java | 25 ++++- .../mode/polling/RedisParserPollingMode.java | 25 ++--- .../redis/mode/polling/ScriptPollingTask.java | 33 +++++-- .../subscribe/RedisParserSubscribeMode.java | 96 +++++++++++-------- .../parser/zk/util/ZkParserHelper.java | 8 +- .../RedisWithXmlELPollSpringbootTest.java | 33 ++++++- 7 files changed, 142 insertions(+), 84 deletions(-) diff --git a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java index 9749f9a59..a71835691 100644 --- a/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-etcd/src/main/java/com/yomahub/liteflow/parser/etcd/util/EtcdParserHelper.java @@ -34,12 +34,8 @@ public class EtcdParserHelper { private static final Logger LOG = LoggerFactory.getLogger(EtcdParserHelper.class); - private final String CHAIN_XML_PATTERN = "{}"; - private final String NODE_XML_PATTERN = "{}"; - private final String NODE_ITEM_XML_PATTERN = ""; - private final String XML_PATTERN = "{}{}"; private static final String SEPARATOR = "/"; @@ -82,7 +78,7 @@ public class EtcdParserHelper { for (String chainName : chainNameList) { RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainName); String chainData = client.get(StrUtil.format("{}/{}", etcdParserVO.getChainPath(), chainName)); - if (StrUtil.isNotBlank(chainData) && chainDto.isEnable()) { + if (StrUtil.isNotBlank(chainData)) { chainItemContentList.add(chainDto.toElXml(chainData)); } } diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ChainPollingTask.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ChainPollingTask.java index 6b66d030b..814e29844 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ChainPollingTask.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ChainPollingTask.java @@ -1,5 +1,6 @@ package com.yomahub.liteflow.parser.redis.mode.polling; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; @@ -8,6 +9,7 @@ import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.parser.redis.mode.RClient; import com.yomahub.liteflow.parser.redis.vo.RedisParserVO; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import java.util.*; @@ -61,11 +63,19 @@ public class ChainPollingTask implements Runnable { for (Map.Entry entry : chainSHAMap.entrySet()) { String chainId = entry.getKey(); String oldSHA = entry.getValue(); + Pair pair = RuleParsePluginUtil.parseIdKey(chainId); + // 如果是停用,就直接进删除 + if (pair.getKey()){ + FlowBus.removeChain(pair.getValue()); + needDelete.add(chainId); + continue; + } + //在redis服务端通过Lua脚本计算SHA值 String newSHA = chainClient.evalSha(valueLua, chainKey, chainId); if (StrUtil.equals(newSHA, "nil")) { //新SHA值为nil, 即未获取到该chain,表示该chain已被删除 - FlowBus.removeChain(chainId); + FlowBus.removeChain(pair.getValue()); LOG.info("starting reload flow config... delete key={}", chainId); //添加到待删除的list 后续统一从SHAMap中移除 @@ -75,7 +85,7 @@ public class ChainPollingTask implements Runnable { else if (!StrUtil.equals(newSHA, oldSHA)) { //SHA值发生变化,表示该chain的值已被修改,重新拉取变化的chain String chainData = chainClient.hget(chainKey, chainId); - LiteFlowChainELBuilder.createChain().setChainId(chainId).setEL(chainData).build(); + LiteFlowChainELBuilder.createChain().setChainId(pair.getValue()).setEL(chainData).build(); LOG.info("starting reload flow config... update key={} new value={},", chainId, chainData); //修改SHAMap @@ -98,12 +108,17 @@ public class ChainPollingTask implements Runnable { //在此处重新拉取所有chainId集合,补充添加新chain Set newChainSet = chainClient.hkeys(chainKey); for (String chainId : newChainSet) { + Pair pair = RuleParsePluginUtil.parseIdKey(chainId); + if (!chainSHAMap.containsKey(chainId)) { //将新chainId添加到LiteFlowChainELBuilder和SHAMap String chainData = chainClient.hget(chainKey, chainId); - LiteFlowChainELBuilder.createChain().setChainId(chainId).setEL(chainData).build(); - LOG.info("starting reload flow config... create key={} new value={},", chainId, chainData); - chainSHAMap.put(chainId, DigestUtil.sha1Hex(chainData)); + // 如果是启用,才装配 + if (pair.getKey()){ + LiteFlowChainELBuilder.createChain().setChainId(pair.getValue()).setEL(chainData).build(); + LOG.info("starting reload flow config... create key={} new value={},", chainId, chainData); + chainSHAMap.put(chainId, DigestUtil.sha1Hex(chainData)); + } } } } diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java index 52370099f..0063acf75 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/RedisParserPollingMode.java @@ -1,7 +1,6 @@ package com.yomahub.liteflow.parser.redis.mode.polling; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.thread.NamedThreadFactory; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -13,11 +12,15 @@ import com.yomahub.liteflow.parser.redis.mode.RedisMode; import com.yomahub.liteflow.parser.redis.mode.RedisParserHelper; import com.yomahub.liteflow.parser.redis.vo.RedisParserVO; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import org.redisson.Redisson; import org.redisson.config.Config; import java.util.*; -import java.util.concurrent.*; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * Redis 轮询机制实现类 @@ -126,8 +129,9 @@ public class RedisParserPollingMode implements RedisParserHelper { List chainItemContentList = new ArrayList<>(); for (String chainName : chainNameSet) { String chainData = chainClient.hget(chainKey, chainName); + RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainName); if (StrUtil.isNotBlank(chainData)) { - chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainName, chainData)); + chainItemContentList.add(chainDto.toElXml(chainData)); }else{ continue; } @@ -154,19 +158,10 @@ public class RedisParserPollingMode implements RedisParserHelper { StrUtil.format("The name of the redis field [{}] in scriptKey [{}] is invalid", scriptFieldValue, scriptKey)); } - String scriptData = scriptClient.hget(scriptKey, scriptFieldValue); - // 有语言类型 - if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, - nodeSimpleVO.getNodeId(), nodeSimpleVO.getName(), nodeSimpleVO.getType(), - nodeSimpleVO.getLanguage(), scriptData)); - } - // 没有语言类型 - else { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, nodeSimpleVO.getNodeId(), - nodeSimpleVO.getName(), nodeSimpleVO.getType(), scriptData)); - } + String scriptData = scriptClient.hget(scriptKey, scriptFieldValue); + nodeSimpleVO.setScript(scriptData); + scriptItemContentList.add(RuleParsePluginUtil.toScriptXml(nodeSimpleVO)); //计算scriptData的SHA值 String scriptSHA = DigestUtil.sha1Hex(scriptData); diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ScriptPollingTask.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ScriptPollingTask.java index 40d4704e6..67229de76 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ScriptPollingTask.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/polling/ScriptPollingTask.java @@ -76,15 +76,23 @@ public class ScriptPollingTask implements Runnable { //添加到待删除的list 后续统一从SHAMap中移除 //不在这里直接移除是为了避免先删除导致scriptSHAMap并没有完全遍历完 script删除不全 needDelete.add(scriptFieldValue); - } - else if (!StrUtil.equals(newSHA, oldSHA)) { + } else if (!StrUtil.equals(newSHA, oldSHA)) { //SHA值发生变化,表示该script的值已被修改,重新拉取变化的script String scriptData = scriptClient.hget(scriptKey, scriptFieldValue); - RedisParserHelper.changeScriptNode(scriptFieldValue, scriptData); - LOG.info("starting reload flow config... update key={} new value={},", scriptFieldValue, scriptData); - //修改SHAMap - scriptSHAMap.put(scriptFieldValue, newSHA); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); + nodeSimpleVO.setScript(scriptData); + if (nodeSimpleVO.getEnable()) { + RedisParserHelper.changeScriptNode(scriptFieldValue, scriptData); + LOG.info("starting reload flow config... update key={} new value={},", scriptFieldValue, scriptData); + + //修改SHAMap + scriptSHAMap.put(scriptFieldValue, newSHA); + } else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + LOG.info("starting reload flow config... delete key={}", scriptFieldValue); + needDelete.add(scriptFieldValue); + } } //SHA值无变化,表示该script未改变 } @@ -106,9 +114,16 @@ public class ScriptPollingTask implements Runnable { if (!scriptSHAMap.containsKey(scriptFieldValue)) { //将新script添加到LiteFlowChainELBuilder和SHAMap String scriptData = scriptClient.hget(scriptKey, scriptFieldValue); - RedisParserHelper.changeScriptNode(scriptFieldValue, scriptData); - LOG.info("starting reload flow config... create key={} new value={},", scriptFieldValue, scriptData); - scriptSHAMap.put(scriptFieldValue, DigestUtil.sha1Hex(scriptData)); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); + if (nodeSimpleVO.getEnable()) { + RedisParserHelper.changeScriptNode(scriptFieldValue, scriptData); + LOG.info("starting reload flow config... create key={} new value={},", scriptFieldValue, scriptData); + scriptSHAMap.put(scriptFieldValue, DigestUtil.sha1Hex(scriptData)); + } else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + LOG.info("starting reload flow config... delete key={}", scriptFieldValue); + needDelete.add(scriptFieldValue); + } } } } diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java index f21116d4d..2deb35beb 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/subscribe/RedisParserSubscribeMode.java @@ -1,10 +1,12 @@ package com.yomahub.liteflow.parser.redis.mode.subscribe; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; +import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.redis.exception.RedisException; @@ -13,10 +15,10 @@ import com.yomahub.liteflow.parser.redis.mode.RedisMode; import com.yomahub.liteflow.parser.redis.mode.RedisParserHelper; import com.yomahub.liteflow.parser.redis.vo.RedisParserVO; import com.yomahub.liteflow.spi.holder.ContextAwareHolder; +import com.yomahub.liteflow.util.RuleParsePluginUtil; import org.redisson.Redisson; import org.redisson.api.map.event.EntryCreatedListener; import org.redisson.api.map.event.EntryRemovedListener; -import org.redisson.api.map.event.EntryUpdatedListener; import org.redisson.config.Config; import java.util.ArrayList; @@ -28,7 +30,7 @@ import java.util.Map; * 使用 Redisson客户端 RMapCache存储结构 * * @author hxinyu - * @since 2.11.0 + * @since 2.11.0 */ public class RedisParserSubscribeMode implements RedisParserHelper { @@ -46,14 +48,13 @@ public class RedisParserSubscribeMode implements RedisParserHelper { try { this.chainClient = ContextAwareHolder.loadContextAware().getBean("chainClient"); this.scriptClient = ContextAwareHolder.loadContextAware().getBean("scriptClient"); - } - catch (Exception ignored) { + } catch (Exception ignored) { } if (ObjectUtil.isNull(chainClient)) { RedisMode redisMode = redisParserVO.getRedisMode(); Config config; //Redis单点模式 - if (redisMode.equals(RedisMode.SINGLE)){ + if (redisMode.equals(RedisMode.SINGLE)) { config = getSingleRedissonConfig(redisParserVO, redisParserVO.getChainDataBase()); this.chainClient = new RClient(Redisson.create(config)); //如果有脚本数据 @@ -74,8 +75,7 @@ public class RedisParserSubscribeMode implements RedisParserHelper { } } } - } - catch (Exception e) { + } catch (Exception e) { throw new RedisException(e.getMessage()); } @@ -91,8 +91,9 @@ public class RedisParserSubscribeMode implements RedisParserHelper { for (Map.Entry entry : chainMap.entrySet()) { String chainId = entry.getKey(); String chainData = entry.getValue(); + RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainId); if (StrUtil.isNotBlank(chainData)) { - chainItemContentList.add(StrUtil.format(CHAIN_XML_PATTERN, chainId, chainData)); + chainItemContentList.add(chainDto.toElXml(chainData)); } } // 合并成所有chain的xml内容 @@ -112,17 +113,9 @@ public class RedisParserSubscribeMode implements RedisParserHelper { StrUtil.format("The name of the redis field [{}] in scriptKey [{}] is invalid", scriptFieldValue, redisParserVO.getScriptKey())); } - // 有语言类型 - if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, - nodeSimpleVO.getNodeId(), nodeSimpleVO.getName(), nodeSimpleVO.getType(), - nodeSimpleVO.getLanguage(), scriptData)); - } - // 没有语言类型 - else { - scriptItemContentList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, nodeSimpleVO.getNodeId(), - nodeSimpleVO.getName(), nodeSimpleVO.getType(), scriptData)); - } + + nodeSimpleVO.setScript(scriptData); + scriptItemContentList.add(RuleParsePluginUtil.toScriptXml(nodeSimpleVO)); } scriptAllContent = StrUtil.format(NODE_XML_PATTERN, @@ -130,8 +123,7 @@ public class RedisParserSubscribeMode implements RedisParserHelper { } return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); - } - catch (Exception e) { + } catch (Exception e) { throw new RedisException(e.getMessage()); } } @@ -145,8 +137,7 @@ public class RedisParserSubscribeMode implements RedisParserHelper { // 存在这个节点,但是子节点不存在 Map scriptMap = scriptClient.getMap(redisParserVO.getScriptKey()); return !CollUtil.isEmpty(scriptMap); - } - catch (Exception e) { + } catch (Exception e) { return false; } } @@ -158,35 +149,58 @@ public class RedisParserSubscribeMode implements RedisParserHelper { public void listenRedis() { //监听 chain String chainKey = redisParserVO.getChainKey(); + EntryCreatedListener chainModifyFunc = event -> { + LOG.info("starting modify flow config... create key={} value={},", event.getKey(), event.getValue()); + String chainName = event.getKey(); + String value = event.getValue(); + Pair pair = RuleParsePluginUtil.parseIdKey(chainName); + String id = pair.getValue(); + // 如果是启用,就正常更新 + if (pair.getKey()) { + LiteFlowChainELBuilder.createChain().setChainId(id).setEL(value).build(); + } + // 如果是禁用,就删除 + else { + FlowBus.removeChain(id); + } + }; //添加新 chain - chainClient.addListener(chainKey, (EntryCreatedListener) event -> { - LOG.info("starting reload flow config... create key={} value={},", event.getKey(), event.getValue()); - LiteFlowChainELBuilder.createChain().setChainId(event.getKey()).setEL(event.getValue()).build(); - }); + chainClient.addListener(chainKey, chainModifyFunc); //修改 chain - chainClient.addListener(chainKey, (EntryUpdatedListener) event -> { - LOG.info("starting reload flow config... update key={} new value={},", event.getKey(), event.getValue()); - LiteFlowChainELBuilder.createChain().setChainId(event.getKey()).setEL(event.getValue()).build(); - }); + chainClient.addListener(chainKey, chainModifyFunc); //删除 chain chainClient.addListener(chainKey, (EntryRemovedListener) event -> { LOG.info("starting reload flow config... delete key={}", event.getKey()); - FlowBus.removeChain(event.getKey()); + Pair pair = RuleParsePluginUtil.parseIdKey(event.getKey()); + FlowBus.removeChain(pair.getValue()); }); //监听 script + EntryCreatedListener scriptModifyFunc = event -> { + LOG.info("starting modify flow config... create key={} value={},", event.getKey(), event.getValue()); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(event.getKey()); + nodeSimpleVO.setScript(event.getValue()); + // 启用就正常更新 + if (nodeSimpleVO.getEnable()) { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(nodeSimpleVO.getScript()) + .setLanguage(nodeSimpleVO.getLanguage()) + .build(); + } + // 禁用就删除 + else { + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + } + }; if (ObjectUtil.isNotNull(scriptClient) && ObjectUtil.isNotNull(redisParserVO.getScriptDataBase())) { String scriptKey = redisParserVO.getScriptKey(); //添加 script - scriptClient.addListener(scriptKey, (EntryCreatedListener) event -> { - LOG.info("starting reload flow config... create key={} value={},", event.getKey(), event.getValue()); - RedisParserHelper.changeScriptNode(event.getKey(), event.getValue()); - }); + scriptClient.addListener(scriptKey, scriptModifyFunc); //修改 script - scriptClient.addListener(scriptKey, (EntryUpdatedListener) event -> { - LOG.info("starting reload flow config... update key={} new value={},", event.getKey(), event.getValue()); - RedisParserHelper.changeScriptNode(event.getKey(), event.getValue()); - }); + scriptClient.addListener(scriptKey, scriptModifyFunc); //删除 script scriptClient.addListener(scriptKey, (EntryRemovedListener) event -> { LOG.info("starting reload flow config... delete key={}", event.getKey()); diff --git a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java index 53b6348f2..9fe7e39c6 100644 --- a/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-zk/src/main/java/com/yomahub/liteflow/parser/zk/util/ZkParserHelper.java @@ -33,14 +33,8 @@ public class ZkParserHelper { private final CuratorFramework client; - private final String CHAIN_XML_PATTERN = "{}"; - private final String NODE_XML_PATTERN = "{}"; - private final String NODE_ITEM_XML_PATTERN = ""; - - private final String NODE_ITEM_XML_WITH_LANGUAGE_PATTERN = ""; - private final String XML_PATTERN = "{}{}"; public ZkParserHelper(ZkParserVO zkParserVO) { @@ -72,7 +66,7 @@ public class ZkParserHelper { RuleParsePluginUtil.ChainDto chainDto = RuleParsePluginUtil.parseChainKey(chainName); String chainData = new String( client.getData().forPath(StrUtil.format("{}/{}", zkParserVO.getChainPath(), chainName))); - if (StrUtil.isNotBlank(chainData) && chainDto.isEnable()) { + if (StrUtil.isNotBlank(chainData)) { chainItemContentList.add(chainDto.toElXml(chainData)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELPollSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELPollSpringbootTest.java index dd1fb52a5..2de4189a9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELPollSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-redis-springboot/src/test/java/com/yomahub/liteflow/test/redis/RedisWithXmlELPollSpringbootTest.java @@ -2,6 +2,8 @@ package com.yomahub.liteflow.test.redis; import cn.hutool.crypto.digest.DigestUtil; import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ChainNotFoundException; +import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; @@ -9,7 +11,9 @@ import com.yomahub.liteflow.parser.redis.mode.RClient; import com.yomahub.liteflow.parser.redis.mode.polling.RedisParserPollingMode; import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; -import org.junit.jupiter.api.*; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -17,13 +21,15 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; + import javax.annotation.Resource; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ScheduledThreadPoolExecutor; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; /** @@ -163,4 +169,27 @@ public class RedisWithXmlELPollSpringbootTest extends BaseTest { Assertions.assertTrue(response.isSuccess()); Assertions.assertEquals("hello world", context.getData("test11")); } + + @Test + public void testDisablePollWithXml() throws InterruptedException { + Set chainNameSet = new HashSet<>(); + chainNameSet.add("chain1122:false"); + String chainValue = "THEN(a, b, c);"; + + when(chainClient.hkeys("pollChainKey")).thenReturn(chainNameSet); + when(chainClient.hget("pollChainKey", "chain1122:true")).thenReturn(chainValue); + + Set scriptFieldSet = new HashSet<>(); + scriptFieldSet.add("s4:script:脚本s3:groovy:false"); + when(scriptClient.hkeys("pollScriptKey")).thenReturn(scriptFieldSet); + when(scriptClient.hget("pollScriptKey", "s4:script:脚本s3:groovy:true")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + + // 测试 chain 停用 + Assertions.assertThrows(ChainNotFoundException.class, () -> { + throw flowExecutor.execute2Resp("chain1122", "arg").getCause(); + }); + + // 测试 script 停用 + Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s4")); + } } From a3e1802319e5627645a93859830e72f03946db06 Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Sat, 20 Jan 2024 23:06:00 +0800 Subject: [PATCH 7/8] =?UTF-8?q?enhancement=20#I61D1N=20=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/parser/helper/ParserHelper.java | 2 +- .../etcd/EtcdWithXmlELSpringbootTest.java | 130 +++++++++--------- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java index e3d8f055e..0fbae743e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java @@ -444,7 +444,7 @@ public class ParserHelper { } private static Boolean getEnableByJsonNode(JsonNode nodeObject) { - String enableStr = nodeObject.hasNonNull(ENABLE) ? nodeObject.get(ENABLE).textValue() : ""; + String enableStr = nodeObject.hasNonNull(ENABLE) ? nodeObject.get(ENABLE).toString() : ""; if (StrUtil.isBlank(enableStr)) { return true; } diff --git a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java index 4dd7e13b1..dc6a1dc3b 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-etcd-springboot/src/test/java/com/yomahub/liteflow/test/etcd/EtcdWithXmlELSpringbootTest.java @@ -17,8 +17,10 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.ComponentScan; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; + import javax.annotation.Resource; import java.util.List; + import static org.mockito.Mockito.*; /** @@ -28,88 +30,88 @@ import static org.mockito.Mockito.*; @TestPropertySource(value = "classpath:/etcd/application-xml-cluster.properties") @SpringBootTest(classes = EtcdWithXmlELSpringbootTest.class) @EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.etcd.cmp" }) +@ComponentScan({"com.yomahub.liteflow.test.etcd.cmp"}) public class EtcdWithXmlELSpringbootTest extends BaseTest { - @MockBean - private EtcdClient etcdClient; + @MockBean + private EtcdClient etcdClient; - @Resource - private FlowExecutor flowExecutor; + @Resource + private FlowExecutor flowExecutor; - private static final String SEPARATOR = "/"; + private static final String SEPARATOR = "/"; - private static final String CHAIN_PATH = "/liteflow/chain"; + private static final String CHAIN_PATH = "/liteflow/chain"; - private static final String SCRIPT_PATH = "/liteflow/script"; + private static final String SCRIPT_PATH = "/liteflow/script"; - @BeforeEach - public void setUp() { - MockitoAnnotations.initMocks(this); - } + @BeforeEach + public void setUp() { + MockitoAnnotations.initMocks(this); + } - @AfterEach - public void after() { - FlowBus.cleanCache(); - FlowBus.clearStat(); - } + @AfterEach + public void after() { + FlowBus.cleanCache(); + FlowBus.clearStat(); + } - @Test - public void testEtcdNodeWithXml1() throws Exception { - List chainNameList = Lists.newArrayList("chain1","chain2:false"); - List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1", "s2:script:脚本s1:groovy:false"); - when(etcdClient.getChildrenKeys(anyString(), anyString())).thenReturn(chainNameList) - .thenReturn(scriptNodeValueList); + @Test + public void testEtcdNodeWithXml1() throws Exception { + List chainNameList = Lists.newArrayList("chain1", "chain2:false"); + List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1", "s2:script:脚本s1:groovy:false"); + when(etcdClient.getChildrenKeys(CHAIN_PATH, SEPARATOR)).thenReturn(chainNameList); + when(etcdClient.getChildrenKeys(SCRIPT_PATH, SEPARATOR)).thenReturn(scriptNodeValueList); - when(etcdClient.get("chain1")).thenReturn("THEN(a, b, c, s1);"); - when(etcdClient.get("chain2:false")).thenReturn("THEN(a, b, c, s1);"); - when(etcdClient.get("s1:script:脚本s1")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); - when(etcdClient.get("s2:script:脚本s1:groovy:false")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + when(etcdClient.get(CHAIN_PATH + "/chain1")).thenReturn("THEN(a, b, c, s1);"); + when(etcdClient.get(CHAIN_PATH + "/chain2:false")).thenReturn("THEN(a, b, c, s1);"); + when(etcdClient.get(SCRIPT_PATH + "/s1:script:脚本s1")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); + when(etcdClient.get(SCRIPT_PATH + "/s2:script:脚本s1:groovy:false")).thenReturn("defaultContext.setData(\"test\",\"hello\");"); - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - DefaultContext context = response.getFirstContextBean(); - Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStr()); - Assertions.assertEquals("hello", context.getData("test")); + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStr()); + Assertions.assertEquals("hello", context.getData("test")); - // 测试 chain 停用 - Assertions.assertThrows(ChainNotFoundException.class, () -> { - throw flowExecutor.execute2Resp("chain2", "arg").getCause(); - }); + // 测试 chain 停用 + Assertions.assertThrows(ChainNotFoundException.class, () -> { + throw flowExecutor.execute2Resp("chain2", "arg").getCause(); + }); - // 测试 script 停用 - Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s2")); - } + // 测试 script 停用 + Assertions.assertTrue(!FlowBus.getNodeMap().containsKey("s2")); + } - @Test - public void testEtcdNodeWithXml2() throws Exception { - List chainNameList = Lists.newArrayList("chain1"); - List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1"); - when(etcdClient.getChildrenKeys(CHAIN_PATH, SEPARATOR)).thenReturn(chainNameList); - when(etcdClient.getChildrenKeys(SCRIPT_PATH, SEPARATOR)).thenReturn(scriptNodeValueList); + @Test + public void testEtcdNodeWithXml2() throws Exception { + List chainNameList = Lists.newArrayList("chain1"); + List scriptNodeValueList = Lists.newArrayList("s1:script:脚本s1"); + when(etcdClient.getChildrenKeys(CHAIN_PATH, SEPARATOR)).thenReturn(chainNameList); + when(etcdClient.getChildrenKeys(SCRIPT_PATH, SEPARATOR)).thenReturn(scriptNodeValueList); - String chain1Data = "THEN(a, b, c, s1);"; - String chain1ChangedData = "THEN(a, b, s1);"; - String scriptNodeValue = "defaultContext.setData(\"test\",\"hello\");"; - String scriptNodeChangedValue = "defaultContext.setData(\"test\",\"hello world\");"; - when(etcdClient.get(CHAIN_PATH + SEPARATOR + "chain1")).thenReturn(chain1Data).thenReturn(chain1ChangedData); - when(etcdClient.get(SCRIPT_PATH + SEPARATOR + "s1:script:脚本s1")).thenReturn(scriptNodeValue) - .thenReturn(scriptNodeChangedValue); + String chain1Data = "THEN(a, b, c, s1);"; + String chain1ChangedData = "THEN(a, b, s1);"; + String scriptNodeValue = "defaultContext.setData(\"test\",\"hello\");"; + String scriptNodeChangedValue = "defaultContext.setData(\"test\",\"hello world\");"; + when(etcdClient.get(CHAIN_PATH + SEPARATOR + "chain1")).thenReturn(chain1Data).thenReturn(chain1ChangedData); + when(etcdClient.get(SCRIPT_PATH + SEPARATOR + "s1:script:脚本s1")).thenReturn(scriptNodeValue) + .thenReturn(scriptNodeChangedValue); - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - DefaultContext context = response.getFirstContextBean(); - Assertions.assertTrue(response.isSuccess()); - Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStr()); - Assertions.assertEquals("hello", context.getData("test")); + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>b==>c==>s1[脚本s1]", response.getExecuteStepStr()); + Assertions.assertEquals("hello", context.getData("test")); - flowExecutor.reloadRule(); + flowExecutor.reloadRule(); - LiteflowResponse response2 = flowExecutor.execute2Resp("chain1", "arg"); - DefaultContext context2 = response2.getFirstContextBean(); - Assertions.assertTrue(response2.isSuccess()); - Assertions.assertEquals("a==>b==>s1[脚本s1]", response2.getExecuteStepStr()); - Assertions.assertEquals("hello world", context2.getData("test")); + LiteflowResponse response2 = flowExecutor.execute2Resp("chain1", "arg"); + DefaultContext context2 = response2.getFirstContextBean(); + Assertions.assertTrue(response2.isSuccess()); + Assertions.assertEquals("a==>b==>s1[脚本s1]", response2.getExecuteStepStr()); + Assertions.assertEquals("hello world", context2.getData("test")); - } + } } From ea54827acead8c175bf2ff396f712d4da501612a Mon Sep 17 00:00:00 2001 From: gaibu <1016771049@qq.com> Date: Wed, 20 Mar 2024 21:52:43 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../liteflow/parser/helper/ParserHelper.java | 181 ++++++++++-------- 1 file changed, 99 insertions(+), 82 deletions(-) diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java index 0fbae743e..f8677cc69 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/ParserHelper.java @@ -8,12 +8,7 @@ import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.builder.prop.NodePropBean; import com.yomahub.liteflow.enums.NodeTypeEnum; -import com.yomahub.liteflow.exception.ChainDuplicateException; -import com.yomahub.liteflow.exception.ChainNotFoundException; -import com.yomahub.liteflow.exception.NodeClassNotFoundException; -import com.yomahub.liteflow.exception.NodeTypeCanNotGuessException; -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.flow.element.Chain; import com.yomahub.liteflow.flow.element.condition.AbstractCondition; @@ -48,7 +43,6 @@ public class ParserHelper { /** * 构建 node - * * @param nodePropBean 构建 node 的中间属性 */ public static void buildNode(NodePropBean nodePropBean) { @@ -71,7 +65,8 @@ public class ParserHelper { if (nodeType != null) { type = nodeType.getCode(); } - } catch (Exception e) { + } + catch (Exception e) { throw new NodeClassNotFoundException(StrUtil.format("cannot find the node[{}]", clazz)); } } @@ -105,7 +100,6 @@ public class ParserHelper { */ /** * xml 形式的主要解析过程 - * * @param documentList documentList */ public static void parseNodeDocument(List documentList) { @@ -125,7 +119,6 @@ public class ParserHelper { file = e.attributeValue(FILE); language = e.attributeValue(LANGUAGE); - // 如果是禁用的,就不编译了 if (!getEnableByElement(e)) { continue; } @@ -145,10 +138,10 @@ public class ParserHelper { } } - public static void parseChainDocument(List documentList, Set chainNameSet, + public static void parseChainDocument(List documentList, Set chainIdSet, Consumer parseOneChainConsumer) { //用于存放抽象chain的map - Map abstratChainMap = new HashMap<>(); + Map abstratChainMap = new HashMap<>(); //用于存放已经解析过的实现chain Set implChainSet = new HashSet<>(); // 先在元数据里放上chain @@ -163,34 +156,34 @@ public class ParserHelper { for (Element e : chainList) { // 校验加载的 chainName 是否有重复的 // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 - String chainName = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); + String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); // 检查 chainName - checkChainId(chainName, e.getText()); - if (!chainNameSet.add(chainName)) { - throw new ChainDuplicateException(StrUtil.format("[chain name duplicate] chainName={}", chainName)); + checkChainId(chainId, e.getText()); + if (!chainIdSet.add(chainId)) { + throw new ChainDuplicateException(StrUtil.format("[chain name duplicate] chainName={}", chainId)); } // 如果是禁用,就不解析了 if (!getEnableByElement(e)) { continue; } - FlowBus.addChain(chainName); - if (ElRegexUtil.isAbstractChain(e.getText())) { - abstratChainMap.put(chainName, e); + FlowBus.addChain(chainId); + if(ElRegexUtil.isAbstractChain(e.getText())){ + abstratChainMap.put(chainId,e); //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain - Chain chain = FlowBus.getChain(chainName); + Chain chain = FlowBus.getChain(chainId); chain.getConditionList().add(new AbstractCondition()); } - } + }; }); // 清空 - chainNameSet.clear(); + chainIdSet.clear(); // 解析每一个chain for (Document document : documentList) { Element rootElement = document.getRootElement(); List chainList = rootElement.elements(CHAIN); - for (Element chain : chainList) { + for(Element chain:chainList){ // 如果是禁用,就不解析了 if (!getEnableByElement(chain)) { continue; @@ -200,7 +193,7 @@ public class ParserHelper { parseImplChain(abstratChainMap, implChainSet, chain); //如果一个chain不为抽象chain,则进行解析 String chainName = Optional.ofNullable(chain.attributeValue(ID)).orElse(chain.attributeValue(NAME)); - if (!abstratChainMap.containsKey(chainName)) { + if(!abstratChainMap.containsKey(chainName)){ parseOneChainConsumer.accept(chain); } } @@ -241,10 +234,10 @@ public class ParserHelper { } } - public static void parseChainJson(List flowJsonObjectList, Set chainNameSet, + public static void parseChainJson(List flowJsonObjectList, Set chainIdSet, Consumer parseOneChainConsumer) { //用于存放抽象chain的map - Map abstratChainMap = new HashMap<>(); + Map abstratChainMap = new HashMap<>(); //用于存放已经解析过的实现chain Set implChainSet = new HashSet<>(); // 先在元数据里放上chain @@ -261,11 +254,11 @@ public class ParserHelper { // TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 JsonNode chainNameJsonNode = Optional.ofNullable(innerJsonObject.get(ID)) .orElse(innerJsonObject.get(NAME)); - String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); + String chainId = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); // 检查 chainName - checkChainId(chainName, innerJsonObject.toPrettyString()); - if (!chainNameSet.add(chainName)) { - throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName)); + checkChainId(chainId, innerJsonObject.toString()); + if (!chainIdSet.add(chainId)) { + throw new ChainDuplicateException(String.format("[chain id duplicate] chainId=%s", chainId)); } // 如果是禁用,就不解析了 @@ -273,17 +266,17 @@ public class ParserHelper { continue; } - FlowBus.addChain(chainName); - if (ElRegexUtil.isAbstractChain(innerJsonObject.get(VALUE).textValue())) { - abstratChainMap.put(chainName, innerJsonObject); + FlowBus.addChain(chainId); + if(ElRegexUtil.isAbstractChain(innerJsonObject.get(VALUE).textValue())){ + abstratChainMap.put(chainId,innerJsonObject); //如果是抽象chain,则向其中添加一个AbstractCondition,用于标记这个chain为抽象chain - Chain chain = FlowBus.getChain(chainName); + Chain chain = FlowBus.getChain(chainId); chain.getConditionList().add(new AbstractCondition()); } } }); // 清空 - chainNameSet.clear(); + chainIdSet.clear(); for (JsonNode flowJsonNode : flowJsonObjectList) { // 解析每一个chain @@ -299,8 +292,8 @@ public class ParserHelper { parseImplChain(abstratChainMap, implChainSet, chainNode); //如果一个chain不为抽象chain,则进行解析 JsonNode chainNameJsonNode = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)); - String chainName = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); - if (!abstratChainMap.containsKey(chainName)) { + String chainId = Optional.ofNullable(chainNameJsonNode).map(JsonNode::textValue).orElse(null); + if(!abstratChainMap.containsKey(chainId)){ parseOneChainConsumer.accept(chainNode); } } @@ -309,40 +302,68 @@ public class ParserHelper { /** * 解析一个chain的过程 - * * @param chainNode chain 节点 */ public static void parseOneChainEl(JsonNode chainNode) { // 构建chainBuilder String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); - String el = chainNode.get(VALUE).textValue(); - LiteFlowChainELBuilder.createChain() - .setChainId(chainId) - .setEL(el) - .build(); + + JsonNode routeJsonNode = chainNode.get(ROUTE); + + LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); + + // 如果有route这个标签,说明是决策表chain + // 决策表链路必须有route和body这两个标签 + if (routeJsonNode != null){ + builder.setRoute(routeJsonNode.textValue()); + + JsonNode bodyJsonNode = chainNode.get(BODY); + if (bodyJsonNode == null){ + String errMsg = StrUtil.format("If you have defined the field route, then you must define the field body in chain[{}]", chainId); + throw new FlowSystemException(errMsg); + } + builder.setEL(bodyJsonNode.textValue()); + }else{ + builder.setEL(chainNode.get(VALUE).textValue()); + } + + builder.build(); } /** * 解析一个chain的过程 - * * @param e chain 节点 */ public static void parseOneChainEl(Element e) { // 构建chainBuilder String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); - String text = e.getText(); - String el = ElRegexUtil.removeComments(text); - LiteFlowChainELBuilder.createChain() - .setChainId(chainId) - .setEL(el) - .build(); + + Element routeElement = e.element(ROUTE); + + LiteFlowChainELBuilder builder = LiteFlowChainELBuilder.createChain().setChainId(chainId); + + // 如果有route这个标签,说明是决策表chain + // 决策表链路必须有route和body这两个标签 + if (routeElement != null){ + builder.setRoute(ElRegexUtil.removeComments(routeElement.getText())); + + Element bodyElement = e.element(BODY); + if (bodyElement == null){ + String errMsg = StrUtil.format("If you have defined the tag , then you must define the tag in chain[{}]", chainId); + throw new FlowSystemException(errMsg); + } + builder.setEL(ElRegexUtil.removeComments(bodyElement.getText())); + }else{ + builder.setEL(ElRegexUtil.removeComments(e.getText())); + } + + builder.build(); } /** * 检查 chainId - * * @param chainId chainId - * @param elData elData + * @param elData elData */ private static void checkChainId(String chainId, String elData) { if (StrUtil.isBlank(chainId)) { @@ -352,18 +373,17 @@ public class ParserHelper { /** * 解析一个带继承关系的Chain,xml格式 - * - * @param chain 实现Chain + * @param chain 实现Chain * @param abstratChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain + * @param implChainSet 已经解析过的实现Chain */ private static void parseImplChain(Map abstratChainMap, Set implChainSet, Element chain) { - if (ObjectUtil.isNotNull(chain.attributeValue(EXTENDS))) { + if(ObjectUtil.isNotNull(chain.attributeValue(EXTENDS))){ String baseChainId = chain.attributeValue(EXTENDS); Element baseChain = abstratChainMap.get(baseChainId); - if (baseChain != null) { - internalParseImplChain(baseChain, chain, abstratChainMap, implChainSet); - } else { + if(baseChain!=null) { + internalParseImplChain(baseChain,chain,abstratChainMap,implChainSet); + }else{ throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); } } @@ -371,18 +391,17 @@ public class ParserHelper { /** * 解析一个带继承关系的Chain,json格式 - * - * @param chainNode 实现Chain + * @param chainNode 实现Chain * @param abstratChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain + * @param implChainSet 已经解析过的实现Chain */ private static void parseImplChain(Map abstratChainMap, Set implChainSet, JsonNode chainNode) { - if (chainNode.hasNonNull(EXTENDS)) { + if(chainNode.hasNonNull(EXTENDS)){ String baseChainId = chainNode.get(EXTENDS).textValue(); - JsonNode baseChain = abstratChainMap.get(baseChainId); - if (baseChain != null) { - internalParseImplChain(baseChain, chainNode, abstratChainMap, implChainSet); - } else { + JsonNode baseChain= abstratChainMap.get(baseChainId); + if(baseChain!=null) { + internalParseImplChain(baseChain,chainNode,abstratChainMap,implChainSet); + }else{ throw new ChainNotFoundException(StrUtil.format("[abstract chain not found] chainName={}", baseChainId)); } } @@ -390,15 +409,14 @@ public class ParserHelper { /** * 解析一个继承自baseChain的implChain,xml格式 - * - * @param baseChain 父Chain - * @param implChain 实现Chain + * @param baseChain 父Chain + * @param implChain 实现Chain * @param abstractChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain + * @param implChainSet 已经解析过的实现Chain */ - private static void internalParseImplChain(JsonNode baseChain, JsonNode implChain, Map abstractChainMap, Set implChainSet) { + private static void internalParseImplChain(JsonNode baseChain,JsonNode implChain,Map abstractChainMap,Set implChainSet) { //如果已经解析过了,就不再解析 - if (implChainSet.contains(implChain)) return; + if(implChainSet.contains(implChain)) return; //如果baseChainId也是继承自其他的chain,需要递归解析 parseImplChain(abstractChainMap, implChainSet, baseChain); //否则根据baseChainId解析implChainId @@ -406,23 +424,22 @@ public class ParserHelper { String baseChainEl = baseChain.get(VALUE).textValue(); //替换baseChainId中的implChainId // 使用正则表达式匹配占位符并替换 - String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl, implChainEl); + String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl,implChainEl); ObjectNode objectNode = (ObjectNode) implChain; - objectNode.put(VALUE, parsedEl); + objectNode.put(VALUE,parsedEl); implChainSet.add(implChain); } /** * 解析一个继承自baseChain的implChain,json格式 - * - * @param baseChain 父Chain - * @param implChain 实现Chain + * @param baseChain 父Chain + * @param implChain 实现Chain * @param abstractChainMap 所有的抽象Chain - * @param implChainSet 已经解析过的实现Chain + * @param implChainSet 已经解析过的实现Chain */ - private static void internalParseImplChain(Element baseChain, Element implChain, Map abstractChainMap, Set implChainSet) { + private static void internalParseImplChain(Element baseChain,Element implChain,Map abstractChainMap,Set implChainSet) { //如果已经解析过了,就不再解析 - if (implChainSet.contains(implChain)) return; + if(implChainSet.contains(implChain)) return; //如果baseChainId也是继承自其他的chain,需要递归解析 parseImplChain(abstractChainMap, implChainSet, baseChain); //否则根据baseChainId解析implChainId @@ -430,7 +447,7 @@ public class ParserHelper { String baseChainEl = baseChain.getText(); //替换baseChainId中的implChainId // 使用正则表达式匹配占位符并替换 - String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl, implChainEl); + String parsedEl = ElRegexUtil.replaceAbstractChain(baseChainEl,implChainEl); implChain.setText(parsedEl); implChainSet.add(implChain); }