mirror of
https://gitee.com/dromara/liteFlow.git
synced 2026-06-13 03:11:10 +08:00
Merge branch 'dev' of https://gitee.com/noear_admin/liteFlow into dev
This commit is contained in:
@@ -1,23 +1,19 @@
|
||||
package com.yomahub.liteflow.builder;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.exception.NodeBuildException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class LiteFlowNodeBuilder {
|
||||
|
||||
@@ -53,6 +49,10 @@ public class LiteFlowNodeBuilder {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.BREAK);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createIteratorNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.ITERATOR);
|
||||
}
|
||||
|
||||
public static LiteFlowNodeBuilder createScriptNode() {
|
||||
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
|
||||
}
|
||||
@@ -130,8 +130,15 @@ public class LiteFlowNodeBuilder {
|
||||
if (StrUtil.isBlank(filePath)) {
|
||||
return this;
|
||||
}
|
||||
String script = ResourceUtil.readUtf8Str(StrUtil.format("classpath: {}", filePath.trim()));
|
||||
return setScript(script);
|
||||
try {
|
||||
List<String> scriptList = PathContentParserHolder.loadContextAware().parseContent(ListUtil.toList(filePath));
|
||||
String script = CollUtil.getFirst(scriptList);
|
||||
setScript(script);
|
||||
} catch (Exception e) {
|
||||
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(), e.getMessage());
|
||||
throw new NodeBuildException(errMsg);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package com.yomahub.liteflow.builder.el;
|
||||
|
||||
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.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.DataNofFoundException;
|
||||
import com.yomahub.liteflow.exception.DataNotFoundException;
|
||||
import com.yomahub.liteflow.exception.ELParseException;
|
||||
import com.yomahub.liteflow.exception.FlowSystemException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.condition.*;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.element.condition.Condition;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -69,6 +73,7 @@ public class LiteFlowChainELBuilder {
|
||||
EXPRESS_RUNNER.addFunction(ChainConstant.NODE, new NodeOperator());
|
||||
EXPRESS_RUNNER.addFunction(ChainConstant.FOR, new ForOperator());
|
||||
EXPRESS_RUNNER.addFunction(ChainConstant.WHILE, new WhileOperator());
|
||||
EXPRESS_RUNNER.addFunction(ChainConstant.ITERATOR, new IteratorOperator());
|
||||
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DO, Object.class, new DoOperator());
|
||||
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.BREAK, Object.class, new BreakOperator());
|
||||
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.DATA, Object.class, new DataOperator());
|
||||
@@ -151,8 +156,10 @@ public class LiteFlowChainELBuilder {
|
||||
return this;
|
||||
} catch (QLException e) {
|
||||
// EL 底层会包装异常,这里是曲线处理
|
||||
if (Objects.equals(e.getCause().getMessage(), DataNofFoundException.MSG)) {
|
||||
throw new ELParseException(String.format("[node/chain is not exist or node/chain not register]elStr=%s", elStr));
|
||||
if (Objects.equals(e.getCause().getMessage(), DataNotFoundException.MSG)) {
|
||||
// 构建错误信息
|
||||
String msg = buildDataNotFoundExceptionMsg(elStr);
|
||||
throw new ELParseException(msg);
|
||||
}
|
||||
throw new ELParseException(e.getCause().getMessage());
|
||||
} catch (Exception e) {
|
||||
@@ -162,17 +169,18 @@ public class LiteFlowChainELBuilder {
|
||||
|
||||
/**
|
||||
* EL表达式校验
|
||||
*
|
||||
* @param elStr EL表达式
|
||||
* @return true 校验成功 false 校验失败
|
||||
*/
|
||||
public static boolean validate(String elStr) {
|
||||
try {
|
||||
LiteFlowChainELBuilder.createChain().setEL(elStr);
|
||||
return Boolean.TRUE;
|
||||
} catch (ELParseException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return Boolean.FALSE;
|
||||
try {
|
||||
LiteFlowChainELBuilder.createChain().setEL(elStr);
|
||||
return Boolean.TRUE;
|
||||
} catch (ELParseException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
public void build() {
|
||||
@@ -183,6 +191,8 @@ public class LiteFlowChainELBuilder {
|
||||
FlowBus.addChain(this.chain);
|
||||
}
|
||||
|
||||
//#region private method
|
||||
|
||||
/**
|
||||
* build 前简单校验
|
||||
*/
|
||||
@@ -195,4 +205,57 @@ public class LiteFlowChainELBuilder {
|
||||
throw new RuntimeException(CollUtil.join(errorList, ",", "[", "]"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 EL 表达式,查找未定义的 id 并构建错误信息
|
||||
*
|
||||
* @param elStr el 表达式
|
||||
*/
|
||||
private String buildDataNotFoundExceptionMsg(String elStr) {
|
||||
String msg = String.format("[node/chain is not exist or node/chain not register]\n EL: %s", StrUtil.trim(elStr));
|
||||
try {
|
||||
InstructionSet parseResult = EXPRESS_RUNNER.getInstructionSetFromLocalCache(elStr);
|
||||
if (parseResult == null) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
String[] outAttrNames = parseResult.getOutAttrNames();
|
||||
if (ArrayUtil.isEmpty(outAttrNames)) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
List<String> chainIds = CollUtil.map(FlowBus.getChainMap().values(), Chain::getChainId, true);
|
||||
List<String> nodeIds = CollUtil.map(FlowBus.getNodeMap().values(), Node::getId, true);
|
||||
for (String attrName : outAttrNames) {
|
||||
if (!chainIds.contains(attrName) && !nodeIds.contains(attrName)) {
|
||||
msg = String.format("[%s] is not exist or [%s] is not registered, you need to define a node or chain with id [%s] and register it \n EL: ", attrName, attrName, attrName);
|
||||
|
||||
// 去除 EL 表达式中的空格和换行符
|
||||
String sourceEl = StrUtil.removeAll(elStr, CharUtil.SPACE, CharUtil.LF, CharUtil.CR);
|
||||
// 这里需要注意的是,nodeId 和 chainId 可能是关键字的一部分,如果直接 indexOf(attrName) 会出现误判
|
||||
// 所以需要判断 attrName 前后是否有 ","
|
||||
int commaRightIndex = sourceEl.indexOf(attrName + StrUtil.COMMA);
|
||||
if (commaRightIndex != -1) {
|
||||
// 需要加上 "EL: " 的长度 4,再加上 "^" 的长度 1,indexOf 从 0 开始,所以还需要加 1
|
||||
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaRightIndex + 6, true);
|
||||
}
|
||||
int commaLeftIndex = sourceEl.indexOf(StrUtil.COMMA + attrName);
|
||||
if (commaLeftIndex != -1) {
|
||||
// 需要加上 "EL: " 的长度 4,再加上 "^" 的长度 1,再加上 "," 的长度 1,indexOf 从 0 开始,所以还需要加 1
|
||||
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 7, true);
|
||||
}
|
||||
// 还有一种特殊情况,就是 EL 表达式中的节点使用 node("a")
|
||||
int nodeIndex = sourceEl.indexOf(String.format("node(\"%s\")", attrName));
|
||||
if (nodeIndex != -1) {
|
||||
// 需要加上 "EL: " 的长度 4,再加上 “node("” 长度 6,再加上 "^" 的长度 1,indexOf 从 0 开始,所以还需要加 1
|
||||
return msg + sourceEl + "\n" + StrUtil.fill("^", CharUtil.SPACE, commaLeftIndex + 12, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// ignore
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class IgnoreErrorOperator extends BaseOperator<WhenCondition> {
|
||||
WhenCondition condition = OperatorHelper.convert(objects[0], WhenCondition.class);
|
||||
|
||||
Boolean ignoreError = OperatorHelper.convert(objects[1], Boolean.class);
|
||||
condition.setErrorResume(ignoreError);
|
||||
condition.setIgnoreError(ignoreError);
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.yomahub.liteflow.builder.el.operator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.element.condition.IteratorCondition;
|
||||
|
||||
public class IteratorOperator extends BaseOperator<IteratorCondition> {
|
||||
@Override
|
||||
public IteratorCondition build(Object[] objects) throws Exception {
|
||||
OperatorHelper.checkObjectSizeEq(objects, 1);
|
||||
|
||||
Node node = OperatorHelper.convert(objects[0], Node.class);
|
||||
if (!ListUtil.toList(NodeTypeEnum.ITERATOR).contains(node.getType())) {
|
||||
throw new QLException("The parameter must be iterator-node item");
|
||||
}
|
||||
|
||||
IteratorCondition iteratorCondition = new IteratorCondition();
|
||||
iteratorCondition.setIteratorNode(node);
|
||||
|
||||
return iteratorCondition;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.yomahub.liteflow.builder.el.operator.base;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ql.util.express.exception.QLException;
|
||||
import com.yomahub.liteflow.exception.DataNofFoundException;
|
||||
import com.yomahub.liteflow.exception.DataNotFoundException;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -156,7 +156,7 @@ public class OperatorHelper {
|
||||
public static void checkNodeAndChainExist(Object[] objects) throws QLException {
|
||||
for (Object object : objects) {
|
||||
if (Objects.isNull(object)) {
|
||||
throw new QLException(DataNofFoundException.MSG);
|
||||
throw new QLException(DataNotFoundException.MSG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,8 @@ public interface ChainConstant {
|
||||
|
||||
String DATA = "data";
|
||||
|
||||
String ITERATOR = "ITERATOR";
|
||||
|
||||
String MONITOR_BUS = "monitorBus";
|
||||
|
||||
String CURR_CHAIN_ID = "currChainId";
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import com.yomahub.liteflow.enums.InnerChainTypeEnum;
|
||||
import com.yomahub.liteflow.exception.*;
|
||||
@@ -82,14 +83,16 @@ public class FlowExecutor {
|
||||
//进行id生成器的初始化
|
||||
IdGeneratorHolder.init();
|
||||
|
||||
if (StrUtil.isBlank(liteflowConfig.getRuleSource())) {
|
||||
String ruleSource = liteflowConfig.getRuleSource();
|
||||
if (StrUtil.isBlank(ruleSource)) {
|
||||
//查看有没有Parser的SPI实现
|
||||
//所有的Parser的SPI实现都是以custom形式放入的,且只支持xml形式
|
||||
ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
|
||||
Iterator<ParserClassNameSpi> it = loader.iterator();
|
||||
if (it.hasNext()){
|
||||
ParserClassNameSpi parserClassNameSpi = it.next();
|
||||
liteflowConfig.setRuleSource("el_xml:" + parserClassNameSpi.getSpiClassName());
|
||||
ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
|
||||
liteflowConfig.setRuleSource(ruleSource);
|
||||
}else{
|
||||
//ruleSource为空,而且没有spi形式的扩展,那么说明真的没有ruleSource
|
||||
//这种情况有可能是基于代码动态构建的
|
||||
@@ -100,10 +103,11 @@ public class FlowExecutor {
|
||||
//如果有前缀的,则不需要再进行分割了,说明是一个整体
|
||||
//如果没有前缀,说明是本地文件,可能配置多个,所以需要分割
|
||||
List<String> sourceRulePathList;
|
||||
if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, liteflowConfig.getRuleSource())){
|
||||
sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource());
|
||||
}else{
|
||||
sourceRulePathList = ListUtil.toList(liteflowConfig.getRuleSource().split(",|;"));
|
||||
if (ReUtil.contains(PREFIX_FORMAT_CONFIG_REGEX, ruleSource)) {
|
||||
sourceRulePathList = ListUtil.toList(ruleSource);
|
||||
} else {
|
||||
String afterHandleRuleSource = ruleSource.replace(StrUtil.SPACE, StrUtil.EMPTY);
|
||||
sourceRulePathList = ListUtil.toList(afterHandleRuleSource.split(",|;"));
|
||||
}
|
||||
|
||||
FlowParser parser = null;
|
||||
@@ -163,9 +167,11 @@ public class FlowExecutor {
|
||||
}
|
||||
|
||||
//如果是ruleSource方式的,最后判断下有没有解析出来,如果没有解析出来则报错
|
||||
if (FlowBus.getChainMap().isEmpty()){
|
||||
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
|
||||
throw new ConfigErrorException(errMsg);
|
||||
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())){
|
||||
if (FlowBus.getChainMap().isEmpty()){
|
||||
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
|
||||
throw new ConfigErrorException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//执行钩子
|
||||
|
||||
@@ -92,7 +92,7 @@ public abstract class NodeComponent{
|
||||
|
||||
try{
|
||||
//前置处理
|
||||
self.beforeProcess(this.getNodeId(), slot);
|
||||
self.beforeProcess();
|
||||
|
||||
//主要的处理逻辑
|
||||
self.process();
|
||||
@@ -118,7 +118,7 @@ public abstract class NodeComponent{
|
||||
throw e;
|
||||
} finally {
|
||||
//后置处理
|
||||
self.afterProcess(this.getNodeId(), slot);
|
||||
self.afterProcess();
|
||||
|
||||
stopWatch.stop();
|
||||
final long timeSpent = stopWatch.getTotalTimeMillis();
|
||||
@@ -135,10 +135,10 @@ public abstract class NodeComponent{
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void beforeProcess(String nodeId, Slot slot){
|
||||
public void beforeProcess(){
|
||||
//全局切面只在spring体系下生效,这里用了spi机制取到相应环境下的实现类
|
||||
//非spring环境下,全局切面为空实现
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(nodeId, slot);
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().beforeProcess(nodeId, this.getSlot());
|
||||
}
|
||||
|
||||
public abstract void process() throws Exception;
|
||||
@@ -151,8 +151,8 @@ public abstract class NodeComponent{
|
||||
//如果需要在抛错后回调某一段逻辑,请覆盖这个方法
|
||||
}
|
||||
|
||||
public <T> void afterProcess(String nodeId, Slot slot){
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(nodeId, slot);
|
||||
public void afterProcess(){
|
||||
CmpAroundAspectHolder.loadCmpAroundAspect().afterProcess(nodeId, this.getSlot());
|
||||
}
|
||||
|
||||
//是否进入该节点
|
||||
@@ -349,6 +349,10 @@ public abstract class NodeComponent{
|
||||
return this.refNodeTL.get().getLoopIndex();
|
||||
}
|
||||
|
||||
public <T> T getCurrLoopObj(){
|
||||
return this.refNodeTL.get().getCurrLoopObject();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void invoke(String chainId, Object param) throws Exception {
|
||||
FlowExecutorHolder.loadInstance().invoke(chainId, param, this.getSlotIndex());
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yomahub.liteflow.core;
|
||||
|
||||
import com.yomahub.liteflow.slot.Slot;
|
||||
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* ITERATOR迭代器循环组件抽象类
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.9.7
|
||||
*/
|
||||
public abstract class NodeIteratorComponent extends NodeComponent{
|
||||
|
||||
@Override
|
||||
public void process() throws Exception {
|
||||
Iterator<?> it = processIterator();
|
||||
Slot slot = this.getSlot();
|
||||
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getClass());
|
||||
slot.setIteratorResult(originalClass.getName(), it);
|
||||
}
|
||||
|
||||
public abstract Iterator<?> processIterator() throws Exception;
|
||||
}
|
||||
@@ -194,26 +194,17 @@ public class ComponentProxy {
|
||||
).findFirst().orElse(null);
|
||||
|
||||
//如果被代理的对象里有此标注标的方法,则调用此被代理的对象里的方法,如果没有,则调用父类里的方法
|
||||
//beforeProcess和afterProcess这2个方法除外
|
||||
if (!ListUtil.toList("beforeProcess","afterProcess").contains(liteFlowMethodBean.getMethodName())) {
|
||||
//进行检查,检查被代理的bean里是否有且仅有NodeComponent这个类型的参数
|
||||
boolean checkFlag = liteFlowMethodBean.getMethod().getParameterTypes().length == 1
|
||||
&& Arrays.asList(liteFlowMethodBean.getMethod().getParameterTypes()).contains(NodeComponent.class);
|
||||
if (!checkFlag) {
|
||||
String errMsg = StrUtil.format("Method[{}.{}] must have NodeComponent parameter(and only one parameter)", bean.getClass().getName(), liteFlowMethodBean.getMethod().getName());
|
||||
LOG.error(errMsg);
|
||||
throw new ComponentMethodDefineErrorException(errMsg);
|
||||
}
|
||||
|
||||
try{
|
||||
return liteFlowMethodBean.getMethod().invoke(bean, proxy);
|
||||
}catch (Exception e){
|
||||
InvocationTargetException targetEx = (InvocationTargetException)e;
|
||||
throw targetEx.getTargetException();
|
||||
}
|
||||
//进行检查,检查被代理的bean里是否有且仅有NodeComponent这个类型的参数
|
||||
boolean checkFlag = liteFlowMethodBean.getMethod().getParameterTypes().length == 1
|
||||
&& Arrays.asList(liteFlowMethodBean.getMethod().getParameterTypes()).contains(NodeComponent.class);
|
||||
if (!checkFlag) {
|
||||
String errMsg = StrUtil.format("Method[{}.{}] must have NodeComponent parameter(and only one parameter)", bean.getClass().getName(), liteFlowMethodBean.getMethod().getName());
|
||||
LOG.error(errMsg);
|
||||
throw new ComponentMethodDefineErrorException(errMsg);
|
||||
}
|
||||
|
||||
try{
|
||||
return liteFlowMethodBean.getMethod().invoke(bean, args);
|
||||
return liteFlowMethodBean.getMethod().invoke(bean, proxy);
|
||||
}catch (Exception e){
|
||||
InvocationTargetException targetEx = (InvocationTargetException)e;
|
||||
throw targetEx.getTargetException();
|
||||
|
||||
@@ -14,7 +14,9 @@ public enum ConditionTypeEnum {
|
||||
|
||||
TYPE_FOR("for", "for"),
|
||||
|
||||
TYPE_WHILE("while", "while")
|
||||
TYPE_WHILE("while", "while"),
|
||||
|
||||
TYPE_ITERATOR("iterator", "iterator")
|
||||
;
|
||||
private String type;
|
||||
private String name;
|
||||
|
||||
@@ -7,6 +7,9 @@ public enum LiteFlowMethodEnum {
|
||||
PROCESS_FOR("processFor", true),
|
||||
PROCESS_WHILE("processWhile", true),
|
||||
PROCESS_BREAK("processBreak", true),
|
||||
|
||||
PROCESS_ITERATOR("processIterator", true),
|
||||
|
||||
IS_ACCESS("isAccess", false),
|
||||
|
||||
IS_END("isEnd", false),
|
||||
|
||||
@@ -35,6 +35,8 @@ public enum NodeTypeEnum {
|
||||
|
||||
BREAK("break", "循环跳出", false, NodeBreakComponent.class),
|
||||
|
||||
ITERATOR("iterator", "循环迭代", false, NodeIteratorComponent.class),
|
||||
|
||||
SCRIPT("script", "脚本", true, ScriptCommonComponent.class),
|
||||
|
||||
SWITCH_SCRIPT("switch_script", "选择脚本", true, ScriptSwitchComponent.class),
|
||||
|
||||
@@ -4,8 +4,8 @@ package com.yomahub.liteflow.exception;
|
||||
* 未找到数据异常
|
||||
* @author tangkc
|
||||
*/
|
||||
public class DataNofFoundException extends RuntimeException {
|
||||
public static final String MSG = "DataNofFoundException";
|
||||
public class DataNotFoundException extends RuntimeException {
|
||||
public static final String MSG = "DataNotFoundException";
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -14,11 +14,11 @@ public class DataNofFoundException extends RuntimeException {
|
||||
*/
|
||||
private String message;
|
||||
|
||||
public DataNofFoundException() {
|
||||
public DataNotFoundException() {
|
||||
this.message = MSG;
|
||||
}
|
||||
|
||||
public DataNofFoundException(String message) {
|
||||
public DataNotFoundException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.yomahub.liteflow.exception;
|
||||
|
||||
/**
|
||||
* 没有节点异常
|
||||
* @author Yun
|
||||
*/
|
||||
public class NoIteratorNodeException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 异常信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
public NoIteratorNodeException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -207,7 +207,7 @@ public class FlowBus {
|
||||
} catch (Exception e) {
|
||||
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name) ? nodeId : StrUtil.format("{}({})", nodeId, name));
|
||||
LOG.error(e.getMessage());
|
||||
throw new ComponentCannotRegisterException(error);
|
||||
throw new ComponentCannotRegisterException(StrUtil.format("{} {}", error, e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ public class Node implements Executable,Cloneable{
|
||||
|
||||
private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
|
||||
|
||||
private TransmittableThreadLocal<Object> currLoopObject = new TransmittableThreadLocal<>();
|
||||
|
||||
public Node(){
|
||||
|
||||
}
|
||||
@@ -241,4 +243,16 @@ public class Node implements Executable,Cloneable{
|
||||
public void removeLoopIndex(){
|
||||
this.loopIndexTL.remove();
|
||||
}
|
||||
|
||||
public void setCurrLoopObject(Object obj){
|
||||
this.currLoopObject.set(obj);
|
||||
}
|
||||
|
||||
public <T> T getCurrLoopObject(){
|
||||
return (T)this.currLoopObject.get();
|
||||
}
|
||||
|
||||
public void removeCurrLoopObject(){
|
||||
this.currLoopObject.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ public class ForCondition extends LoopCondition{
|
||||
//如果break组件不为空,则去执行
|
||||
if (ObjectUtil.isNotNull(breakNode)){
|
||||
breakNode.setCurrChainId(this.getCurrChainId());
|
||||
setLoopIndex(breakNode, i);
|
||||
breakNode.execute(slotIndex);
|
||||
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
|
||||
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
|
||||
@@ -64,10 +65,6 @@ public class ForCondition extends LoopCondition{
|
||||
return ConditionTypeEnum.TYPE_FOR;
|
||||
}
|
||||
|
||||
public Executable getDoExecutor() {
|
||||
return this.getExecutableList().get(0);
|
||||
}
|
||||
|
||||
public Node getForNode() {
|
||||
return forNode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.yomahub.liteflow.flow.element.condition;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.enums.ConditionTypeEnum;
|
||||
import com.yomahub.liteflow.exception.NoIteratorNodeException;
|
||||
import com.yomahub.liteflow.flow.element.Executable;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.slot.DataBus;
|
||||
import com.yomahub.liteflow.slot.Slot;
|
||||
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public class IteratorCondition extends LoopCondition{
|
||||
|
||||
private Node iteratorNode;
|
||||
|
||||
@Override
|
||||
public void execute(Integer slotIndex) throws Exception {
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
if (ObjectUtil.isNull(iteratorNode)){
|
||||
String errorInfo = StrUtil.format("[{}]:no iterator-node found", slot.getRequestId());
|
||||
throw new NoIteratorNodeException(errorInfo);
|
||||
}
|
||||
|
||||
//执行Iterator组件
|
||||
iteratorNode.setCurrChainId(this.getCurrChainId());
|
||||
iteratorNode.execute(slotIndex);
|
||||
|
||||
//这里可能会有spring代理过的bean,所以拿到user原始的class
|
||||
Class<?> originalForCountClass = LiteFlowProxyUtil.getUserClass(this.iteratorNode.getInstance().getClass());
|
||||
//获得迭代器
|
||||
Iterator<?> it = slot.getIteratorResult(originalForCountClass.getName());
|
||||
|
||||
//获得要循环的可执行对象
|
||||
Executable executableItem = this.getDoExecutor();
|
||||
|
||||
int index = 0;
|
||||
while(it.hasNext()){
|
||||
Object itObj = it.next();
|
||||
|
||||
executableItem.setCurrChainId(this.getCurrChainId());
|
||||
//设置循环index
|
||||
setLoopIndex(executableItem, index);
|
||||
//设置循环迭代器对象
|
||||
setCurrLoopObject(executableItem, itObj);
|
||||
//执行可执行对象
|
||||
executableItem.execute(slotIndex);
|
||||
//如果break组件不为空,则去执行
|
||||
if (ObjectUtil.isNotNull(breakNode)){
|
||||
breakNode.setCurrChainId(this.getCurrChainId());
|
||||
setLoopIndex(breakNode, index);
|
||||
setCurrLoopObject(breakNode, itObj);
|
||||
breakNode.execute(slotIndex);
|
||||
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
|
||||
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
|
||||
if (isBreak){
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionTypeEnum getConditionType() {
|
||||
return ConditionTypeEnum.TYPE_ITERATOR;
|
||||
}
|
||||
|
||||
public Node getIteratorNode() {
|
||||
return iteratorNode;
|
||||
}
|
||||
|
||||
public void setIteratorNode(Node iteratorNode) {
|
||||
this.iteratorNode = iteratorNode;
|
||||
}
|
||||
}
|
||||
@@ -32,4 +32,18 @@ public abstract class LoopCondition extends Condition {
|
||||
((Node)executableItem).setLoopIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setCurrLoopObject(Executable executableItem, Object obj){
|
||||
if (executableItem instanceof Chain){
|
||||
((Chain)executableItem).getConditionList().forEach(condition -> setCurrLoopObject(condition, obj));
|
||||
}else if(executableItem instanceof Condition){
|
||||
((Condition)executableItem).getExecutableList().forEach(executable -> setCurrLoopObject(executable, obj));
|
||||
}else if(executableItem instanceof Node){
|
||||
((Node)executableItem).setCurrLoopObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
protected Executable getDoExecutor() {
|
||||
return this.getExecutableList().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +45,9 @@ public class SwitchCondition extends Condition{
|
||||
//这里可能会有spring代理过的bean,所以拿到user原始的class
|
||||
Class<?> originalClass = LiteFlowProxyUtil.getUserClass(this.getSwitchNode().getInstance().getClass());
|
||||
String targetId = slot.getSwitchResult(originalClass.getName());
|
||||
if (StrUtil.isNotBlank(targetId)) {
|
||||
Executable targetExecutor;
|
||||
|
||||
Executable targetExecutor = null;
|
||||
if (StrUtil.isNotBlank(targetId)) {
|
||||
//这里要判断是否使用tag模式跳转
|
||||
if (targetId.contains(TAG_FLAG)){
|
||||
String[] target = targetId.split(TAG_FLAG, 2);
|
||||
@@ -66,26 +66,26 @@ public class SwitchCondition extends Condition{
|
||||
executable -> executable.getExecuteId().equals(targetId)
|
||||
).findFirst().orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNull(targetExecutor)) {
|
||||
//没有匹配到执行节点,则走默认的执行节点
|
||||
targetExecutor = defaultExecutor;
|
||||
}
|
||||
if (ObjectUtil.isNull(targetExecutor)) {
|
||||
//没有匹配到执行节点,则走默认的执行节点
|
||||
targetExecutor = defaultExecutor;
|
||||
}
|
||||
|
||||
if (ObjectUtil.isNotNull(targetExecutor)) {
|
||||
//switch的目标不能是Pre节点或者Finally节点
|
||||
if (targetExecutor instanceof PreCondition || targetExecutor instanceof FinallyCondition){
|
||||
String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally",
|
||||
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
|
||||
throw new SwitchTargetCannotBePreOrFinallyException(errorInfo);
|
||||
}
|
||||
targetExecutor.setCurrChainId(this.getCurrChainId());
|
||||
targetExecutor.execute(slotIndex);
|
||||
}else{
|
||||
String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}],target str is [{}]",
|
||||
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName(), targetId);
|
||||
throw new NoSwitchTargetNodeException(errorInfo);
|
||||
if (ObjectUtil.isNotNull(targetExecutor)) {
|
||||
//switch的目标不能是Pre节点或者Finally节点
|
||||
if (targetExecutor instanceof PreCondition || targetExecutor instanceof FinallyCondition){
|
||||
String errorInfo = StrUtil.format("[{}]:switch component[{}] error, switch target node cannot be pre or finally",
|
||||
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName());
|
||||
throw new SwitchTargetCannotBePreOrFinallyException(errorInfo);
|
||||
}
|
||||
targetExecutor.setCurrChainId(this.getCurrChainId());
|
||||
targetExecutor.execute(slotIndex);
|
||||
}else{
|
||||
String errorInfo = StrUtil.format("[{}]:no target node find for the component[{}],target str is [{}]",
|
||||
slot.getRequestId(), this.getSwitchNode().getInstance().getDisplayName(), targetId);
|
||||
throw new NoSwitchTargetNodeException(errorInfo);
|
||||
}
|
||||
}else{
|
||||
throw new SwitchTypeErrorException("switch instance must be NodeSwitchComponent");
|
||||
|
||||
@@ -37,7 +37,7 @@ public class WhenCondition extends Condition {
|
||||
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
//只在when类型下有效,以区分当when调用链调用失败时是否继续往下执行 默认false不继续执行
|
||||
private boolean errorResume = false;
|
||||
private boolean ignoreError = false;
|
||||
|
||||
//只在when类型下有效,用于不同node进行同组合并,相同的组会进行合并,不同的组不会进行合并
|
||||
//此属性已弃用
|
||||
@@ -150,8 +150,8 @@ public class WhenCondition extends Condition {
|
||||
timeOutWhenFutureObjList.forEach(whenFutureObj ->
|
||||
LOG.warn("requestId [{}] executing thread has reached max-wait-seconds, thread canceled.Execute-item: [{}]", slot.getRequestId(), whenFutureObj.getExecutorName()));
|
||||
|
||||
//当配置了errorResume = false,出现interrupted或者!f.get()的情况,将抛出WhenExecuteException
|
||||
if (!this.isErrorResume()) {
|
||||
//当配置了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()));
|
||||
}
|
||||
@@ -164,17 +164,17 @@ public class WhenCondition extends Condition {
|
||||
}
|
||||
}
|
||||
} else if (interrupted[0]) {
|
||||
// 这里由于配置了errorResume,所以只打印warn日志
|
||||
// 这里由于配置了ignoreError,所以只打印warn日志
|
||||
LOG.warn("requestId [{}] executing when condition timeout , but ignore with errorResume.", slot.getRequestId());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isErrorResume() {
|
||||
return errorResume;
|
||||
public boolean isIgnoreError() {
|
||||
return ignoreError;
|
||||
}
|
||||
|
||||
public void setErrorResume(boolean errorResume) {
|
||||
this.errorResume = errorResume;
|
||||
public void setIgnoreError(boolean ignoreError) {
|
||||
this.ignoreError = ignoreError;
|
||||
}
|
||||
|
||||
public String getGroup() {
|
||||
|
||||
@@ -35,11 +35,12 @@ public class WhileCondition extends LoopCondition{
|
||||
int index = 0;
|
||||
while(getWhileResult(slotIndex)){
|
||||
executableItem.setCurrChainId(this.getCurrChainId());
|
||||
setLoopIndex(executableItem, index++);
|
||||
setLoopIndex(executableItem, index);
|
||||
executableItem.execute(slotIndex);
|
||||
//如果break组件不为空,则去执行
|
||||
if (ObjectUtil.isNotNull(breakNode)){
|
||||
breakNode.setCurrChainId(this.getCurrChainId());
|
||||
setLoopIndex(breakNode, index);
|
||||
breakNode.execute(slotIndex);
|
||||
Class<?> originalBreakClass = LiteFlowProxyUtil.getUserClass(this.breakNode.getInstance().getClass());
|
||||
boolean isBreak = slot.getBreakResult(originalBreakClass.getName());
|
||||
@@ -47,22 +48,19 @@ public class WhileCondition extends LoopCondition{
|
||||
break;
|
||||
}
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean getWhileResult(Integer slotIndex) throws Exception{
|
||||
Slot slot = DataBus.getSlot(slotIndex);
|
||||
//执行while组件
|
||||
whileNode.setCurrChainName(this.getCurrChainName());
|
||||
whileNode.setCurrChainId(this.getCurrChainId());
|
||||
whileNode.execute(slotIndex);
|
||||
Class<?> originalWhileClass = LiteFlowProxyUtil.getUserClass(this.whileNode.getInstance().getClass());
|
||||
return slot.getWhileResult(originalWhileClass.getName());
|
||||
}
|
||||
|
||||
public Executable getDoExecutor() {
|
||||
return this.getExecutableList().get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionTypeEnum getConditionType() {
|
||||
return ConditionTypeEnum.TYPE_WHILE;
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.yomahub.liteflow.slot;
|
||||
import cn.hutool.core.collection.ConcurrentHashSet;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.entity.CmpStep;
|
||||
@@ -52,6 +53,8 @@ public class Slot{
|
||||
|
||||
private static final String WHILE_PREFIX = "_while_";
|
||||
|
||||
private static final String ITERATOR_PREFIX = "_iterator_";
|
||||
|
||||
private static final String BREAK_PREFIX = "_break_";
|
||||
|
||||
private static final String NODE_INPUT_PREFIX = "_input_";
|
||||
@@ -89,6 +92,16 @@ public class Slot{
|
||||
return metaDataMap.containsKey(key);
|
||||
}
|
||||
|
||||
private <T> void putThreadMetaDataMap(String key, T t){
|
||||
String threadKey = StrUtil.format("{}_{}", key, Thread.currentThread().getName());
|
||||
putMetaDataMap(threadKey, t);
|
||||
}
|
||||
|
||||
private <T> T getThreadMetaData(String key){
|
||||
String threadKey = StrUtil.format("{}_{}", key, Thread.currentThread().getName());
|
||||
return (T)metaDataMap.get(threadKey);
|
||||
}
|
||||
|
||||
private <T> void putMetaDataMap(String key, T t) {
|
||||
if (ObjectUtil.isNull(t)) {
|
||||
//data slot is a ConcurrentHashMap, so null value will trigger NullPointerException
|
||||
@@ -197,43 +210,51 @@ public class Slot{
|
||||
}
|
||||
|
||||
public <T> void setSwitchResult(String key, T t){
|
||||
putMetaDataMap(SWITCH_NODE_PREFIX + key, t);
|
||||
putThreadMetaDataMap(SWITCH_NODE_PREFIX + key, t);
|
||||
}
|
||||
|
||||
public <T> T getSwitchResult(String key){
|
||||
return (T) metaDataMap.get(SWITCH_NODE_PREFIX + key);
|
||||
return getThreadMetaData(SWITCH_NODE_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setIfResult(String key, boolean result){
|
||||
putMetaDataMap(IF_NODE_PREFIX + key, result);
|
||||
putThreadMetaDataMap(IF_NODE_PREFIX + key, result);
|
||||
}
|
||||
|
||||
public boolean getIfResult(String key){
|
||||
return (boolean) metaDataMap.get(IF_NODE_PREFIX + key);
|
||||
return getThreadMetaData(IF_NODE_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setForResult(String key, int forCount){
|
||||
putMetaDataMap(FOR_PREFIX + key, forCount);
|
||||
putThreadMetaDataMap(FOR_PREFIX + key, forCount);
|
||||
}
|
||||
|
||||
public int getForResult(String key){
|
||||
return (int) metaDataMap.get(FOR_PREFIX + key);
|
||||
return getThreadMetaData(FOR_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setWhileResult(String key, boolean whileFlag){
|
||||
putMetaDataMap(WHILE_PREFIX + key, whileFlag);
|
||||
putThreadMetaDataMap(WHILE_PREFIX + key, whileFlag);
|
||||
}
|
||||
|
||||
public boolean getWhileResult(String key){
|
||||
return (boolean) metaDataMap.get(WHILE_PREFIX + key);
|
||||
return getThreadMetaData(WHILE_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setBreakResult(String key, boolean breakFlag){
|
||||
putMetaDataMap(BREAK_PREFIX + key, breakFlag);
|
||||
putThreadMetaDataMap(BREAK_PREFIX + key, breakFlag);
|
||||
}
|
||||
|
||||
public boolean getBreakResult(String key){
|
||||
return (boolean) metaDataMap.get(BREAK_PREFIX + key);
|
||||
return getThreadMetaData(BREAK_PREFIX + key);
|
||||
}
|
||||
|
||||
public void setIteratorResult(String key, Iterator<?> it){
|
||||
putThreadMetaDataMap(ITERATOR_PREFIX + key, it);
|
||||
}
|
||||
|
||||
public Iterator<?> getIteratorResult(String key){
|
||||
return getThreadMetaData(ITERATOR_PREFIX + key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!ELEMENT flow ((nodes) , (chain)+)>
|
||||
<!ELEMENT flow ((nodes)? , (chain)+)>
|
||||
<!ELEMENT nodes (node)+>
|
||||
<!ELEMENT node (#PCDATA | EMPTY)*>
|
||||
<!ELEMENT chain (#PCDATA)>
|
||||
|
||||
<!ATTLIST node
|
||||
id CDATA #REQUIRED
|
||||
name CDATA #IMPLIED
|
||||
type (script|if_script|switch_script|while_script|for_script|break_script) #IMPLIED
|
||||
class CDATA #IMPLIED
|
||||
file CDATA #IMPLIED
|
||||
language (groovy|js|python|lua) #IMPLIED
|
||||
>
|
||||
<!ATTLIST chain
|
||||
name CDATA>
|
||||
id CDATA #IMPLIED
|
||||
name CDATA #IMPLIED
|
||||
>
|
||||
@@ -53,7 +53,7 @@ public class NodeComponentOfMethod extends NodeComponent {
|
||||
|
||||
|
||||
@Override
|
||||
public <T> void beforeProcess(String nodeId, Slot slot) {
|
||||
public void beforeProcess() {
|
||||
if(methodEnum != LiteFlowMethodEnum.BEFORE_PROCESS){
|
||||
return;
|
||||
}
|
||||
@@ -68,7 +68,7 @@ public class NodeComponentOfMethod extends NodeComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void afterProcess(String nodeId, Slot slot) {
|
||||
public void afterProcess() {
|
||||
if (methodEnum != LiteFlowMethodEnum.AFTER_PROCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.yomahub.liteflow.spi.spring;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
@@ -9,19 +8,15 @@ import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.exception.ConfigErrorException;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.property.LiteflowConfigGetter;
|
||||
import com.yomahub.liteflow.spi.PathContentParser;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SpringPathContentParser implements PathContentParser {
|
||||
@Override
|
||||
|
||||
@@ -20,12 +20,12 @@ public class CmpConfig {
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.BEFORE_PROCESS,nodeId = "a")
|
||||
public void beforeAcmp(String nodeId, Slot slot){
|
||||
public void beforeAcmp(NodeComponent bindCmp){
|
||||
System.out.println("before A");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.AFTER_PROCESS,nodeId = "a")
|
||||
public void afterAcmp(String nodeId, Slot slot){
|
||||
public void afterAcmp(NodeComponent bindCmp){
|
||||
System.out.println("after A");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.yomahub.liteflow.test.iterator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* springboot环境最普通的例子测试
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.4
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/iterator/application.properties")
|
||||
@SpringBootTest(classes = IteratorELDeclMultiSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
|
||||
public class IteratorELDeclMultiSpringbootTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testIt1() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("123", str);
|
||||
}
|
||||
|
||||
//迭代器带break
|
||||
@Test
|
||||
public void testIt2() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("12", str);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.yomahub.liteflow.test.iterator.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;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.base.cmp.TestDomain;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@LiteflowComponent
|
||||
public class CmpConfig {
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a")
|
||||
public void processA(NodeComponent bindCmp) {
|
||||
String key = "test";
|
||||
DefaultContext context = bindCmp.getFirstContextBean();
|
||||
if (!context.hasData(key)){
|
||||
context.setData(key, bindCmp.getCurrLoopObj());
|
||||
}else{
|
||||
String str = context.getData(key);
|
||||
str += bindCmp.getCurrLoopObj();
|
||||
context.setData(key, str);
|
||||
}
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_BREAK, nodeId = "b", nodeType = NodeTypeEnum.BREAK)
|
||||
public boolean processB(NodeComponent bindCmp) {
|
||||
return bindCmp.getLoopIndex() == 1;
|
||||
}
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "it", nodeType = NodeTypeEnum.ITERATOR)
|
||||
public Iterator<?> processIT(NodeComponent bindCmp) {
|
||||
List<String> list = bindCmp.getRequestData();
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
liteflow.rule-source=iterator/flow.xml
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
ITERATOR(it).DO(a);
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
ITERATOR(it).DO(a).BREAK(b);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -27,12 +27,12 @@ public class ACmp{
|
||||
}
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.BEFORE_PROCESS)
|
||||
public void beforeAcmp(String nodeId, Slot slot){
|
||||
public void beforeAcmp(NodeComponent bindCmp){
|
||||
System.out.println("before A");
|
||||
}
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.AFTER_PROCESS)
|
||||
public void afterAcmp(String nodeId, Slot slot){
|
||||
public void afterAcmp(NodeComponent bindCmp){
|
||||
System.out.println("after A");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.yomahub.liteflow.test.iterator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* springboot环境最普通的例子测试
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.6.4
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/iterator/application.properties")
|
||||
@SpringBootTest(classes = IteratorELDeclSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
|
||||
public class IteratorELDeclSpringbootTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testIt1() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("123", str);
|
||||
}
|
||||
|
||||
//迭代器带break
|
||||
@Test
|
||||
public void testIt2() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("12", str);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
|
||||
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 org.springframework.stereotype.Component;
|
||||
|
||||
@Component("a")
|
||||
@LiteflowCmpDefine(NodeTypeEnum.COMMON)
|
||||
public class ACmp{
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
|
||||
public void process(NodeComponent bindCmp) {
|
||||
String key = "test";
|
||||
DefaultContext context = bindCmp.getFirstContextBean();
|
||||
if (!context.hasData(key)){
|
||||
context.setData(key, bindCmp.getCurrLoopObj());
|
||||
}else{
|
||||
String str = context.getData(key);
|
||||
str += bindCmp.getCurrLoopObj();
|
||||
context.setData(key, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeBreakComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("b")
|
||||
@LiteflowCmpDefine(NodeTypeEnum.BREAK)
|
||||
public class BCmp{
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS_BREAK)
|
||||
public boolean processBreak(NodeComponent bindCmp) throws Exception {
|
||||
return bindCmp.getLoopIndex() == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.core.NodeIteratorComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import com.yomahub.liteflow.enums.NodeTypeEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Component("it")
|
||||
@LiteflowCmpDefine(NodeTypeEnum.ITERATOR)
|
||||
public class ITCmp{
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS_ITERATOR)
|
||||
public Iterator<?> processIterator(NodeComponent bindCmp) throws Exception {
|
||||
List<String> list = bindCmp.getRequestData();
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class SubflowInDifferentConfigELDeclSpringbootTest extends BaseTest {
|
||||
@Test(expected = MultipleParsersException.class)
|
||||
public void testExplicitSubFlow2() {
|
||||
LiteflowConfig config = context.getBean(LiteflowConfig.class);
|
||||
config.setRuleSource("subflow/flow-main.xml,subflow/flow-sub1.xml,subflow/flow-sub2.yml");
|
||||
config.setRuleSource("subflow/flow-main.xml, subflow/flow-sub1.xml,subflow/flow-sub2.yml");
|
||||
flowExecutor.reloadRule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
liteflow.rule-source=iterator/flow.xml
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
ITERATOR(it).DO(a);
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
ITERATOR(it).DO(a).BREAK(b);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.yomahub.liteflow.test.iterator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IteratorTest extends BaseTest{
|
||||
|
||||
private static FlowExecutor flowExecutor;
|
||||
|
||||
@BeforeClass
|
||||
public static void init(){
|
||||
LiteflowConfig config = new LiteflowConfig();
|
||||
config.setRuleSource("iterator/flow.xml");
|
||||
flowExecutor = FlowExecutorHolder.loadInstance(config);
|
||||
}
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testIt1() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("123", str);
|
||||
}
|
||||
|
||||
//迭代器带break
|
||||
@Test
|
||||
public void testIt2() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("12", str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
|
||||
public class ACmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
String key = "test";
|
||||
DefaultContext context = this.getFirstContextBean();
|
||||
if (!context.hasData(key)){
|
||||
context.setData(key, this.getCurrLoopObj());
|
||||
}else{
|
||||
String str = context.getData(key);
|
||||
str += this.getCurrLoopObj();
|
||||
context.setData(key, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeBreakComponent;
|
||||
|
||||
public class BCmp extends NodeBreakComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBreak() throws Exception {
|
||||
return this.getLoopIndex() == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeIteratorComponent;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ITCmp extends NodeIteratorComponent {
|
||||
@Override
|
||||
public Iterator<?> processIterator() throws Exception {
|
||||
List<String> list = this.getRequestData();
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<nodes>
|
||||
<node id="a" class="com.yomahub.liteflow.test.iterator.cmp.ACmp"/>
|
||||
<node id="b" class="com.yomahub.liteflow.test.iterator.cmp.BCmp"/>
|
||||
<node id="it" class="com.yomahub.liteflow.test.iterator.cmp.ITCmp"/>
|
||||
</nodes>
|
||||
|
||||
<chain name="chain1">
|
||||
ITERATOR(it).DO(a);
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
ITERATOR(it).DO(a).BREAK(b);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.yomahub.liteflow.test.script.groovy.common;
|
||||
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
|
||||
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
@@ -25,6 +26,44 @@ public class LiteFlowXmlScriptBuilderGroovyELTest extends BaseTest {
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
/**
|
||||
* 测试通过builder方式运行普通script节点,以file绝对路径的方式运行
|
||||
*/
|
||||
@Test
|
||||
public void testAbsoluteScriptFilePath(){
|
||||
String absolutePath = new ClassPathResource("classpath:builder/s2.groovy").getAbsolutePath();
|
||||
LiteFlowNodeBuilder.createNode().setId("d")
|
||||
.setName("组件D")
|
||||
.setType(NodeTypeEnum.COMMON)
|
||||
.setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.DCmp")
|
||||
.build();
|
||||
LiteFlowNodeBuilder.createNode().setId("s2")
|
||||
.setName("条件脚本S2")
|
||||
.setType(NodeTypeEnum.SWITCH_SCRIPT)
|
||||
.setFile(absolutePath)
|
||||
.build();
|
||||
LiteFlowNodeBuilder.createNode().setId("a")
|
||||
.setName("组件A")
|
||||
.setType(NodeTypeEnum.COMMON)
|
||||
.setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.ACmp")
|
||||
.build();
|
||||
LiteFlowNodeBuilder.createNode().setId("b")
|
||||
.setName("组件B")
|
||||
.setType(NodeTypeEnum.COMMON)
|
||||
.setClazz("com.yomahub.liteflow.test.script.groovy.common.cmp.BCmp")
|
||||
.build();
|
||||
|
||||
LiteFlowChainELBuilder.createChain().setChainName("chain2")
|
||||
.setEL("THEN(d,SWITCH(s2).to(a,b))")
|
||||
.build();
|
||||
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2","arg1");
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
Assert.assertEquals("d[组件D]==>s2[条件脚本S2]==>a[组件A]", response.getExecuteStepStr());
|
||||
}
|
||||
|
||||
//测试通过builder方式运行普通script节点,以脚本文本的方式运行
|
||||
@Test
|
||||
public void testBuilderScript1() {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.yomahub.liteflow.test.script.groovy.scriptbean;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.exception.ScriptBeanMethodInvokeException;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.script.ScriptBeanManager;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import org.junit.Assert;
|
||||
@@ -15,6 +16,8 @@ import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/scriptbean/application.properties")
|
||||
@@ -75,4 +78,15 @@ public class LiteFlowScriptScriptbeanGroovyELTest extends BaseTest {
|
||||
Assert.assertFalse(response.isSuccess());
|
||||
Assert.assertEquals(ScriptBeanMethodInvokeException.class, response.getCause().getClass());
|
||||
}
|
||||
|
||||
//测试在ScriptBeanManager里放入上下文,实现自定义脚本引用名称
|
||||
@Test
|
||||
public void testScriptBean7() throws Exception{
|
||||
Map<String, String> map = new HashMap<>();
|
||||
ScriptBeanManager.addScriptBean("abcCx", map);
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg", map);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
Map<String, String> context = response.getFirstContextBean();
|
||||
Assert.assertEquals("hello", context.get("demo"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,13 @@
|
||||
defaultContext.setData("demo", str)
|
||||
]]>
|
||||
</node>
|
||||
|
||||
<node id="s5" type="script" language="groovy">
|
||||
<![CDATA[
|
||||
def str = demo.getDemoStr1()
|
||||
abcCx.put("demo", str)
|
||||
]]>
|
||||
</node>
|
||||
</nodes>
|
||||
|
||||
<chain name="chain1">
|
||||
@@ -68,4 +75,8 @@
|
||||
<chain name="chain6">
|
||||
THEN(a,b,c,s4);
|
||||
</chain>
|
||||
|
||||
<chain name="chain7">
|
||||
THEN(a,b,c,s5);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -1,8 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<nodes>
|
||||
<node id="s1" name="普通脚本1" type="script">
|
||||
<node id="s1" name="普通脚本1" type="script" language="python">
|
||||
<![CDATA[
|
||||
import json
|
||||
x='{"name": "Jack", "age": 75, "nationality": "China"}'
|
||||
jsonData=json.loads(x)
|
||||
temperature=jsonData['name']
|
||||
print(temperature)
|
||||
|
||||
|
||||
a=6
|
||||
b=10
|
||||
if a>5:
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.yomahub.liteflow.test.iterator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* springboot环境EL常规的例子测试
|
||||
* @author Bryan.Zhang
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/iterator/application.properties")
|
||||
@SpringBootTest(classes = IteratorELSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.iterator.cmp"})
|
||||
public class IteratorELSpringbootTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testIt1() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("123", str);
|
||||
}
|
||||
|
||||
//迭代器带break
|
||||
@Test
|
||||
public void testIt2() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("12", str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("a")
|
||||
public class ACmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
String key = "test";
|
||||
DefaultContext context = this.getFirstContextBean();
|
||||
if (!context.hasData(key)){
|
||||
context.setData(key, this.getCurrLoopObj());
|
||||
}else{
|
||||
String str = context.getData(key);
|
||||
str += this.getCurrLoopObj();
|
||||
context.setData(key, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeBreakComponent;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("b")
|
||||
public class BCmp extends NodeBreakComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBreak() throws Exception {
|
||||
return this.getLoopIndex() == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeIteratorComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Component("it")
|
||||
public class ITCmp extends NodeIteratorComponent {
|
||||
@Override
|
||||
public Iterator<?> processIterator() throws Exception {
|
||||
List<String> list = this.getRequestData();
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
@@ -84,4 +84,12 @@ public class SwitchELSpringbootTest extends BaseTest {
|
||||
Assert.assertEquals("a==>i==>d",response.getExecuteStepStr());
|
||||
}
|
||||
|
||||
//switch返回如果是空,会走default选项
|
||||
@Test
|
||||
public void testSwitch8() throws Exception{
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
Assert.assertEquals("a==>j==>d",response.getExecuteStepStr());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Tingliang Wang
|
||||
* @email bytlwang@126.com
|
||||
* @Date 2022/12/09
|
||||
*/
|
||||
package com.yomahub.liteflow.test.switchcase.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeSwitchComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("j")
|
||||
public class JSwitchCmp extends NodeSwitchComponent {
|
||||
|
||||
@Override
|
||||
public String processSwitch() throws Exception {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
liteflow.rule-source=iterator/flow.xml
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
ITERATOR(it).DO(a);
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
ITERATOR(it).DO(a).BREAK(b);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -50,4 +50,10 @@
|
||||
);
|
||||
</chain>
|
||||
|
||||
<chain name="chain8">
|
||||
THEN(
|
||||
a,
|
||||
SWITCH(j).to(b, c).DEFAULT(d)
|
||||
);
|
||||
</chain>
|
||||
</flow>
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.yomahub.liteflow.test.iterator;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
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.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration("classpath:/iterator/application.xml")
|
||||
public class IteratorELSpringTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
//最简单的情况
|
||||
@Test
|
||||
public void testIt1() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("123", str);
|
||||
}
|
||||
|
||||
//迭代器带break
|
||||
@Test
|
||||
public void testIt2() throws Exception{
|
||||
List<String> list = ListUtil.toList("1","2","3");
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain2", list);
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
String str = context.getData("test");
|
||||
Assert.assertEquals("12", str);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("a")
|
||||
public class ACmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
String key = "test";
|
||||
DefaultContext context = this.getFirstContextBean();
|
||||
if (!context.hasData(key)){
|
||||
context.setData(key, this.getCurrLoopObj());
|
||||
}else{
|
||||
String str = context.getData(key);
|
||||
str += this.getCurrLoopObj();
|
||||
context.setData(key, str);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeBreakComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("b")
|
||||
public class BCmp extends NodeBreakComponent {
|
||||
|
||||
@Override
|
||||
public boolean processBreak() throws Exception {
|
||||
return this.getLoopIndex() == 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.yomahub.liteflow.test.iterator.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeIteratorComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
@Component("it")
|
||||
public class ITCmp extends NodeIteratorComponent {
|
||||
@Override
|
||||
public Iterator<?> processIterator() throws Exception {
|
||||
List<String> list = this.getRequestData();
|
||||
return list.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
|
||||
http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
|
||||
|
||||
<context:component-scan base-package="com.yomahub.liteflow.test.iterator.cmp" />
|
||||
|
||||
<bean id="springAware" class="com.yomahub.liteflow.spi.spring.SpringAware"/>
|
||||
|
||||
<bean class="com.yomahub.liteflow.spring.ComponentScanner"/>
|
||||
|
||||
<bean id="liteflowConfig" class="com.yomahub.liteflow.property.LiteflowConfig">
|
||||
<property name="ruleSource" value="iterator/flow.xml"/>
|
||||
</bean>
|
||||
|
||||
<bean id="flowExecutor" class="com.yomahub.liteflow.core.FlowExecutor">
|
||||
<constructor-arg name="liteflowConfig" ref="liteflowConfig"/>
|
||||
</bean>
|
||||
</beans>
|
||||
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
ITERATOR(it).DO(a);
|
||||
</chain>
|
||||
|
||||
<chain name="chain2">
|
||||
ITERATOR(it).DO(a).BREAK(b);
|
||||
</chain>
|
||||
</flow>
|
||||
8
pom.xml
8
pom.xml
@@ -39,7 +39,7 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<revision>2.9.6</revision>
|
||||
<revision>2.9.7</revision>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
@@ -52,7 +52,7 @@
|
||||
<dom4j.version>2.1.3</dom4j.version>
|
||||
<curator.version>5.3.0</curator.version>
|
||||
<junit.version>4.12</junit.version>
|
||||
<hutool-core.version>5.8.8</hutool-core.version>
|
||||
<hutool-core.version>5.8.11</hutool-core.version>
|
||||
<transmittable-thread-local.version>2.12.3</transmittable-thread-local.version>
|
||||
<curator-test.version>5.1.0</curator-test.version>
|
||||
<zkclient.version>0.10</zkclient.version>
|
||||
@@ -64,9 +64,7 @@
|
||||
<bytebuddy.version>1.11.13</bytebuddy.version>
|
||||
<aspectjweaver.version>1.8.13</aspectjweaver.version>
|
||||
<logback-classic.version>1.2.3</logback-classic.version>
|
||||
|
||||
<solon.version>1.12.0</solon.version>
|
||||
|
||||
<solon.version>2.0.0</solon.version>
|
||||
<netty.version>4.1.84.Final</netty.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
<httpclient.version>4.5.13</httpclient.version>
|
||||
|
||||
Reference in New Issue
Block a user