diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/FallbackCmp.java b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/FallbackCmp.java new file mode 100644 index 000000000..a1e9ae2bd --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/annotation/FallbackCmp.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.annotation; + +import com.yomahub.liteflow.enums.NodeTypeEnum; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 降级组件 + * + * @author DaleLee + * @since 2.11.1 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface FallbackCmp { +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java index 97e3de3e5..cb6fd7748 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/LiteFlowChainELBuilder.java @@ -4,19 +4,19 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.CharUtil; import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.ql.util.express.DefaultContext; import com.ql.util.express.ExpressRunner; import com.ql.util.express.InstructionSet; import com.ql.util.express.exception.QLException; import com.yomahub.liteflow.builder.el.operator.*; import com.yomahub.liteflow.common.ChainConstant; -import com.yomahub.liteflow.exception.DataNotFoundException; -import com.yomahub.liteflow.exception.ELParseException; -import com.yomahub.liteflow.exception.FlowSystemException; +import com.yomahub.liteflow.exception.*; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.flow.element.Chain; -import com.yomahub.liteflow.flow.element.Node; import com.yomahub.liteflow.flow.element.Condition; +import com.yomahub.liteflow.flow.element.Node; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; @@ -34,6 +34,8 @@ public class LiteFlowChainELBuilder { private static final LFLog LOG = LFLoggerManager.getLogger(LiteFlowChainELBuilder.class); + private static ObjectMapper objectMapper =new ObjectMapper(); + private Chain chain; /** @@ -72,6 +74,7 @@ public class LiteFlowChainELBuilder { EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DEFAULT, Object.class, new DefaultOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.TAG, Object.class, new TagOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ANY, Object.class, new AnyOperator()); + EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MUST, Object.class, new MustOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.ID, Object.class, new IdOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.IGNORE_ERROR, Object.class, new IgnoreErrorOperator()); EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.THREAD_POOL, Object.class, new ThreadPoolOperator()); @@ -195,6 +198,16 @@ public class LiteFlowChainELBuilder { if (CollUtil.isNotEmpty(errorList)) { throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]")); } + // 对每一个 chain 进行循环引用检测 + try { + objectMapper.writeValueAsString(this.chain); + } catch (Exception e) { + if (e instanceof JsonMappingException) { + throw new CyclicDependencyException(StrUtil.format("There is a circular dependency in the chain[{}], please check carefully.", chain.getChainId(), e)); + } else { + throw new ParseException(e.getMessage()); + } + } } /** diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/AnyOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/AnyOperator.java index 144f3a275..4f326dd68 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/AnyOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/AnyOperator.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.builder.el.operator; import com.yomahub.liteflow.builder.el.operator.base.BaseOperator; import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; +import com.yomahub.liteflow.enums.ParallelStrategyEnum; import com.yomahub.liteflow.flow.element.condition.WhenCondition; /** @@ -19,7 +20,7 @@ public class AnyOperator extends BaseOperator { WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class); Boolean any = OperatorHelper.convert(objects[1], Boolean.class); - whenCondition.setAny(any); + whenCondition.setParallelStrategy(any ? ParallelStrategyEnum.ANY : ParallelStrategyEnum.ALL); return whenCondition; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/ForOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/ForOperator.java index 44bccfa92..e87f9c279 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/ForOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/ForOperator.java @@ -27,7 +27,7 @@ public class ForOperator extends BaseOperator { Node node; if (objects[0] instanceof Node) { node = OperatorHelper.convert(objects[0], Node.class); - if (!ListUtil.toList(NodeTypeEnum.FOR, NodeTypeEnum.FOR_SCRIPT).contains(node.getType())) { + if (!ListUtil.toList(NodeTypeEnum.FOR, NodeTypeEnum.FOR_SCRIPT, NodeTypeEnum.FALLBACK).contains(node.getType())) { throw new QLException("The parameter must be for-node item"); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java index c96379808..696fdc1e6 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/IteratorOperator.java @@ -15,7 +15,7 @@ public class IteratorOperator extends BaseOperator { OperatorHelper.checkObjectSizeEq(objects, 1); Node node = OperatorHelper.convert(objects[0], Node.class); - if (!ListUtil.toList(NodeTypeEnum.ITERATOR).contains(node.getType())) { + if (!ListUtil.toList(NodeTypeEnum.ITERATOR, NodeTypeEnum.FALLBACK).contains(node.getType())) { throw new QLException("The parameter must be iterator-node item"); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MustOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MustOperator.java new file mode 100644 index 000000000..9c819d3be --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/MustOperator.java @@ -0,0 +1,43 @@ +package com.yomahub.liteflow.builder.el.operator; + +import com.yomahub.liteflow.builder.el.operator.base.BaseOperator; +import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; +import com.yomahub.liteflow.enums.ParallelStrategyEnum; +import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.condition.WhenCondition; + +import java.util.HashSet; +import java.util.Set; + +/** + * EL 规则中的 must 的操作符 + * + * @author luo yi + * @since 2.11.0 + */ +public class MustOperator extends BaseOperator { + + @Override + public WhenCondition build(Object[] objects) throws Exception { + OperatorHelper.checkObjectSizeGtTwo(objects); + + WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class); + + // 解析指定完成的任务 ID 集合 + Set specifyIdSet = new HashSet<>(); + + for (int i = 1; i < objects.length; i++) { + Object task = objects[i]; + if (task instanceof String) { + specifyIdSet.add(OperatorHelper.convert(task, String.class)); + } else if (task instanceof Executable) { + specifyIdSet.add(OperatorHelper.convert(task, Executable.class).getId()); + } + } + + whenCondition.setSpecifyIdSet(specifyIdSet); + whenCondition.setParallelStrategy(ParallelStrategyEnum.SPECIFY); + return whenCondition; + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java index 825b6a758..5735dcd87 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/NodeOperator.java @@ -1,58 +1,32 @@ package com.yomahub.liteflow.builder.el.operator; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.ql.util.express.exception.QLException; import com.yomahub.liteflow.builder.el.operator.base.BaseOperator; import com.yomahub.liteflow.builder.el.operator.base.OperatorHelper; import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.flow.element.FallbackNodeProxy; import com.yomahub.liteflow.flow.element.Node; -import com.yomahub.liteflow.property.LiteflowConfig; -import com.yomahub.liteflow.property.LiteflowConfigGetter; /** * EL规则中的node的操作符 * * @author Bryan.Zhang + * @author DaleLee * @since 2.8.3 */ public class NodeOperator extends BaseOperator { - @Override - public Node build(Object[] objects) throws Exception { - OperatorHelper.checkObjectSizeEqOne(objects); + @Override + public Node build(Object[] objects) throws Exception { + OperatorHelper.checkObjectSizeEqOne(objects); + String nodeId = OperatorHelper.convert(objects[0], String.class); - String nodeId = OperatorHelper.convert(objects[0], String.class); - - if (FlowBus.containNode(nodeId)) { - return FlowBus.getNode(nodeId); - } - else { - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - if (StrUtil.isNotBlank(liteflowConfig.getSubstituteCmpClass())) { - Node substituteNode = FlowBus.getNodeMap() - .values() - .stream() - .filter(node -> node.getInstance() - .getClass() - .getName() - .equals(liteflowConfig.getSubstituteCmpClass())) - .findFirst() - .orElse(null); - if (ObjectUtil.isNotNull(substituteNode)) { - return substituteNode; - } - else { - String error = StrUtil.format("This node[{}] cannot be found", nodeId); - throw new QLException(error); - } - } - else { - String error = StrUtil.format("This node[{}] cannot be found, or you can configure an substitute node", - nodeId); - throw new QLException(error); - } - } - } + if (FlowBus.containNode(nodeId)) { + // 找到对应节点 + return FlowBus.getNode(nodeId); + } else { + // 生成代理节点 + return new FallbackNodeProxy(nodeId); + } + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/SwitchOperator.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/SwitchOperator.java index aa1d4c199..041628ee3 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/SwitchOperator.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/SwitchOperator.java @@ -21,7 +21,7 @@ public class SwitchOperator extends BaseOperator { OperatorHelper.checkObjectSizeEqOne(objects); Node switchNode = OperatorHelper.convert(objects[0], Node.class); - if (!ListUtil.toList(NodeTypeEnum.SWITCH, NodeTypeEnum.SWITCH_SCRIPT).contains(switchNode.getType())) { + if (!ListUtil.toList(NodeTypeEnum.SWITCH, NodeTypeEnum.SWITCH_SCRIPT, NodeTypeEnum.FALLBACK).contains(switchNode.getType())) { throw new QLException("The caller must be Switch item"); } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java index 0a181205e..298cdc254 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/builder/el/operator/base/OperatorHelper.java @@ -145,7 +145,8 @@ public class OperatorHelper { if (!(object instanceof Node && ListUtil.toList( NodeTypeEnum.IF, NodeTypeEnum.IF_SCRIPT, NodeTypeEnum.WHILE, NodeTypeEnum.WHILE_SCRIPT, - NodeTypeEnum.BREAK, NodeTypeEnum.BREAK_SCRIPT).contains(((Node) object).getType()) + NodeTypeEnum.BREAK, NodeTypeEnum.BREAK_SCRIPT, NodeTypeEnum.FALLBACK) + .contains(((Node) object).getType()) || object instanceof AndOrCondition || object instanceof NotCondition)) { throw new QLException("The first parameter must be boolean type Node or boolean type condition"); } 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 8f34d1652..e71b0c0ac 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 @@ -30,6 +30,8 @@ public interface ChainConstant { String ANY = "any"; + String MUST = "must"; + String TYPE = "type"; String THEN = "THEN"; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java index 830ad1025..8c1068d1a 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java @@ -441,7 +441,7 @@ public class FlowExecutor { while(cmpStepIterator.hasNext()) { CmpStep cmpStep = cmpStepIterator.next(); if(cmpStep.getInstance().isRollback()) { - Rollbackable rollbackItem = new Node(cmpStep.getInstance()); + Rollbackable rollbackItem = cmpStep.getRefNode(); rollbackItem.rollback(slotIndex); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowInitHook.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowInitHook.java index 4e4ea12c2..ba1628d62 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowInitHook.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowInitHook.java @@ -14,20 +14,20 @@ import java.util.function.BooleanSupplier; */ public class FlowInitHook { - private static final List supplierList = new ArrayList<>(); + private static final List SUPPLIER_LIST = new ArrayList<>(); public static void executeHook() { - if (CollUtil.isNotEmpty(supplierList)) { - supplierList.forEach(BooleanSupplier::getAsBoolean); + if (CollUtil.isNotEmpty(SUPPLIER_LIST)) { + SUPPLIER_LIST.forEach(BooleanSupplier::getAsBoolean); } } public static void addHook(BooleanSupplier hookSupplier) { - supplierList.add(hookSupplier); + SUPPLIER_LIST.add(hookSupplier); } public static void cleanHook() { - supplierList.clear(); + SUPPLIER_LIST.clear(); } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java index 35f19f765..2ecca94ca 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/core/NodeComponent.java @@ -87,10 +87,10 @@ public abstract class NodeComponent { Class clazz = this.getClass(); try { Method method = clazz.getDeclaredMethod("rollback"); - if(ObjectUtil.isNotNull(method)) + if(ObjectUtil.isNotNull(method)){ this.setRollback(true); - } catch (Exception e) { - } + } + } catch (Exception ignored) {} } public void execute() throws Exception { @@ -100,6 +100,7 @@ public abstract class NodeComponent { CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE); cmpStep.setTag(this.getTag()); cmpStep.setInstance(this); + cmpStep.setRefNode(this.getRefNode()); slot.addStep(cmpStep); StopWatch stopWatch = new StopWatch(); @@ -158,6 +159,7 @@ public abstract class NodeComponent { CmpStep cmpStep = new CmpStep(nodeId, name, CmpStepTypeEnum.SINGLE); cmpStep.setTag(this.getTag()); + cmpStep.setInstance(this); slot.addRollbackStep(cmpStep); StopWatch stopWatch = new StopWatch(); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java index 93ee30ea9..18370814e 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/NodeTypeEnum.java @@ -34,6 +34,8 @@ public enum NodeTypeEnum { BREAK("break", "循环跳出", false, NodeBreakComponent.class), ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class), + + FALLBACK("fallback", "降级", false, null), SCRIPT("script", "脚本", true, ScriptCommonComponent.class), diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ParallelStrategyEnum.java b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ParallelStrategyEnum.java new file mode 100644 index 000000000..09c9af437 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/enums/ParallelStrategyEnum.java @@ -0,0 +1,57 @@ +package com.yomahub.liteflow.enums; + +import com.yomahub.liteflow.flow.parallel.strategy.AllOfParallelExecutor; +import com.yomahub.liteflow.flow.parallel.strategy.AnyOfParallelExecutor; +import com.yomahub.liteflow.flow.parallel.strategy.ParallelStrategyExecutor; +import com.yomahub.liteflow.flow.parallel.strategy.SpecifyParallelExecutor; + +/** + * 并行策略枚举类 + * + * @author luo yi + * @since 2.11.0 + */ +public enum ParallelStrategyEnum { + + ANY("anyOf", "完成任一任务", AnyOfParallelExecutor.class), + + ALL("allOf", "完成全部任务", AllOfParallelExecutor.class), + + SPECIFY("must", "完成指定 ID 任务", SpecifyParallelExecutor.class); + + private String strategyType; + + private String description; + + private Class clazz; + + ParallelStrategyEnum(String strategyType, String description, Class clazz) { + this.strategyType = strategyType; + this.description = description; + this.clazz = clazz; + } + + public String getStrategyType() { + return strategyType; + } + + public void setStrategyType(String strategyType) { + this.strategyType = strategyType; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Class getClazz() { + return clazz; + } + + public void setClazz(Class clazz) { + this.clazz = clazz; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/FallbackCmpNotFoundException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/FallbackCmpNotFoundException.java new file mode 100644 index 000000000..5a1eca0b2 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/FallbackCmpNotFoundException.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.exception; + +/** + * 没有找到降级组件异常 + * + * @author DaleLee + * @since 2.11.1 + */ +public class FallbackCmpNotFoundException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * 异常信息 + */ + private String message; + + public FallbackCmpNotFoundException(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ParallelExecutorCreateException.java b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ParallelExecutorCreateException.java new file mode 100644 index 000000000..68ea8a39a --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/exception/ParallelExecutorCreateException.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.exception; + +/** + * 并行策略执行器创建异常 + * + * @author luo yi + * @since 2.11.0 + */ +public class ParallelExecutorCreateException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** 异常信息 */ + private String message; + + public ParallelExecutorCreateException(String message) { + this.message = message; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java index 1f8d1954d..db3c0e654 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/FlowBus.java @@ -11,7 +11,11 @@ package com.yomahub.liteflow.flow; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.StrUtil; -import com.yomahub.liteflow.core.*; +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.util.AnnoUtil; +import com.yomahub.liteflow.core.ComponentInitializer; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.core.ScriptComponent; import com.yomahub.liteflow.enums.FlowParserTypeEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.exception.ComponentCannotRegisterException; @@ -31,6 +35,7 @@ import com.yomahub.liteflow.spi.holder.ContextAwareHolder; import com.yomahub.liteflow.spi.local.LocalContextAware; import com.yomahub.liteflow.util.CopyOnWriteHashMap; import com.yomahub.liteflow.util.LiteFlowProxyUtil; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -41,6 +46,7 @@ import java.util.stream.Collectors; * 流程元数据类 * * @author Bryan.Zhang + * @author DaleLee */ public class FlowBus { @@ -50,6 +56,8 @@ public class FlowBus { private static final Map nodeMap = new CopyOnWriteHashMap<>(); + private static final Map fallbackNodeMap = new CopyOnWriteHashMap<>(); + private FlowBus() { } @@ -92,8 +100,10 @@ public class FlowBus { throw new NullNodeTypeException(StrUtil.format("node type is null for node[{}]", nodeId)); } - nodeMap.put(nodeId, - new Node(ComponentInitializer.loadInstance().initComponent(nodeComponent, type, nodeComponent.getName(), nodeId))); + Node node = new Node(ComponentInitializer.loadInstance() + .initComponent(nodeComponent, type, nodeComponent.getName(), nodeId)); + nodeMap.put(nodeId, node); + addFallbackNode(node); } /** @@ -203,6 +213,7 @@ public class FlowBus { String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId(); nodeMap.put(activeNodeId, node); + addFallbackNode(node); } } @@ -226,9 +237,14 @@ public class FlowBus { return chainMap; } + public static Node getFallBackNode(NodeTypeEnum nodeType) { + return fallbackNodeMap.get(nodeType); + } + public static void cleanCache() { chainMap.clear(); nodeMap.clear(); + fallbackNodeMap.clear(); cleanScriptCache(); } @@ -269,4 +285,16 @@ public class FlowBus { Arrays.stream(chainIds).forEach(FlowBus::removeChain); } + // 判断是否是降级组件,如果是则添加到 fallbackNodeMap + private static void addFallbackNode(Node node) { + NodeComponent nodeComponent = node.getInstance(); + FallbackCmp fallbackCmp = AnnoUtil.getAnnotation(nodeComponent.getClass(), FallbackCmp.class); + if (fallbackCmp == null) { + return; + } + + NodeTypeEnum nodeType = node.getType(); + fallbackNodeMap.put(nodeType, node); + } + } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java index 60ccafe18..3bfbe3203 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Condition.java @@ -10,10 +10,9 @@ package com.yomahub.liteflow.flow.element; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.enums.ExecuteTypeEnum; import com.yomahub.liteflow.exception.ChainEndException; -import com.yomahub.liteflow.flow.element.Executable; -import com.yomahub.liteflow.enums.ConditionTypeEnum; import com.yomahub.liteflow.flow.element.condition.ConditionKey; import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.Slot; @@ -27,6 +26,7 @@ import java.util.Map; * Condition的抽象类 * * @author Bryan.Zhang + * @author DaleLee */ public abstract class Condition implements Executable{ @@ -46,7 +46,10 @@ public abstract class Condition implements Executable{ @Override public void execute(Integer slotIndex) throws Exception { + Slot slot = DataBus.getSlot(slotIndex); try { + // 当前 Condition 入栈 + slot.pushCondition(this); executeCondition(slotIndex); } catch (ChainEndException e) { @@ -55,7 +58,6 @@ public abstract class Condition implements Executable{ throw e; } catch (Exception e) { - Slot slot = DataBus.getSlot(slotIndex); String chainId = this.getCurrChainId(); // 这里事先取到exception set到slot里,为了方便finally取到exception if (slot.isSubChain(chainId)) { @@ -65,6 +67,9 @@ public abstract class Condition implements Executable{ slot.setException(e); } throw e; + } finally { + // 当前 Condition 出栈 + slot.popCondition(); } } @@ -120,10 +125,12 @@ public abstract class Condition implements Executable{ public abstract ConditionTypeEnum getConditionType(); + @Override public String getId() { return id; } + @Override public void setId(String id) { this.id = id; } @@ -133,6 +140,7 @@ public abstract class Condition implements Executable{ return tag; } + @Override public void setTag(String tag) { this.tag = tag; } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/FallbackNodeProxy.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/FallbackNodeProxy.java new file mode 100644 index 000000000..e19119c93 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/FallbackNodeProxy.java @@ -0,0 +1,188 @@ +package com.yomahub.liteflow.flow.element; + +import cn.hutool.core.text.StrFormatter; +import cn.hutool.core.util.ObjectUtil; +import com.yomahub.liteflow.enums.ConditionTypeEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.exception.FallbackCmpNotFoundException; +import com.yomahub.liteflow.exception.FlowSystemException; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.flow.element.condition.ConditionKey; +import com.yomahub.liteflow.flow.element.condition.ForCondition; +import com.yomahub.liteflow.flow.element.condition.IfCondition; +import com.yomahub.liteflow.flow.element.condition.IteratorCondition; +import com.yomahub.liteflow.flow.element.condition.LoopCondition; +import com.yomahub.liteflow.flow.element.condition.SwitchCondition; +import com.yomahub.liteflow.flow.element.condition.WhileCondition; +import com.yomahub.liteflow.slot.DataBus; +import com.yomahub.liteflow.slot.Slot; + +/** + * 降级组件代理 + * + * @author DaleLee + * @since 2.11.1 + */ +public class FallbackNodeProxy extends Node { + + // 原节点 id + private String expectedNodeId; + + // 降级节点 + private Node fallbackNode; + + public FallbackNodeProxy() { + this.setType(NodeTypeEnum.FALLBACK); + } + + public FallbackNodeProxy(String expectedNodeId) { + this(); + this.expectedNodeId = expectedNodeId; + } + + @Override + public void execute(Integer slotIndex) throws Exception { + loadFallBackNode(slotIndex); + this.fallbackNode.setCurrChainId(this.getCurrChainId()); + this.fallbackNode.execute(slotIndex); + } + + private void loadFallBackNode(Integer slotIndex) throws Exception { + if (ObjectUtil.isNotNull(this.fallbackNode)) { + // 已经加载过了 + return; + } + Slot slot = DataBus.getSlot(slotIndex); + Condition curCondition = slot.getCurrentCondition(); + if (ObjectUtil.isNull(curCondition)) { + throw new FlowSystemException("The current executing condition could not be found."); + } + Node node = findFallbackNode(curCondition); + if (ObjectUtil.isNull(node)) { + throw new FallbackCmpNotFoundException( + StrFormatter.format("No fallback component found for [{}] in chain[{}].", this.expectedNodeId, + this.getCurrChainId())); + } + // 使用 node 的副本 + this.fallbackNode = node.copy(); + } + + private Node findFallbackNode(Condition condition) { + ConditionTypeEnum conditionType = condition.getConditionType(); + switch (conditionType) { + case TYPE_THEN: + case TYPE_WHEN: + case TYPE_PRE: + case TYPE_FINALLY: + case TYPE_CATCH: + return FlowBus.getFallBackNode(NodeTypeEnum.COMMON); + case TYPE_IF: + return findNodeInIf((IfCondition) condition); + case TYPE_SWITCH: + return findNodeInSwitch((SwitchCondition) condition); + case TYPE_FOR: + return findNodeInFor((ForCondition) condition); + case TYPE_WHILE: + return findNodeInWhile((WhileCondition) condition); + case TYPE_ITERATOR: + return findNodeInIterator((IteratorCondition) condition); + case TYPE_NOT_OPT: + case TYPE_AND_OR_OPT: + return FlowBus.getFallBackNode(NodeTypeEnum.IF); + default: + return null; + } + } + + private Node findNodeInIf(IfCondition ifCondition) { + Executable ifItem = ifCondition.getIfItem(); + if (ifItem == this) { + // 需要条件组件 + return FlowBus.getFallBackNode(NodeTypeEnum.IF); + } + + // 需要普通组件 + return FlowBus.getFallBackNode(NodeTypeEnum.COMMON); + } + + private Node findNodeInSwitch(SwitchCondition switchCondition) { + Node switchNode = switchCondition.getSwitchNode(); + if (switchNode == this) { + return FlowBus.getFallBackNode(NodeTypeEnum.SWITCH); + } + + return FlowBus.getFallBackNode(NodeTypeEnum.COMMON); + } + + private Node findNodeInFor(ForCondition forCondition) { + Node forNode = forCondition.getForNode(); + if (forNode == this) { + return FlowBus.getFallBackNode(NodeTypeEnum.FOR); + } + + return findNodeInLoop(forCondition); + } + + private Node findNodeInWhile(WhileCondition whileCondition) { + Executable whileItem = whileCondition.getWhileItem(); + if (whileItem == this) { + return FlowBus.getFallBackNode(NodeTypeEnum.WHILE); + } + + return findNodeInLoop(whileCondition); + } + + private Node findNodeInIterator(IteratorCondition iteratorCondition) { + Node iteratorNode = iteratorCondition.getIteratorNode(); + if (iteratorNode == this) { + return FlowBus.getFallBackNode(NodeTypeEnum.ITERATOR); + } + + return findNodeInLoop(iteratorCondition); + } + + private Node findNodeInLoop(LoopCondition loopCondition) { + Executable breakItem = loopCondition.getExecutableOne(ConditionKey.BREAK_KEY); + if (breakItem == this) { + return FlowBus.getFallBackNode(NodeTypeEnum.BREAK); + } + + return FlowBus.getFallBackNode(NodeTypeEnum.COMMON); + } + + @Override + public T getItemResultMetaValue(Integer slotIndex) { + return this.fallbackNode.getItemResultMetaValue(slotIndex); + } + + @Override + public boolean isAccess(Integer slotIndex) throws Exception { + // 可能会先访问这个方法,所以在这里就要加载降级节点 + loadFallBackNode(slotIndex); + return this.fallbackNode.isAccess(slotIndex); + } + + @Override + public String getId() { + return this.fallbackNode == null ? null : this.fallbackNode.getId(); + } + + @Override + public Node copy() { + // 代理节点不复制 + return this; + } + + @Override + public NodeTypeEnum getType() { + return NodeTypeEnum.FALLBACK; + } + + public String getExpectedNodeId() { + return expectedNodeId; + } + + public void setExpectedNodeId(String expectedNodeId) { + this.expectedNodeId = expectedNodeId; + } +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java index 5d90e3c16..269a4dd62 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/Node.java @@ -8,23 +8,23 @@ package com.yomahub.liteflow.flow.element; -import cn.hutool.core.util.BooleanUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.ttl.TransmittableThreadLocal; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.ExecuteTypeEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.exception.ChainEndException; +import com.yomahub.liteflow.exception.FlowSystemException; +import com.yomahub.liteflow.flow.executor.NodeExecutor; +import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.property.LiteflowConfig; import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.slot.DataBus; import com.yomahub.liteflow.slot.Slot; -import com.yomahub.liteflow.flow.executor.NodeExecutor; -import com.yomahub.liteflow.flow.executor.NodeExecutorHelper; -import com.yomahub.liteflow.enums.ExecuteTypeEnum; -import com.yomahub.liteflow.enums.NodeTypeEnum; -import com.yomahub.liteflow.exception.ChainEndException; -import com.yomahub.liteflow.exception.FlowSystemException; /** * Node节点,实现可执行器 Node节点并不是单例的,每构建一次都会copy出一个新的实例 @@ -47,6 +47,8 @@ public class Node implements Executable, Cloneable, Rollbackable{ private String language; + // 增加该注解,避免在使用 Jackson 序列化检测循环引用时出现不必要异常 + @JsonIgnore private NodeComponent instance; private String tag; diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java index 3555d37af..15eb31cbd 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/element/condition/WhenCondition.java @@ -7,28 +7,18 @@ */ package com.yomahub.liteflow.flow.element.condition; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.common.LocalDefaultFlowConstant; import com.yomahub.liteflow.enums.ConditionTypeEnum; -import com.yomahub.liteflow.exception.WhenExecuteException; +import com.yomahub.liteflow.enums.ParallelStrategyEnum; import com.yomahub.liteflow.flow.element.Condition; -import com.yomahub.liteflow.flow.parallel.CompletableFutureTimeout; -import com.yomahub.liteflow.flow.parallel.ParallelSupplier; -import com.yomahub.liteflow.flow.parallel.WhenFutureObj; +import com.yomahub.liteflow.flow.parallel.strategy.ParallelStrategyExecutor; +import com.yomahub.liteflow.flow.parallel.strategy.ParallelStrategyHelper; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; -import com.yomahub.liteflow.property.LiteflowConfig; -import com.yomahub.liteflow.property.LiteflowConfigGetter; -import com.yomahub.liteflow.slot.DataBus; -import com.yomahub.liteflow.slot.Slot; import com.yomahub.liteflow.thread.ExecutorHelper; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; + +import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; /** * 并行器 @@ -46,8 +36,11 @@ public class WhenCondition extends Condition { // 此属性已弃用 private String group = LocalDefaultFlowConstant.DEFAULT; - // 只在when类型下有效,为true的话说明在多个并行节点下,任意一个成功,整个when就成功 - private boolean any = false; + // 当前 When 对应并行策略,默认为 ALL + private ParallelStrategyEnum parallelStrategy; + + // 只有 must 条件下,才会赋值 specifyIdSet + private Set specifyIdSet; // when单独的线程池名称 private String threadExecutorClass; @@ -68,136 +61,16 @@ public class WhenCondition extends Condition { return ConditionTypeEnum.TYPE_WHEN; } - // 使用线程池执行when并发流程 + // 使用线程池执行 when 并发流程 // 这块涉及到挺多的多线程逻辑,所以注释比较详细,看到这里的童鞋可以仔细阅读 private void executeAsyncCondition(Integer slotIndex) throws Exception { - Slot slot = DataBus.getSlot(slotIndex); - String currChainName = this.getCurrChainId(); + // 获取并发执行策略 + ParallelStrategyExecutor parallelStrategyExecutor = ParallelStrategyHelper.loadInstance().buildParallelExecutor(this.getParallelStrategy()); - // 此方法其实只会初始化一次Executor,不会每次都会初始化。Executor是唯一的 - ExecutorService parallelExecutor = ExecutorHelper.loadInstance() - .buildWhenExecutor(this.getThreadExecutorClass()); + // 执行并发逻辑 + parallelStrategyExecutor.execute(this, slotIndex); - // 获得liteflow的参数 - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - - // 定义是否中断参数 - // 这里为什么要定义成数组呢,因为后面lambda要用到,根据final不能修改引用的原则,这里用了数组对象 - final boolean[] interrupted = { false }; - - // 这里主要是做了封装CompletableFuture对象,用lumbda表达式做了很多事情,这句代码要仔细理清 - // 1.先进行过滤,前置和后置组件过滤掉,因为在EL Chain处理的时候已经提出来了 - // 2.过滤isAccess为false的情况,因为不过滤这个的话,如果加上了any,那么isAccess为false那就是最快的了 - // 3.根据condition.getNodeList()的集合进行流处理,用map进行把executable对象转换成List> - // 4.在转的过程中,套入CompletableFutureTimeout方法进行超时判断,如果超时则用WhenFutureObj.timeOut返回超时的对象 - // 5.第2个参数是主要的本体CompletableFuture,传入了ParallelSupplier和线程池对象 - if (ObjectUtil.isNull(this.getMaxWaitTime())) { - if (ObjectUtil.isNotNull(liteflowConfig.getWhenMaxWaitSeconds())) { - // 获取全局异步线程最长等待秒数 - this.setMaxWaitTime(liteflowConfig.getWhenMaxWaitSeconds()); - this.setMaxWaitTimeUnit(TimeUnit.SECONDS); - } else { - // 获取全局异步线程最⻓的等待时间 - this.setMaxWaitTime(liteflowConfig.getWhenMaxWaitTime()); - } - } - - if (ObjectUtil.isNull(this.getMaxWaitTimeUnit())) { - // 获取全局异步线程最⻓的等待时间单位 - this.setMaxWaitTimeUnit(liteflowConfig.getWhenMaxWaitTimeUnit()); - } - - List> completableFutureList = this.getExecutableList() - .stream() - .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)) - .filter(executable -> { - try { - return executable.isAccess(slotIndex); - } catch (Exception e) { - LOG.error("there was an error when executing the when component isAccess", e); - return false; - } - }) - .map(executable -> CompletableFutureTimeout.completeOnTimeout( - WhenFutureObj.timeOut(executable.getId()), - CompletableFuture.supplyAsync(new ParallelSupplier(executable, currChainName, slotIndex), - parallelExecutor), - this.getMaxWaitTime(), this.getMaxWaitTimeUnit())) - .collect(Collectors.toList()); - - CompletableFuture resultCompletableFuture; - - // 这里判断执行方式 - // 如果any为false,说明这些异步任务全部执行好或者超时,才返回 - // 如果any为true,说明这些异步任务只要任意一个执行完成,就返回 - if (this.isAny()) { - // 把这些CompletableFuture通过anyOf合成一个CompletableFuture - resultCompletableFuture = CompletableFuture - .anyOf(completableFutureList.toArray(new CompletableFuture[] {})); - } else { - // 把这些CompletableFuture通过allOf合成一个CompletableFuture - resultCompletableFuture = CompletableFuture - .allOf(completableFutureList.toArray(new CompletableFuture[] {})); - } - - try { - // 进行执行,这句执行完后,就意味着所有的任务要么执行完毕,要么超时返回 - resultCompletableFuture.get(); - } catch (InterruptedException | ExecutionException e) { - LOG.error("there was an error when executing the CompletableFuture", e); - interrupted[0] = true; - } - - // 拿到已经完成的CompletableFuture - // 如果any为false,那么所有任务都已经完成 - // 如果any为true,那么这里拿到的是第一个完成的任务 - // 这里过滤和转换一起用lumbda做了 - List allCompletableWhenFutureObjList = completableFutureList.stream().filter(f -> { - // 过滤出已经完成的,没完成的就直接终止 - if (f.isDone()) { - return true; - } else { - f.cancel(true); - return false; - } - }).map(f -> { - try { - return f.get(); - } catch (InterruptedException | ExecutionException e) { - interrupted[0] = true; - return null; - } - }).collect(Collectors.toList()); - - // 判断超时,上面已经拿到了所有已经完成的CompletableFuture - // 那我们只要过滤出超时的CompletableFuture - List timeOutWhenFutureObjList = allCompletableWhenFutureObjList.stream() - .filter(WhenFutureObj::isTimeout) - .collect(Collectors.toList()); - - // 输出超时信息 - timeOutWhenFutureObjList.forEach(whenFutureObj -> LOG.warn( - "executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", whenFutureObj.getExecutorName())); - - // 当配置了ignoreError = false,出现interrupted或者!f.get()的情况,将抛出WhenExecuteException - if (!this.isIgnoreError()) { - if (interrupted[0]) { - throw new WhenExecuteException(StrUtil - .format("requestId [{}] when execute interrupted. errorResume [false].", slot.getRequestId())); - } - - // 循环判断CompletableFuture的返回值,如果异步执行失败,则抛出相应的业务异常 - for (WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList) { - if (!whenFutureObj.isSuccess()) { - LOG.info(StrUtil.format("when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName())); - throw whenFutureObj.getEx(); - } - } - } else if (interrupted[0]) { - // 这里由于配置了ignoreError,所以只打印warn日志 - LOG.warn("executing when condition timeout , but ignore with errorResume."); - } } public boolean isIgnoreError() { @@ -216,12 +89,20 @@ public class WhenCondition extends Condition { this.group = group; } - public boolean isAny() { - return any; + public ParallelStrategyEnum getParallelStrategy() { + return parallelStrategy; } - public void setAny(boolean any) { - this.any = any; + public void setParallelStrategy(ParallelStrategyEnum parallelStrategy) { + this.parallelStrategy = parallelStrategy; + } + + public Set getSpecifyIdSet() { + return specifyIdSet; + } + + public void setSpecifyIdSet(Set specifyIdSet) { + this.specifyIdSet = specifyIdSet; } public String getThreadExecutorClass() { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java index eacc479e9..f5eff5165 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/entity/CmpStep.java @@ -12,6 +12,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.enums.CmpStepTypeEnum; +import com.yomahub.liteflow.flow.element.Node; /** * 组件步骤对象 @@ -43,6 +44,10 @@ public class CmpStep { // 回滚消耗的时间 private Long rollbackTimeSpent; + // 当前执行的node + private Node refNode; + + public CmpStep(String nodeId, String nodeName, CmpStepTypeEnum stepType) { this.nodeId = nodeId; this.nodeName = nodeName; @@ -113,6 +118,14 @@ public class CmpStep { this.rollbackTimeSpent = rollbackTimeSpent; } + public Node getRefNode() { + return refNode; + } + + public void setRefNode(Node refNode) { + this.refNode = refNode; + } + public String buildString() { if (stepType.equals(CmpStepTypeEnum.SINGLE)) { if (StrUtil.isBlank(nodeName)) { diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java new file mode 100644 index 000000000..313e02d4d --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AllOfParallelExecutor.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.flow.parallel.strategy; + +import com.yomahub.liteflow.flow.element.condition.WhenCondition; +import com.yomahub.liteflow.flow.parallel.WhenFutureObj; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * 完成全部任务 + * + * @author luo yi + * @since 2.11.0 + */ +public class AllOfParallelExecutor extends ParallelStrategyExecutor { + + @Override + public void execute(WhenCondition whenCondition, Integer slotIndex) throws Exception { + + // 获取所有 CompletableFuture 任务 + List> whenAllTaskList = this.getWhenAllTaskList(whenCondition, slotIndex); + + // 把这些 CompletableFuture 通过 allOf 合成一个 CompletableFuture,表明完成所有任务 + CompletableFuture specifyTask = CompletableFuture.allOf(whenAllTaskList.toArray(new CompletableFuture[] {})); + + // 结果处理 + this.handleTaskResult(whenCondition, slotIndex, whenAllTaskList, specifyTask); + + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java new file mode 100644 index 000000000..02fcc4646 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/AnyOfParallelExecutor.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.flow.parallel.strategy; + +import com.yomahub.liteflow.flow.element.condition.WhenCondition; +import com.yomahub.liteflow.flow.parallel.WhenFutureObj; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * 完成任一任务 + * + * @author luo yi + * @since 2.11.0 + */ +public class AnyOfParallelExecutor extends ParallelStrategyExecutor { + + @Override + public void execute(WhenCondition whenCondition, Integer slotIndex) throws Exception { + + // 获取所有 CompletableFuture 任务 + List> whenAllTaskList = this.getWhenAllTaskList(whenCondition, slotIndex); + + // 把这些 CompletableFuture 通过 anyOf 合成一个 CompletableFuture,表明完成任一任务 + CompletableFuture specifyTask = CompletableFuture.anyOf(whenAllTaskList.toArray(new CompletableFuture[] {})); + + // 结果处理 + this.handleTaskResult(whenCondition, slotIndex, whenAllTaskList, specifyTask); + + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java new file mode 100644 index 000000000..95adb04ee --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyExecutor.java @@ -0,0 +1,199 @@ +package com.yomahub.liteflow.flow.parallel.strategy; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.enums.ParallelStrategyEnum; +import com.yomahub.liteflow.exception.WhenExecuteException; +import com.yomahub.liteflow.flow.element.Executable; +import com.yomahub.liteflow.flow.element.condition.FinallyCondition; +import com.yomahub.liteflow.flow.element.condition.PreCondition; +import com.yomahub.liteflow.flow.element.condition.WhenCondition; +import com.yomahub.liteflow.flow.parallel.CompletableFutureTimeout; +import com.yomahub.liteflow.flow.parallel.ParallelSupplier; +import com.yomahub.liteflow.flow.parallel.WhenFutureObj; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.slot.DataBus; +import com.yomahub.liteflow.slot.Slot; +import com.yomahub.liteflow.thread.ExecutorHelper; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 并发策略执行器抽象类 + * + * @author luo yi + * @since 2.11.0 + */ +public abstract class ParallelStrategyExecutor { + + protected final LFLog LOG = LFLoggerManager.getLogger(this.getClass()); + + /** + * 封装 CompletableFuture 对象 + * @param executable + * @param parallelExecutor + * @param whenCondition + * @param currChainName + * @param slotIndex + * @return + */ + protected CompletableFuture wrappedFutureObj(Executable executable, ExecutorService parallelExecutor, + WhenCondition whenCondition, String currChainName, Integer slotIndex) { + // 套入 CompletableFutureTimeout 方法进行超时判断,如果超时则用 WhenFutureObj.timeOut 返回超时的对象 + // 第 2 个参数是主要的本体 CompletableFuture,传入了 ParallelSupplier 和线程池对象 + return CompletableFutureTimeout.completeOnTimeout( + WhenFutureObj.timeOut(executable.getId()), + CompletableFuture.supplyAsync(new ParallelSupplier(executable, currChainName, slotIndex), parallelExecutor), + whenCondition.getMaxWaitTime(), + whenCondition.getMaxWaitTimeUnit()); + } + + /** + * 设置 WhenCondition 参数 + * @param whenCondition + */ + protected void setWhenConditionParams(WhenCondition whenCondition) { + // 获得 liteflow 的参数 + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + if (ObjectUtil.isNull(whenCondition.getMaxWaitTime())) { + if (ObjectUtil.isNotNull(liteflowConfig.getWhenMaxWaitSeconds())) { + // 获取全局异步线程最长等待秒数 + whenCondition.setMaxWaitTime(liteflowConfig.getWhenMaxWaitSeconds()); + whenCondition.setMaxWaitTimeUnit(TimeUnit.SECONDS); + } else { + // 获取全局异步线程最⻓的等待时间 + whenCondition.setMaxWaitTime(liteflowConfig.getWhenMaxWaitTime()); + } + } + + if (ObjectUtil.isNull(whenCondition.getMaxWaitTimeUnit())) { + // 获取全局异步线程最⻓的等待时间单位 + whenCondition.setMaxWaitTimeUnit(liteflowConfig.getWhenMaxWaitTimeUnit()); + } + } + + /** + * 获取所有任务 CompletableFuture 集合 + * @param whenCondition + * @param slotIndex + * @return + */ + protected List> getWhenAllTaskList(WhenCondition whenCondition, Integer slotIndex) { + + String currChainName = whenCondition.getCurrChainId(); + + // 此方法其实只会初始化一次 Executor,不会每次都会初始化。Executor是唯一的 + ExecutorService parallelExecutor = ExecutorHelper.loadInstance().buildWhenExecutor(whenCondition.getThreadExecutorClass()); + + // 设置 whenCondition 参数 + setWhenConditionParams(whenCondition); + + // 这里主要是做了封装 CompletableFuture 对象,用 lumbda 表达式做了很多事情,这句代码要仔细理清 + // 1.先进行过滤,前置和后置组件过滤掉,因为在 EL Chain 处理的时候已经提出来了 + // 2.过滤 isAccess 为 false 的情况,因为不过滤这个的话,如果加上了 any,那么 isAccess 为 false 那就是最快的了 + // 3.根据 condition.getNodeList() 的集合进行流处理,用 map 进行把 executable 对象转换成 List> + List> completableFutureList = whenCondition.getExecutableList() + .stream() + .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)) + .filter(executable -> { + try { + return executable.isAccess(slotIndex); + } catch (Exception e) { + LOG.error("there was an error when executing the when component isAccess", e); + return false; + } + }) + .map(executable -> wrappedFutureObj(executable, parallelExecutor, whenCondition, currChainName, slotIndex)) + .collect(Collectors.toList()); + + return completableFutureList; + } + + /** + * 任务结果处理 + * @param whenCondition 并行组件对象 + * @param slotIndex 当前 slot 的 index + * @param whenAllTaskList 并行组件中所有任务列表 + * @param specifyTask 指定预先完成的任务,详见 {@link ParallelStrategyEnum} + * @throws Exception + */ + protected void handleTaskResult(WhenCondition whenCondition, Integer slotIndex, List> whenAllTaskList, + CompletableFuture specifyTask) throws Exception { + + Slot slot = DataBus.getSlot(slotIndex); + + // 定义是否中断参数 + // 这里为什么要定义成数组呢,因为后面 lambda 要用到,根据 final 不能修改引用的原则,这里用了数组对象 + final boolean[] interrupted = { false }; + + try { + // 进行执行,这句执行完后有三种可能,所有任务执行完成、任一任务执行完成、指定的任务执行完成 + specifyTask.get(); + } catch (InterruptedException | ExecutionException e) { + LOG.error("there was an error when executing the CompletableFuture", e); + interrupted[0] = true; + } + + // 拿到已经完成的 CompletableFuture 对象 + // 如果 any 为 false,那么所有任务都已经完成 + // 如果 any 为 true,那么这里拿到的是第一个完成的任务 + // 如果为 must,那么这里获取到的就是指定的任务 + // 这里过滤和转换一起用 lambda 做了 + List allCompletableWhenFutureObjList = whenAllTaskList.stream().filter(f -> { + // 过滤出已经完成的,没完成的就直接终止 + if (f.isDone()) { + return true; + } else { + f.cancel(true); + return false; + } + }).map(f -> { + try { + return f.get(); + } catch (InterruptedException | ExecutionException e) { + interrupted[0] = true; + return null; + } + }).collect(Collectors.toList()); + + // 判断超时,上面已经拿到了所有已经完成的 CompletableFuture + // 那我们只要过滤出超时的 CompletableFuture + List timeOutWhenFutureObjList = allCompletableWhenFutureObjList.stream() + .filter(WhenFutureObj::isTimeout) + .collect(Collectors.toList()); + + // 输出超时信息 + timeOutWhenFutureObjList.forEach(whenFutureObj -> LOG.warn( + "executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", whenFutureObj.getExecutorName())); + + // 当配置了 ignoreError = false,出现 interrupted 或者 !f.get() 的情况,将抛出 WhenExecuteException + if (!whenCondition.isIgnoreError()) { + if (interrupted[0]) { + throw new WhenExecuteException(StrUtil + .format("requestId [{}] when execute interrupted. errorResume [false].", slot.getRequestId())); + } + + // 循环判断CompletableFuture的返回值,如果异步执行失败,则抛出相应的业务异常 + for (WhenFutureObj whenFutureObj : allCompletableWhenFutureObjList) { + if (!whenFutureObj.isSuccess()) { + LOG.info(StrUtil.format("when-executor[{}] execute failed. errorResume [false].", whenFutureObj.getExecutorName())); + throw whenFutureObj.getEx(); + } + } + } else if (interrupted[0]) { + // 这里由于配置了 ignoreError,所以只打印 warn 日志 + LOG.warn("executing when condition timeout , but ignore with errorResume."); + } + } + + public abstract void execute(WhenCondition whenCondition, Integer slotIndex) throws Exception; + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyHelper.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyHelper.java new file mode 100644 index 000000000..250fccfb5 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/ParallelStrategyHelper.java @@ -0,0 +1,79 @@ +package com.yomahub.liteflow.flow.parallel.strategy; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjUtil; +import com.yomahub.liteflow.enums.ParallelStrategyEnum; +import com.yomahub.liteflow.exception.ParallelExecutorCreateException; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.spi.holder.ContextAwareHolder; + +import java.util.Map; + +/** + * WHEN 并发策略辅助 + * + * @author luo yi + * @since 2.11.0 + */ +public class ParallelStrategyHelper { + + private final LFLog LOG = LFLoggerManager.getLogger(ParallelStrategyHelper.class); + + /** + * 此处使用Map缓存线程池信息 key - 线程池构建者的Class全类名 value - 线程池对象 + */ + private final Map strategyExecutorMap; + + private ParallelStrategyHelper() { + strategyExecutorMap = MapUtil.newConcurrentHashMap(); + } + + /** + * 使用静态内部类实现单例模式 + */ + private static class Holder { + + static final ParallelStrategyHelper INSTANCE = new ParallelStrategyHelper(); + + } + + public static ParallelStrategyHelper loadInstance() { + return ParallelStrategyHelper.Holder.INSTANCE; + } + + private ParallelStrategyExecutor getParallelStrategyExecutor(ParallelStrategyEnum parallelStrategyEnum) { + try { + ParallelStrategyExecutor strategyExecutor = strategyExecutorMap.get(parallelStrategyEnum); + if (ObjUtil.isNotNull(strategyExecutor)) return strategyExecutor; + + Class executorClass = (Class) Class.forName(parallelStrategyEnum.getClazz().getName()); + strategyExecutor = ContextAwareHolder.loadContextAware().registerBean(executorClass); + strategyExecutorMap.put(parallelStrategyEnum, strategyExecutor); + return strategyExecutor; + } catch (Exception e) { + LOG.error(e.getMessage()); + throw new ParallelExecutorCreateException(e.getMessage()); + } + } + + public ParallelStrategyExecutor buildParallelExecutor(ParallelStrategyEnum parallelStrategyEnum) { + if (ObjUtil.isNull(parallelStrategyEnum)) return buildParallelExecutor(); + return getParallelStrategyExecutor(parallelStrategyEnum); + } + + /** + * 默认需完成所有任务 + * @return + */ + public ParallelStrategyExecutor buildParallelExecutor() { + return buildParallelExecutor(ParallelStrategyEnum.ALL); + } + + public void clearStrategyExecutorMap() { + if (MapUtil.isNotEmpty(strategyExecutorMap)) { + strategyExecutorMap.clear(); + } + } + +} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java new file mode 100644 index 000000000..e397608ec --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/flow/parallel/strategy/SpecifyParallelExecutor.java @@ -0,0 +1,90 @@ +package com.yomahub.liteflow.flow.parallel.strategy; + +import cn.hutool.core.collection.CollUtil; +import com.yomahub.liteflow.flow.element.condition.FinallyCondition; +import com.yomahub.liteflow.flow.element.condition.PreCondition; +import com.yomahub.liteflow.flow.element.condition.WhenCondition; +import com.yomahub.liteflow.flow.parallel.WhenFutureObj; +import com.yomahub.liteflow.thread.ExecutorHelper; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; + +/** + * 完成指定任务执行器,使用 ID 进行比较 + * + * @author luo yi + * @since 2.11.0 + */ +public class SpecifyParallelExecutor extends ParallelStrategyExecutor { + + @Override + public void execute(WhenCondition whenCondition, Integer slotIndex) throws Exception { + + String currChainName = whenCondition.getCurrChainId(); + + // 设置 whenCondition 参数 + this.setWhenConditionParams(whenCondition); + + // 此方法其实只会初始化一次Executor,不会每次都会初始化。Executor 是唯一的 + ExecutorService parallelExecutor = ExecutorHelper.loadInstance().buildWhenExecutor(whenCondition.getThreadExecutorClass()); + + // 指定完成的任务 + CompletableFuture specifyTask; + + // 已存在的任务 ID 集合 + Set exitingTaskIdSet = new HashSet<>(); + + // 指定任务列表,可以为 0 或者多个 + List> specifyTaskList = new ArrayList<>(); + + // 所有任务集合 + List> allTaskList = new ArrayList<>(); + + // 遍历 when 所有 node,进行筛选及处理 + whenCondition.getExecutableList() + .stream() + .filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition)) + .filter(executable -> { + try { + return executable.isAccess(slotIndex); + } catch (Exception e) { + LOG.error("there was an error when executing the when component isAccess", e); + return false; + } + }) + .forEach(executable -> { + // 处理 task,封装成 CompletableFuture 对象 + CompletableFuture completableFutureTask = wrappedFutureObj(executable, parallelExecutor, whenCondition, currChainName, slotIndex); + // 存在 must 指定 ID 的 task,且该任务只会有一个或者没有 + if (whenCondition.getSpecifyIdSet().contains(executable.getId())) { + // 设置指定任务 future 对象 + specifyTaskList.add(completableFutureTask); + // 记录已存在的任务 ID + exitingTaskIdSet.add(executable.getId()); + } + // 组合所有任务 + allTaskList.add(completableFutureTask); + }); + + if (CollUtil.isEmpty(specifyTaskList)) { + LOG.warn("The specified task{} was not found, waiting for all tasks to complete by default.", whenCondition.getSpecifyIdSet()); + // 不存在指定任务,则需要等待所有任务都执行完成 + specifyTask = CompletableFuture.allOf(allTaskList.toArray(new CompletableFuture[] {})); + } else { + // 判断 specifyIdSet 中有哪些任务是不存在的,给出提示 + Collection absentTaskIdSet = CollUtil.subtract(whenCondition.getSpecifyIdSet(), exitingTaskIdSet); + if (CollUtil.isNotEmpty(absentTaskIdSet)) { + LOG.warn("The specified task{} was not found, you need to define and register it.", absentTaskIdSet); + } + // 将指定要完成的任务通过 allOf 合成一个 CompletableFuture,表示需要等待 must 方法里面所有任务完成 + specifyTask = CompletableFuture.allOf(specifyTaskList.toArray(new CompletableFuture[]{})); + } + + // 结果处理 + this.handleTaskResult(whenCondition, slotIndex, allTaskList, specifyTask); + + } + +} 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 new file mode 100644 index 000000000..b4309eaa9 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/parser/helper/NodeConvertHelper.java @@ -0,0 +1,108 @@ +package com.yomahub.liteflow.parser.helper; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +import java.util.List; + +/** + * 冒号形式节点NodeSimpleVO转换 通用 Helper + * + * @author hxinyu + */ +public class NodeConvertHelper { + + /*script节点的修改/添加*/ + public static void changeScriptNode(NodeSimpleVO nodeSimpleVO, String newValue) { + // 有语言类型 + if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(newValue) + .setLanguage(nodeSimpleVO.getLanguage()) + .build(); + } + // 没有语言类型 + else { + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(newValue) + .build(); + } + } + + public static NodeSimpleVO convert(String scriptKey){ + // 不需要去理解这串正则,就是一个匹配冒号的 + // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 + List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", scriptKey); + 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)); + } + + if (matchItemList.size() > 3) { + nodeSimpleVO.setLanguage(matchItemList.get(3)); + } + + return nodeSimpleVO; + } + + + public static class NodeSimpleVO { + + private String nodeId; + + private String type; + + private String name = StrUtil.EMPTY; + + private String language; + + 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 getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + } +} \ No newline at end of file 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 bd4adcc40..7d41be117 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 @@ -15,7 +15,10 @@ import com.yomahub.liteflow.flow.FlowBus; import org.dom4j.Document; import org.dom4j.Element; -import java.util.*; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -269,8 +272,10 @@ public class ParserHelper { // 构建chainBuilder String chainId = Optional.ofNullable(chainNode.get(ID)).orElse(chainNode.get(NAME)).textValue(); String el = chainNode.get(VALUE).textValue(); - LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId); - chainELBuilder.setEL(el).build(); + LiteFlowChainELBuilder.createChain() + .setChainId(chainId) + .setEL(el) + .build(); } /** @@ -282,8 +287,10 @@ public class ParserHelper { String chainId = Optional.ofNullable(e.attributeValue(ID)).orElse(e.attributeValue(NAME)); String text = e.getText(); String el = RegexUtil.removeComments(text); - LiteFlowChainELBuilder chainELBuilder = LiteFlowChainELBuilder.createChain().setChainId(chainId); - chainELBuilder.setEL(el).build(); + LiteFlowChainELBuilder.createChain() + .setChainId(chainId) + .setEL(el) + .build(); } /** diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java b/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java index 814ccf49a..c0cbf0391 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java @@ -97,9 +97,6 @@ public class LiteflowConfig { // 是否打印执行中的日志 private Boolean printExecutionLog; - // 替补组件class路径 - private String substituteCmpClass; - // 规则文件/脚本文件变更监听 private Boolean enableMonitorFile = Boolean.FALSE; @@ -111,6 +108,9 @@ public class LiteflowConfig { //使用默认并行循环线程池时,最大队列数 private Integer parallelQueueLimit; + + // 是否启用组件降级 + private Boolean fallbackCmpEnable; public Boolean getEnableMonitorFile() { return enableMonitorFile; @@ -373,14 +373,6 @@ public class LiteflowConfig { this.printExecutionLog = printExecutionLog; } - public String getSubstituteCmpClass() { - return substituteCmpClass; - } - - public void setSubstituteCmpClass(String substituteCmpClass) { - this.substituteCmpClass = substituteCmpClass; - } - public String getRuleSourceExtData() { return ruleSourceExtData; } @@ -454,4 +446,16 @@ public class LiteflowConfig { public void setParallelLoopExecutorClass(String parallelLoopExecutorClass) { this.parallelLoopExecutorClass = parallelLoopExecutorClass; } + + public Boolean getFallbackCmpEnable() { + if (ObjectUtil.isNull(this.fallbackCmpEnable)) { + return false; + } else { + return fallbackCmpEnable; + } + } + + public void setFallbackCmpEnable(Boolean fallbackCmpEnable) { + this.fallbackCmpEnable = fallbackCmpEnable; + } } diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java index 3a2516266..8bd133c14 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/slot/Slot.java @@ -13,26 +13,29 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.exception.NoSuchContextBeanException; import com.yomahub.liteflow.exception.NullParamException; +import com.yomahub.liteflow.flow.element.Condition; import com.yomahub.liteflow.flow.entity.CmpStep; import com.yomahub.liteflow.flow.id.IdGeneratorHolder; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; import com.yomahub.liteflow.property.LiteflowConfigGetter; + import java.util.Deque; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.function.Consumer; /** * Slot的抽象类实现 * * @author Bryan.Zhang * @author LeoLee + * @author DaleLee */ @SuppressWarnings("unchecked") public class Slot { @@ -88,6 +91,8 @@ public class Slot { protected ConcurrentHashMap metaDataMap = new ConcurrentHashMap<>(); private List contextBeanList; + + private static final ThreadLocal> conditionStack = ThreadLocal.withInitial(LinkedList::new); public Slot() { } @@ -287,6 +292,18 @@ public class Slot { public Iterator getIteratorResult(String key) { return getThreadMetaData(ITERATOR_PREFIX + key); } + + public Condition getCurrentCondition() { + return conditionStack.get().peek(); + } + + public void pushCondition(Condition condition) { + conditionStack.get().push(condition); + } + + public void popCondition() { + conditionStack.get().pop(); + } /** * @deprecated 请使用 {@link #setChainId(String)} diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java index e750468db..64a397f7d 100644 --- a/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java @@ -9,6 +9,7 @@ import cn.hutool.core.util.ClassLoaderUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.exception.ConfigErrorException; import com.yomahub.liteflow.spi.PathContentParser; +import com.yomahub.liteflow.util.PathMatchUtil; import java.util.ArrayList; import java.util.List; @@ -24,10 +25,11 @@ public class LocalPathContentParser implements PathContentParser { if (CollectionUtil.isEmpty(pathList)) { throw new ConfigErrorException("rule source must not be null"); } + List absolutePathList = PathMatchUtil.searchAbsolutePath(pathList); List contentList = new ArrayList<>(); - for (String path : pathList) { + for (String path : absolutePathList) { if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) { path = FILE_URL_PREFIX + path; } @@ -50,10 +52,10 @@ public class LocalPathContentParser implements PathContentParser { if (CollectionUtil.isEmpty(pathList)) { throw new ConfigErrorException("rule source must not be null"); } - + List absolutePathList = PathMatchUtil.searchAbsolutePath(pathList); List result = new ArrayList<>(); - for (String path : pathList) { + for (String path : absolutePathList) { if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) { path = FILE_URL_PREFIX + path; result.add(new FileResource(path).getFile().getAbsolutePath()); diff --git a/liteflow-core/src/main/java/com/yomahub/liteflow/util/PathMatchUtil.java b/liteflow-core/src/main/java/com/yomahub/liteflow/util/PathMatchUtil.java new file mode 100644 index 000000000..31e9fbb36 --- /dev/null +++ b/liteflow-core/src/main/java/com/yomahub/liteflow/util/PathMatchUtil.java @@ -0,0 +1,73 @@ +package com.yomahub.liteflow.util; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.text.AntPathMatcher; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 用于获取模糊匹配的路径 + * + * @author Rain + * @since 2.11.1 + */ +public class PathMatchUtil { + + public static List searchAbsolutePath(List pathList) { + + List absolutePathList = new ArrayList<>(); + + for (String path : pathList) { + // 只对绝对路径进行处理 + if(FileUtil.isAbsolutePath(path)) { + if(!path.contains("*")) { + absolutePathList.add(path); + } + else { + String[] pathSegments = path.split("/"); + StringBuilder baseDir = new StringBuilder(); + + // 找到最大基础路径 + for(int i = 0; i < pathSegments.length; i ++) { + if(!pathSegments[i].contains("*")) { + baseDir.append(pathSegments[i]).append(File.separator); + } else { + baseDir.deleteCharAt(baseDir.length() - 1); + searchAbsolutePath(baseDir.toString(), path, absolutePathList); + break; + } + } + } + } else { + absolutePathList.add(path); + } + } + // 路径去重 + List newAbsolutePathList = absolutePathList.stream() + .distinct() + .collect(Collectors.toList()); + return newAbsolutePathList; + } + + private static void searchAbsolutePath(String baseDir, String path, List absolutePathList) { + AntPathMatcher pathMatcher = new AntPathMatcher(); + File dir = new File(baseDir); + File[] files = dir.listFiles(); + + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + searchAbsolutePath(file.getAbsolutePath(), path, absolutePathList); + } else { + String absolutePath = file.getAbsolutePath().replace("\\", "/"); + if (pathMatcher.match(path, absolutePath)) { + absolutePathList.add(absolutePath); + } + } + } + } + } +} 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 468704be1..01dfc4acc 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 @@ -15,6 +15,7 @@ import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.flow.FlowBus; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,215 +25,222 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import static com.ctrip.framework.apollo.enums.PropertyChangeType.DELETED; + /** * @author zhanghua * @since 2.9.5 */ public class ApolloParseHelper { - private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(ApolloParseHelper.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 final ApolloParserConfigVO apolloParserConfigVO; + private final ApolloParserConfigVO apolloParserConfigVO; - private Config chainConfig; + private Config chainConfig; - private Config scriptConfig; + private Config scriptConfig; - public ApolloParseHelper(ApolloParserConfigVO apolloParserConfigVO) { - this.apolloParserConfigVO = apolloParserConfigVO; + public ApolloParseHelper(ApolloParserConfigVO apolloParserConfigVO) { + this.apolloParserConfigVO = apolloParserConfigVO; - try { - try { - // 这里本身对于程序运行来说没有什么意义,拿到的永远是null - // 其实config对象也没有注入到spring容器中 - // 这里这样写的目的是为了单测中的mockito,当有@MockBean的时候,这里就能拿到了 - this.chainConfig = ContextAwareHolder.loadContextAware().getBean("chainConfig"); - this.scriptConfig = ContextAwareHolder.loadContextAware().getBean("scriptConfig"); - } - catch (Exception ignored) { - } + try { + try { + // 这里本身对于程序运行来说没有什么意义,拿到的永远是null + // 其实config对象也没有注入到spring容器中 + // 这里这样写的目的是为了单测中的mockito,当有@MockBean的时候,这里就能拿到了 + this.chainConfig = ContextAwareHolder.loadContextAware().getBean("chainConfig"); + this.scriptConfig = ContextAwareHolder.loadContextAware().getBean("scriptConfig"); + } catch (Exception ignored) { + } - if (ObjectUtil.isNull(chainConfig)) { - chainConfig = ConfigService.getConfig(apolloParserConfigVO.getChainNamespace()); - String scriptNamespace; - // scriptConfig is optional - if (StrUtil.isNotBlank(scriptNamespace = apolloParserConfigVO.getScriptNamespace())) { - scriptConfig = ConfigService.getConfig(scriptNamespace); - } - } - } - catch (Exception e) { - throw new ApolloException(e.getMessage()); - } - } + if (ObjectUtil.isNull(chainConfig)) { + chainConfig = ConfigService.getConfig(apolloParserConfigVO.getChainNamespace()); + String scriptNamespace; + // scriptConfig is optional + if (StrUtil.isNotBlank(scriptNamespace = apolloParserConfigVO.getScriptNamespace())) { + scriptConfig = ConfigService.getConfig(scriptNamespace); + } + } + } catch (Exception e) { + throw new ApolloException(e.getMessage()); + } + } - public String getContent() { + public String getContent() { - try { - // 1. handle chain - Set propertyNames = chainConfig.getPropertyNames(); - if (CollectionUtil.isEmpty(propertyNames)) { - throw new ApolloException(StrUtil.format("There are no chains in namespace : {}", - apolloParserConfigVO.getChainNamespace())); - } - List chainItemContentList = propertyNames.stream() - .map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) - .collect(Collectors.toList()); - // merge all chain content - String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); + try { + // 1. handle chain + Set propertyNames = chainConfig.getPropertyNames(); + if (CollectionUtil.isEmpty(propertyNames)) { + throw new ApolloException(StrUtil.format("There are no chains in namespace : {}", + apolloParserConfigVO.getChainNamespace())); + } + List chainItemContentList = propertyNames.stream() + .map(item -> StrUtil.format(CHAIN_XML_PATTERN, item, chainConfig.getProperty(item, StrUtil.EMPTY))) + .collect(Collectors.toList()); + // merge all chain content + String chainAllContent = CollUtil.join(chainItemContentList, StrUtil.EMPTY); - // 2. handle script if needed - String scriptAllContent = StrUtil.EMPTY; - Set scriptNamespaces; - if (Objects.nonNull(scriptConfig) - && CollectionUtil.isNotEmpty(scriptNamespaces = scriptConfig.getPropertyNames())) { + // 2. handle script if needed + String scriptAllContent = StrUtil.EMPTY; + Set scriptNamespaces; + if (Objects.nonNull(scriptConfig) + && CollectionUtil.isNotEmpty(scriptNamespaces = scriptConfig.getPropertyNames())) { - 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())) - .collect(Collectors.toList()); + 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())) + .collect(Collectors.toList()); - 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 ApolloException(e.getMessage()); - } - } + return StrUtil.format(XML_PATTERN, scriptAllContent, chainAllContent); + } catch (Exception e) { + throw new ApolloException(e.getMessage()); + } + } - /** - * listen apollo config change - */ - public void listenApollo() { + /** + * listen apollo config change + */ + public void listenApollo() { + // chain + chainConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { + ConfigChange configChange = changeEvent.getChange(changeKey); + String newValue = configChange.getNewValue(); + PropertyChangeType changeType = configChange.getChangeType(); + 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(); + break; + case DELETED: + LOG.info("starting reload flow config... delete key={}", changeKey); + FlowBus.removeChain(changeKey); + break; + default: + } + })); - // chain - chainConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { - ConfigChange configChange = changeEvent.getChange(changeKey); - String newValue = configChange.getNewValue(); - PropertyChangeType changeType = configChange.getChangeType(); - 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(); - break; - case DELETED: - LOG.info("starting reload flow config... delete key={}", changeKey); - FlowBus.removeChain(changeKey); + if (StrUtil.isNotBlank(apolloParserConfigVO.getScriptNamespace())) { + scriptConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { + ConfigChange configChange = changeEvent.getChange(changeKey); + String newValue = configChange.getNewValue(); - } - })); + PropertyChangeType changeType = configChange.getChangeType(); + if (DELETED.equals(changeType)) { + newValue = null; + } + NodeSimpleVO nodeSimpleVO = convert(changeKey, newValue); + if (Objects.isNull(nodeSimpleVO)) { + // key不符合规范的时候,直接忽略 + LOG.error("key={} is not a valid node config, ignore it", changeKey); + return; + } + switch (changeType) { + case ADDED: + case MODIFIED: + LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, + newValue); - if (StrUtil.isNotBlank(apolloParserConfigVO.getScriptNamespace())) { - scriptConfig.addChangeListener(changeEvent -> changeEvent.changedKeys().forEach(changeKey -> { - ConfigChange configChange = changeEvent.getChange(changeKey); - String newValue = configChange.getNewValue(); + LiteFlowNodeBuilder.createScriptNode() + .setId(nodeSimpleVO.getNodeId()) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) + .setName(nodeSimpleVO.getName()) + .setScript(nodeSimpleVO.getScript()) + .build(); + break; + case DELETED: + LOG.info("starting reload flow config... delete key={}", changeKey); + FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); + break; + default: + } + })); + } + } - PropertyChangeType changeType = configChange.getChangeType(); + 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; - switch (changeType) { - case ADDED: - case MODIFIED: - LOG.info("starting reload flow config... {} key={} value={},", changeType.name(), changeKey, - newValue); - nodeSimpleVO = convert(changeKey, newValue); - LiteFlowNodeBuilder.createScriptNode() - .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) - .setName(nodeSimpleVO.getName()) - .setScript(nodeSimpleVO.getScript()) - .build(); - break; - case DELETED: - LOG.info("starting reload flow config... delete key={}", changeKey); - nodeSimpleVO = convert(changeKey, null); - FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); - } - })); - } - } + NodeSimpleVO nodeSimpleVO = new NodeSimpleVO(); + if (matchItemList.size() > 1) { + nodeSimpleVO.setNodeId(matchItemList.get(0)); + nodeSimpleVO.setType(matchItemList.get(1)); + } - private NodeSimpleVO convert(String key, String value) { - // 不需要去理解这串正则,就是一个匹配冒号的 - // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 - List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", key); - if (CollUtil.isEmpty(matchItemList)) { - return null; - } + if (matchItemList.size() > 2) { + nodeSimpleVO.setName(matchItemList.get(2)); + } - NodeSimpleVO nodeSimpleVO = new NodeSimpleVO(); - if (matchItemList.size() > 1) { - nodeSimpleVO.setNodeId(matchItemList.get(0)); - nodeSimpleVO.setType(matchItemList.get(1)); - } + // set script + nodeSimpleVO.setScript(value); - if (matchItemList.size() > 2) { - nodeSimpleVO.setName(matchItemList.get(2)); - } + return nodeSimpleVO; + } - // set script - nodeSimpleVO.setScript(value); + private static class NodeSimpleVO { - return nodeSimpleVO; - } + private String nodeId; - private static class NodeSimpleVO { + private String type; - private String nodeId; + private String name = StrUtil.EMPTY; - private String type; + private String script; - private String name = StrUtil.EMPTY; + public String getNodeId() { + return nodeId; + } - private String script; + public void setNodeId(String nodeId) { + this.nodeId = nodeId; + } - public String getNodeId() { - return nodeId; - } + public String getType() { + return type; + } - public void setNodeId(String nodeId) { - this.nodeId = nodeId; - } + public void setType(String type) { + this.type = type; + } - public String getType() { - return type; - } + public String getName() { + return name; + } - public void setType(String type) { - this.type = type; - } + public void setName(String name) { + this.name = name; + } - public String getName() { - return name; - } + public String getScript() { + return script; + } - public void setName(String name) { - this.name = name; - } + public void setScript(String script) { + this.script = script; + } - public String getScript() { - return script; - } - - public void setScript(String script) { - this.script = script; - } - - } + } } diff --git a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RedisParserHelper.java b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RedisParserHelper.java index eb60d2cdd..68bb1bc68 100644 --- a/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RedisParserHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-redis/src/main/java/com/yomahub/liteflow/parser/redis/mode/RedisParserHelper.java @@ -1,19 +1,16 @@ package com.yomahub.liteflow.parser.redis.mode; -import cn.hutool.core.collection.CollUtil; import cn.hutool.core.text.StrFormatter; -import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; import com.yomahub.liteflow.enums.NodeTypeEnum; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.redis.vo.RedisParserVO; import org.redisson.config.Config; import org.redisson.config.SentinelServersConfig; -import java.util.List; - /** * Redis 解析器通用接口 * @@ -112,7 +109,7 @@ public interface RedisParserHelper { * @param newValue 新的script值 */ static void changeScriptNode(String scriptFieldValue, String newValue) { - NodeSimpleVO nodeSimpleVO = convert(scriptFieldValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); // 有语言类型 if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { LiteFlowNodeBuilder.createScriptNode() @@ -133,73 +130,4 @@ public interface RedisParserHelper { .build(); } } - - static NodeSimpleVO convert(String str) { - // 不需要去理解这串正则,就是一个匹配冒号的 - // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 - List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", str); - 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)); - } - - if (matchItemList.size() > 3) { - nodeSimpleVO.setLanguage(matchItemList.get(3)); - } - - return nodeSimpleVO; - } - - class NodeSimpleVO { - - private String nodeId; - - private String type; - - private String name = StrUtil.EMPTY; - - private String language; - - 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 getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - } } 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 662b801b5..352bf1f9e 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 @@ -6,6 +6,7 @@ import cn.hutool.core.thread.NamedThreadFactory; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.redis.exception.RedisException; import com.yomahub.liteflow.parser.redis.mode.RClient; import com.yomahub.liteflow.parser.redis.mode.RedisMode; @@ -148,7 +149,7 @@ public class RedisParserPollingMode implements RedisParserHelper { List scriptItemContentList = new ArrayList<>(); for (String scriptFieldValue : scriptFieldSet) { - NodeSimpleVO nodeSimpleVO = RedisParserHelper.convert(scriptFieldValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); if (ObjectUtil.isNull(nodeSimpleVO)) { throw new RedisException( StrUtil.format("The name of the redis field [{}] in scriptKey [{}] is invalid", 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 a23965c37..40d4704e6 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 @@ -5,6 +5,7 @@ import cn.hutool.crypto.digest.DigestUtil; import com.yomahub.liteflow.flow.FlowBus; import com.yomahub.liteflow.log.LFLog; import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.redis.mode.RClient; import com.yomahub.liteflow.parser.redis.mode.RedisParserHelper; import com.yomahub.liteflow.parser.redis.vo.RedisParserVO; @@ -68,7 +69,7 @@ public class ScriptPollingTask implements Runnable { String newSHA = scriptClient.evalSha(valueLua, scriptKey, scriptFieldValue); if (StrUtil.equals(newSHA, "nil")) { //新SHA值为nil, 即未获取到该script,表示该script已被删除 - RedisParserHelper.NodeSimpleVO nodeSimpleVO = RedisParserHelper.convert(scriptFieldValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); LOG.info("starting reload flow config... delete key={}", 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 b115baec5..aff895cd9 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 @@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; import com.yomahub.liteflow.parser.redis.exception.RedisException; import com.yomahub.liteflow.parser.redis.mode.RClient; import com.yomahub.liteflow.parser.redis.mode.RedisMode; @@ -109,7 +110,7 @@ public class RedisParserSubscribeMode implements RedisParserHelper { for (Map.Entry entry : scriptMap.entrySet()) { String scriptFieldValue = entry.getKey(); String scriptData = entry.getValue(); - NodeSimpleVO nodeSimpleVO = RedisParserHelper.convert(scriptFieldValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptFieldValue); if (ObjectUtil.isNull(nodeSimpleVO)) { throw new RedisException( StrUtil.format("The name of the redis field [{}] in scriptKey [{}] is invalid", @@ -193,7 +194,7 @@ public class RedisParserSubscribeMode implements RedisParserHelper { //删除 script scriptClient.addListener(scriptKey, (EntryRemovedListener) event -> { LOG.info("starting reload flow config... delete key={}", event.getKey()); - NodeSimpleVO nodeSimpleVO = RedisParserHelper.convert(event.getKey()); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(event.getKey()); FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); }); } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/pom.xml b/liteflow-rule-plugin/liteflow-rule-sql/pom.xml index e4933af70..f725f7cb8 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/pom.xml +++ b/liteflow-rule-plugin/liteflow-rule-sql/pom.xml @@ -20,5 +20,11 @@ true provided + + + cn.hutool + hutool-crypto + ${hutool-crypto.version} + \ No newline at end of file diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/ReadType.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/ReadType.java new file mode 100644 index 000000000..68cb72c98 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/ReadType.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.parser.constant; + +/** + * sql 类型枚举 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public enum ReadType { + CHAIN, + SCRIPT; +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java new file mode 100644 index 000000000..68d8e5ee8 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/constant/SqlReadConstant.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.parser.constant; + +/** + * sql 读取常量类 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class SqlReadConstant { + + public static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?"; + + public static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} "; + + public static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?"; + + public static final String SCRIPT_WITH_LANGUAGE_SQL_PATTERN = "SELECT {},{},{},{},{} FROM {} WHERE {}=?"; + + public static final String CHAIN_XML_PATTERN = ""; + + public static final String NODE_XML_PATTERN = "{}"; + + public static final String NODE_ITEM_XML_PATTERN = ""; + + public static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = ""; + + public static final String XML_PATTERN = "{}{}"; + + public static final Integer FETCH_SIZE_MAX = 1000; +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java index ce3bbe536..4a08df95c 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/SQLXmlELParser.java @@ -5,8 +5,11 @@ import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.StrFormatter; import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.core.FlowInitHook; +import com.yomahub.liteflow.parser.constant.ReadType; import com.yomahub.liteflow.parser.el.ClassXmlFlowELParser; import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.read.SqlReadFactory; import com.yomahub.liteflow.parser.sql.util.JDBCHelper; import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; import com.yomahub.liteflow.property.LiteflowConfig; @@ -23,69 +26,87 @@ import java.util.Objects; */ public class SQLXmlELParser extends ClassXmlFlowELParser { - private static final String ERROR_MSG_PATTERN = "rule-source-ext-data {} is blank"; + private static SQLParserVO sqlParserVO; - private static final String ERROR_COMMON_MSG = "rule-source-ext-data is empty"; + private static final String ERROR_MSG_PATTERN = "rule-source-ext-data {} is blank"; - /** - * 构造函数 - */ - public SQLXmlELParser() { - LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + private static final String ERROR_COMMON_MSG = "rule-source-ext-data is empty"; - try { - SQLParserVO sqlParserVO = null; - if (MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))) { - sqlParserVO = BeanUtil.toBean(liteflowConfig.getRuleSourceExtDataMap(), SQLParserVO.class, - CopyOptions.create()); - } - else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) { - sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); - } - if (Objects.isNull(sqlParserVO)) { - throw new ELSQLException(ERROR_COMMON_MSG); - } + /** + * 构造函数 + */ + public SQLXmlELParser() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); - // 检查配置文件 - checkParserVO(sqlParserVO); + try { + if (MapUtil.isNotEmpty((liteflowConfig.getRuleSourceExtDataMap()))) { + sqlParserVO = BeanUtil.toBean(liteflowConfig.getRuleSourceExtDataMap(), SQLParserVO.class, + CopyOptions.create()); + } else if (StrUtil.isNotBlank(liteflowConfig.getRuleSourceExtData())) { + sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + } + if (Objects.isNull(sqlParserVO)) { + throw new ELSQLException(ERROR_COMMON_MSG); + } - // 初始化 JDBCHelper - JDBCHelper.init(sqlParserVO); - } - catch (ELSQLException elsqlException) { - throw elsqlException; - } - catch (Exception ex) { - throw new ELSQLException(ex.getMessage()); - } + // 检查配置文件 + checkParserVO(sqlParserVO); - } + // 初始化 JDBCHelper + JDBCHelper.init(sqlParserVO); - @Override - public String parseCustom() { - return JDBCHelper.getInstance().getContent(); - } + // 初始化 SqlReadFactory + SqlReadFactory.registerRead(sqlParserVO); - /** - * 检查配置文件并设置默认值 - * @param sqlParserVO sqlParserVO - */ - private void checkParserVO(SQLParserVO sqlParserVO) { - if (sqlParserVO.isDefaultDataSource()) { - return; - } - if (StrUtil.isEmpty(sqlParserVO.getUrl())) { - throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "url")); - } - if (StrUtil.isEmpty(sqlParserVO.getDriverClassName())) { - throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "driverClassName")); - } - if (Objects.isNull(sqlParserVO.getUsername())) { - throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "username")); - } - if (Objects.isNull(sqlParserVO.getPassword())) { - throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "password")); - } - } + // 注册轮询任务 + SqlReadFactory.registerSqlReadPollTask(ReadType.CHAIN); + SqlReadFactory.registerSqlReadPollTask(ReadType.SCRIPT); + } catch (ELSQLException elsqlException) { + throw elsqlException; + } catch (Exception ex) { + throw new ELSQLException(ex.getMessage()); + } + + } + + @Override + public String parseCustom() { + try { + JDBCHelper jdbcHelper = JDBCHelper.getInstance(); + String content = jdbcHelper.getContent(); + if (sqlParserVO.getPollingEnabled()) { + FlowInitHook.addHook(() -> { + jdbcHelper.listenSQL(); + return true; + }); + } + return content; + } catch (Exception ex) { + throw new ELSQLException(ex.getMessage()); + } + } + + /** + * 检查配置文件并设置默认值 + * + * @param sqlParserVO sqlParserVO + */ + private void checkParserVO(SQLParserVO sqlParserVO) { + if (sqlParserVO.isDefaultDataSource()) { + return; + } + if (StrUtil.isEmpty(sqlParserVO.getUrl())) { + throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "url")); + } + if (StrUtil.isEmpty(sqlParserVO.getDriverClassName())) { + throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "driverClassName")); + } + if (Objects.isNull(sqlParserVO.getUsername())) { + throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "username")); + } + if (Objects.isNull(sqlParserVO.getPassword())) { + throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "password")); + } + } } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/AbstractSqlReadPollTask.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/AbstractSqlReadPollTask.java new file mode 100644 index 000000000..d4049a4ca --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/AbstractSqlReadPollTask.java @@ -0,0 +1,100 @@ +package com.yomahub.liteflow.parser.sql.polling; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.crypto.digest.DigestUtil; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.read.SqlRead; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * sql 轮询任务抽象类,维护公共方法 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public abstract class AbstractSqlReadPollTask implements SqlReadPollTask { + private final Map DATA_SHA_MAP = new HashMap<>(); + private final SqlRead read; + + public AbstractSqlReadPollTask(SqlRead read) { + this.read = read; + + if (!read.type().equals(type())) { + throw new ELSQLException("SqlReadPollTask type not match"); + } + } + + @Override + public void execute() { + Map newData = read.read(); + // 新增或者更新的元素 + Map saveElementMap = new HashMap<>(); + // 删除的元素 + List deleteElementIds = new ArrayList<>(); + + for (Map.Entry entry : newData.entrySet()) { + String id = entry.getKey(); + String element = entry.getValue(); + String newSHA = DigestUtil.sha1Hex(element); + + // 新增 + // 如果封装的SHAMap中不存在该chain, 表示该元素为新增 + if (!DATA_SHA_MAP.containsKey(id)) { + saveElementMap.put(id, element); + + DATA_SHA_MAP.put(id, newSHA); + } + // 修改 + // SHA值发生变化,表示该元素的值已被修改,重新拉取变化的chain + else if (!StrUtil.equals(newSHA, DATA_SHA_MAP.get(id))) { + saveElementMap.put(id, element); + + DATA_SHA_MAP.put(id, newSHA); + } + } + + Set oldIdList = DATA_SHA_MAP.keySet(); // 旧的 id 列表 + Set newIdList = newData.keySet(); // 新的 id 列表 + // 计算单差集 + // 计算集合的单差集,即只返回【oldIdList】中有,但是【newIdList】中没有的元素,例如: + // subtractToList([1,2,3,4],[2,3,4,5]) -》 [1] + deleteElementIds = CollUtil.subtractToList(oldIdList, newIdList); + + for (String id : deleteElementIds) { + DATA_SHA_MAP.remove(id); + } + + if (CollUtil.isNotEmpty(saveElementMap)) { + doSave(saveElementMap); + } + + if (CollUtil.isNotEmpty(deleteElementIds)) { + doDelete(deleteElementIds); + } + } + + @Override + public void initData(Map dataMap) { + DATA_SHA_MAP.putAll(shaMapValue(dataMap)); + } + + public abstract void doSave(Map saveElementMap); + + public abstract void doDelete(List deleteElementId); + + private Map shaMapValue(Map dataMap) { + Map result = new HashMap<>(); + dataMap.forEach((k, v) -> { + result.put(k, DigestUtil.sha1Hex(v)); + }); + + return result; + } +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/SqlReadPollTask.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/SqlReadPollTask.java new file mode 100644 index 000000000..8c491e9ad --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/SqlReadPollTask.java @@ -0,0 +1,34 @@ +package com.yomahub.liteflow.parser.sql.polling; + +import com.yomahub.liteflow.parser.constant.ReadType; + +import java.util.Map; + +/** + * sql 轮询任务接口 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public interface SqlReadPollTask { + + /** + * 执行 + */ + void execute(); + + /** + * 初始化数据 + * + * @param dataMap 数据 + */ + void initData(Map dataMap); + + /** + * 类型 + * + * @return 类型 + */ + ReadType type(); +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ChainReadPollTask.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ChainReadPollTask.java new file mode 100644 index 000000000..717ebc106 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ChainReadPollTask.java @@ -0,0 +1,47 @@ +package com.yomahub.liteflow.parser.sql.polling.impl; + +import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.parser.constant.ReadType; +import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask; +import com.yomahub.liteflow.parser.sql.read.SqlRead; + +import java.util.List; +import java.util.Map; + +/** + * chain 读取任务 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class ChainReadPollTask extends AbstractSqlReadPollTask { + + public ChainReadPollTask(SqlRead read) { + super(read); + } + + @Override + public void doSave(Map saveElementMap) { + for (Map.Entry entry : saveElementMap.entrySet()) { + String chainName = entry.getKey(); + String newData = entry.getValue(); + + LiteFlowChainELBuilder.createChain().setChainId(chainName).setEL(newData).build(); + } + } + + @Override + public void doDelete(List deleteElementId) { + for (String id : deleteElementId) { + FlowBus.removeChain(id); + } + } + + @Override + public ReadType type() { + return ReadType.CHAIN; + } + +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ScriptReadPollTask.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ScriptReadPollTask.java new file mode 100644 index 000000000..77c6ce74d --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/polling/impl/ScriptReadPollTask.java @@ -0,0 +1,49 @@ +package com.yomahub.liteflow.parser.sql.polling.impl; + +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.parser.constant.ReadType; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; +import com.yomahub.liteflow.parser.sql.polling.AbstractSqlReadPollTask; +import com.yomahub.liteflow.parser.sql.read.SqlRead; + +import java.util.List; +import java.util.Map; + +/** + * 脚本轮询任务 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class ScriptReadPollTask extends AbstractSqlReadPollTask { + public ScriptReadPollTask(SqlRead read) { + super(read); + } + + @Override + public void doSave(Map saveElementMap) { + for (Map.Entry entry : saveElementMap.entrySet()) { + String scriptKey = entry.getKey(); + String newData = entry.getValue(); + + NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(scriptKey); + NodeConvertHelper.changeScriptNode(scriptVO, newData); + } + } + + @Override + public void doDelete(List deleteElementId) { + for (String id : deleteElementId) { + NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(id); + + // 删除script + FlowBus.getNodeMap().remove(scriptVO.getNodeId()); + } + } + + @Override + public ReadType type() { + return ReadType.SCRIPT; + } +} 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 new file mode 100644 index 000000000..4c144aef6 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/AbstractSqlRead.java @@ -0,0 +1,97 @@ +package com.yomahub.liteflow.parser.sql.read; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.parser.constant.SqlReadConstant; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; + +/** + * sql 读取抽象类,维护公共方法 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public abstract class AbstractSqlRead implements SqlRead { + public final SQLParserVO config; + private static LFLog LOG = LFLoggerManager.getLogger(AbstractSqlRead.class); + + public AbstractSqlRead(SQLParserVO config) { + this.config = config; + } + + @Override + public Map read() { + // 如果不需要读取直接返回 + if (!needRead()) { + return new HashMap<>(); + } + + Map result = new HashMap<>(); + String sqlCmd = buildQuerySql(); + if (config.getSqlLogEnabled()) { + LOG.info("query sql:{}", sqlCmd.replace("?", "'" + config.getApplicationName() + "'")); + } + + Connection conn = null; + PreparedStatement stmt = null; + ResultSet rs = null; + try { + conn = LiteFlowJdbcUtil.getConn(config); + stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + // 设置游标拉取数量 + stmt.setFetchSize(SqlReadConstant.FETCH_SIZE_MAX); + stmt.setString(1, config.getApplicationName()); + rs = stmt.executeQuery(); + + while (rs.next()) { + String xml = buildXmlElement(rs); + String uniqueKey = buildXmlElementUniqueKey(rs); + + result.put(uniqueKey, xml); + } + } catch (Exception e) { + throw new ELSQLException(e.getMessage()); + } finally { + // 关闭连接 + LiteFlowJdbcUtil.close(conn, stmt, rs); + } + + return result; + } + + public abstract String buildQuerySql(); + + public abstract String buildXmlElement(ResultSet rs) throws SQLException; + + public abstract String buildXmlElementUniqueKey(ResultSet rs) throws SQLException; + + /** + * 是否可以读取 + * chain 默认可以读取 + * script 需要判断是否有配置 + * @return 布尔值 + */ + public boolean needRead() { + return true; + } + + + public String getStringFromResultSet(ResultSet rs, String field) throws SQLException { + String data = rs.getString(field); + if (StrUtil.isBlank(data)) { + throw new ELSQLException(StrUtil.format("exist {} field value is empty", field)); + } + return data; + } +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlRead.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlRead.java new file mode 100644 index 000000000..af003f8ea --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlRead.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.parser.sql.read; + +import com.yomahub.liteflow.parser.constant.ReadType; + +import java.util.Map; + +/** + * sql 读取接口 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public interface SqlRead { + + /** + * 读取 + * + * @return 返回读取到的数据 + */ + Map read(); + + /** + * 类型 + * + * @return 返回类型 + */ + ReadType type(); +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlReadFactory.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlReadFactory.java new file mode 100644 index 000000000..b08de78a9 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/SqlReadFactory.java @@ -0,0 +1,46 @@ +package com.yomahub.liteflow.parser.sql.read; + +import com.yomahub.liteflow.parser.constant.ReadType; +import com.yomahub.liteflow.parser.sql.polling.SqlReadPollTask; +import com.yomahub.liteflow.parser.sql.polling.impl.ChainReadPollTask; +import com.yomahub.liteflow.parser.sql.polling.impl.ScriptReadPollTask; +import com.yomahub.liteflow.parser.sql.read.impl.ChainRead; +import com.yomahub.liteflow.parser.sql.read.impl.ScriptRead; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; + +import java.util.HashMap; +import java.util.Map; + +/** + * sql 读取工厂类 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class SqlReadFactory { + private static final Map READ_MAP = new HashMap<>(); + private static final Map POLL_TASK_MAP = new HashMap<>(); + + public static void registerRead(SQLParserVO config) { + READ_MAP.put(ReadType.CHAIN, new ChainRead(config)); + READ_MAP.put(ReadType.SCRIPT, new ScriptRead(config)); + } + + public static void registerSqlReadPollTask(ReadType readType) { + SqlRead sqlRead = getSqlRead(readType); + if (ReadType.CHAIN.equals(readType)) { + POLL_TASK_MAP.put(ReadType.CHAIN, new ChainReadPollTask(sqlRead)); + } else if (ReadType.SCRIPT.equals(readType)) { + POLL_TASK_MAP.put(ReadType.SCRIPT, new ScriptReadPollTask(sqlRead)); + } + } + + public static SqlRead getSqlRead(ReadType readType) { + return READ_MAP.get(readType); + } + + public static SqlReadPollTask getSqlReadPollTask(ReadType readType) { + return POLL_TASK_MAP.get(readType); + } +} 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 new file mode 100644 index 000000000..69eadd692 --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ChainRead.java @@ -0,0 +1,66 @@ +package com.yomahub.liteflow.parser.sql.read.impl; + +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.parser.constant.ReadType; +import com.yomahub.liteflow.parser.constant.SqlReadConstant; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; + +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * chain 读取 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class ChainRead extends AbstractSqlRead { + + public ChainRead(SQLParserVO config) { + super(config); + } + + @Override + public String buildQuerySql() { + String chainTableName = super.config.getChainTableName(); + String elDataField = super.config.getElDataField(); + String chainNameField = super.config.getChainNameField(); + String chainApplicationNameField = super.config.getChainApplicationNameField(); + String applicationName = super.config.getApplicationName(); + + if (StrUtil.isBlank(chainTableName)) { + throw new ELSQLException("You did not define the chainTableName property"); + } + + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)) { + throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); + } + + String sqlCmd = StrUtil.format(SqlReadConstant.SQL_PATTERN, chainNameField, elDataField, chainTableName, + chainApplicationNameField); + + return sqlCmd; + } + + @Override + public String buildXmlElement(ResultSet rs) throws SQLException { + String elDataField = super.config.getElDataField(); + + return getStringFromResultSet(rs, elDataField); + } + + @Override + public String buildXmlElementUniqueKey(ResultSet rs) throws SQLException { + String chainNameField = super.config.getChainNameField(); + + return getStringFromResultSet(rs, chainNameField); + } + + @Override + public ReadType type() { + return ReadType.CHAIN; + } +} 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 new file mode 100644 index 000000000..a0b054b0d --- /dev/null +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/read/impl/ScriptRead.java @@ -0,0 +1,147 @@ +package com.yomahub.liteflow.parser.sql.read.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.enums.ScriptTypeEnum; +import com.yomahub.liteflow.parser.constant.ReadType; +import com.yomahub.liteflow.parser.constant.SqlReadConstant; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; +import com.yomahub.liteflow.parser.sql.util.LiteFlowJdbcUtil; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.Objects; + +/** + * 脚本读取 + * + * @author tangkc + * @author houxinyu + * @since 2.11.1 + */ +public class ScriptRead extends AbstractSqlRead { + + public ScriptRead(SQLParserVO config) { + super(config); + } + + @Override + public String buildQuerySql() { + String scriptLanguageField = super.config.getScriptLanguageField(); + String scriptTableName = super.config.getScriptTableName(); + String scriptIdField = super.config.getScriptIdField(); + String scriptDataField = super.config.getScriptDataField(); + String scriptNameField = super.config.getScriptNameField(); + String scriptTypeField = super.config.getScriptTypeField(); + String scriptApplicationNameField = super.config.getScriptApplicationNameField(); + String applicationName = super.config.getApplicationName(); + + if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { + throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); + } + + String sqlCmd = null; + // 脚本节点(带语言) + if (withLanguage()) { + sqlCmd = StrUtil.format( + SqlReadConstant.SCRIPT_WITH_LANGUAGE_SQL_PATTERN, + scriptIdField, + scriptDataField, + scriptNameField, + scriptTypeField, + scriptLanguageField, + scriptTableName, + scriptApplicationNameField + ); + + return sqlCmd; + } + // 脚本节点(不带语言) + else { + sqlCmd = StrUtil.format( + SqlReadConstant.SCRIPT_SQL_PATTERN, + scriptIdField, + scriptDataField, + scriptNameField, + scriptTypeField, + scriptTableName, + scriptApplicationNameField + ); + } + + + return sqlCmd; + } + + @Override + public String buildXmlElement(ResultSet rs) throws SQLException { + String scriptDataField = super.config.getScriptDataField(); + + return getStringFromResultSet(rs, scriptDataField); + + } + + @Override + public String buildXmlElementUniqueKey(ResultSet rs) throws SQLException { + String scriptIdField = super.config.getScriptIdField(); + String scriptNameField = super.config.getScriptNameField(); + String scriptTypeField = super.config.getScriptTypeField(); + String scriptLanguageField = super.config.getScriptLanguageField(); + + String id = getStringFromResultSet(rs, scriptIdField); + String name = getStringFromResultSet(rs, scriptNameField); + String type = getStringFromResultSet(rs, scriptTypeField); + String language = withLanguage() ? getStringFromResultSet(rs, scriptLanguageField) : null; + + NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); + if (Objects.isNull(nodeTypeEnum)) { + throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); + } + + if (!nodeTypeEnum.isScript()) { + throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); + } + + if (withLanguage() && !ScriptTypeEnum.checkScriptType(language)) { + throw new ELSQLException(StrUtil.format("The language value[{}] is invalid", language)); + } + List keys = CollUtil.newArrayList(id, type, name); + if (StrUtil.isNotBlank(language)) { + keys.add(language); + } + + return StrUtil.join(StrUtil.COLON, keys); + } + + @Override + public boolean needRead() { + if (StrUtil.isBlank(super.config.getScriptTableName())) { + return false; + } + + String sqlCmd = StrUtil.format( + SqlReadConstant.SCRIPT_SQL_CHECK_PATTERN, + super.config.getScriptTableName() + ); + + Connection conn = LiteFlowJdbcUtil.getConn(super.config); + return LiteFlowJdbcUtil.checkConnectionCanExecuteSql(conn, sqlCmd); + } + + @Override + public ReadType type() { + return ReadType.SCRIPT; + } + + /** + * 脚本是否带语言 + */ + private boolean withLanguage() { + return StrUtil.isNotBlank(super.config.getScriptLanguageField()); + } +} diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java index 58d374b6e..7b9b0efda 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/util/JDBCHelper.java @@ -1,20 +1,29 @@ package com.yomahub.liteflow.parser.sql.util; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.thread.NamedThreadFactory; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.XmlUtil; -import com.yomahub.liteflow.enums.NodeTypeEnum; -import com.yomahub.liteflow.enums.ScriptTypeEnum; -import com.yomahub.liteflow.parser.sql.exception.ELSQLException; -import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.parser.constant.ReadType; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +import com.yomahub.liteflow.parser.helper.NodeConvertHelper; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.read.AbstractSqlRead; +import com.yomahub.liteflow.parser.sql.read.SqlRead; +import com.yomahub.liteflow.parser.sql.read.SqlReadFactory; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; +import org.apache.commons.lang.StringUtils; + +import java.util.*; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import static com.yomahub.liteflow.parser.constant.SqlReadConstant.*; /** * jdbc 工具类 @@ -24,30 +33,22 @@ import java.util.Objects; */ public class JDBCHelper { - private static final String SQL_PATTERN = "SELECT {},{} FROM {} WHERE {}=?"; - - private static final String SCRIPT_SQL_CHECK_PATTERN = "SELECT 1 FROM {} WHERE {}=?"; - - private static final String SCRIPT_SQL_PATTERN = "SELECT {},{},{},{} FROM {} WHERE {}=?"; - - private static final String SCRIPT_WITH_LANGUAG_SQL_PATTERN = "SELECT {},{},{},{},{} FROM {} WHERE {}=?"; - - private static final String CHAIN_XML_PATTERN = ""; - - private static final String NODE_XML_PATTERN = "{}"; - - private static final String NODE_ITEM_XML_PATTERN = ""; - - private static final String NODE_ITEM_WITH_LANGUAGE_XML_PATTERN = ""; - - private static final String XML_PATTERN = "{}{}"; - - private static final Integer FETCH_SIZE_MAX = 1000; - private SQLParserVO sqlParserVO; private static JDBCHelper INSTANCE; + /** + * 定时任务线程池核心线程数 + */ + private static final int CORE_POOL_SIZE = 2; + + /** + * 定时任务线程池 + */ + private static ScheduledThreadPoolExecutor pollExecutor; + + private static LFLog LOG = LFLoggerManager.getLogger(JDBCHelper.class); + /** * 初始化 INSTANCE */ @@ -58,6 +59,12 @@ public class JDBCHelper { Class.forName(sqlParserVO.getDriverClassName()); } INSTANCE.setSqlParserVO(sqlParserVO); + // 创建定时任务线程池 + if (sqlParserVO.getPollingEnabled() && ObjectUtil.isNull(getPollExecutor())) { + ThreadFactory namedThreadFactory = new NamedThreadFactory("SQL-Polling-", false); + ScheduledThreadPoolExecutor threadPoolExecutor = new ScheduledThreadPoolExecutor(CORE_POOL_SIZE, namedThreadFactory, new ThreadPoolExecutor.DiscardOldestPolicy()); + setPollExecutor(threadPoolExecutor); + } } catch (ClassNotFoundException e) { throw new ELSQLException(e.getMessage()); } @@ -65,6 +72,8 @@ public class JDBCHelper { /** * 获取 INSTANCE + * + * @return 实例 */ public static JDBCHelper getInstance() { return INSTANCE; @@ -72,231 +81,88 @@ public class JDBCHelper { /** * 获取 ElData 数据内容 + * + * @return 数据内容 */ public String getContent() { - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; + SqlRead chainRead = SqlReadFactory.getSqlRead(ReadType.CHAIN); + SqlRead scriptRead = SqlReadFactory.getSqlRead(ReadType.SCRIPT); - String chainTableName = sqlParserVO.getChainTableName(); - String elDataField = sqlParserVO.getElDataField(); - String chainNameField = sqlParserVO.getChainNameField(); - String chainApplicationNameField = sqlParserVO.getChainApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); + // 获取 chain 数据 + Map chainMap = chainRead.read(); + List chainList = new ArrayList<>(); + chainMap.forEach((chainName, elData) -> { + chainList.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData)); + }); + String chainsContent = CollUtil.join(chainList, StrUtil.EMPTY); - if (StrUtil.isBlank(chainTableName)) { - throw new ELSQLException("You did not define the chainTableName property"); - } + // 获取脚本数据 + Map scriptMap = scriptRead.read(); + List scriptList = new ArrayList<>(); + scriptMap.forEach((scriptKey, elData) -> { + NodeConvertHelper.NodeSimpleVO scriptVO = NodeConvertHelper.convert(scriptKey); + String id = scriptVO.getNodeId(); + String name = scriptVO.getName(); + String type = scriptVO.getType(); + String language = scriptVO.getLanguage(); - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(chainApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or chainApplicationNameField property"); - } - - String sqlCmd = StrUtil.format(SQL_PATTERN, chainNameField, elDataField, chainTableName, - chainApplicationNameField); - - List result = new ArrayList<>(); - try { - conn = LiteFlowJdbcUtil.getConn(sqlParserVO); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); - - while (rs.next()) { - String elData = getStringFromResultSet(rs, elDataField); - String chainName = getStringFromResultSet(rs, chainNameField); - - result.add(StrUtil.format(CHAIN_XML_PATTERN, XmlUtil.escape(chainName), elData)); + if (StringUtils.isNotBlank(scriptVO.getLanguage())) { + scriptList.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, language, elData)); + } else { + scriptList.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, elData)); } - } catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } finally { - // 关闭连接 - LiteFlowJdbcUtil.close(conn, stmt, rs); - } - - String chainsContent = CollUtil.join(result, StrUtil.EMPTY); - - String nodesContent; - if (hasScriptData()) { - nodesContent = getScriptNodes(); - } else { - nodesContent = StrUtil.EMPTY; - } + }); + String nodesContent = StrUtil.format(NODE_XML_PATTERN, CollUtil.join(scriptList, StrUtil.EMPTY)); + // 初始化轮询任务 + SqlReadFactory.getSqlReadPollTask(ReadType.CHAIN).initData(chainMap); + SqlReadFactory.getSqlReadPollTask(ReadType.SCRIPT).initData(scriptMap); return StrUtil.format(XML_PATTERN, nodesContent, chainsContent); } /** - * 获取脚本节点内容 + * 定时轮询拉取SQL中变化的数据 */ - public String getScriptNodes() { - String scriptLanguageField = sqlParserVO.getScriptLanguageField(); - if (StrUtil.isNotBlank(scriptLanguageField)) { - return getScriptNodesWithLanguage(); - } + public void listenSQL() { + // 添加轮询chain的定时任务 + pollExecutor.scheduleAtFixedRate( + () -> { + try { + SqlReadFactory.getSqlReadPollTask(ReadType.CHAIN).execute(); + } catch (Exception ex) { + LOG.error("poll chain fail", ex); + } + }, + sqlParserVO.getPollingStartSeconds().longValue(), + sqlParserVO.getPollingIntervalSeconds().longValue(), + TimeUnit.SECONDS + ); - List result = new ArrayList<>(); - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; + // 添加轮询script的定时任务 + pollExecutor.scheduleAtFixedRate( + () -> { + try { + SqlReadFactory.getSqlReadPollTask(ReadType.SCRIPT).execute(); + } catch (Exception ex) { + LOG.error("poll script fail", ex); + } + }, + sqlParserVO.getPollingStartSeconds().longValue(), + sqlParserVO.getPollingIntervalSeconds().longValue(), + TimeUnit.SECONDS + ); - String scriptTableName = sqlParserVO.getScriptTableName(); - String scriptIdField = sqlParserVO.getScriptIdField(); - String scriptDataField = sqlParserVO.getScriptDataField(); - String scriptNameField = sqlParserVO.getScriptNameField(); - String scriptTypeField = sqlParserVO.getScriptTypeField(); - String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); - - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); - } - - String sqlCmd = StrUtil.format(SCRIPT_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, - scriptTypeField, scriptTableName, scriptApplicationNameField); - try { - conn = LiteFlowJdbcUtil.getConn(sqlParserVO); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); - - while (rs.next()) { - String id = getStringFromResultSet(rs, scriptIdField); - String data = getStringFromResultSet(rs, scriptDataField); - String name = getStringFromResultSet(rs, scriptNameField); - String type = getStringFromResultSet(rs, scriptTypeField); - - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (Objects.isNull(nodeTypeEnum)) { - throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); - } - - if (!nodeTypeEnum.isScript()) { - throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); - } - - result.add(StrUtil.format(NODE_ITEM_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), type, data)); - } - } catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } finally { - // 关闭连接 - LiteFlowJdbcUtil.close(conn, stmt, rs); - } - return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); - } - - /** - * 获取脚本节点(带语言) - * - * @return - */ - public String getScriptNodesWithLanguage() { - - List result = new ArrayList<>(); - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; - - String scriptTableName = sqlParserVO.getScriptTableName(); - String scriptIdField = sqlParserVO.getScriptIdField(); - String scriptDataField = sqlParserVO.getScriptDataField(); - String scriptNameField = sqlParserVO.getScriptNameField(); - String scriptTypeField = sqlParserVO.getScriptTypeField(); - String scriptApplicationNameField = sqlParserVO.getScriptApplicationNameField(); - String applicationName = sqlParserVO.getApplicationName(); - String scriptLanguageField = sqlParserVO.getScriptLanguageField(); - - if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(scriptApplicationNameField)) { - throw new ELSQLException("You did not define the applicationName or scriptApplicationNameField property"); - } - - String sqlCmd = StrUtil.format(SCRIPT_WITH_LANGUAG_SQL_PATTERN, scriptIdField, scriptDataField, scriptNameField, - scriptTypeField, scriptLanguageField, scriptTableName, scriptApplicationNameField); - try { - conn = LiteFlowJdbcUtil.getConn(sqlParserVO); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - // 设置游标拉取数量 - stmt.setFetchSize(FETCH_SIZE_MAX); - stmt.setString(1, applicationName); - rs = stmt.executeQuery(); - - while (rs.next()) { - String id = getStringFromResultSet(rs, scriptIdField); - String data = getStringFromResultSet(rs, scriptDataField); - String name = getStringFromResultSet(rs, scriptNameField); - String type = getStringFromResultSet(rs, scriptTypeField); - String language = getStringFromResultSet(rs, scriptLanguageField); - - NodeTypeEnum nodeTypeEnum = NodeTypeEnum.getEnumByCode(type); - if (Objects.isNull(nodeTypeEnum)) { - throw new ELSQLException(StrUtil.format("Invalid type value[{}]", type)); - } - - if (!nodeTypeEnum.isScript()) { - throw new ELSQLException(StrUtil.format("The type value[{}] is not a script type", type)); - } - - if (!ScriptTypeEnum.checkScriptType(language)) { - throw new ELSQLException(StrUtil.format("The language value[{}] is invalid", language)); - } - - result.add(StrUtil.format(NODE_ITEM_WITH_LANGUAGE_XML_PATTERN, XmlUtil.escape(id), XmlUtil.escape(name), - type, language, data)); - } - } catch (Exception e) { - throw new ELSQLException(e.getMessage()); - } finally { - // 关闭连接 - LiteFlowJdbcUtil.close(conn, stmt, rs); - } - return StrUtil.format(NODE_XML_PATTERN, CollUtil.join(result, StrUtil.EMPTY)); - } - - private boolean hasScriptData() { - if (StrUtil.isBlank(sqlParserVO.getScriptTableName())) { - return false; - } - - Connection conn = null; - PreparedStatement stmt = null; - ResultSet rs = null; - String sqlCmd = StrUtil.format(SCRIPT_SQL_CHECK_PATTERN, sqlParserVO.getScriptTableName(), - sqlParserVO.getScriptApplicationNameField()); - try { - conn = LiteFlowJdbcUtil.getConn(sqlParserVO); - stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - stmt.setFetchSize(1); - stmt.setString(1, sqlParserVO.getApplicationName()); - rs = stmt.executeQuery(); - return rs.next(); - } catch (Exception e) { - return false; - } finally { - // 关闭连接 - LiteFlowJdbcUtil.close(conn, stmt, rs); - } - } - - private String getStringFromResultSet(ResultSet rs, String field) throws SQLException { - String data = rs.getString(field); - if (StrUtil.isBlank(data)) { - throw new ELSQLException(StrUtil.format("exist {} field value is empty", field)); - } - return data; - } - - private SQLParserVO getSqlParserVO() { - return sqlParserVO; } private void setSqlParserVO(SQLParserVO sqlParserVO) { this.sqlParserVO = sqlParserVO; } + public static ScheduledThreadPoolExecutor getPollExecutor() { + return pollExecutor; + } + + public static void setPollExecutor(ScheduledThreadPoolExecutor pollExecutor) { + JDBCHelper.pollExecutor = pollExecutor; + } } diff --git a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java index b549bafdc..8824328dd 100644 --- a/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java +++ b/liteflow-rule-plugin/liteflow-rule-sql/src/main/java/com/yomahub/liteflow/parser/sql/vo/SQLParserVO.java @@ -90,6 +90,26 @@ public class SQLParserVO { */ private String scriptLanguageField; + /** + * 轮询机制是否开启 默认不开启 + */ + private Boolean pollingEnabled = false; + + /** + * 轮询时间间隔(s) 默认60s + */ + private Integer pollingIntervalSeconds = 60; + + /** + * 规则配置后首次轮询的起始时间 默认为60s + */ + private Integer pollingStartSeconds = 60; + + /** + * 是否开启sql日志 + */ + private Boolean sqlLogEnabled = true; + public String getUrl() { return url; } @@ -225,4 +245,35 @@ public class SQLParserVO { return StrUtil.isBlank(url) && StrUtil.isBlank(username) && StrUtil.isBlank(password) && StrUtil.isBlank(driverClassName); } + public Boolean getPollingEnabled() { + return pollingEnabled; + } + + public void setPollingEnabled(Boolean pollingEnabled) { + this.pollingEnabled = pollingEnabled; + } + + public Integer getPollingIntervalSeconds() { + return pollingIntervalSeconds; + } + + public void setPollingIntervalSeconds(Integer pollingIntervalSeconds) { + this.pollingIntervalSeconds = pollingIntervalSeconds; + } + + public Integer getPollingStartSeconds() { + return pollingStartSeconds; + } + + public void setPollingStartSeconds(Integer pollingStartSeconds) { + this.pollingStartSeconds = pollingStartSeconds; + } + + public Boolean getSqlLogEnabled() { + return sqlLogEnabled; + } + + public void setSqlLogEnabled(Boolean sqlLogEnabled) { + this.sqlLogEnabled = sqlLogEnabled; + } } 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 c994b2b56..28f81702e 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 @@ -4,12 +4,12 @@ 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.util.ReUtil; 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.zk.exception.ZkException; import com.yomahub.liteflow.parser.zk.vo.ZkParserVO; import org.apache.curator.framework.CuratorFramework; @@ -87,7 +87,7 @@ public class ZkParserHelper { List scriptItemContentList = new ArrayList<>(); for (String scriptNodeValue : scriptNodeValueList) { - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); if (Objects.isNull(nodeSimpleVO)) { throw new ZkException(StrUtil.format("The name of the zk node is invalid:{}", scriptNodeValue)); } @@ -179,12 +179,12 @@ public class ZkParserHelper { .contains(type)) { LOG.info("starting reload flow config... {} path={} value={},", type.name(), path, value); String scriptNodeValue = FileNameUtil.getName(path); - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); // 有语言类型 if (StrUtil.isNotBlank(nodeSimpleVO.getLanguage())) { LiteFlowNodeBuilder.createScriptNode() .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.type)) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) .setName(nodeSimpleVO.getName()) .setScript(value) .setLanguage(nodeSimpleVO.getLanguage()) @@ -194,7 +194,7 @@ public class ZkParserHelper { else { LiteFlowNodeBuilder.createScriptNode() .setId(nodeSimpleVO.getNodeId()) - .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.type)) + .setType(NodeTypeEnum.getEnumByCode(nodeSimpleVO.getType())) .setName(nodeSimpleVO.getName()) .setScript(value) .build(); @@ -203,80 +203,10 @@ public class ZkParserHelper { else if (CuratorCacheListener.Type.NODE_DELETED.equals(type)) { LOG.info("starting reload flow config... delete path={}", path); String scriptNodeValue = FileNameUtil.getName(path); - NodeSimpleVO nodeSimpleVO = convert(scriptNodeValue); + NodeConvertHelper.NodeSimpleVO nodeSimpleVO = NodeConvertHelper.convert(scriptNodeValue); FlowBus.getNodeMap().remove(nodeSimpleVO.getNodeId()); } }); } } - - public NodeSimpleVO convert(String str) { - // 不需要去理解这串正则,就是一个匹配冒号的 - // 一定得是a:b,或是a:b:c...这种完整类型的字符串的 - List matchItemList = ReUtil.findAllGroup0("(?<=[^:]:)[^:]+|[^:]+(?=:[^:])", str); - 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)); - } - - if (matchItemList.size() > 3) { - nodeSimpleVO.setLanguage(matchItemList.get(3)); - } - - return nodeSimpleVO; - } - - private static class NodeSimpleVO { - - private String nodeId; - - private String type; - - private String name = ""; - - private String language; - - 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 getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - } - } diff --git a/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java b/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java index cdec58a2e..b0693fda4 100644 --- a/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java +++ b/liteflow-script-plugin/liteflow-script-java/src/main/java/com/yomahub/liteflow/script/java/JavaExecutor.java @@ -16,12 +16,11 @@ public class JavaExecutor extends ScriptExecutor { private final Map compiledScriptMap = new CopyOnWriteHashMap<>(); - - @Override public void load(String nodeId, String script) { try{ IScriptEvaluator se = CompilerFactoryFactory.getDefaultCompilerFactory(this.getClass().getClassLoader()).newScriptEvaluator(); + se.setTargetVersion(8); se.setReturnType(Object.class); se.setParameters(new String[] {"_meta"}, new Class[] {ScriptExecuteWrap.class}); se.cook(convertScript(script)); @@ -40,7 +39,7 @@ public class JavaExecutor extends ScriptExecutor { throw new ScriptLoadException(errorMsg); } IScriptEvaluator se = compiledScriptMap.get(wrap.getNodeId()); - return se.evaluate(wrap); + return se.evaluate(new Object[]{wrap}); } @Override diff --git a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowAutoConfiguration.java b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowAutoConfiguration.java index 082f41ea5..f8ccebff6 100644 --- a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowAutoConfiguration.java +++ b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowAutoConfiguration.java @@ -46,10 +46,10 @@ public class LiteflowAutoConfiguration { liteflowConfig.setMainExecutorWorks(property.getMainExecutorWorks()); liteflowConfig.setMainExecutorClass(property.getMainExecutorClass()); liteflowConfig.setPrintExecutionLog(property.isPrintExecutionLog()); - liteflowConfig.setSubstituteCmpClass(property.getSubstituteCmpClass()); liteflowConfig.setParallelMaxWorkers(property.getParallelMaxWorkers()); liteflowConfig.setParallelQueueLimit(property.getParallelQueueLimit()); liteflowConfig.setParallelLoopExecutorClass(property.getParallelLoopExecutorClass()); + liteflowConfig.setFallbackCmpEnable(property.isFallbackCmpEnable()); return liteflowConfig; } diff --git a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowProperty.java b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowProperty.java index 2454ba472..6790bcd57 100644 --- a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowProperty.java +++ b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/LiteflowProperty.java @@ -67,9 +67,6 @@ public class LiteflowProperty { // 是否打印执行过程中的日志 private boolean printExecutionLog; - // 替补组件的class路径 - private String substituteCmpClass; - //并行循环线程池类路径 private String parallelLoopExecutorClass; @@ -78,6 +75,9 @@ public class LiteflowProperty { //使用默认并行循环线程池时,最大队列数 private Integer parallelQueueLimit; + + // 是否启用组件降级 + private Boolean fallbackCmpEnable; public boolean isEnable() { return enable; @@ -212,14 +212,6 @@ public class LiteflowProperty { this.requestIdGeneratorClass = requestIdGeneratorClass; } - public String getSubstituteCmpClass() { - return substituteCmpClass; - } - - public void setSubstituteCmpClass(String substituteCmpClass) { - this.substituteCmpClass = substituteCmpClass; - } - public String getRuleSourceExtData() { return ruleSourceExtData; } @@ -251,4 +243,12 @@ public class LiteflowProperty { public void setParallelQueueLimit(Integer parallelQueueLimit) { this.parallelQueueLimit = parallelQueueLimit; } + + public Boolean isFallbackCmpEnable() { + return fallbackCmpEnable; + } + + public void setFallbackCmpEnable(Boolean fallbackCmpEnable) { + this.fallbackCmpEnable = fallbackCmpEnable; + } } diff --git a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/PathsUtils.java b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/PathsUtils.java index 08e9d9fb4..879328004 100644 --- a/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/PathsUtils.java +++ b/liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/solon/config/PathsUtils.java @@ -1,8 +1,11 @@ package com.yomahub.liteflow.solon.config; +import cn.hutool.core.io.FileUtil; +import com.yomahub.liteflow.util.PathMatchUtil; import org.noear.solon.core.util.ScanUtil; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.regex.Pattern; @@ -17,51 +20,55 @@ public class PathsUtils { public static Collection resolvePaths(String pathExpr) { List paths = new ArrayList<>(); + if(!FileUtil.isAbsolutePath(pathExpr)) { + if (pathExpr.contains("/*") == false) { // 说明没有*符 + paths.add(pathExpr); + return paths; + } - if (pathExpr.contains("/*") == false) { // 说明没有*符 - paths.add(pathExpr); - return paths; + // 确定目录 + int dirIdx = pathExpr.indexOf("/*"); + String dir = pathExpr.substring(0, dirIdx); + + // 确定后缀 + int sufIdx = pathExpr.lastIndexOf("."); + String suf = null; + if (sufIdx > 0) { + suf = pathExpr.substring(sufIdx); + if (suf.contains("*")) { + sufIdx = -1; + suf = null; + } + } + + int sufIdx2 = sufIdx; + String suf2 = suf; + + // 匹配表达式 + String expr = pathExpr.replaceAll("/\\*\\.", "/[^\\.]*\\."); + expr = expr.replaceAll("/\\*\\*/", "(/[^/]*)*/"); + + Pattern pattern = Pattern.compile(expr); + + List finalPaths = paths; + ScanUtil.scan(dir, n -> { + // 进行后缀过滤,相对比较快 + if (sufIdx2 > 0) { + return n.endsWith(suf2); + } + else { + return true; + } + }).forEach(uri -> { + // 再进行表达式过滤 + if (pattern.matcher(uri).find()) { + finalPaths.add(uri); + } + }); + } else { + String[] pathExprs = pathExpr.split(","); + paths = PathMatchUtil.searchAbsolutePath(Arrays.asList(pathExprs)); } - - // 确定目录 - int dirIdx = pathExpr.indexOf("/*"); - String dir = pathExpr.substring(0, dirIdx); - - // 确定后缀 - int sufIdx = pathExpr.lastIndexOf("."); - String suf = null; - if (sufIdx > 0) { - suf = pathExpr.substring(sufIdx); - if (suf.contains("*")) { - sufIdx = -1; - suf = null; - } - } - - int sufIdx2 = sufIdx; - String suf2 = suf; - - // 匹配表达式 - String expr = pathExpr.replaceAll("/\\*\\.", "/[^\\.]*\\."); - expr = expr.replaceAll("/\\*\\*/", "(/[^/]*)*/"); - - Pattern pattern = Pattern.compile(expr); - - ScanUtil.scan(dir, n -> { - // 进行后缀过滤,相对比较快 - if (sufIdx2 > 0) { - return n.endsWith(suf2); - } - else { - return true; - } - }).forEach(uri -> { - // 再进行表达式过滤 - if (pattern.matcher(uri).find()) { - paths.add(uri); - } - }); - return paths; } diff --git a/liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties b/liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties index 35a41afd5..c1fda2e94 100644 --- a/liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties +++ b/liteflow-solon-plugin/src/main/resources/META-INF/liteflow-default.properties @@ -13,8 +13,8 @@ liteflow.retry-count=0 liteflow.support-multiple-type=false liteflow.node-executor-class=com.yomahub.liteflow.flow.executor.DefaultNodeExecutor liteflow.print-execution-log=true -liteflow.substitute-cmp-class= liteflow.monitor.enable-log=false liteflow.monitor.queue-limit=200 liteflow.monitor.delay=300000 liteflow.monitor.period=300000 +liteflow.fallback-cmp-enable=false diff --git a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java index d13fdd402..2ac3b5dbb 100644 --- a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java +++ b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java @@ -74,9 +74,6 @@ public class LiteflowProperty { // 是否打印执行过程中的日志 private boolean printExecutionLog; - // 替补组件的class路径 - private String substituteCmpClass; - // 规则文件/脚本文件变更监听 private Boolean enableMonitorFile; @@ -87,6 +84,9 @@ public class LiteflowProperty { //使用默认并行循环线程池时,最大队列数 private Integer parallelQueueLimit; + + // 是否启用组件降级 + private Boolean fallbackCmpEnable; public Boolean getEnableMonitorFile() { return enableMonitorFile; @@ -226,14 +226,6 @@ public class LiteflowProperty { this.requestIdGeneratorClass = requestIdGeneratorClass; } - public String getSubstituteCmpClass() { - return substituteCmpClass; - } - - public void setSubstituteCmpClass(String substituteCmpClass) { - this.substituteCmpClass = substituteCmpClass; - } - public String getRuleSourceExtData() { return ruleSourceExtData; } @@ -289,4 +281,12 @@ public class LiteflowProperty { public void setParallelQueueLimit(Integer parallelQueueLimit) { this.parallelQueueLimit = parallelQueueLimit; } + + public Boolean isFallbackCmpEnable() { + return fallbackCmpEnable; + } + + public void setFallbackCmpEnable(Boolean fallbackCmpEnable) { + this.fallbackCmpEnable = fallbackCmpEnable; + } } diff --git a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java index 39bf982b0..71c98bb1a 100644 --- a/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java +++ b/liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java @@ -46,11 +46,11 @@ public class LiteflowPropertyAutoConfiguration { liteflowConfig.setMainExecutorWorks(property.getMainExecutorWorks()); liteflowConfig.setMainExecutorClass(property.getMainExecutorClass()); liteflowConfig.setPrintExecutionLog(property.isPrintExecutionLog()); - liteflowConfig.setSubstituteCmpClass(property.getSubstituteCmpClass()); liteflowConfig.setEnableMonitorFile(property.getEnableMonitorFile()); liteflowConfig.setParallelMaxWorkers(property.getParallelMaxWorkers()); liteflowConfig.setParallelQueueLimit(property.getParallelQueueLimit()); liteflowConfig.setParallelLoopExecutorClass(property.getParallelLoopExecutorClass()); + liteflowConfig.setFallbackCmpEnable(property.isFallbackCmpEnable()); return liteflowConfig; } diff --git a/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json index b6297cbee..a2fd62a47 100644 --- a/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -138,13 +138,6 @@ "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", "defaultValue": true }, - { - "name": "liteflow.substitute-cmp-class", - "type": "java.lang.String", - "description": "substitute component class.", - "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", - "defaultValue": "" - }, { "name": "liteflow.monitor.enable-log", "type": "java.lang.Boolean", @@ -200,6 +193,13 @@ "description": "Custom thread pool implement for parallel-loop executor.", "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", "defaultValue": "com.yomahub.liteflow.thread.LiteFlowDefaultParallelLoopExecutorBuilder" + }, + { + "name": "liteflow.fallback-cmp-enable", + "type": "java.lang.Boolean", + "description": "Enable fallback component.", + "sourceType": "com.yomahub.liteflow.springboot.LiteflowProperty", + "defaultValue": false } ] } \ No newline at end of file diff --git a/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java b/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java index a3f6fc0b4..137208541 100644 --- a/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java +++ b/liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java @@ -10,6 +10,7 @@ import cn.hutool.core.util.CharsetUtil; import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.exception.ConfigErrorException; import com.yomahub.liteflow.spi.PathContentParser; +import com.yomahub.liteflow.util.PathMatchUtil; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; @@ -26,7 +27,8 @@ public class SpringPathContentParser implements PathContentParser { @Override public List parseContent(List pathList) throws Exception { - List allResource = getResources(pathList); + List absolutePathList = PathMatchUtil.searchAbsolutePath(pathList); + List allResource = getResources(absolutePathList); // 转换成内容List List contentList = new ArrayList<>(); @@ -42,7 +44,8 @@ public class SpringPathContentParser implements PathContentParser { @Override public List getFileAbsolutePath(List pathList) throws Exception { - List allResource = getResources(pathList); + List absolutePathList = PathMatchUtil.searchAbsolutePath(pathList); + List allResource = getResources(absolutePathList); return StreamUtil.of(allResource) // 过滤非 file 类型 Resource diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclMultiSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclMultiSpringbootTest.java index 293b56d83..fd619cdf2 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclMultiSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclMultiSpringbootTest.java @@ -1,9 +1,16 @@ package com.yomahub.liteflow.test.absoluteConfigPath; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -16,6 +23,7 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; +import java.util.Objects; /** * springboot环境下异步线程超时日志打印测试 @@ -24,21 +32,56 @@ import javax.annotation.Resource; * @since 2.6.4 */ @ExtendWith(SpringExtension.class) -@TestPropertySource(value = "classpath:/absoluteConfigPath/application.properties") @SpringBootTest(classes = AbsoluteConfigPathELDeclMultiSpringbootTest.class) @EnableAutoConfiguration @ComponentScan({ "com.yomahub.liteflow.test.absoluteConfigPath.cmp" }) public class AbsoluteConfigPathELDeclMultiSpringbootTest extends BaseTest { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static String rootDir; @Resource private FlowExecutor flowExecutor; @Test public void testAbsoluteConfig() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathELDeclMultiSpringbootTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java index a012753ac..022dc9aec 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclMultiSpringbootTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -90,4 +91,13 @@ public class RollbackELDeclMultiSpringbootTest extends BaseTest { Assertions.assertEquals("", response.getRollbackStepStr()); } + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java index fb61212f6..1c1e43ad8 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/CmpConfig.java @@ -6,6 +6,7 @@ import com.yomahub.liteflow.annotation.LiteflowMethod; import com.yomahub.liteflow.core.NodeComponent; import com.yomahub.liteflow.enums.LiteFlowMethodEnum; import com.yomahub.liteflow.enums.NodeTypeEnum; +import com.yomahub.liteflow.slot.DefaultContext; import java.util.Iterator; import java.util.List; @@ -20,6 +21,17 @@ public class CmpConfig { @LiteflowMethod(value = LiteFlowMethodEnum.ROLLBACK, nodeId = "a") public void rollbackA(NodeComponent bindCmp) throws Exception { + String testKey = "test"; + + DefaultContext context = bindCmp.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, bindCmp.getTag()); + } + else { + String s = context.getData(testKey); + s += bindCmp.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/application.properties deleted file mode 100644 index 348a63af9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=/usr/local/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index 0d670c770..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml index 1857166a3..887403167 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/rollback/flow.el.xml @@ -31,4 +31,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclSpringbootTest.java index 55c900075..1acd5c5f3 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELDeclSpringbootTest.java @@ -1,9 +1,16 @@ package com.yomahub.liteflow.test.absoluteConfigPath; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -16,6 +23,7 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringRunner; import javax.annotation.Resource; +import java.util.Objects; /** * springboot环境下异步线程超时日志打印测试 @@ -24,21 +32,56 @@ import javax.annotation.Resource; * @since 2.6.4 */ @ExtendWith(SpringExtension.class) -@TestPropertySource(value = "classpath:/absoluteConfigPath/application.properties") @SpringBootTest(classes = AbsoluteConfigPathELDeclSpringbootTest.class) @EnableAutoConfiguration @ComponentScan({ "com.yomahub.liteflow.test.absoluteConfigPath.cmp" }) public class AbsoluteConfigPathELDeclSpringbootTest extends BaseTest { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private static String rootDir; @Resource private FlowExecutor flowExecutor; @Test public void testAbsoluteConfig() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathELDeclSpringbootTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELDeclSpringbootTest.java new file mode 100644 index 000000000..2f9f592ed --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELDeclSpringbootTest.java @@ -0,0 +1,225 @@ +package com.yomahub.liteflow.test.fallback; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; + +import javax.annotation.Resource; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * SpringBoot 降级组件测试 + * + * @author DaleLee + * @since 2.11.1 + */ +@TestPropertySource(value = "classpath:/fallback/application.properties") +@SpringBootTest(classes = FallbackELDeclSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.fallback.cmp"}) +public class FallbackELDeclSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testThen1() { + LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testThen2() { + LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhen1() { + LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String executeStepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr)); + } + + @Test + public void testIf1() { + LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIf2() { + LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor1() { + LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor2() { + LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile1() { + LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile2() { + LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator1() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator2() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak1() { + LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak2() { + LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak3() { + LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch1() { + LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch2() { + LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testAnd1() { + LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testOr1() { + LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testNot1() { + LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testCatch1() { + LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti1() { + LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti2() { + LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti3() { + LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testConcurrent1() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent2() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent3() throws ExecutionException, InterruptedException { + // 执行多条 chain + Future future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object()); + Future future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object()); + Thread.sleep(1000); + LiteflowResponse response1 = future1.get(); + LiteflowResponse response2 = future2.get(); + Assertions.assertTrue(response1.isSuccess()); + String stepStr1 = response1.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1)); + Assertions.assertTrue(response2.isSuccess()); + String stepStr2 = response2.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java new file mode 100644 index 000000000..913035fd4 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; + +@LiteflowComponent("a") +public class ACmp { + + @LiteflowMethod(LiteFlowMethodEnum.PROCESS) + public void process(NodeComponent bindCmp) { + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java new file mode 100644 index 000000000..af597c5b4 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; + +@LiteflowComponent("b") +public class BCmp { + + @LiteflowMethod(LiteFlowMethodEnum.PROCESS) + public void process(NodeComponent bindCmp) { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java new file mode 100644 index 000000000..fcbd8e7eb --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("bn1") +@LiteflowCmpDefine(NodeTypeEnum.BREAK) +@FallbackCmp +public class BreakCmp { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK) + public boolean processBreak(NodeComponent bindCmp) throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java new file mode 100644 index 000000000..e74168e37 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; + +@LiteflowComponent("c") +@FallbackCmp +public class CCmp { + + @LiteflowMethod(LiteFlowMethodEnum.PROCESS) + public void process(NodeComponent bindCmp) { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java new file mode 100644 index 000000000..23107e654 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; + +@LiteflowComponent("d") +public class DCmp { + + @LiteflowMethod(LiteFlowMethodEnum.PROCESS) + public void process(NodeComponent bindCmp) throws Exception { + throw new RuntimeException("component[d]"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java new file mode 100644 index 000000000..99596035d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("for1") +@LiteflowCmpDefine(NodeTypeEnum.FOR) +@FallbackCmp +public class ForCmp { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR) + public int processFor(NodeComponent bindCmp) throws Exception { + return 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java new file mode 100644 index 000000000..bd1c6f51e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("ifn1") +@LiteflowCmpDefine(NodeTypeEnum.IF) +public class IfCmp1 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF) + public boolean processIf(NodeComponent bindCmp) throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java new file mode 100644 index 000000000..5af938e89 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("ifn2") +@LiteflowCmpDefine(NodeTypeEnum.IF) +@FallbackCmp +public class IfCmp2 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF) + public boolean processIf(NodeComponent bindCmp) throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java new file mode 100644 index 000000000..1ee36ed1d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java @@ -0,0 +1,21 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +import java.util.Arrays; +import java.util.Iterator; + +@LiteflowComponent("itn1") +@LiteflowCmpDefine(NodeTypeEnum.ITERATOR) +public class IteratorCmp1 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR) + public Iterator processIterator(NodeComponent bindCmp) throws Exception { + return Arrays.asList("a", "b", "c").iterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java new file mode 100644 index 000000000..eab745bb8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java @@ -0,0 +1,23 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +import java.util.Collections; +import java.util.Iterator; + +@LiteflowComponent("itn2") +@LiteflowCmpDefine(NodeTypeEnum.ITERATOR) +@FallbackCmp +public class IteratorCmp2 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR) + public Iterator processIterator(NodeComponent bindCmp) throws Exception { + return Collections.emptyIterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java new file mode 100644 index 000000000..46948f736 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("swn1") +@LiteflowCmpDefine(NodeTypeEnum.SWITCH) +public class SwitchCmp1 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH) + public String processSwitch(NodeComponent bindCmp) throws Exception { + return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java new file mode 100644 index 000000000..7da7c7100 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("swn2") +@LiteflowCmpDefine(NodeTypeEnum.SWITCH) +@FallbackCmp +public class SwitchCmp2 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH) + public String processSwitch(NodeComponent bindCmp) throws Exception { + return "b"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java new file mode 100644 index 000000000..0d65cfb12 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java @@ -0,0 +1,31 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +import java.util.HashSet; +import java.util.Set; + +@LiteflowComponent("wn1") +@LiteflowCmpDefine(NodeTypeEnum.WHILE) +public class WhileCmp1 { + private int count = 0; + + // 执行过的 chain + Set executedChain = new HashSet<>(); + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE) + public boolean processWhile(NodeComponent bindCmp) throws Exception { + // 判断是否切换了 chain + if (!executedChain.contains(bindCmp.getCurrChainId())) { + count = 0; + executedChain.add(bindCmp.getCurrChainId()); + } + count++; + return count <= 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java new file mode 100644 index 000000000..a9bef49b7 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java @@ -0,0 +1,20 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowCmpDefine; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.annotation.LiteflowMethod; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.enums.LiteFlowMethodEnum; +import com.yomahub.liteflow.enums.NodeTypeEnum; + +@LiteflowComponent("wn2") +@LiteflowCmpDefine(NodeTypeEnum.WHILE) +@FallbackCmp +public class WhileCmp2 { + + @LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE) + public boolean processWhile(NodeComponent bindCmp) throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java index c6dc23465..c89f3c72c 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackELDeclSpringbootTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import com.yomahub.liteflow.test.whenTimeOut.WhenTimeOutELDeclSpringbootTest1; import org.junit.jupiter.api.Assertions; @@ -92,4 +93,13 @@ public class RollbackELDeclSpringbootTest extends BaseTest { Assertions.assertEquals("", response.getRollbackStepStr()); } + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java index cb52a08b3..a29efd0db 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; import org.springframework.stereotype.Component; @Component("a") @@ -20,6 +21,17 @@ public class ACmp extends NodeComponent { @Override public void rollback() throws Exception { + String testKey = "test"; + + DefaultContext context = this.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, this.getTag()); + } + else { + String s = context.getData(testKey); + s += this.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/application.properties deleted file mode 100644 index 348a63af9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=/usr/local/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index 0d670c770..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/application.properties b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/application.properties new file mode 100644 index 000000000..b6a1da886 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/application.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=fallback/flow.el.xml +liteflow.fallback-cmp-enable=true \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/flow.el.xml new file mode 100644 index 000000000..9647a51d6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/fallback/flow.el.xml @@ -0,0 +1,136 @@ + + + + + THEN(a, node("x")); + + + + THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3"))); + + + + + WHEN(b, node("x")); + + + + + IF(node("x"), a) + + + + + IF(ifn1, node("x")) + + + + + FOR(node("x")).DO(a); + + + + + FOR(3).DO(node("x")); + + + + + WHILE(node("x")).DO(a) + + + + + WHILE(wn1).DO(node("x")) + + + + + ITERATOR(node("x")).DO(a) + + + + + ITERATOR(itn1).DO(node("x")) + + + + + FOR(3).DO(a).BREAK(node("x")); + + + + WHILE(wn1).DO(a).BREAK(node("x")); + + + + ITERATOR(itn1).DO(a).BREAK(node("x")); + + + + + SWITCH(node("x")).to(a,b); + + + + + SWITCH(swn1).to(node("x"),a); + + + + + IF(AND(node("x"),ifn1), a); + + + + + IF(OR(node("x"),ifn1), a); + + + + + IF(NOT(node("x")), a); + + + + + CATCH(THEN(a, d)).DO(node("x")) + + + + + THEN( + a, + node("x1"), + IF(node("x2"), b) + ); + + + + IF( + OR(node("x1"), ifn1), + THEN(a, node("x2")) + ); + + + + FOR(node("x1")).DO( + THEN(b, node("x2")) + ); + + + + + WHEN( + THEN(node("x1")), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + + + WHEN( + node("x1"), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml index b0cb7fcd9..2e46ba7af 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/rollback/flow.el.xml @@ -31,4 +31,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathTest.java index 120c3984a..1cd83da4a 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathTest.java @@ -1,14 +1,21 @@ package com.yomahub.liteflow.test.absoluteConfigPath; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutorHolder; import com.yomahub.liteflow.flow.LiteflowResponse; import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.util.Objects; + /** * 非spring环境下异步线程超时日志打印测试 * @@ -17,19 +24,50 @@ import org.junit.jupiter.api.Test; */ public class AbsoluteConfigPathTest extends BaseTest { + private static String rootDir; + private static FlowExecutor flowExecutor; - @BeforeAll - public static void init() { - LiteflowConfig config = new LiteflowConfig(); - config.setRuleSource("/usr/local/flow2.xml"); - flowExecutor = FlowExecutorHolder.loadInstance(config); - } @Test public void testAbsoluteConfig() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(() -> { + LiteflowConfig config = new LiteflowConfig(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor = FlowExecutorHolder.loadInstance(config); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); } + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = new LiteflowConfig(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor = FlowExecutorHolder.loadInstance(config); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigTest.java new file mode 100644 index 000000000..3017205c4 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigTest.java @@ -0,0 +1,31 @@ + +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + + +/** + * 测试多文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +public class FlowInDifferentConfigTest extends BaseTest { + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow-main.el.xml,endlessLoop/flow-sub1.el.xml"); + FlowExecutorHolder.loadInstance(config); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonTest.java new file mode 100644 index 000000000..5f36f22aa --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonTest.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * 测试 json 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +public class FlowJsonTest extends BaseTest { + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.json"); + FlowExecutorHolder.loadInstance(config); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLTest.java new file mode 100644 index 000000000..3091e60a9 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLTest.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * 测试 xml 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +public class FlowXMLTest extends BaseTest { + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.xml"); + FlowExecutorHolder.loadInstance(config); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYMLTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYMLTest.java new file mode 100644 index 000000000..654cf63db --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYMLTest.java @@ -0,0 +1,29 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * 测试 yml 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +public class FlowYMLTest extends BaseTest { + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.yml"); + FlowExecutorHolder.loadInstance(config); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java new file mode 100644 index 000000000..a98b541c2 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("Acomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java new file mode 100644 index 000000000..f3699371e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("Bcomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java new file mode 100644 index 000000000..d9abde6be --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class CCmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Ccomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java new file mode 100644 index 000000000..b5ffafe78 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Dcomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java new file mode 100644 index 000000000..fce4af378 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class ECmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Ecomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/FallbackTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/FallbackTest.java new file mode 100644 index 000000000..3ffb7dbc3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/FallbackTest.java @@ -0,0 +1,225 @@ +package com.yomahub.liteflow.test.fallback; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.core.FlowExecutorHolder; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * 非 Spring 环境下组件降级测试 + * + * @author DaleLee + * @since 2.11.1 + */ +public class FallbackTest extends BaseTest { + private static FlowExecutor flowExecutor; + + @BeforeAll + public static void init() { + LiteflowConfig config = new LiteflowConfig(); + config.setRuleSource("fallback/flow.el.xml"); + config.setFallbackCmpEnable(true); + flowExecutor = FlowExecutorHolder.loadInstance(config); + } + + @Test + public void testThen1() { + LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testThen2() { + LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhen1() { + LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String executeStepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr)); + } + + @Test + public void testIf1() { + LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIf2() { + LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor1() { + LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor2() { + LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile1() { + LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile2() { + LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator1() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator2() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak1() { + LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak2() { + LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak3() { + LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch1() { + LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch2() { + LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testAnd1() { + LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testOr1() { + LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testNot1() { + LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testCatch1() { + LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti1() { + LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti2() { + LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti3() { + LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testConcurrent1() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent2() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent3() throws ExecutionException, InterruptedException { + // 执行多条 chain + Future future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object()); + Future future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object()); + Thread.sleep(1000); + LiteflowResponse response1 = future1.get(); + LiteflowResponse response2 = future2.get(); + Assertions.assertTrue(response1.isSuccess()); + String stepStr1 = response1.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1)); + Assertions.assertTrue(response2.isSuccess()); + String stepStr2 = response2.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java new file mode 100644 index 000000000..ed6d7fe76 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java new file mode 100644 index 000000000..82b029643 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java @@ -0,0 +1,12 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java new file mode 100644 index 000000000..155bdfae6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeBreakComponent; + +@FallbackCmp +public class BreakCmp extends NodeBreakComponent { + + @Override + public boolean processBreak() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java new file mode 100644 index 000000000..8e1915878 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeComponent; + +@FallbackCmp +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java new file mode 100644 index 000000000..94c0eaabb --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java @@ -0,0 +1,11 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeComponent; + +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + throw new RuntimeException("component[d]"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java new file mode 100644 index 000000000..0a69a7638 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeForComponent; + +@FallbackCmp +public class ForCmp extends NodeForComponent { + + @Override + public int processFor() throws Exception { + return 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java new file mode 100644 index 000000000..5437cc8fe --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java @@ -0,0 +1,11 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeIfComponent; + +public class IfCmp1 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java new file mode 100644 index 000000000..67187c399 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeIfComponent; + +@FallbackCmp +public class IfCmp2 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java new file mode 100644 index 000000000..b6a5a7336 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Arrays; +import java.util.Iterator; + +public class IteratorCmp1 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Arrays.asList("a", "b", "c").iterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java new file mode 100644 index 000000000..1a6456da9 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Collections; +import java.util.Iterator; + +@FallbackCmp +public class IteratorCmp2 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Collections.emptyIterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java new file mode 100644 index 000000000..6fa17eacc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java @@ -0,0 +1,11 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeSwitchComponent; + +public class SwitchCmp1 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java new file mode 100644 index 000000000..78b10c31b --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@FallbackCmp +public class SwitchCmp2 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "b"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java new file mode 100644 index 000000000..d525911ba --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java @@ -0,0 +1,24 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.core.NodeWhileComponent; + +import java.util.HashSet; +import java.util.Set; + +public class WhileCmp1 extends NodeWhileComponent { + private int count = 0; + + // 执行过的 chain + Set executedChain = new HashSet<>(); + + @Override + public boolean processWhile() throws Exception { + // 判断是否切换了 chain + if (!executedChain.contains(this.getCurrChainId())) { + count = 0; + executedChain.add(this.getCurrChainId()); + } + count++; + return count <= 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java new file mode 100644 index 000000000..531a77d7e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@FallbackCmp +public class WhileCmp2 extends NodeWhileComponent { + + @Override + public boolean processWhile() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java index 4a3239e46..f8d58918e 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/RollbackTest.java @@ -3,7 +3,9 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.core.FlowExecutorHolder; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.flow.entity.CmpStep; import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; @@ -88,5 +90,13 @@ public class RollbackTest extends BaseTest { Assertions.assertEquals("", response.getRollbackStepStr()); } + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java index 269b3ba9a..f1a43cfb1 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; public class ACmp extends NodeComponent { @@ -18,6 +19,17 @@ public class ACmp extends NodeComponent { @Override public void rollback() throws Exception { + String testKey = "test"; + + DefaultContext context = this.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, this.getTag()); + } + else { + String s = context.getData(testKey); + s += this.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index 0d670c770..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-main.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-main.el.xml new file mode 100644 index 000000000..2100fc65f --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-main.el.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + THEN(a, b, chain2); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-sub1.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-sub1.el.xml new file mode 100644 index 000000000..39b89324e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow-sub1.el.xml @@ -0,0 +1,6 @@ + + + + THEN(b, a, chain1); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.json b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.json new file mode 100644 index 000000000..b63faed4d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.json @@ -0,0 +1,42 @@ +{ + "flow": { + "nodes": { + "node": [ + { + "id": "a", + "class": "com.yomahub.liteflow.test.endlessLoop.cmp.ACmp" + }, + { + "id": "b", + "class": "com.yomahub.liteflow.test.endlessLoop.cmp.BCmp" + }, + { + "id": "c", + "class": "com.yomahub.liteflow.test.endlessLoop.cmp.CCmp" + }, + { + "id": "d", + "class": "com.yomahub.liteflow.test.endlessLoop.cmp.DCmp" + }, + { + "id": "e", + "class": "com.yomahub.liteflow.test.endlessLoop.cmp.ECmp" + } + ] + }, + "chain": [ + { + "name": "chain7", + "value": "THEN(a, chain8);" + }, + { + "name": "chain8", + "value": "THEN(b, chain9);" + }, + { + "name": "chain9", + "value": "WHEN(c, chain7);" + } + ] + } +} \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.xml new file mode 100644 index 000000000..fdd72433d --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + THEN(a, chain2); + + + + THEN(b, chain3); + + + + THEN(c, chain1); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.yml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.yml new file mode 100644 index 000000000..c82163e7c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/endlessLoop/flow.el.yml @@ -0,0 +1,20 @@ +flow: + nodes: + node: + - id: a + class: com.yomahub.liteflow.test.endlessLoop.cmp.ACmp + - id: b + class: com.yomahub.liteflow.test.endlessLoop.cmp.BCmp + - id: c + class: com.yomahub.liteflow.test.endlessLoop.cmp.CCmp + - id: d + class: com.yomahub.liteflow.test.endlessLoop.cmp.DCmp + - id: e + class: com.yomahub.liteflow.test.endlessLoop.cmp.ECmp + chain: + - name: chain4 + value: "THEN(a, chain5);" + - name: chain5 + value: "THEN(b, chain6);" + - name: chain6 + value: "THEN(c, chain5);" \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/fallback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/fallback/flow.el.xml new file mode 100644 index 000000000..24eba9db2 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/fallback/flow.el.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + THEN(a, node("x")); + + + + THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3"))); + + + + + WHEN(b, node("x")); + + + + + IF(node("x"), a) + + + + + IF(ifn1, node("x")) + + + + + FOR(node("x")).DO(a); + + + + + FOR(3).DO(node("x")); + + + + + WHILE(node("x")).DO(a) + + + + + WHILE(wn1).DO(node("x")) + + + + + ITERATOR(node("x")).DO(a) + + + + + ITERATOR(itn1).DO(node("x")) + + + + + FOR(3).DO(a).BREAK(node("x")); + + + + WHILE(wn1).DO(a).BREAK(node("x")); + + + + ITERATOR(itn1).DO(a).BREAK(node("x")); + + + + + SWITCH(node("x")).to(a,b); + + + + + SWITCH(swn1).to(node("x"),a); + + + + + IF(AND(node("x"),ifn1), a); + + + + + IF(OR(node("x"),ifn1), a); + + + + + IF(NOT(node("x")), a); + + + + + CATCH(THEN(a, d)).DO(node("x")) + + + + + THEN( + a, + node("x1"), + IF(node("x2"), b) + ); + + + + IF( + OR(node("x1"), ifn1), + THEN(a, node("x2")) + ); + + + + FOR(node("x1")).DO( + THEN(b, node("x2")) + ); + + + + + WHEN( + THEN(node("x1")), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + + + WHEN( + node("x1"), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml index d67b21e92..7072cf2b9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/rollback/flow.el.xml @@ -44,4 +44,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/pom.xml b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/pom.xml index 2b450a298..3c50140b2 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/pom.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/pom.xml @@ -28,6 +28,11 @@ org.springframework.boot spring-boot-starter-test + + com.alibaba.fastjson2 + fastjson2 + 2.0.39 + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/resources/common/flow.xml b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/resources/common/flow.xml index 0bb72ea6d..a4ca812ad 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/resources/common/flow.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-script-java-springboot/src/test/resources/common/flow.xml @@ -4,21 +4,22 @@ { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathELSpringbootTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSolonTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSolonTest.java new file mode 100644 index 000000000..db1603212 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSolonTest.java @@ -0,0 +1,222 @@ +package com.yomahub.liteflow.test.fallback; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.noear.solon.annotation.Inject; +import org.noear.solon.test.SolonJUnit5Extension; +import org.noear.solon.test.annotation.TestPropertySource; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Solon 降级组件测试 + * + * @author DaleLee + * @since 2.11.1 + */ +@ExtendWith(SolonJUnit5Extension.class) +@TestPropertySource("classpath:/fallback/application.properties") +public class FallbackELSolonTest extends BaseTest { + + @Inject + private FlowExecutor flowExecutor; + + @Test + public void testThen1() { + LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testThen2() { + LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhen1() { + LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String executeStepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr)); + } + + @Test + public void testIf1() { + LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIf2() { + LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor1() { + LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor2() { + LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile1() { + LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile2() { + LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator1() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator2() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak1() { + LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak2() { + LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak3() { + LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch1() { + LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch2() { + LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testAnd1() { + LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testOr1() { + LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testNot1() { + LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testCatch1() { + LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti1() { + LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti2() { + LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti3() { + LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testConcurrent1() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent2() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent3() throws ExecutionException, InterruptedException { + // 执行多条 chain + Future future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object()); + Future future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object()); + Thread.sleep(1000); + LiteflowResponse response1 = future1.get(); + LiteflowResponse response2 = future2.get(); + Assertions.assertTrue(response1.isSuccess()); + String stepStr1 = response1.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1)); + Assertions.assertTrue(response2.isSuccess()); + String stepStr2 = response2.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java new file mode 100644 index 000000000..8a6739027 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java new file mode 100644 index 000000000..632bd78be --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java new file mode 100644 index 000000000..32813ffe3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeBreakComponent; + +@LiteflowComponent("bn1") +@FallbackCmp +public class BreakCmp extends NodeBreakComponent { + + @Override + public boolean processBreak() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java new file mode 100644 index 000000000..54d0799b5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("c") +@FallbackCmp +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java new file mode 100644 index 000000000..5d7fd18ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("d") +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + throw new RuntimeException("component[d]"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java new file mode 100644 index 000000000..2607f8acc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeForComponent; + +@LiteflowComponent("for1") +@FallbackCmp +public class ForCmp extends NodeForComponent { + + @Override + public int processFor() throws Exception { + return 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java new file mode 100644 index 000000000..62b47dec1 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn1") +public class IfCmp1 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java new file mode 100644 index 000000000..c5b20fcd8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn2") +@FallbackCmp +public class IfCmp2 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java new file mode 100644 index 000000000..b63cb7c71 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Arrays; +import java.util.Iterator; + +@LiteflowComponent("itn1") +public class IteratorCmp1 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Arrays.asList("a", "b", "c").iterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java new file mode 100644 index 000000000..c45f19034 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Collections; +import java.util.Iterator; + +@LiteflowComponent("itn2") +@FallbackCmp +public class IteratorCmp2 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Collections.emptyIterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java new file mode 100644 index 000000000..1cb3fe1c3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn1") +public class SwitchCmp1 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java new file mode 100644 index 000000000..28b1edcfd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn2") +@FallbackCmp +public class SwitchCmp2 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "b"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java new file mode 100644 index 000000000..6769480c6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java @@ -0,0 +1,26 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +import java.util.HashSet; +import java.util.Set; + +@LiteflowComponent("wn1") +public class WhileCmp1 extends NodeWhileComponent { + private int count = 0; + + // 执行过的 chain + Set executedChain = new HashSet<>(); + + @Override + public boolean processWhile() throws Exception { + // 判断是否切换了 chain + if (!executedChain.contains(this.getCurrChainId())) { + count = 0; + executedChain.add(this.getCurrChainId()); + } + count++; + return count <= 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java new file mode 100644 index 000000000..b198faa0e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@LiteflowComponent("wn2") +@FallbackCmp +public class WhileCmp2 extends NodeWhileComponent { + + @Override + public boolean processWhile() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java index 9d1e93a2e..07d7486f6 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -84,4 +85,13 @@ public class RollbackSpringbootTest extends BaseTest { Assertions.assertEquals("", response.getRollbackStepStr()); } + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java index bb44d8522..d953f86fa 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; import org.noear.solon.annotation.Component; @@ -21,6 +22,17 @@ public class ACmp extends NodeComponent { @Override public void rollback() throws Exception { + String testKey = "test"; + + DefaultContext context = this.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, this.getTag()); + } + else { + String s = context.getData(testKey); + s += this.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java deleted file mode 100644 index 623662234..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.yomahub.liteflow.test.substituteNode; - -import com.yomahub.liteflow.core.FlowExecutor; -import com.yomahub.liteflow.flow.LiteflowResponse; -import com.yomahub.liteflow.test.BaseTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.noear.solon.annotation.Inject; -import org.noear.solon.test.SolonJUnit5Extension; -import org.noear.solon.test.annotation.TestPropertySource; - -/** - * springboot环境EL替补节点的测试 - * - * @author Bryan.Zhang - */ -@ExtendWith(SolonJUnit5Extension.class) -@TestPropertySource("classpath:/substituteNode/application.properties") -public class SubstituteSpringbootTest extends BaseTest { - - @Inject - private FlowExecutor flowExecutor; - - // 最简单的情况 - @Test - public void testSub1() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - - // 有替补节点 - @Test - public void testSub2() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - - // 测试特殊命名的节点 - @Test - public void testSub3() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java deleted file mode 100644 index a314cdd38..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.noear.solon.annotation.Component; - -@Component("a") -public class ACmp extends NodeComponent { - - @Override - public void process() { - System.out.println("ACmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java deleted file mode 100644 index 7bfec97e7..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.noear.solon.annotation.Component; - -@Component("b") -public class BCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("BCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java deleted file mode 100644 index 040bbcbca..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.noear.solon.annotation.Component; - -@Component("c") -public class CCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("CCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java deleted file mode 100644 index 14aa20e9c..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.noear.solon.annotation.Component; - -@Component("88-ffc") -public class DCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("DCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java deleted file mode 100644 index 776a5b4cf..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.noear.solon.annotation.Component; - -@Component("sub") -public class SubCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("SubCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/application.properties deleted file mode 100644 index 348a63af9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=/usr/local/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index 0d670c770..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/application.properties new file mode 100644 index 000000000..b6a1da886 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/application.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=fallback/flow.el.xml +liteflow.fallback-cmp-enable=true \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/flow.el.xml new file mode 100644 index 000000000..9647a51d6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/fallback/flow.el.xml @@ -0,0 +1,136 @@ + + + + + THEN(a, node("x")); + + + + THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3"))); + + + + + WHEN(b, node("x")); + + + + + IF(node("x"), a) + + + + + IF(ifn1, node("x")) + + + + + FOR(node("x")).DO(a); + + + + + FOR(3).DO(node("x")); + + + + + WHILE(node("x")).DO(a) + + + + + WHILE(wn1).DO(node("x")) + + + + + ITERATOR(node("x")).DO(a) + + + + + ITERATOR(itn1).DO(node("x")) + + + + + FOR(3).DO(a).BREAK(node("x")); + + + + WHILE(wn1).DO(a).BREAK(node("x")); + + + + ITERATOR(itn1).DO(a).BREAK(node("x")); + + + + + SWITCH(node("x")).to(a,b); + + + + + SWITCH(swn1).to(node("x"),a); + + + + + IF(AND(node("x"),ifn1), a); + + + + + IF(OR(node("x"),ifn1), a); + + + + + IF(NOT(node("x")), a); + + + + + CATCH(THEN(a, d)).DO(node("x")) + + + + + THEN( + a, + node("x1"), + IF(node("x2"), b) + ); + + + + IF( + OR(node("x1"), ifn1), + THEN(a, node("x2")) + ); + + + + FOR(node("x1")).DO( + THEN(b, node("x2")) + ); + + + + + WHEN( + THEN(node("x1")), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + + + WHEN( + node("x1"), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml index b0cb7fcd9..2e46ba7af 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/rollback/flow.el.xml @@ -31,4 +31,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/application.properties b/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/application.properties deleted file mode 100644 index 35bdc3a1e..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -liteflow.rule-source=substituteNode/flow.el.xml -liteflow.substitute-cmp-class=com.yomahub.liteflow.test.substituteNode.cmp.SubCmp \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringbootTest.java index ca844f9cc..f036c48a5 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringbootTest.java @@ -1,9 +1,16 @@ package com.yomahub.liteflow.test.absoluteConfigPath; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +20,7 @@ import org.springframework.context.annotation.ComponentScan; import org.springframework.test.context.TestPropertySource; import javax.annotation.Resource; +import java.util.Objects; /** * springboot环境下异步线程超时日志打印测试 @@ -20,21 +28,57 @@ import javax.annotation.Resource; * @author Bryan.Zhang * @since 2.6.4 */ -@TestPropertySource(value = "classpath:/absoluteConfigPath/application.properties") @SpringBootTest(classes = AbsoluteConfigPathELSpringbootTest.class) @EnableAutoConfiguration @ComponentScan({ "com.yomahub.liteflow.test.absoluteConfigPath.cmp" }) public class AbsoluteConfigPathELSpringbootTest extends BaseTest { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private static String rootDir; @Resource private FlowExecutor flowExecutor; @Test public void testAbsoluteConfig() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain2", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathELSpringbootTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java index 30703e603..541a2fe38 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/AsyncNodeELSpringbootTest.java @@ -139,4 +139,49 @@ public class AsyncNodeELSpringbootTest extends BaseTest { Assertions.assertTrue(context.getData("check").toString().startsWith("habc")); } + // 测试 must 关键字 + @Test + public void testAsyncFlow9() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "it's a base request"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(context.getData("check").toString().startsWith("habc")); + } + + // 测试 must 与 ignoreError 关键字,不忽略异常 + @Test + public void testAsyncFlow10() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain10", "it's a base request"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(context.getData("check").toString().startsWith("kg")); + Assertions.assertFalse(response.isSuccess()); + } + + // 测试 must 与 ignoreError 关键字,忽略异常 + @Test + public void testAsyncFlow11() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain11", "it's a base request"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(context.getData("check").toString().startsWith("kgdabc")); + } + + // 测试 must 、 ignoreError 、 id 关键字 + @Test + public void testAsyncFlow12() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain12", "it's a base request"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(context.getData("check").toString().startsWith("akbc")); + } + + // 测试 must 指定多个任务, ignoreError 以及 id 关键字 + @Test + public void testAsyncFlow13() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain13", "it's a base request"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertTrue(context.getData("check").toString().startsWith("akbgc")); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/KCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/KCmp.java new file mode 100644 index 000000000..f4b27d714 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/KCmp.java @@ -0,0 +1,27 @@ +package com.yomahub.liteflow.test.asyncNode.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; +import org.springframework.stereotype.Component; + +@Component("k") +public class KCmp extends NodeComponent { + + @Override + public void process() throws Exception { + Thread.sleep(200); + DefaultContext context = this.getFirstContextBean(); + synchronized (NodeComponent.class) { + if (context.hasData("check")) { + String str = context.getData("check"); + str += this.getNodeId(); + context.setData("check", str); + } + else { + context.setData("check", this.getNodeId()); + } + } + System.out.println("Kcomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/LCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/LCmp.java new file mode 100644 index 000000000..87e08565c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/asyncNode/cmp/LCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.asyncNode.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("l") +public class LCmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Lcomp executed! Throw exception"); + int i = 1/0; + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigELSpringbootTest.java new file mode 100644 index 000000000..90efa473c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowInDifferentConfigELSpringbootTest.java @@ -0,0 +1,41 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; + +/** + * 测试多文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +@SpringBootTest(classes = FlowInDifferentConfigELSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.endlessLoop.cmp"}) +public class FlowInDifferentConfigELSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow-sub1.el.xml,endlessLoop/flow-sub2.el.yml"); + config.setSupportMultipleType(true); + flowExecutor.reloadRule(); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonELSpringBootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonELSpringBootTest.java new file mode 100644 index 000000000..8bcb71cbf --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowJsonELSpringBootTest.java @@ -0,0 +1,40 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; + +/** + * 测试 json 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +@SpringBootTest(classes = FlowJsonELSpringBootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.endlessLoop.cmp" }) +public class FlowJsonELSpringBootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.json"); + flowExecutor.reloadRule(); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLELSpringBootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLELSpringBootTest.java new file mode 100644 index 000000000..585cb02eb --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowXMLELSpringBootTest.java @@ -0,0 +1,40 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; + +/** + * 测试 xml 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +@SpringBootTest(classes = FlowXMLELSpringBootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.endlessLoop.cmp" }) +public class FlowXMLELSpringBootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.xml"); + flowExecutor.reloadRule(); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYmlELSpringBootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYmlELSpringBootTest.java new file mode 100644 index 000000000..4df3d56db --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/FlowYmlELSpringBootTest.java @@ -0,0 +1,40 @@ +package com.yomahub.liteflow.test.endlessLoop; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.CyclicDependencyException; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; + +import javax.annotation.Resource; + +/** + * 测试 yml 文件情况下 chain 死循环逻辑 + * + * @author luo yi + * @since 2.11.1 + */ +@SpringBootTest(classes = FlowYmlELSpringBootTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.endlessLoop.cmp" }) +public class FlowYmlELSpringBootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + // 测试 chain 死循环 + @Test + public void testChainEndlessLoop() { + Assertions.assertThrows(CyclicDependencyException.class, () -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource("endlessLoop/flow.el.yml"); + flowExecutor.reloadRule(); + }); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java new file mode 100644 index 000000000..17c75c038 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ACmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("Acomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java new file mode 100644 index 000000000..cdaa88716 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/BCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("Bcomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java new file mode 100644 index 000000000..7d9ec1ab6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/CCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("c") +public class CCmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Ccomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java new file mode 100644 index 000000000..bf2f50f39 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/DCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("d") +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Dcomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java new file mode 100644 index 000000000..5a010ea22 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/endlessLoop/cmp/ECmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.endlessLoop.cmp; + +import com.yomahub.liteflow.core.NodeComponent; +import org.springframework.stereotype.Component; + +@Component("e") +public class ECmp extends NodeComponent { + + @Override + public void process() throws Exception { + System.out.println("Ecomp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringbootTest.java new file mode 100644 index 000000000..dfa7f1ab1 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringbootTest.java @@ -0,0 +1,225 @@ +package com.yomahub.liteflow.test.fallback; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; + +import javax.annotation.Resource; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * SpringBoot 降级组件测试 + * + * @author DaleLee + * @since 2.11.1 + */ +@TestPropertySource(value = "classpath:/fallback/application.properties") +@SpringBootTest(classes = FallbackELSpringbootTest.class) +@EnableAutoConfiguration +@ComponentScan({"com.yomahub.liteflow.test.fallback.cmp"}) +public class FallbackELSpringbootTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testThen1() { + LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testThen2() { + LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhen1() { + LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String executeStepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr)); + } + + @Test + public void testIf1() { + LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIf2() { + LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor1() { + LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor2() { + LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile1() { + LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile2() { + LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator1() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator2() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak1() { + LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak2() { + LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak3() { + LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch1() { + LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch2() { + LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testAnd1() { + LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testOr1() { + LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testNot1() { + LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testCatch1() { + LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti1() { + LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti2() { + LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti3() { + LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testConcurrent1() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent2() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent3() throws ExecutionException, InterruptedException { + // 执行多条 chain + Future future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object()); + Future future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object()); + Thread.sleep(1000); + LiteflowResponse response1 = future1.get(); + LiteflowResponse response2 = future2.get(); + Assertions.assertTrue(response1.isSuccess()); + String stepStr1 = response1.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1)); + Assertions.assertTrue(response2.isSuccess()); + String stepStr2 = response2.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java new file mode 100644 index 000000000..8a6739027 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java new file mode 100644 index 000000000..6216dd712 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java @@ -0,0 +1,17 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.test.customNodes.domain.DemoDomain; + +import javax.annotation.Resource; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java new file mode 100644 index 000000000..32813ffe3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeBreakComponent; + +@LiteflowComponent("bn1") +@FallbackCmp +public class BreakCmp extends NodeBreakComponent { + + @Override + public boolean processBreak() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java new file mode 100644 index 000000000..a18c23d48 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; +import org.springframework.stereotype.Component; + +@LiteflowComponent("c") +@FallbackCmp +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java new file mode 100644 index 000000000..5d7fd18ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("d") +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + throw new RuntimeException("component[d]"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java new file mode 100644 index 000000000..2607f8acc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeForComponent; + +@LiteflowComponent("for1") +@FallbackCmp +public class ForCmp extends NodeForComponent { + + @Override + public int processFor() throws Exception { + return 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java new file mode 100644 index 000000000..62b47dec1 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn1") +public class IfCmp1 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java new file mode 100644 index 000000000..c5b20fcd8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn2") +@FallbackCmp +public class IfCmp2 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java new file mode 100644 index 000000000..b63cb7c71 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Arrays; +import java.util.Iterator; + +@LiteflowComponent("itn1") +public class IteratorCmp1 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Arrays.asList("a", "b", "c").iterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java new file mode 100644 index 000000000..c45f19034 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Collections; +import java.util.Iterator; + +@LiteflowComponent("itn2") +@FallbackCmp +public class IteratorCmp2 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Collections.emptyIterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java new file mode 100644 index 000000000..1cb3fe1c3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn1") +public class SwitchCmp1 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java new file mode 100644 index 000000000..28b1edcfd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn2") +@FallbackCmp +public class SwitchCmp2 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "b"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java new file mode 100644 index 000000000..6769480c6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java @@ -0,0 +1,26 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +import java.util.HashSet; +import java.util.Set; + +@LiteflowComponent("wn1") +public class WhileCmp1 extends NodeWhileComponent { + private int count = 0; + + // 执行过的 chain + Set executedChain = new HashSet<>(); + + @Override + public boolean processWhile() throws Exception { + // 判断是否切换了 chain + if (!executedChain.contains(this.getCurrChainId())) { + count = 0; + executedChain.add(this.getCurrChainId()); + } + count++; + return count <= 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java new file mode 100644 index 000000000..b198faa0e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@LiteflowComponent("wn2") +@FallbackCmp +public class WhileCmp2 extends NodeWhileComponent { + + @Override + public boolean processWhile() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java index ecc1f9e91..51dbd9e53 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java @@ -38,6 +38,22 @@ public class MonitorFileELSpringbootTest extends BaseTest { Assertions.assertEquals("a==>c==>b", response.getExecuteStepStr()); } + /** + * 对绝对路径模糊匹配功能的测试 + */ + @Test + public void testMonitorAbsolutePath() throws Exception { + String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath(); + FileUtil.writeString("THEN(a, b, c);", new File(absolutePath), CharsetUtil.CHARSET_UTF_8); + String content = FileUtil.readUtf8String(absolutePath); + String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);"); + Thread.sleep(1000); + FileUtil.writeString(newContent, new File(absolutePath), CharsetUtil.CHARSET_UTF_8); + Thread.sleep(3000); + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertEquals("a==>c==>b", response.getExecuteStepStr()); + } + /** * 测试文件变更,但是 EL 规则错误情况 * 输出 ERROR 日志异常信息,但是不会停止监听线程,当下一次变更正确后替换为新规则 diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java index 7984bb192..8e230f7fa 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringbootTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -87,4 +88,13 @@ public class RollbackSpringbootTest extends BaseTest { Assertions.assertNull(response.getCause()); Assertions.assertEquals("", response.getRollbackStepStr()); } + + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java index cb52a08b3..a29efd0db 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; import org.springframework.stereotype.Component; @Component("a") @@ -20,6 +21,17 @@ public class ACmp extends NodeComponent { @Override public void rollback() throws Exception { + String testKey = "test"; + + DefaultContext context = this.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, this.getTag()); + } + else { + String s = context.getData(testKey); + s += this.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java deleted file mode 100644 index 9323de377..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/SubstituteSpringbootTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.yomahub.liteflow.test.substituteNode; - -import com.yomahub.liteflow.core.FlowExecutor; -import com.yomahub.liteflow.flow.LiteflowResponse; -import com.yomahub.liteflow.test.BaseTest; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.test.context.TestPropertySource; - -import javax.annotation.Resource; - -/** - * springboot环境EL替补节点的测试 - * - * @author Bryan.Zhang - */ -@TestPropertySource(value = "classpath:/substituteNode/application.properties") -@SpringBootTest(classes = SubstituteSpringbootTest.class) -@EnableAutoConfiguration -@ComponentScan({ "com.yomahub.liteflow.test.substituteNode.cmp" }) -public class SubstituteSpringbootTest extends BaseTest { - - @Resource - private FlowExecutor flowExecutor; - - // 最简单的情况 - @Test - public void testSub1() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - - // 有替补节点 - @Test - public void testSub2() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - - // 测试特殊命名的节点 - @Test - public void testSub3() throws Exception { - LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg"); - Assertions.assertTrue(response.isSuccess()); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java deleted file mode 100644 index b6e1d7d80..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/ACmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.springframework.stereotype.Component; - -@Component("a") -public class ACmp extends NodeComponent { - - @Override - public void process() { - System.out.println("ACmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java deleted file mode 100644 index 495db7ac3..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/BCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.springframework.stereotype.Component; - -@Component("b") -public class BCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("BCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java deleted file mode 100644 index dca6da62a..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/CCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.springframework.stereotype.Component; - -@Component("c") -public class CCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("CCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java deleted file mode 100644 index 728b48ff9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/DCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.springframework.stereotype.Component; - -@Component("88-ffc") -public class DCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("DCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java deleted file mode 100644 index 620490341..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/substituteNode/cmp/SubCmp.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - *

Title: liteflow

- *

Description: 轻量级的组件式流程框架

- * @author Bryan.Zhang - * @email weenyc31@163.com - * @Date 2020/4/1 - */ -package com.yomahub.liteflow.test.substituteNode.cmp; - -import com.yomahub.liteflow.core.NodeComponent; -import org.springframework.stereotype.Component; - -@Component("sub") -public class SubCmp extends NodeComponent { - - @Override - public void process() { - System.out.println("SubCmp executed!"); - } - -} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/application.properties deleted file mode 100644 index 348a63af9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/application.properties +++ /dev/null @@ -1 +0,0 @@ -liteflow.rule-source=/usr/local/flow.el.xml \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index 0d670c770..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/asyncNode/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/asyncNode/flow.el.xml index 06373bb2f..b02c48cf8 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/asyncNode/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/asyncNode/flow.el.xml @@ -46,4 +46,24 @@ THEN(WHEN(d, g, h).any(true), THEN(a, b, c)); + + THEN(WHEN(d, g, h).must("h"), THEN(a, b, c)); + + + + THEN(WHEN(d, g, k, l).must("g").ignoreError(false), THEN(a, b, c)); + + + + THEN(WHEN(d, g, k, l).ignoreError(true).must("d"), THEN(a, b, c)); + + + + THEN(WHEN(d, g, l, a, THEN(k, b).id("z")).ignoreError(true).must("z"), c); + + + + THEN(WHEN(d, g, l, a, THEN(k, b).id("z")).ignoreError(true).must("z", g, "task1", "task2"), c); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub1.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub1.el.xml new file mode 100644 index 000000000..3f1a585ab --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub1.el.xml @@ -0,0 +1,6 @@ + + + + THEN(a, b); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub2.el.yml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub2.el.yml new file mode 100644 index 000000000..6861be3cc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow-sub2.el.yml @@ -0,0 +1,6 @@ +flow: + chain: + - name: chain2 + value: "THEN(c, d, chain3);" + - name: chain3 + value: "THEN(a, chain2);" \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.json b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.json new file mode 100644 index 000000000..1a54d6c6c --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.json @@ -0,0 +1,18 @@ +{ + "flow": { + "chain": [ + { + "name": "chain7", + "value": "THEN(a, chain8);" + }, + { + "name": "chain8", + "value": "THEN(b, chain9);" + }, + { + "name": "chain9", + "value": "WHEN(c, chain7);" + } + ] + } +} \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.xml similarity index 55% rename from liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/flow.el.xml rename to liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.xml index 7730921b9..d615bf81d 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/substituteNode/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.xml @@ -1,14 +1,16 @@ + - THEN(node("a"), node("b"), node("c")); + THEN(a, chain2); - THEN(node("a"), node("b"), node("93-nodeTEST")); + THEN(b, chain3); - THEN(a, b, node("88-ffc")); + THEN(c, chain1); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.yml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.yml new file mode 100644 index 000000000..1699d501b --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/endlessLoop/flow.el.yml @@ -0,0 +1,8 @@ +flow: + chain: + - name: chain4 + value: "THEN(a, chain5);" + - name: chain5 + value: "THEN(b, chain6);" + - name: chain6 + value: "THEN(c, chain5);" \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/application.properties new file mode 100644 index 000000000..b6a1da886 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/application.properties @@ -0,0 +1,2 @@ +liteflow.rule-source=fallback/flow.el.xml +liteflow.fallback-cmp-enable=true \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/flow.el.xml new file mode 100644 index 000000000..9647a51d6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/fallback/flow.el.xml @@ -0,0 +1,136 @@ + + + + + THEN(a, node("x")); + + + + THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3"))); + + + + + WHEN(b, node("x")); + + + + + IF(node("x"), a) + + + + + IF(ifn1, node("x")) + + + + + FOR(node("x")).DO(a); + + + + + FOR(3).DO(node("x")); + + + + + WHILE(node("x")).DO(a) + + + + + WHILE(wn1).DO(node("x")) + + + + + ITERATOR(node("x")).DO(a) + + + + + ITERATOR(itn1).DO(node("x")) + + + + + FOR(3).DO(a).BREAK(node("x")); + + + + WHILE(wn1).DO(a).BREAK(node("x")); + + + + ITERATOR(itn1).DO(a).BREAK(node("x")); + + + + + SWITCH(node("x")).to(a,b); + + + + + SWITCH(swn1).to(node("x"),a); + + + + + IF(AND(node("x"),ifn1), a); + + + + + IF(OR(node("x"),ifn1), a); + + + + + IF(NOT(node("x")), a); + + + + + CATCH(THEN(a, d)).DO(node("x")) + + + + + THEN( + a, + node("x1"), + IF(node("x2"), b) + ); + + + + IF( + OR(node("x1"), ifn1), + THEN(a, node("x2")) + ); + + + + FOR(node("x1")).DO( + THEN(b, node("x2")) + ); + + + + + WHEN( + THEN(node("x1")), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + + + WHEN( + node("x1"), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties index 361f7e90e..a292d3445 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties @@ -1,2 +1,3 @@ liteflow.rule-source=monitorFile/flow.el.xml +#liteflow.rule-source=/usr/**/*.xml liteflow.enable-monitor-file=true \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml index b0cb7fcd9..2e46ba7af 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/rollback/flow.el.xml @@ -31,4 +31,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/application.properties b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/application.properties deleted file mode 100644 index 35bdc3a1e..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/application.properties +++ /dev/null @@ -1,2 +0,0 @@ -liteflow.rule-source=substituteNode/flow.el.xml -liteflow.substitute-cmp-class=com.yomahub.liteflow.test.substituteNode.cmp.SubCmp \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/flow.el.xml deleted file mode 100644 index 7730921b9..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/substituteNode/flow.el.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - THEN(node("a"), node("b"), node("c")); - - - - THEN(node("a"), node("b"), node("93-nodeTEST")); - - - - THEN(a, b, node("88-ffc")); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringTest.java index 5a662976c..50aa12094 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/absoluteConfigPath/AbsoluteConfigPathELSpringTest.java @@ -1,15 +1,23 @@ package com.yomahub.liteflow.test.absoluteConfigPath; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.StrUtil; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import javax.annotation.Resource; +import java.util.Objects; /** * spring环境下,规则配置文件通过绝对路径获取 @@ -21,13 +29,51 @@ import javax.annotation.Resource; @ContextConfiguration("classpath:/absoluteConfigPath/application.xml") public class AbsoluteConfigPathELSpringTest extends BaseTest { + private static String rootDir; + @Resource private FlowExecutor flowExecutor; @Test - public void testAbsoluteConfig() { - LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); - Assertions.assertTrue(response.isSuccess()); + public void testAbsoluteConfig() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/a/flow1.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @Test + public void testAbsolutePathMatch() throws Exception { + Assertions.assertTrue(() -> { + LiteflowConfig config = LiteflowConfigGetter.get(); + config.setRuleSource(StrUtil.format("{}/sub/**/*.xml",rootDir)); + flowExecutor.reloadRule(); + return flowExecutor.execute2Resp("chain1", "arg").isSuccess(); + }); + } + + @BeforeAll + public static void createFiles() { + rootDir = Objects.requireNonNull(AbsoluteConfigPathELSpringTest.class.getResource("/")).getPath(); + + String path1 = StrUtil.format("{}/sub/a", rootDir); + String path2 = StrUtil.format("{}/sub/b", rootDir); + + FileUtil.mkdir(path1); + FileUtil.mkdir(path2); + + String content1 = "WHEN(a, b, c);"; + String content2 = "THEN(c, chain1);"; + + FileUtil.writeString(content1, path1 + "/flow1.xml", CharsetUtil.CHARSET_UTF_8); + FileUtil.writeString(content2, path2 + "/flow2.xml", CharsetUtil.CHARSET_UTF_8); + } + + @AfterAll + public static void removeFiles() { + FileUtil.del(StrUtil.format("{}/sub", rootDir)); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringTest.java new file mode 100644 index 000000000..a6552f9fd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/FallbackELSpringTest.java @@ -0,0 +1,221 @@ +package com.yomahub.liteflow.test.fallback; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.test.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.annotation.Resource; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +/** + * Spring 降级组件测试 + * + * @author DaleLee + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration("classpath:/fallback/application.xml") +public class FallbackELSpringTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + @Test + public void testThen1() { + LiteflowResponse response = flowExecutor.execute2Resp("then1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testThen2() { + LiteflowResponse response = flowExecutor.execute2Resp("then2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhen1() { + LiteflowResponse response = flowExecutor.execute2Resp("when1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String executeStepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("b==>c".equals(executeStepStr) || "c==>b".equals(executeStepStr)); + } + + @Test + public void testIf1() { + LiteflowResponse response = flowExecutor.execute2Resp("if1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIf2() { + LiteflowResponse response = flowExecutor.execute2Resp("if2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn1==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor1() { + LiteflowResponse response = flowExecutor.execute2Resp("for1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>a==>a==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testFor2() { + LiteflowResponse response = flowExecutor.execute2Resp("for2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile1() { + LiteflowResponse response = flowExecutor.execute2Resp("while1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testWhile2() { + LiteflowResponse response = flowExecutor.execute2Resp("while2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>c==>wn1==>c==>wn1==>c==>wn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator1() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testIterator2() { + LiteflowResponse response = flowExecutor.execute2Resp("iterator2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>c==>c==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak1() { + LiteflowResponse response = flowExecutor.execute2Resp("break1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("LOOP_3==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak2() { + LiteflowResponse response = flowExecutor.execute2Resp("break2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("wn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testBreak3() { + LiteflowResponse response = flowExecutor.execute2Resp("break3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("itn1==>a==>bn1", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch1() { + LiteflowResponse response = flowExecutor.execute2Resp("switch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn2==>b", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testSwitch2() { + LiteflowResponse response = flowExecutor.execute2Resp("switch2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("swn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testAnd1() { + LiteflowResponse response = flowExecutor.execute2Resp("and1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testOr1() { + LiteflowResponse response = flowExecutor.execute2Resp("or1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testNot1() { + LiteflowResponse response = flowExecutor.execute2Resp("not1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>a", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testCatch1() { + LiteflowResponse response = flowExecutor.execute2Resp("catch1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>d==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti1() { + LiteflowResponse response = flowExecutor.execute2Resp("multi1", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("a==>c==>ifn2", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti2() { + LiteflowResponse response = flowExecutor.execute2Resp("multi2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("ifn2==>ifn1==>a==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testMulti3() { + LiteflowResponse response = flowExecutor.execute2Resp("multi3", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("for1==>b==>c==>b==>c==>b==>c", response.getExecuteStepStrWithoutTime()); + } + + @Test + public void testConcurrent1() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent1", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent2() { + LiteflowResponse response = flowExecutor.execute2Resp("concurrent2", "arg"); + Assertions.assertTrue(response.isSuccess()); + String stepStr = response.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr) || "ifn2==>c".equals(stepStr)); + } + + @Test + public void testConcurrent3() throws ExecutionException, InterruptedException { + // 执行多条 chain + Future future1 = flowExecutor.execute2Future("concurrent1", "arg", new Object()); + Future future2 = flowExecutor.execute2Future("concurrent2", "arg", new Object()); + Thread.sleep(1000); + LiteflowResponse response1 = future1.get(); + LiteflowResponse response2 = future2.get(); + Assertions.assertTrue(response1.isSuccess()); + String stepStr1 = response1.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr1) || "ifn2==>c".equals(stepStr1)); + Assertions.assertTrue(response2.isSuccess()); + String stepStr2 = response2.getExecuteStepStrWithoutTime(); + Assertions.assertTrue("c==>ifn2".equals(stepStr2) || "ifn2==>c".equals(stepStr2)); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java new file mode 100644 index 000000000..8a6739027 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ACmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("a") +public class ACmp extends NodeComponent { + + @Override + public void process() { + System.out.println("ACmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java new file mode 100644 index 000000000..632bd78be --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BCmp.java @@ -0,0 +1,14 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("b") +public class BCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("BCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java new file mode 100644 index 000000000..32813ffe3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/BreakCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeBreakComponent; + +@LiteflowComponent("bn1") +@FallbackCmp +public class BreakCmp extends NodeBreakComponent { + + @Override + public boolean processBreak() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java new file mode 100644 index 000000000..54d0799b5 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/CCmp.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("c") +@FallbackCmp +public class CCmp extends NodeComponent { + + @Override + public void process() { + System.out.println("CCmp executed!"); + } + +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java new file mode 100644 index 000000000..5d7fd18ee --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/DCmp.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; + +@LiteflowComponent("d") +public class DCmp extends NodeComponent { + + @Override + public void process() throws Exception { + throw new RuntimeException("component[d]"); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java new file mode 100644 index 000000000..2607f8acc --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/ForCmp.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeForComponent; + +@LiteflowComponent("for1") +@FallbackCmp +public class ForCmp extends NodeForComponent { + + @Override + public int processFor() throws Exception { + return 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java new file mode 100644 index 000000000..62b47dec1 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn1") +public class IfCmp1 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return true; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java new file mode 100644 index 000000000..c5b20fcd8 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IfCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIfComponent; + +@LiteflowComponent("ifn2") +@FallbackCmp +public class IfCmp2 extends NodeIfComponent { + + @Override + public boolean processIf() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java new file mode 100644 index 000000000..b63cb7c71 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp1.java @@ -0,0 +1,16 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Arrays; +import java.util.Iterator; + +@LiteflowComponent("itn1") +public class IteratorCmp1 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Arrays.asList("a", "b", "c").iterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java new file mode 100644 index 000000000..c45f19034 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/IteratorCmp2.java @@ -0,0 +1,18 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeIteratorComponent; + +import java.util.Collections; +import java.util.Iterator; + +@LiteflowComponent("itn2") +@FallbackCmp +public class IteratorCmp2 extends NodeIteratorComponent { + + @Override + public Iterator processIterator() throws Exception { + return Collections.emptyIterator(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java new file mode 100644 index 000000000..1cb3fe1c3 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp1.java @@ -0,0 +1,13 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn1") +public class SwitchCmp1 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "a"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java new file mode 100644 index 000000000..28b1edcfd --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/SwitchCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; + +@LiteflowComponent("swn2") +@FallbackCmp +public class SwitchCmp2 extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + return "b"; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java new file mode 100644 index 000000000..6769480c6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp1.java @@ -0,0 +1,26 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +import java.util.HashSet; +import java.util.Set; + +@LiteflowComponent("wn1") +public class WhileCmp1 extends NodeWhileComponent { + private int count = 0; + + // 执行过的 chain + Set executedChain = new HashSet<>(); + + @Override + public boolean processWhile() throws Exception { + // 判断是否切换了 chain + if (!executedChain.contains(this.getCurrChainId())) { + count = 0; + executedChain.add(this.getCurrChainId()); + } + count++; + return count <= 3; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java new file mode 100644 index 000000000..b198faa0e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/fallback/cmp/WhileCmp2.java @@ -0,0 +1,15 @@ +package com.yomahub.liteflow.test.fallback.cmp; + +import com.yomahub.liteflow.annotation.FallbackCmp; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeWhileComponent; + +@LiteflowComponent("wn2") +@FallbackCmp +public class WhileCmp2 extends NodeWhileComponent { + + @Override + public boolean processWhile() throws Exception { + return false; + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java index 28429f0dd..e4b08ccc9 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/RollbackSpringTest.java @@ -2,6 +2,7 @@ package com.yomahub.liteflow.test.rollback; import com.yomahub.liteflow.core.FlowExecutor; import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.slot.DefaultContext; import com.yomahub.liteflow.test.BaseTest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -85,5 +86,14 @@ public class RollbackSpringTest extends BaseTest { Assertions.assertEquals("", response.getRollbackStepStr()); } + @Test + // 对获取数据的测试 + public void testData() throws Exception { + LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertFalse(response.isSuccess()); + Assertions.assertEquals("321", context.getData("test")); + } + } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java index cb52a08b3..a29efd0db 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/java/com/yomahub/liteflow/test/rollback/cmp/ACmp.java @@ -8,6 +8,7 @@ package com.yomahub.liteflow.test.rollback.cmp; import com.yomahub.liteflow.core.NodeComponent; +import com.yomahub.liteflow.slot.DefaultContext; import org.springframework.stereotype.Component; @Component("a") @@ -20,6 +21,17 @@ public class ACmp extends NodeComponent { @Override public void rollback() throws Exception { + String testKey = "test"; + + DefaultContext context = this.getFirstContextBean(); + if (context.getData(testKey) == null) { + context.setData(testKey, this.getTag()); + } + else { + String s = context.getData(testKey); + s += this.getTag(); + context.setData(testKey, s); + } System.out.println("ACmp rollback!"); } } diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/application.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/application.xml index 3a5cc59d9..014574a06 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/application.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/application.xml @@ -14,7 +14,6 @@ - diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/flow.el.xml deleted file mode 100644 index fd7d34c72..000000000 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/absoluteConfigPath/flow.el.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - WHEN(a,b,c); - - \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/application.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/application.xml new file mode 100644 index 000000000..950c3106e --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/application.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/flow.el.xml new file mode 100644 index 000000000..9647a51d6 --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/fallback/flow.el.xml @@ -0,0 +1,136 @@ + + + + + THEN(a, node("x")); + + + + THEN(PRE(node("x1")), node("x2"), FINALLY(node("x3"))); + + + + + WHEN(b, node("x")); + + + + + IF(node("x"), a) + + + + + IF(ifn1, node("x")) + + + + + FOR(node("x")).DO(a); + + + + + FOR(3).DO(node("x")); + + + + + WHILE(node("x")).DO(a) + + + + + WHILE(wn1).DO(node("x")) + + + + + ITERATOR(node("x")).DO(a) + + + + + ITERATOR(itn1).DO(node("x")) + + + + + FOR(3).DO(a).BREAK(node("x")); + + + + WHILE(wn1).DO(a).BREAK(node("x")); + + + + ITERATOR(itn1).DO(a).BREAK(node("x")); + + + + + SWITCH(node("x")).to(a,b); + + + + + SWITCH(swn1).to(node("x"),a); + + + + + IF(AND(node("x"),ifn1), a); + + + + + IF(OR(node("x"),ifn1), a); + + + + + IF(NOT(node("x")), a); + + + + + CATCH(THEN(a, d)).DO(node("x")) + + + + + THEN( + a, + node("x1"), + IF(node("x2"), b) + ); + + + + IF( + OR(node("x1"), ifn1), + THEN(a, node("x2")) + ); + + + + FOR(node("x1")).DO( + THEN(b, node("x2")) + ); + + + + + WHEN( + THEN(node("x1")), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + + + WHEN( + node("x1"), + IF(node("x2"), b) + ).maxWaitSeconds(10000); + + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml index b0cb7fcd9..2e46ba7af 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml +++ b/liteflow-testcase-el/liteflow-testcase-el-springnative/src/test/resources/rollback/flow.el.xml @@ -31,4 +31,8 @@ CATCH( THEN(b, c, d) ).DO(a); + + + THEN(a.tag("1"), a.tag("2"), a.tag("3"), d); + \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/SQLWithXmlELSpringbootPollingTest.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/SQLWithXmlELSpringbootPollingTest.java new file mode 100644 index 000000000..5993a2faa --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/SQLWithXmlELSpringbootPollingTest.java @@ -0,0 +1,241 @@ +package com.yomahub.liteflow.test.sql; + +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.exception.ChainNotFoundException; +import com.yomahub.liteflow.flow.LiteflowResponse; +import com.yomahub.liteflow.log.LFLog; +import com.yomahub.liteflow.log.LFLoggerManager; +import com.yomahub.liteflow.parser.sql.exception.ELSQLException; +import com.yomahub.liteflow.parser.sql.util.JDBCHelper; +import com.yomahub.liteflow.parser.sql.vo.SQLParserVO; +import com.yomahub.liteflow.property.LiteflowConfig; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.slot.DefaultContext; +import com.yomahub.liteflow.test.BaseTest; +import com.yomahub.liteflow.util.JsonUtil; +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; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.util.Assert; + +import javax.annotation.Resource; +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * @author hxinyu + * @since 2.11.1 + */ +@ExtendWith(SpringExtension.class) +@TestPropertySource(value = "classpath:/application-poll-xml.properties") +@SpringBootTest(classes = SQLWithXmlELSpringbootPollingTest.class) +@EnableAutoConfiguration +@ComponentScan({ "com.yomahub.liteflow.test.sql.cmp" }) +public class SQLWithXmlELSpringbootPollingTest extends BaseTest { + + @Resource + private FlowExecutor flowExecutor; + + static LFLog LOG = LFLoggerManager.getLogger(SQLWithXmlELSpringbootPollingTest.class); + + @AfterAll + public static void after(){ + try{ + //关闭定时轮询线程池 + Field pollExecutor = JDBCHelper.class.getDeclaredField("pollExecutor"); + pollExecutor.setAccessible(true); + ScheduledThreadPoolExecutor threadPoolExecutor = (ScheduledThreadPoolExecutor) pollExecutor.get(null); + threadPoolExecutor.shutdownNow(); + LOG.info("[SQL Polling thread pool closed]"); + }catch (Exception ignored) { + LOG.error("[SQL Polling thread pool not closed]", ignored); + } + } + + @Test + public void testSQLWithXml() throws InterruptedException { + LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg"); + Assertions.assertEquals("a==>b==>c", response.getExecuteStepStr()); + + // 修改chain + changeData(); + Thread.sleep(4000); + Assertions.assertEquals("a==>c==>b", flowExecutor.execute2Resp("chain1", "arg").getExecuteStepStr()); + + // 新增chain + insertData(); + Thread.sleep(4000); + Assertions.assertEquals("a==>b", flowExecutor.execute2Resp("chain5", "arg").getExecuteStepStr()); + + // 删除 chain + deleteData(); + Thread.sleep(4000); + Exception cause = flowExecutor.execute2Resp("chain5", "arg").getCause(); + Assertions.assertTrue(cause instanceof ChainNotFoundException,"删除 chain 测试失败"); + } + + + @Test + public void testSQLWithScriptXml() throws InterruptedException { + LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg"); + Assertions.assertTrue(response.isSuccess()); + Assertions.assertEquals("x1[if 脚本]==>a==>b", response.getExecuteStepStrWithoutTime()); + + // 修改script + changeScriptData(); + Thread.sleep(4000); + Assertions.assertEquals("x1[if 脚本]", flowExecutor.execute2Resp("chain2", "arg").getExecuteStepStr()); + + // 新増script + insertScriptData(); + Thread.sleep(2500); + insertChainData(); + Thread.sleep(2500); + response = flowExecutor.execute2Resp("chain6", "arg"); + DefaultContext context = response.getFirstContextBean(); + Assertions.assertEquals("a==>x3[x3脚本]", response.getExecuteStepStrWithoutTime()); + Assertions.assertEquals("hello", context.getData("test")); + + } + + /** + * 删除chain数据 + */ + private void deleteData(){ + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate("DELETE FROM EL_TABLE WHERE chain_name='chain5'"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 修改chain数据 + */ + private void changeData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate("UPDATE EL_TABLE SET EL_DATA='THEN(a, c, b);' WHERE chain_name='chain1'"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 增加chain数据 + */ + private void insertData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate("INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain5','THEN(a, b);')"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 修改脚本数据 + */ + private void changeScriptData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + //修改script data + statement.executeUpdate( + "UPDATE SCRIPT_NODE_TABLE SET SCRIPT_NODE_DATA='return false' WHERE SCRIPT_NODE_ID='x1'"); + //修改script名 + statement.executeUpdate( + "UPDATE SCRIPT_NODE_TABLE SET SCRIPT_NODE_NAME='x0_script' WHERE SCRIPT_NODE_ID='x0'"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 增加脚本数据 + */ + private void insertScriptData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate( + "INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA,SCRIPT_LANGUAGE) values ('demo','x3','x3脚本','script','defaultContext.setData(\"test\",\"hello\");','groovy');"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + /** + * 删除脚本 + */ + private void deleteScriptData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate( + "DELETE FROM SCRIPT_NODE_TABLE WHERE SCRIPT_NODE_ID = 'x3'"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } + + private void insertChainData() { + LiteflowConfig liteflowConfig = LiteflowConfigGetter.get(); + SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class); + Connection connection; + try { + connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), + sqlParserVO.getPassword()); + Statement statement = connection.createStatement(); + statement.executeUpdate( + "INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain6','THEN(a, x3);');"); + } + catch (SQLException e) { + throw new ELSQLException(e.getMessage()); + } + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/cmp/refresh.java b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/cmp/refresh.java new file mode 100644 index 000000000..70d7f7a0b --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/java/com/yomahub/liteflow/test/sql/cmp/refresh.java @@ -0,0 +1,22 @@ +package com.yomahub.liteflow.test.sql.cmp; + +import com.yomahub.liteflow.core.FlowInitHook; +import com.yomahub.liteflow.flow.FlowBus; +import com.yomahub.liteflow.property.LiteflowConfigGetter; +import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner; +import com.yomahub.liteflow.spring.ComponentScanner; +import com.yomahub.liteflow.thread.ExecutorHelper; +import org.junit.jupiter.api.Test; + +public class refresh { + + @Test + public void cleanScanCache() { + ComponentScanner.cleanCache(); + FlowBus.cleanCache(); + ExecutorHelper.loadInstance().clearExecutorServiceMap(); + SpiFactoryCleaner.clean(); + LiteflowConfigGetter.clean(); + FlowInitHook.cleanHook(); + } +} diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-poll-xml.properties b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-poll-xml.properties new file mode 100644 index 000000000..1962f270b --- /dev/null +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/application-poll-xml.properties @@ -0,0 +1,29 @@ +liteflow.rule-source-ext-data={\ + "url":"jdbc:h2:mem:test_db;MODE=MySQL",\ + "driverClassName":"org.h2.Driver",\ + "username":"root",\ + "password":"123456",\ + "applicationName":"demo",\ + "chainTableName":"EL_TABLE",\ + "chainApplicationNameField":"application_name",\ + "chainNameField":"chain_name",\ + "elDataField":"EL_DATA",\ + "scriptTableName":"script_node_table",\ + "scriptApplicationNameField":"application_name",\ + "scriptIdField":"script_node_id",\ + "scriptNameField":"script_node_name",\ + "scriptDataField":"script_node_data",\ + "scriptLanguageField":"script_language",\ + "scriptTypeField":"script_node_type",\ + "pollingEnabled":true,\ + "pollingIntervalSeconds":2,\ + "pollingStartSeconds":2\ + } + +spring.datasource.driver-class-name=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:test_db;MODE=MySQL +spring.datasource.username=root +spring.datasource.password=123456 +spring.datasource.schema=classpath:/sql/schema.sql +spring.datasource.data=classpath:/sql/data.sql +spring.datasource.platform=h2 \ No newline at end of file diff --git a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/sql/data.sql b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/sql/data.sql index dda527ee6..5900dfa0a 100644 --- a/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/sql/data.sql +++ b/liteflow-testcase-el/liteflow-testcase-el-sql-springboot/src/test/resources/sql/data.sql @@ -1,7 +1,7 @@ DELETE FROM EL_TABLE; INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain1','THEN(a, b, c);'); -INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain2','THEN(a, b, c);'); +INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain2','IF(x1, THEN(a, b));'); INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain3','IF(x0, THEN(a, b));'); INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','','IF(x0, THEN(a, b));'); INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain4','IF(x2, IF(x0, THEN(a, b)));'); @@ -9,6 +9,6 @@ INSERT INTO EL_TABLE (APPLICATION_NAME,CHAIN_NAME,EL_DATA) values ('demo','chain DELETE FROM SCRIPT_NODE_TABLE; INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA,SCRIPT_LANGUAGE) values ('demo','x0','if 脚本','if_script','return true','groovy'); -INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA,SCRIPT_LANGUAGE) values ('demo','x1','if 脚本','if_script','return false','groovy'); +INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA,SCRIPT_LANGUAGE) values ('demo','x1','if 脚本','if_script','return true','groovy'); INSERT INTO SCRIPT_NODE_TABLE (APPLICATION_NAME,SCRIPT_NODE_ID,SCRIPT_NODE_NAME,SCRIPT_NODE_TYPE,SCRIPT_NODE_DATA,SCRIPT_LANGUAGE) values ('demo','x2','python脚本','if_script','return true','js'); diff --git a/pom.xml b/pom.xml index b17900eb1..22fa42648 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,7 @@ - 2.11.0 + 2.11.1 UTF-8 UTF-8 8 @@ -58,7 +58,7 @@ 0.10 0.7.3 1.4.4 - 3.3.1 + 3.3.2 3.0.8 21.3.3.1 1.12.23 @@ -77,7 +77,7 @@ 1.3.5 3.21.0 5.8.18 - 3.1.9 + 3.1.10