diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/FlowParserTypeEnum.java b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/FlowParserTypeEnum.java index c5c96b026..62ee1580a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/FlowParserTypeEnum.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/FlowParserTypeEnum.java @@ -5,10 +5,13 @@ package com.yomahub.liteflow.enums; * @since 2.5.0 */ public enum FlowParserTypeEnum { - TYPE_XML("xml","xml"), - TYPE_YML("yml","yml"), - TYPE_JSON("json","json") - ; + TYPE_XML("xml", "xml"), + TYPE_YML("yml", "yml"), + TYPE_JSON("json", "json"), + TYPE_EL_XML("el_xml", "el_xml"), + TYPE_EL_JSON("el_json", "el_json"), + TYPE_EL_YML("el_yml", "el_yml") + ; private String type; private String name; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/parser/el/ELXmlFlowParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/el/ELXmlFlowParser.java new file mode 100644 index 000000000..faaca1230 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/el/ELXmlFlowParser.java @@ -0,0 +1,157 @@ +package com.yomahub.liteflow.parser.el; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.builder.LiteFlowChainBuilder; +import com.yomahub.liteflow.builder.prop.ChainPropBean; +import com.yomahub.liteflow.builder.prop.NodePropBean; +import com.yomahub.liteflow.enums.ConditionTypeEnum; +import com.yomahub.liteflow.exception.ChainDuplicateException; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.parser.BaseFlowParser; +import com.yomahub.liteflow.parser.XmlFlowParser; +import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder; +import org.dom4j.Document; +import org.dom4j.DocumentHelper; +import org.dom4j.Element; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import static com.yomahub.liteflow.common.ChainConstant.*; +import static com.yomahub.liteflow.common.ChainConstant.THREAD_EXECUTOR_CLASS; + +/** + * EL表达引擎Xml形式的解析抽象引擎 + * @author Bryan.Zhang + * @since 2.8.0 + */ +public abstract class ELXmlFlowParser extends BaseFlowParser { + + private final Logger LOG = LoggerFactory.getLogger(XmlFlowParser.class); + + private final Set CHAIN_NAME_SET = new CopyOnWriteArraySet<>(); + + public void parse(String content) throws Exception { + parse(ListUtil.toList(content)); + } + + @Override + public void parse(List contentList) throws Exception { + if (CollectionUtil.isEmpty(contentList)) { + return; + } + List documentList = ListUtil.toList(); + for (String content : contentList) { + Document document = DocumentHelper.parseText(content); + documentList.add(document); + } + parseDocument(documentList); + } + + //xml形式的主要解析过程 + public void parseDocument(List documentList) throws Exception { + //在相应的环境下进行节点的初始化工作 + //在spring体系下会获得spring扫描后的节点,接入元数据 + //在非spring体系下是一个空实现,等于不做此步骤 + ContextCmpInitHolder.loadContextCmpInit().initCmp(); + + //先在元数据里放上chain + //先放有一个好处,可以在parse的时候先映射到FlowBus的chainMap,然后再去解析 + //这样就不用去像之前的版本那样回归调用 + //同时也解决了不能循环依赖的问题 + documentList.forEach(document -> { + // 解析chain节点 + List chainList = document.getRootElement().elements(CHAIN); + + //先在元数据里放上chain + chainList.forEach(e -> { + //校验加载的 chainName 是否有重复的 + //TODO 这里是否有个问题,当混合格式加载的时候,2个同名的Chain在不同的文件里,就不行了 + String chainName = e.attributeValue(NAME); + if (!CHAIN_NAME_SET.add(chainName)) { + throw new ChainDuplicateException(String.format("[chain name duplicate] chainName=%s", chainName)); + } + + FlowBus.addChain(chainName); + }); + }); + // 清空 + CHAIN_NAME_SET.clear(); + + 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; + for (Element e : nodeList) { + id = e.attributeValue(ID); + name = e.attributeValue(NAME); + clazz = e.attributeValue(_CLASS); + type = e.attributeValue(TYPE); + script = e.getTextTrim(); + file = e.attributeValue(FILE); + + // 构建 node + NodePropBean nodePropBean = new NodePropBean() + .setId(id) + .setName(name) + .setClazz(clazz) + .setScript(script) + .setType(type) + .setFile(file); + + buildNode(nodePropBean); + } + } + + //解析每一个chain + List chainList = rootElement.elements(CHAIN); + chainList.forEach(this::parseOneChain); + } + } + + /** + * 解析一个chain的过程 + */ + private void parseOneChain(Element e) { + String condValueStr; + String group; + String errorResume; + String any; + String threadExecutorClass; + ConditionTypeEnum conditionType; + + //构建chainBuilder + String chainName = e.attributeValue(NAME); + LiteFlowChainBuilder chainBuilder = LiteFlowChainBuilder.createChain().setChainName(chainName); + + for (Iterator it = e.elementIterator(); it.hasNext(); ) { + Element condE = it.next(); + conditionType = ConditionTypeEnum.getEnumByCode(condE.getName()); + condValueStr = condE.attributeValue(VALUE); + errorResume = condE.attributeValue(ERROR_RESUME); + group = condE.attributeValue(GROUP); + any = condE.attributeValue(ANY); + threadExecutorClass = condE.attributeValue(THREAD_EXECUTOR_CLASS); + + ChainPropBean chainPropBean = new ChainPropBean() + .setCondValueStr(condValueStr) + .setGroup(group) + .setErrorResume(errorResume) + .setAny(any) + .setThreadExecutorClass(threadExecutorClass) + .setConditionType(conditionType); + + // 构建 chain + buildChain(chainPropBean, chainBuilder); + } + } +}