Merge branch 'dev' of https://gitee.com/dromara/liteFlow into issue/#I8MW6Q-2

 Conflicts:
	liteflow-core/src/main/java/com/yomahub/liteflow/script/ScriptExecutor.java
This commit is contained in:
DaleLee 2024-02-02 21:42:19 +08:00
commit d6b0e684cf
109 changed files with 2964 additions and 121 deletions

View File

@ -62,6 +62,31 @@ Looking forward to your use
Discord Link: [https://discord.gg/MpdBSBnFTu](https://discord.gg/MpdBSBnFTu)
## 👑LF CLUB Community
LF CLUB is a premium paid community founded by the author of LiteFlow.
LF CLUB can help all users of the LiteFlow framework, as well as potential developers who want to use LiteFlow.
LF CLUB provides the following services:
**1.Weekly releases of a condensed analysis series for LF. As long as users study along with the Planet series articles, they will definitely be able to fully grasp LF.**
**2.Provide a Q&A service where members can ask unlimited questions and receive detailed replies and guidance on the same day.**
**3.Each enrolled user is entitled to two remote one-on-one tutoring sessions per year as part of the remote assistance service.**
**4.Every 1 to 2 days, there will be updates on LF's current progress and the focus of the next version.**
The LF CLUB can solve all the problems you encounter when using the LiteFlow framework. It offers a series of courses to help you gain a deep understanding of the LiteFlow framework. Unlike the WeChat community, the LF CLUB prioritizes the importance of questions and provides detailed answers.
Exclusive content helps you gain a profound understanding without the need to search for answers on other platforms. The author personally teaches, providing expert guidance at your fingertips, eliminating the need to seek help from others.
To join the LF CLUB, please scan the QR code below or click on the image to go directly to the website.
<a href="https://t.zsxq.com/16VxfATen"><img src="static/img/zsxq-github.png"></a>
## 🦾Sponsor
**MISBoot低代码开发平台**

View File

@ -57,6 +57,30 @@ LiteFlow拥有极其详细易懂的文档体系能帮助你解决在使用框
LiteFlow期待你的了解
## 👑LF CLUB社区
LF CLUB是由LiteFlow作者创办的高级付费社区
LF CLUB能帮助到所有LiteFlow框架的使用者以及想使用LiteFlow的潜在开发者。
LF CLUB提供以下服务
**1.每周发布一篇LF的解析精华系列。从头开始解析LF只要跟着星球解析系列走使用者一定能完全掌握LF。**
**2.提供答疑服务,会员可以无限制提问,当天必定得到详细的回复和指导建议。**
**3.每个加入的用户每年提供2次远程一对一答疑远程协助服务。**
**4.每1到2天会分享LF目前的进度以及下一个版本的重点。**
LF CLUB里能解决你在使用LiteFlow框架时碰到的所有问题并有系列课程能帮助你深刻理解LiteFlow框架不同于微信社区LF CLUB的问题优先级程度是最高的且答疑非常详细。
独家内容帮助深刻理解,不用在其他平台去搜索问题的答案。作者亲授,相当于随时拥有专家在身边,不用再去求助其他人。
加入LF CLUB请扫描以下二维码或者直接点击图片也可以直达
<a href="https://t.zsxq.com/16jLy4Bj6"><img src="static/img/zsxq-gitee.png"></a>
## 🦾赞助商
**MISBoot低代码开发平台**

View File

@ -12,6 +12,12 @@ import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Deprecated
/**
* This class has been deprecated due to its only component retry function. Please use the retry method in the EL expression.
* @Deprecated
* @see # retry(int retryTimes) e.g. THEN( a, b.retry(3) ); WHEN( a, b ).retry(3);
*/
public @interface LiteflowRetry {
@LFAliasFor("retry")

View File

@ -3,6 +3,7 @@ package com.yomahub.liteflow.annotation.util;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LFAliasFor;
import java.lang.annotation.Annotation;
@ -10,15 +11,25 @@ import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 注解工具类
* 此工具类带缓存
*
* @author Bryan.Zhang
*/
public class AnnoUtil {
private static Map<String, Annotation> annoCacheMap = new ConcurrentHashMap<>();
public static <A extends Annotation> A getAnnotation(AnnotatedElement annotatedElement, Class<A> annotationType) {
String cacheKey = StrUtil.format("{}-{}", annotatedElement, annotationType.getSimpleName());
if (annoCacheMap.containsKey(cacheKey)){
return (A)annoCacheMap.get(cacheKey);
}
A annotation = AnnotationUtil.getAnnotation(annotatedElement, annotationType);
if (ObjectUtil.isNull(annotation)) {
return null;
@ -42,6 +53,8 @@ public class AnnoUtil {
}
});
annoCacheMap.put(cacheKey, annotation);
return annotation;
}
@ -53,5 +66,4 @@ public class AnnoUtil {
return null;
}
}
}

View File

@ -92,6 +92,7 @@ public class LiteFlowChainELBuilder {
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_SECONDS, Object.class, new MaxWaitSecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.MAX_WAIT_MILLISECONDS, Object.class, new MaxWaitMillisecondsOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.PARALLEL, Object.class, new ParallelOperator());
EXPRESS_RUNNER.addFunctionAndClassMethod(ChainConstant.RETRY, Object.class, new RetryOperator());
}
public static LiteFlowChainELBuilder createChain() {

View File

@ -0,0 +1,35 @@
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.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.RetryCondition;
/**
*
* @author Rain
* @since 2.12.0
*
*/
public class RetryOperator extends BaseOperator<Condition> {
@Override
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGteTwo(objects);
Executable executable = OperatorHelper.convert(objects[0], Executable.class);
Integer retryTimes = OperatorHelper.convert(objects[1], Integer.class);
RetryCondition retryCondition = new RetryCondition();
retryCondition.addExecutable(executable);
retryCondition.setRetryTimes(retryTimes);
if(objects.length > 2) {
Class[] forExceptions = new Class[objects.length - 2];
for(int i = 2; i < objects.length; i ++) {
String className = OperatorHelper.convert(objects[i], String.class);
forExceptions[i - 2] = Class.forName(className);
}
retryCondition.setRetryForExceptions(forExceptions);
}
return retryCondition;
}
}

View File

@ -94,4 +94,6 @@ public interface ChainConstant {
String EXTENDS = "extends";
String RETRY = "retry";
}

View File

@ -280,6 +280,10 @@ public abstract class NodeComponent {
return this.getSlot().getContextBean(contextBeanClazz);
}
public <T> T getContextBean(String contextName) {
return this.getSlot().getContextBean(contextName);
}
public String getNodeId() {
return nodeId;
}

View File

@ -101,6 +101,10 @@ public class LiteflowResponse {
return this.getSlot().getContextBean(contextBeanClazz);
}
public <T> T getContextBean(String contextName) {
return this.getSlot().getContextBean(contextName);
}
public Map<String, List<CmpStep>> getExecuteSteps() {
Map<String, List<CmpStep>> map = new LinkedHashMap<>();
this.getSlot().getExecuteSteps().forEach(cmpStep -> {

View File

@ -21,8 +21,6 @@ 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;
@ -30,6 +28,7 @@ import com.yomahub.liteflow.slot.Slot;
* Node节点实现可执行器 Node节点并不是单例的每构建一次都会copy出一个新的实例
*
* @author Bryan.Zhang
* @author luo yi
*/
public class Node implements Executable, Cloneable, Rollbackable{
@ -57,6 +56,9 @@ public class Node implements Executable, Cloneable, Rollbackable{
private String currChainId;
// node isAccess 结果主要用于 WhenCondition 的提前 isAccess 判断避免 isAccess 方法重复执行
private TransmittableThreadLocal<Boolean> accessResult = new TransmittableThreadLocal<>();
private TransmittableThreadLocal<Integer> loopIndexTL = new TransmittableThreadLocal<>();
private TransmittableThreadLocal<Object> currLoopObject = new TransmittableThreadLocal<>();
@ -125,16 +127,13 @@ public class Node implements Executable, Cloneable, Rollbackable{
throw new FlowSystemException("there is no instance for node id " + id);
}
Slot slot = DataBus.getSlot(slotIndex);
try {
// 把线程属性赋值给组件对象
instance.setSlotIndex(slotIndex);
instance.setRefNode(this);
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
// 判断是否可执行所以isAccess经常作为一个组件进入的实际判断要素用作检查slot里的参数的完备性
if (instance.isAccess()) {
if (getAccessResult() || instance.isAccess()) {
LOG.info("[O]start component[{}] execution", instance.getDisplayName());
// 这里开始进行重试的逻辑和主逻辑的运行
@ -142,8 +141,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
.buildNodeExecutor(instance.getNodeExecutorClass());
// 调用节点执行器进行执行
nodeExecutor.execute(instance);
}
else {
} else {
LOG.info("[X]skip component[{}] execution", instance.getDisplayName());
}
// 如果组件覆盖了isEnd方法或者在在逻辑中主要调用了setEnd(true)的话流程就会立马结束
@ -178,6 +176,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
instance.removeIsEnd();
instance.removeRefNode();
removeLoopIndex();
removeAccessResult();
}
}
@ -253,6 +252,19 @@ public class Node implements Executable, Cloneable, Rollbackable{
return currChainId;
}
public boolean getAccessResult() {
Boolean result = this.accessResult.get();
return result == null ? false : result;
}
public void setAccessResult(boolean accessResult) {
this.accessResult.set(accessResult);
}
public void removeAccessResult() {
this.accessResult.remove();
}
public void setLoopIndex(int index) {
this.loopIndexTL.set(index);
}
@ -299,6 +311,7 @@ public class Node implements Executable, Cloneable, Rollbackable{
Node node = (Node)this.clone();
node.loopIndexTL = new TransmittableThreadLocal<>();
node.currLoopObject = new TransmittableThreadLocal<>();
node.accessResult = new TransmittableThreadLocal<>();
return node;
}
}

View File

@ -0,0 +1,108 @@
package com.yomahub.liteflow.flow.element.condition;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.exception.ChainEndException;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Condition;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.slot.DataBus;
import java.util.Arrays;
import java.util.List;
/**
*
* @author Rain
* @since 2.12.0
*
*/
public class RetryCondition extends ThenCondition{
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
private Integer retryTimes;
private Class<? extends Exception>[] retryForExceptions = new Class[] { Exception.class };
public Class<? extends Exception>[] getRetryForExceptions() {
return retryForExceptions;
}
public void setRetryForExceptions(Class<? extends Exception>[] retryForExceptions) {
this.retryForExceptions = retryForExceptions;
}
public Integer getRetryTimes() {
return retryTimes;
}
public void setRetryTimes(Integer retryTimes) {
this.retryTimes = retryTimes;
}
@Override
public void executeCondition(Integer slotIndex) throws Exception {
int retryTimes = this.getRetryTimes() < 0 ? 0 : this.getRetryTimes();
List<Class<? extends Exception>> forExceptions = Arrays.asList(this.getRetryForExceptions());
for (int i = 0; i <= retryTimes; i ++) {
try {
if(i == 0) {
super.executeCondition(slotIndex);
} else {
retry(slotIndex, i);
}
break;
} catch (ChainEndException e) {
throw e;
} catch (Exception e) {
// 判断抛出的异常是不是指定异常的子类
boolean flag = forExceptions.stream().anyMatch(clazz -> clazz.isAssignableFrom(e.getClass()));
if(!flag || i >= retryTimes) {
if(retryTimes > 0) {
String retryFailMsg = StrFormatter.format("retry fail when executing the chain[{}] because {} occurs {}.",
this.getCurrChainId(), this.getCurrentExecutableId(), e);
LOG.error(retryFailMsg);
}
throw e;
} else {
DataBus.getSlot(slotIndex).removeException();
}
}
}
}
private void retry(Integer slotIndex, int retryTime) throws Exception {
LOG.info("{} performs {} retry ", this.getCurrentExecutableId(), retryTime);
super.executeCondition(slotIndex);
}
/**
* 获取当前组件的 id
*
* @return
*/
private String getCurrentExecutableId() {
// retryCondition 只有一个 Executable
Executable executable = this.getExecutableList().get(0);
if (ObjectUtil.isNotNull(executable.getId())) {
// 已经有 id
return executable.getId();
}
// 定义 id
switch (executable.getExecuteType()) {
// chain node 一般都有 id
case CHAIN:
return ((Chain) executable).getChainId();
case CONDITION:
return "condition-" + ((Condition) executable).getConditionType().getName();
case NODE:
return "node-" + ((Node) executable).getType().getCode();
default:
return "unknown-executable";
}
}
}

View File

@ -51,13 +51,17 @@ public class ThenCondition extends Condition {
}
catch (Exception e) {
Slot slot = DataBus.getSlot(slotIndex);
String chainId = this.getCurrChainId();
// 这里事先取到exception set到slot里为了方便finally取到exception
if (slot.isSubChain(chainId)) {
slot.setSubException(chainId, e);
}
else {
slot.setException(e);
//正常情况下slot不可能为null
//当设置了超时后还在运行的组件就有可能因为主流程已经结束释放slot而导致slot为null
if (slot != null){
String chainId = this.getCurrChainId();
// 这里事先取到exception set到slot里为了方便finally取到exception
if (slot.isSubChain(chainId)) {
slot.setSubException(chainId, e);
}
else {
slot.setException(e);
}
}
throw e;
}

View File

@ -31,9 +31,10 @@ public class AllOfParallelExecutor extends ParallelStrategyExecutor {
}
//allOf这个场景中不需要过滤
// allOf 这个场景中不需要过滤
@Override
protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
return stream;
}
}

View File

@ -1,12 +1,10 @@
package com.yomahub.liteflow.flow.parallel.strategy;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import com.yomahub.liteflow.flow.parallel.WhenFutureObj;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
/**
* 完成任一任务
@ -31,18 +29,4 @@ public class AnyOfParallelExecutor extends ParallelStrategyExecutor {
}
//在anyOf这个场景中需要过滤掉isAccess为false的场景
//因为不过滤这个的话如果加上了 any那么 isAccess false 那就是最快的了
//换句话说就是anyOf这个场景isAccess会被执行两次
@Override
protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
return stream.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;
}
});
}
}

View File

@ -6,6 +6,7 @@ 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.Node;
import com.yomahub.liteflow.flow.element.condition.FinallyCondition;
import com.yomahub.liteflow.flow.element.condition.PreCondition;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
@ -93,20 +94,25 @@ public abstract class ParallelStrategyExecutor {
// 1.先进行过滤前置和后置组件过滤掉因为在 EL Chain 处理的时候已经提出来了
// 2.过滤 isAccess false 的情况因为不过滤这个的话如果加上了 any那么 isAccess false 那就是最快的了
Stream<Executable> stream = executableList.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;
}
});
.filter(executable -> !(executable instanceof PreCondition) && !(executable instanceof FinallyCondition));
return filterAccess(stream, slotIndex);
}
//过滤isAccess的抽象接口方法
protected abstract Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex);
// 过滤 isAccess 的方法默认实现同时为避免同一个 node isAccess 方法重复执行 node 设置 isAccess 方法执行结果
protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
return stream.filter(executable -> {
try {
boolean access = executable.isAccess(slotIndex);
if (executable instanceof Node) {
((Node) executable).setAccessResult(access);
}
return access;
} catch (Exception e) {
LOG.error("there was an error when executing the when component isAccess", e);
return false;
}
});
}
/**
* 获取 WHEN 所需线程池

View File

@ -1,14 +1,12 @@
package com.yomahub.liteflow.flow.parallel.strategy;
import cn.hutool.core.collection.CollUtil;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.WhenCondition;
import com.yomahub.liteflow.flow.parallel.WhenFutureObj;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
/**
* 完成指定任务执行器使用 ID 进行比较
@ -77,19 +75,4 @@ public class SpecifyParallelExecutor extends ParallelStrategyExecutor {
}
//在must这个场景中需要过滤掉isAccess为false的场景
//因为不过滤这个的话如果加上了 any那么 isAccess false 那就是最快的了
//换句话说就是must这个场景isAccess会被执行两次
@Override
protected Stream<Executable> filterAccess(Stream<Executable> stream, Integer slotIndex) {
return stream.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;
}
});
}
}

View File

@ -7,11 +7,10 @@ import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.context.ContextBean;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.slot.DataBus;
import com.yomahub.liteflow.slot.Slot;
import java.util.List;
import javax.script.ScriptException;
import java.util.Map;
import java.util.function.BiConsumer;
@ -29,12 +28,6 @@ public abstract class ScriptExecutor {
public abstract void load(String nodeId, String script);
// 卸载脚本不包含 node
public abstract void unLoad(String nodeId);
// 获取该执行器下的所有 nodeId
public abstract List<String> getNodeIds();
public Object execute(ScriptExecuteWrap wrap) throws Exception{
try {
return executeScript(wrap);
@ -62,17 +55,7 @@ public abstract class ScriptExecutor {
// key的规则为自定义上下文的simpleName
// 比如你的自定义上下文为AbcContext那么key就为:abcContext
// 这里不统一放一个map的原因是考虑到有些用户会调用上下文里的方法而不是参数所以脚本语言的绑定表里也是放多个上下文
DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(o -> {
ContextBean contextBean = AnnoUtil.getAnnotation(o.getClass(), ContextBean.class);
String key;
if (contextBean != null && contextBean.value().trim().length() > 0) {
key = contextBean.value();
}
else {
key = StrUtil.lowerFirst(o.getClass().getSimpleName());
}
putConsumer.accept(key, o);
});
DataBus.getContextBeanList(wrap.getSlotIndex()).forEach(tuple -> putConsumer.accept(tuple.get(0), tuple.get(1)));
// 把wrap对象转换成元数据map
Map<String, Object> metaMap = BeanUtil.beanToMap(wrap);
@ -94,4 +77,12 @@ public abstract class ScriptExecutor {
ScriptBeanManager.getScriptBeanMap().forEach(putIfAbsentConsumer);
}
/**
* 利用相应框架编译脚本
*
* @param script 脚本
* @return boolean
* @throws Exception 例外
*/
public abstract Object compile(String script) throws Exception;
}

View File

@ -46,8 +46,7 @@ public abstract class JSR223ScriptExecutor extends ScriptExecutor {
@Override
public void load(String nodeId, String script) {
try {
CompiledScript compiledScript = ((Compilable) scriptEngine).compile(convertScript(script));
compiledScriptMap.put(nodeId, compiledScript);
compiledScriptMap.put(nodeId, (CompiledScript) compile(script));
}
catch (Exception e) {
String errorMsg = StrUtil.format("script loading error for node[{}], error msg:{}", nodeId, e.getMessage());
@ -85,4 +84,12 @@ public abstract class JSR223ScriptExecutor extends ScriptExecutor {
compiledScriptMap.clear();
}
@Override
public Object compile(String script) throws ScriptException {
if(scriptEngine == null) {
LOG.error("script engine has not init");
}
return ((Compilable) scriptEngine).compile(convertScript(script));
}
}

View File

@ -0,0 +1,104 @@
package com.yomahub.liteflow.script.validator;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.script.ScriptExecutor;
import java.util.*;
/**
* 脚本验证类
*
* @author Ge_Zuao
* @since 2.12.0
*/
public class ScriptValidator {
private static final LFLog LOG = LFLoggerManager.getLogger(ScriptValidator.class);
private static Map<ScriptTypeEnum, ScriptExecutor> scriptExecutors;
static {
List<ScriptExecutor> scriptExecutorList = new ArrayList<>();
scriptExecutors = new HashMap<>();
ServiceLoader.load(ScriptExecutor.class).forEach(scriptExecutorList::add);
scriptExecutorList.stream()
.peek(ScriptExecutor::init)
.forEach(scriptExecutor -> scriptExecutors.put(scriptExecutor.scriptType(), scriptExecutor));
}
/**
* 验证脚本逻辑的公共部分
*
* @param script 脚本
* @param scriptType 脚本类型
* @return boolean
*/
private static boolean validateScript(String script, ScriptTypeEnum scriptType){
// 未加载任何脚本模块
if(scriptExecutors.isEmpty()){
LOG.error("The loaded script modules not found.");
return false;
}
// 指定脚本语言未加载
if (scriptType != null && !scriptExecutors.containsKey(scriptType)) {
LOG.error(StrUtil.format("Specified script language {} was not found.", scriptType));
return false;
}
// 加载多个脚本语言需要指定语言验证
if (scriptExecutors.size() > 1 && scriptType == null) {
LOG.error("The loaded script modules more than 1. Please specify the script language.");
return false;
}
ScriptExecutor scriptExecutor = (scriptType != null) ? scriptExecutors.get(scriptType) : scriptExecutors.values().iterator().next();
try {
scriptExecutor.compile(script);
} catch (Exception e) {
LOG.error(StrUtil.format("{} Script component validate failure. ", scriptExecutor.scriptType()) + e.getMessage());
return false;
}
return true;
}
/**
* 只引入一种脚本语言时可以不指定语言验证
*
* @param script 脚本
* @return boolean
*/
public static boolean validate(String script){
return validateScript(script, null);
}
/**
* 指定脚本语言验证
*
* @param script 脚本
* @param scriptType 脚本类型
* @return boolean
*/
public static boolean validate(String script, ScriptTypeEnum scriptType){
return validateScript(script, scriptType);
}
/**
* 多语言脚本批量验证
*
* @param scripts 脚本
* @return boolean
*/
public static boolean validate(Map<ScriptTypeEnum, String> scripts){
for(Map.Entry<ScriptTypeEnum, String> script : scripts.entrySet()){
if(!validate(script.getValue(), script.getKey())){
return false;
}
}
return true;
}
}

View File

@ -7,10 +7,14 @@
*/
package com.yomahub.liteflow.slot;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.util.AnnoUtil;
import com.yomahub.liteflow.context.ContextBean;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.property.LiteflowConfig;
@ -74,13 +78,22 @@ public class DataBus {
.map((Function<Class<?>, Object>) ReflectUtil::newInstanceIfPossible)
.collect(Collectors.toList());
Slot slot = new Slot(contextBeanList);
return offerIndex(slot);
return offerSlotByBean(contextBeanList);
}
public static int offerSlotByBean(List<Object> contextList) {
Slot slot = new Slot(contextList);
List<Tuple> contextBeanList = contextList.stream().map(object -> {
ContextBean contextBean = AnnoUtil.getAnnotation(object.getClass(), ContextBean.class);
String contextKey;
if (contextBean != null && StrUtil.isNotBlank(contextBean.value())){
contextKey = contextBean.value();
}else{
contextKey = StrUtil.lowerFirst(object.getClass().getSimpleName());
}
return new Tuple(contextKey, object);
}).collect(Collectors.toList());
Slot slot = new Slot(contextBeanList);
return offerIndex(slot);
}
@ -128,7 +141,7 @@ public class DataBus {
return SLOTS.get(slotIndex);
}
public static List<Object> getContextBeanList(int slotIndex) {
public static List<Tuple> getContextBeanList(int slotIndex) {
Slot slot = getSlot(slotIndex);
return slot.getContextBeanList();
}

View File

@ -9,8 +9,10 @@ package com.yomahub.liteflow.slot;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.yomahub.liteflow.exception.NoSuchContextBeanException;
import com.yomahub.liteflow.exception.NullParamException;
import com.yomahub.liteflow.flow.element.Condition;
@ -29,6 +31,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Predicate;
/**
* Slot的抽象类实现
@ -90,14 +93,14 @@ public class Slot {
protected ConcurrentHashMap<String, Object> metaDataMap = new ConcurrentHashMap<>();
private List<Object> contextBeanList;
private List<Tuple> contextBeanList;
private static final ThreadLocal<Deque<Condition>> conditionStack = ThreadLocal.withInitial(LinkedList::new);
private static final TransmittableThreadLocal<Deque<Condition>> conditionStack = TransmittableThreadLocal.withInitial(ConcurrentLinkedDeque::new);
public Slot() {
}
public Slot(List<Object> contextBeanList) {
public Slot(List<Tuple> contextBeanList) {
this.contextBeanList = contextBeanList;
}
@ -448,21 +451,30 @@ public class Slot {
metaDataMap.remove(SUB_EXCEPTION_PREFIX + chainId);
}
public List<Object> getContextBeanList() {
public List<Tuple> getContextBeanList() {
return this.contextBeanList;
}
public <T> T getContextBean(Class<T> contextBeanClazz) {
T t = (T) contextBeanList.stream().filter(o -> o.getClass().getName().equals(contextBeanClazz.getName())).findFirst().orElse(null);
if (t == null) {
Tuple contextTuple = contextBeanList.stream().filter(tuple -> tuple.get(1).getClass().getName().equals(contextBeanClazz.getName())).findFirst().orElse(null);
if (contextTuple == null) {
contextBeanList.forEach(o -> LOG.info("ChainId[{}], Context class:{},Request class:{}", this.getChainId(), o.getClass().getName(), contextBeanClazz.getName()));
throw new NoSuchContextBeanException("this type is not in the context type passed in");
}
return t;
return contextTuple.get(1);
}
public <T> T getContextBean(String contextBeanKey) {
Tuple contextTuple = contextBeanList.stream().filter(tuple -> tuple.get(0).equals(contextBeanKey)).findFirst().orElse(null);
if (contextTuple == null) {
contextBeanList.forEach(o -> LOG.info("ChainId[{}], Context class:{},Request contextBeanKey:{}", this.getChainId(), o.getClass().getName(), contextBeanKey));
throw new NoSuchContextBeanException("this context key is not defined");
}
return contextTuple.get(1);
}
public <T> T getFirstContextBean() {
Class<T> firstContextBeanClazz = (Class<T>) this.getContextBeanList().get(0).getClass();
Class<T> firstContextBeanClazz = (Class<T>) this.getContextBeanList().get(0).get(1).getClass();
return this.getContextBean(firstContextBeanClazz);
}

View File

@ -38,8 +38,7 @@ public class GraalJavaScriptExecutor extends ScriptExecutor {
@Override
public void load(String nodeId, String script) {
try {
String wrapScript = StrUtil.format("function process(){{}} process();", script);
scriptMap.put(nodeId, Source.create("js", wrapScript));
scriptMap.put(nodeId, Source.create("js", (CharSequence) compile(script)));
}
catch (Exception e) {
String errorMsg = StrUtil.format("script loading error for node[{}], error msg:{}", nodeId, e.getMessage());
@ -99,4 +98,12 @@ public class GraalJavaScriptExecutor extends ScriptExecutor {
return ScriptTypeEnum.JS;
}
@Override
public Object compile(String script) throws Exception {
String wrapScript = StrUtil.format("function process(){{}} process();", script);
Context context = Context.newBuilder().allowAllAccess(true).engine(engine).build();
context.parse(Source.create("js", wrapScript));
return wrapScript;
}
}

View File

@ -23,12 +23,7 @@ public class JavaExecutor extends ScriptExecutor {
@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));
compiledScriptMap.put(nodeId, se);
compiledScriptMap.put(nodeId, (IScriptEvaluator) compile(script));
}catch (Exception e){
String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage());
throw new ScriptLoadException(errorMsg);
@ -66,6 +61,16 @@ public class JavaExecutor extends ScriptExecutor {
return ScriptTypeEnum.JAVA;
}
@Override
public Object compile(String script) throws Exception {
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));
return se;
}
private String convertScript(String script){
//替换掉publicprivateprotected等修饰词
String script1 = script.replaceAll("public class", "class")

View File

@ -13,6 +13,8 @@ import com.yomahub.liteflow.script.exception.ScriptLoadException;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -41,8 +43,7 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
@Override
public void load(String nodeId, String script) {
try {
InstructionSet instructionSet = expressRunner.getInstructionSetFromLocalCache(script);
compiledScriptMap.put(nodeId, instructionSet);
compiledScriptMap.put(nodeId, (InstructionSet) compile(script));
}
catch (Exception e) {
String errorMsg = StrUtil.format("script loading error for node[{}],error msg:{}", nodeId, e.getMessage());
@ -96,4 +97,9 @@ public class QLExpressScriptExecutor extends ScriptExecutor {
return ScriptTypeEnum.QLEXPRESS;
}
@Override
public Object compile(String script) throws Exception {
return expressRunner.getInstructionSetFromLocalCache(script);
}
}

View File

@ -4,6 +4,8 @@ import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.util.ReflectUtil;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.proxy.DeclWarpBean;
import com.yomahub.liteflow.log.LFLog;
import com.yomahub.liteflow.log.LFLoggerManager;
import com.yomahub.liteflow.spi.holder.DeclComponentParserHolder;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
@ -12,6 +14,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -23,38 +26,42 @@ import java.util.Map;
* @since 2.11.4
*/
public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor {
private final LFLog LOG = LFLoggerManager.getLogger(this.getClass());
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) registry;
Map<String, BeanDefinition> beanDefinitionHolderMap = (Map<String, BeanDefinition>)ReflectUtil.getFieldValue(defaultListableBeanFactory, "mergedBeanDefinitions");
beanDefinitionHolderMap.entrySet().stream().filter(entry -> {
Class<?> rawClass = entry.getValue().getResolvableType().getRawClass();
String[] beanDefinitionNames = defaultListableBeanFactory.getBeanDefinitionNames();
Arrays.stream(beanDefinitionNames).filter(beanName -> {
BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName);
Class<?> rawClass = getRawClassFromBeanDefinition(beanDefinition);
if (rawClass == null){
return false;
}else{
return Arrays.stream(rawClass.getMethods()).anyMatch(method -> AnnotationUtil.getAnnotation(method, LiteflowMethod.class) != null);
}
}).forEach(entry -> {
Class<?> rawClass = entry.getValue().getResolvableType().getRawClass();
}).forEach(beanName -> {
BeanDefinition beanDefinition = defaultListableBeanFactory.getMergedBeanDefinition(beanName);
Class<?> rawClass = getRawClassFromBeanDefinition(beanDefinition);
List<DeclWarpBean> declWarpBeanList = DeclComponentParserHolder.loadDeclComponentParser().parseDeclBean(rawClass);
declWarpBeanList.forEach(declWarpBean -> {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DeclWarpBean.class);
beanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
GenericBeanDefinition newBeanDefinition = new GenericBeanDefinition();
newBeanDefinition.setBeanClass(DeclWarpBean.class);
newBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
mutablePropertyValues.add("nodeId", declWarpBean.getNodeId());
mutablePropertyValues.add("nodeName", declWarpBean.getNodeName());
mutablePropertyValues.add("nodeType", declWarpBean.getNodeType());
mutablePropertyValues.add("rawClazz", declWarpBean.getRawClazz());
mutablePropertyValues.add("methodWrapBeanList", declWarpBean.getMethodWrapBeanList());
mutablePropertyValues.add("rawBean", entry.getValue());
beanDefinition.setPropertyValues(mutablePropertyValues);
mutablePropertyValues.add("rawBean", beanDefinition);
newBeanDefinition.setPropertyValues(mutablePropertyValues);
defaultListableBeanFactory.setAllowBeanDefinitionOverriding(true);
defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), beanDefinition);
defaultListableBeanFactory.registerBeanDefinition(declWarpBean.getNodeId(), newBeanDefinition);
});
});
}
@ -62,4 +69,19 @@ public class DeclBeanDefinition implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private Class<?> getRawClassFromBeanDefinition(BeanDefinition beanDefinition){
try{
Method method = ReflectUtil.getMethodByName(DeclBeanDefinition.class, "getResolvableType");
if (method != null){
Object resolvableType = ReflectUtil.invoke(beanDefinition, method);
return ReflectUtil.invoke(resolvableType, "getRawClass");
}else{
return ReflectUtil.invoke(beanDefinition, "getTargetType");
}
}catch (Exception e){
LOG.error("An error occurred while obtaining the rowClass.",e);
return null;
}
}
}

View File

@ -0,0 +1,120 @@
package com.yomahub.liteflow.test.retry;
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.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 javax.annotation.Resource;
@ExtendWith(SpringExtension.class)
@TestPropertySource(value = "classpath:/retry/application.properties")
@SpringBootTest(classes = RetryELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
public class RetryELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,89 @@
package com.yomahub.liteflow.test.retry.cmp;
import cn.hutool.core.collection.ListUtil;
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.exception.ELParseException;
import java.util.Iterator;
import java.util.List;
@LiteflowComponent
public class CmpConfig {
int flagb = 0;
int flagc = 0;
int flagd = 0;
int flagf = 0;
int flagi = 0;
int flagn = 0;
int flagm = 0;
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "a")
public void processA(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "b")
public void processB(NodeComponent bindCmp) {
flagb ++;
System.out.println("BCmp executed!");
if(flagb < 4) throw new RuntimeException();
else flagb = 0;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_FOR, nodeId = "c", nodeType = NodeTypeEnum.FOR)
public int processC(NodeComponent bindCmp) {
flagc ++;
System.out.println("CCmp executed!");
if(flagc < 4) throw new RuntimeException();
else return 1;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH, nodeId = "d", nodeType = NodeTypeEnum.SWITCH)
public String processD(NodeComponent bindCmp) {
flagd ++;
System.out.println("DCmp executed!");
if(flagd < 4) throw new RuntimeException();
else return "a";
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_IF, nodeId = "f", nodeType = NodeTypeEnum.IF)
public boolean processF(NodeComponent bindCmp) {
System.out.println("FCmp executed!");
flagf ++;
if(flagf < 4) throw new RuntimeException();
else return true;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_ITERATOR, nodeId = "i", nodeType = NodeTypeEnum.ITERATOR)
public Iterator<?> processI(NodeComponent bindCmp) {
flagi ++;
if(flagi < 4) throw new RuntimeException();
else {
List<String> list = ListUtil.toList("jack");
return list.iterator();
}
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS, nodeId = "m")
public void processM(NodeComponent bindCmp) {
flagm ++;
System.out.println("MCmp executed!");
if(flagm < 4) throw new ELParseException("MCmp error!");
else flagm = 0;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_WHILE, nodeId = "n", nodeType = NodeTypeEnum.WHILE)
public boolean processN(NodeComponent bindCmp) {
flagn ++;
System.out.println("NCmp executed!");
if(flagn < 4) throw new RuntimeException();
else return flagn == 4 ? true : false;
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=retry/flow.el.xml

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN( a, b ).retry(3);
</chain>
<chain name="chain2">
WHEN( a, b ).retry(3);
</chain>
<chain name="chain3">
THEN( a, b.retry(3) );
</chain>
<chain name="chain4">
FOR(c).DO(a).retry(3);
</chain>
<chain name="chain5">
SWITCH(d).TO(a).retry(3);
</chain>
<chain name="chain6">
IF(f, a).retry(3);
</chain>
<chain name="chain7">
WHILE(n).DO(a).retry(3);
</chain>
<chain name="chain8">
ITERATOR(i).DO(a).retry(3);
</chain>
<chain name="chain9">
THEN( a, b ).retry(1);
</chain>
<chain name="chain10">
THEN( a, FINALLY(b, a).retry(3) );
</chain>
<chain name="chain11">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
</chain>
<chain name="chain12">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
</chain>
</flow>

View File

@ -0,0 +1,117 @@
package com.yomahub.liteflow.test.retry;
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;
@TestPropertySource(value = "classpath:/retry/application.properties")
@SpringBootTest(classes = RetryELDeclSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
public class RetryELDeclSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.retry.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!");
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("b")
public class BCmp extends NodeComponent {
int flag = 0;
@Override
public void process() {
flag ++;
System.out.println("BCmp executed!");
if(flag < 4) throw new RuntimeException();
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeForComponent;
@LiteflowComponent("c")
public class CCmp extends NodeForComponent {
int flag = 0;
@Override
public int processFor() throws Exception {
flag ++;
System.out.println("CCmp executed!");
if(flag < 4) throw new RuntimeException();
else return 1;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
@LiteflowComponent("d")
public class DCmp extends NodeSwitchComponent {
int flag = 0;
@Override
public String processSwitch() throws Exception {
flag ++;
System.out.println("DCmp executed!");
if(flag < 4) throw new RuntimeException();
else return "a";
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
@LiteflowComponent("f")
public class FCmp extends NodeIfComponent {
int flag = 0;
@Override
public boolean processIf() throws Exception {
System.out.println("FCmp executed!");
flag ++;
if(flag < 4) throw new RuntimeException();
else return true;
}
}

View File

@ -0,0 +1,23 @@
package com.yomahub.liteflow.test.retry.cmp;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeIteratorComponent;
import java.util.Iterator;
import java.util.List;
@LiteflowComponent("i")
public class ICmp extends NodeIteratorComponent {
int flag = 0;
@Override
public Iterator<?> processIterator() throws Exception {
flag ++;
if(flag < 4) throw new RuntimeException();
else {
List<String> list = ListUtil.toList("jack");
return list.iterator();
}
}
}

View File

@ -0,0 +1,18 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ELParseException;
@LiteflowComponent("m")
public class MCmp extends NodeComponent {
int flag = 0;
@Override
public void process() throws Exception {
flag ++;
System.out.println("MCmp executed!");
if(flag < 4) throw new ELParseException("MCmp error!");
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeWhileComponent;
@LiteflowComponent("n")
public class NCmp extends NodeWhileComponent {
int flag = 0;
@Override
public boolean processWhile() throws Exception {
flag ++;
System.out.println("NCmp executed!");
if(flag < 4) throw new RuntimeException();
else return flag == 4 ? true : false;
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=retry/flow.el.xml

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN( a, b ).retry(3);
</chain>
<chain name="chain2">
WHEN( a, b ).retry(3);
</chain>
<chain name="chain3">
THEN( a, b.retry(3) );
</chain>
<chain name="chain4">
FOR(c).DO(a).retry(3);
</chain>
<chain name="chain5">
SWITCH(d).TO(a).retry(3);
</chain>
<chain name="chain6">
IF(f, a).retry(3);
</chain>
<chain name="chain7">
WHILE(n).DO(a).retry(3);
</chain>
<chain name="chain8">
ITERATOR(i).DO(a).retry(3);
</chain>
<chain name="chain9">
THEN( a, b ).retry(1);
</chain>
<chain name="chain10">
THEN( a, FINALLY(b, a).retry(3) );
</chain>
<chain name="chain11">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
</chain>
<chain name="chain12">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
</chain>
</flow>

View File

@ -0,0 +1,116 @@
package com.yomahub.liteflow.test.retry;
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;
public class RetryTest extends BaseTest {
private static FlowExecutor flowExecutor;
@BeforeAll
public static void init() {
LiteflowConfig config = new LiteflowConfig();
config.setRuleSource("retry/flow.el.xml");
flowExecutor = FlowExecutorHolder.loadInstance(config);
}
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,11 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class ACmp extends NodeComponent {
@Override
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class BCmp extends NodeComponent {
int flag = 0;
@Override
public void process() {
flag ++;
System.out.println("BCmp executed!");
if(flag < 4) throw new RuntimeException();
else flag = 0;
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeForComponent;
public class CCmp extends NodeForComponent {
int flag = 0;
@Override
public int processFor() throws Exception {
flag ++;
System.out.println("CCmp executed!");
if(flag < 4) throw new RuntimeException();
else return 1;
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeSwitchComponent;
public class DCmp extends NodeSwitchComponent {
int flag = 0;
@Override
public String processSwitch() throws Exception {
flag ++;
System.out.println("DCmp executed!");
if(flag < 4) throw new RuntimeException();
else return "a";
}
}

View File

@ -0,0 +1,15 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeIfComponent;
public class FCmp extends NodeIfComponent {
int flag = 0;
@Override
public boolean processIf() throws Exception {
System.out.println("FCmp executed!");
flag ++;
if(flag < 4) throw new RuntimeException();
else return true;
}
}

View File

@ -0,0 +1,21 @@
package com.yomahub.liteflow.test.retry.cmp;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.core.NodeIteratorComponent;
import java.util.Iterator;
import java.util.List;
public class ICmp extends NodeIteratorComponent {
int flag = 0;
@Override
public Iterator<?> processIterator() throws Exception {
flag ++;
if(flag < 4) throw new RuntimeException();
else {
List<String> list = ListUtil.toList("jack");
return list.iterator();
}
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ELParseException;
public class MCmp extends NodeComponent {
int flag = 0;
@Override
public void process() throws Exception {
flag ++;
System.out.println("MCmp executed!");
if(flag < 4) throw new ELParseException("MCmp error!");
else flag = 0;
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeWhileComponent;
public class NCmp extends NodeWhileComponent {
int flag = 0;
@Override
public boolean processWhile() throws Exception {
flag ++;
System.out.println("NCmp executed!");
if(flag < 4) throw new RuntimeException();
else return flag == 4 ? true : false;
}
}

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="a" class="com.yomahub.liteflow.test.retry.cmp.ACmp"/>
<node id="b" class="com.yomahub.liteflow.test.retry.cmp.BCmp"/>
<node id="c" class="com.yomahub.liteflow.test.retry.cmp.CCmp"/>
<node id="d" class="com.yomahub.liteflow.test.retry.cmp.DCmp"/>
<node id="f" class="com.yomahub.liteflow.test.retry.cmp.FCmp"/>
<node id="i" class="com.yomahub.liteflow.test.retry.cmp.ICmp"/>
<node id="n" class="com.yomahub.liteflow.test.retry.cmp.NCmp"/>
<node id="m" class="com.yomahub.liteflow.test.retry.cmp.MCmp"/>
</nodes>
<chain name="chain1">
THEN( a, b ).retry(3);
</chain>
<chain name="chain2">
WHEN( a, b ).retry(3);
</chain>
<chain name="chain3">
THEN( a, b.retry(3) );
</chain>
<chain name="chain4">
FOR(c).DO(a).retry(3);
</chain>
<chain name="chain5">
SWITCH(d).TO(a).retry(3);
</chain>
<chain name="chain6">
IF(f, a).retry(3);
</chain>
<chain name="chain7">
WHILE(n).DO(a).retry(3);
</chain>
<chain name="chain8">
ITERATOR(i).DO(a).retry(3);
</chain>
<chain name="chain9">
THEN( a, b ).retry(1);
</chain>
<chain name="chain10">
THEN( a, FINALLY(b, a).retry(3) );
</chain>
<chain name="chain11">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
</chain>
<chain name="chain12">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
</chain>
</flow>

View File

@ -0,0 +1,47 @@
package com.yomahub.liteflow.test.script.aviator.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.aviator.AviatorScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@SpringBootTest(classes = ValidateAviatorScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateAviatorScriptComponentTest {
@Test
public void testAviatorScriptComponentValidateFunction(){
String correctScript = " use java.util.Date;\n" +
" use cn.hutool.core.date.DateUtil;\n" +
" let d = DateUtil.formatDateTime(new Date());\n" +
" println(d);\n" +
"\n" +
" a = 2;\n" +
" b = 3;\n" +
"\n" +
" setData(defaultContext, \"s1\", a*b);";
// 语法错误
String wrongScript = " use java.util.Date;\n" +
" use cn.hutool.core.date.DateUtil;\n" +
" lt d = DateUtil.formatDateTime(new Date());\n" +
" println(d);\n" +
"\n" +
" a = 2;\n" +
" b = 3;\n" +
"\n" +
" setData(defaultContext, \"s1\", a*b);";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
}
}

View File

@ -0,0 +1,55 @@
package com.yomahub.liteflow.test.script.graaljs.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.graaljs.GraalJavaScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateGraaljsScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateGraaljsScriptComponentTest {
@Test
public void testGraaljsScriptComponentValidateFunction(){
String correctScript = " var a=3;\n" +
" var b=2;\n" +
" var c=1;\n" +
" var d=5;\n" +
"\n" +
" function addByArray(values) {\n" +
" var sum = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" sum += values[i];\n" +
" }\n" +
" return sum;\n" +
" }\n" +
"\n" +
" var result = addByArray([a,b,c,d]);\n" +
"\n" +
" defaultContext.setData(\"s1\",parseInt(result));";
// 语法错误
String wrongScript = " var a=3;\n" +
" var b=2;\n" +
" var c=1;\n" +
" var d=5;\n" +
"\n" +
" fn addByArray(values) {\n" +
" var sum = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" sum += values[i];\n" +
" }\n" +
" return sum;\n" +
" }\n" +
"\n" +
" var result = addByArray([a,b,c,d]);\n" +
"\n" +
" defaultContext.setData(\"s1\",parseInt(result));";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.AVIATOR));
}
}

View File

@ -0,0 +1,83 @@
package com.yomahub.liteflow.test.script.groovy.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.groovy.GroovyScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateGroovyScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateGroovyScriptComponentTest {
@Test
public void testGroovyScriptComponentValidateFunction(){
String correctScript = " import cn.hutool.core.collection.ListUtil\n" +
" import cn.hutool.core.date.DateUtil\n" +
"\n" +
" import java.util.function.Consumer\n" +
" import java.util.function.Function\n" +
" import java.util.stream.Collectors\n" +
"\n" +
" def date = DateUtil.parse(\"2022-10-17 13:31:43\")\n" +
" println(date)\n" +
" defaultContext.setData(\"demoDate\", date)\n" +
"\n" +
" List<String> list = ListUtil.toList(\"a\", \"b\", \"c\")\n" +
"\n" +
" List<String> resultList = list.stream().map(s -> \"hello,\" + s).collect(Collectors.toList())\n" +
"\n" +
" defaultContext.setData(\"resultList\", resultList)\n" +
"\n" +
" class Student {\n" +
" int studentID\n" +
" String studentName\n" +
" }\n" +
"\n" +
" Student student = new Student()\n" +
" student.studentID = 100301\n" +
" student.studentName = \"张三\"\n" +
" defaultContext.setData(\"student\", student)\n" +
"\n" +
" def a = 3\n" +
" def b = 2\n" +
" defaultContext.setData(\"s1\", a * b)";
// 语法错误
String wrongScript = " import cn.hutool.core.collection.ListUtil\n" +
" import cn.hutool.core.date.DateUtil\n" +
"\n" +
" import java.util.function.Consumer\n" +
" import java.util.function.Function\n" +
" import java.util.stream.Collectors\n" +
"\n" +
" d date = DateUtil.parse(\"2022-10-17 13:31:43\")\n" +
" println(date)\n" +
" defaultContext.setData(\"demoDate\", date)\n" +
"\n" +
" List<String> list = ListUtil.toList(\"a\", \"b\", \"c\")\n" +
"\n" +
" List<String> resultList = list.stream().map(s -> \"hello,\" + s).collect(Collectors.toList())\n" +
"\n" +
" defaultContext.setData(\"resultList\", resultList)\n" +
"\n" +
" class Student {\n" +
" int studentID\n" +
" String studentName\n" +
" }\n" +
"\n" +
" Student student = new Student()\n" +
" student.studentID = 100301\n" +
" student.studentName = \"张三\"\n" +
" defaultContext.setData(\"student\", student)\n" +
"\n" +
" def a = 3\n" +
" def b = 2\n" +
" defaultContext.setData(\"s1\", a * b)";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
}
}

View File

@ -0,0 +1,67 @@
package com.yomahub.liteflow.test.script.java.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.java.JavaExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateJavaScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateJavaScriptComponentTest {
@Test
public void testJavaScriptComponentValidateFunction(){
String correctScript = "import com.alibaba.fastjson2.JSON;\n" +
" import com.yomahub.liteflow.slot.DefaultContext;\n" +
" import com.yomahub.liteflow.spi.holder.ContextAwareHolder;\n" +
" import com.yomahub.liteflow.test.script.java.common.cmp.TestDomain;\n" +
" import com.yomahub.liteflow.script.body.JaninoCommonScriptBody;\n" +
" import com.yomahub.liteflow.script.ScriptExecuteWrap;\n" +
"\n" +
" public class Demo implements JaninoCommonScriptBody {\n" +
" public Void body(ScriptExecuteWrap wrap) {\n" +
" int v1 = 2;\n" +
" int v2 = 3;\n" +
" DefaultContext ctx = (DefaultContext) wrap.getCmp().getFirstContextBean();\n" +
" ctx.setData(\"s1\", v1 * v2);\n" +
"\n" +
" TestDomain domain = (TestDomain) ContextAwareHolder.loadContextAware().getBean(TestDomain.class);\n" +
" System.out.println(JSON.toJSONString(domain));\n" +
" String str = domain.sayHello(\"jack\");\n" +
" ctx.setData(\"hi\", str);\n" +
"\n" +
" return null;\n" +
" }\n" +
" }";
// 未指定类型名错误
String wrongScript = "import com.alibaba.fastjson2.JSON;\n" +
" import com.yomahub.liteflow.slot.DefaultContext;\n" +
" import com.yomahub.liteflow.spi.holder.ContextAwareHolder;\n" +
" import com.yomahub.liteflow.test.script.java.common.cmp.TestDomain;\n" +
" import com.yomahub.liteflow.script.body.JaninoCommonScriptBody;\n" +
" import com.yomahub.liteflow.script.ScriptExecuteWrap;\n" +
"\n" +
" public class Demo implements JaninoCommonScriptBody {\n" +
" public Void body(ScriptExecuteWrap wrap) {\n" +
" v1 = 2;\n" +
" int v2 = 3;\n" +
" DefaultContext ctx = (DefaultContext) wrap.getCmp().getFirstContextBean();\n" +
" ctx.setData(\"s1\", v1 * v2);\n" +
"\n" +
" TestDomain domain = (TestDomain) ContextAwareHolder.loadContextAware().getBean(TestDomain.class);\n" +
" System.out.println(JSON.toJSONString(domain));\n" +
" String str = domain.sayHello(\"jack\");\n" +
" ctx.setData(\"hi\", str);\n" +
"\n" +
" return null;\n" +
" }\n" +
" }";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.GROOVY));
}
}

View File

@ -0,0 +1,55 @@
package com.yomahub.liteflow.test.script.javascript.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.javascript.JavaScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateJavaScriptScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateJavaScriptScriptComponentTest {
@Test
public void testJavaScriptScriptComponentValidateFunction(){
String correctScript = "var a=3;\n" +
" var b=2;\n" +
" var c=1;\n" +
" var d=5;\n" +
"\n" +
" function addByArray(values) {\n" +
" var sum = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" sum += values[i];\n" +
" }\n" +
" return sum;\n" +
" }\n" +
"\n" +
" var result = addByArray([a,b,c,d]);\n" +
"\n" +
" defaultContext.setData(\"s1\",parseInt(result));";
// 语法错误
String wrongScript = "var a=3;\n" +
" var b=2;\n" +
" var c=1;\n" +
" var d=5;\n" +
"\n" +
" fon addByArray(values) {\n" +
" var sum = 0;\n" +
" for (var i = 0; i < values.length; i++) {\n" +
" sum += values[i];\n" +
" }\n" +
" return sum;\n" +
" }\n" +
"\n" +
" var result = addByArray([a,b,c,d]);\n" +
"\n" +
" defaultContext.setData(\"s1\",parseInt(result));";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JAVA));
}
}

View File

@ -0,0 +1,41 @@
package com.yomahub.liteflow.test.script.lua.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.lua.LuaScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateLuaScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateLuaScriptComponentTest {
@Test
public void testLuaScriptComponentValidateFunction(){
String correctScript = " local a=6\n" +
" local b=10\n" +
" if(a>5) then\n" +
" b=5\n" +
" else\n" +
" b=2\n" +
" end\n" +
" defaultContext:setData(\"s1\",a*b)\n" +
" defaultContext:setData(\"s2\",_meta:get(\"nodeId\"))";
// 语法错误
String wrongScript = " local a=6\n" +
" local b=10\n" +
" if(a>5) tn\n" +
" b=5\n" +
" else\n" +
" b=2\n" +
" end\n" +
" defaultContext:setData(\"s1\",a*b)\n" +
" defaultContext:setData(\"s2\",_meta:get(\"nodeId\"))";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.JS));
}
}

View File

@ -0,0 +1,62 @@
package com.yomahub.liteflow.test.script.multi.language.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.ScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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 java.util.HashMap;
import java.util.Map;
@SpringBootTest(classes = ValidateMultiLanguageScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateMultiLanguageScriptComponentTest {
@Test
public void testMultiLanguageScriptComponentValidateFunction(){
String correctGroovyScript = " class Student {\n" +
" int studentID;\n" +
" String studentName;\n" +
"\n" +
" public void setStudentID(int id){\n" +
" this.studentID = id;\n" +
" }\n" +
" }\n" +
"\n" +
" Student student = new Student()\n" +
" student.studentID = 100301\n" +
" student.studentName = \"张三\"\n" +
" defaultContext.setData(\"student\", student)\n" +
"\n" +
" def a = 3\n" +
" def b = 2\n" +
" defaultContext.setData(\"s1\", a * b)";
String correctJavascriptScript = " var student = defaultContext.getData(\"student\");\n" +
" student.setStudentID(10032);";
String correctPythonScript = " a = 3\n" +
" s1 = defaultContext.getData(\"s1\")\n" +
" defaultContext.setData(\"s1\",s1*a)";
// 语法错误 缩进
String wrongPythonScript = " a = 3\n" +
" s1 = defaultContext.getData(\"s1\")\n" +
" defaultContext.setData(\"s1\",s1*a)";
// 在加载多脚本时使用默认验证方法会错误
Assertions.assertFalse(ScriptValidator.validate(correctGroovyScript));
// 多语言脚本验证 正确样例
Map<ScriptTypeEnum, String> correctData = new HashMap<>();
correctData.put(ScriptTypeEnum.GROOVY, correctGroovyScript);
correctData.put(ScriptTypeEnum.JS, correctJavascriptScript);
correctData.put(ScriptTypeEnum.PYTHON, correctPythonScript);
Assertions.assertTrue(ScriptValidator.validate(correctData));
// 多语言脚本验证 错误样例
Map<ScriptTypeEnum, String> wrongData = new HashMap<>();
wrongData.put(ScriptTypeEnum.GROOVY, correctGroovyScript);
wrongData.put(ScriptTypeEnum.JS, correctJavascriptScript);
wrongData.put(ScriptTypeEnum.PYTHON, wrongPythonScript);
Assertions.assertFalse(ScriptValidator.validate(wrongData));
}
}

View File

@ -0,0 +1,57 @@
package com.yomahub.liteflow.test.script.python.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.python.PythonScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidatePythonScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidatePythonScriptComponentTest {
@Test
public void testPythonScriptComponentValidateFunction(){
String correctScript = " import json\n" +
"\n" +
" x='{\"name\": \"杰克\", \"age\": 75, \"nationality\": \"China\"}'\n" +
" jsonData=json.loads(x)\n" +
" name=jsonData['name']\n" +
" defaultContext.setData(\"name\", name.decode('utf-8'))\n" +
"\n" +
"\n" +
" a=6\n" +
" b=10\n" +
" if a>5:\n" +
" b=5\n" +
" print '你好'.decode('UTF-8')\n" +
" else:\n" +
" print 'hi'\n" +
" defaultContext.setData(\"s1\",a*b)\n" +
" defaultContext.setData(\"td\", td.sayHi(\"jack\"))";
// 语法错误 缩进
String wrongScript = " import json\n" +
"\n" +
" x='{\"name\": \"杰克\", \"age\": 75, \"nationality\": \"China\"}'\n" +
" jsonData=json.loads(x)\n" +
" name=jsonData['name']\n" +
" defaultContext.setData(\"name\", name.decode('utf-8'))\n" +
"\n" +
"\n" +
" a=6\n" +
" b=10\n" +
" if a>5:\n" +
" b=5\n" +
" print '你好'.decode('UTF-8')\n" +
" else:\n" +
" print 'hi'\n" +
" defaultContext.setData(\"s1\",a*b)\n" +
" defaultContext.setData(\"td\", td.sayHi(\"jack\"))";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.LUA));
}
}

View File

@ -0,0 +1,35 @@
package com.yomahub.liteflow.test.script.qlexpress.validate;
import com.yomahub.liteflow.enums.ScriptTypeEnum;
import com.yomahub.liteflow.script.qlexpress.QLExpressScriptExecutor;
import com.yomahub.liteflow.script.validator.ScriptValidator;
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;
@SpringBootTest(classes = ValidateQLExpressScriptComponentTest.class)
@EnableAutoConfiguration
public class ValidateQLExpressScriptComponentTest {
@Test
public void testQLExpressScriptComponentValidateFunction(){
String correctScript = " count = defaultContext.getData(\"count\");\n" +
" if(count > 100){\n" +
" return \"a\";\n" +
" }else{\n" +
" return \"b\";\n" +
" }";
// 语法错误
String wrongScript = " count = defaultContext.getData(\"count\");\n" +
" if(count > 100){\n" +
" return \"a\";\n" +
" }el{\n" +
" return \"b\";\n" +
" }";
Assertions.assertTrue(ScriptValidator.validate(correctScript));
Assertions.assertFalse(ScriptValidator.validate(wrongScript));
Assertions.assertTrue(ScriptValidator.validate(correctScript, ScriptTypeEnum.QLEXPRESS));
Assertions.assertFalse(ScriptValidator.validate(correctScript, ScriptTypeEnum.PYTHON));
}
}

View File

@ -6,7 +6,6 @@ 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.snack.ONode;
import org.noear.solon.annotation.Import;
import org.noear.solon.annotation.Inject;
import org.noear.solon.test.SolonJUnit5Extension;

View File

@ -10,6 +10,7 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.noear.solon.annotation.Import;
import org.noear.solon.annotation.Inject;
import org.noear.solon.test.SolonJUnit5Extension;
import java.util.concurrent.Future;
/**

View File

@ -0,0 +1,113 @@
package com.yomahub.liteflow.test.retry;
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;
@ExtendWith(SolonJUnit5Extension.class)
@TestPropertySource("classpath:/retry/application.properties")
public class RetrySpringbootTest extends BaseTest {
@Inject
private FlowExecutor flowExecutor;
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.retry.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!");
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.noear.solon.annotation.Component;
@Component("b")
public class BCmp extends NodeComponent {
int flag = 0;
@Override
public void process() {
flag ++;
System.out.println("BCmp executed!");
if(flag < 4) throw new RuntimeException();
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeForComponent;
import org.noear.solon.annotation.Component;
@Component("c")
public class CCmp extends NodeForComponent {
int flag = 0;
@Override
public int processFor() throws Exception {
flag ++;
System.out.println("CCmp executed!");
if(flag < 4) throw new RuntimeException();
else return 1;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import org.noear.solon.annotation.Component;
@Component("d")
public class DCmp extends NodeSwitchComponent {
int flag = 0;
@Override
public String processSwitch() throws Exception {
flag ++;
System.out.println("DCmp executed!");
if(flag < 4) throw new RuntimeException();
else return "a";
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeIfComponent;
import org.noear.solon.annotation.Component;
@Component("f")
public class FCmp extends NodeIfComponent {
int flag = 0;
@Override
public boolean processIf() throws Exception {
System.out.println("FCmp executed!");
flag ++;
if(flag < 4) throw new RuntimeException();
else return true;
}
}

View File

@ -0,0 +1,23 @@
package com.yomahub.liteflow.test.retry.cmp;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.core.NodeIteratorComponent;
import org.noear.solon.annotation.Component;
import java.util.Iterator;
import java.util.List;
@Component("i")
public class ICmp extends NodeIteratorComponent {
int flag = 0;
@Override
public Iterator<?> processIterator() throws Exception {
flag ++;
if(flag < 4) throw new RuntimeException();
else {
List<String> list = ListUtil.toList("jack");
return list.iterator();
}
}
}

View File

@ -0,0 +1,18 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ELParseException;
import org.noear.solon.annotation.Component;
@Component("m")
public class MCmp extends NodeComponent {
int flag = 0;
@Override
public void process() throws Exception {
flag ++;
System.out.println("MCmp executed!");
if(flag < 4) throw new ELParseException("MCmp error!");
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeWhileComponent;
import org.noear.solon.annotation.Component;
@Component("n")
public class NCmp extends NodeWhileComponent {
int flag = 0;
@Override
public boolean processWhile() throws Exception {
flag ++;
System.out.println("NCmp executed!");
if(flag < 4) throw new RuntimeException();
else return flag == 4 ? true : false;
}
}

View File

@ -10,7 +10,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.noear.solon.annotation.Import;
import org.noear.solon.annotation.Inject;
import org.noear.solon.test.SolonJUnit5Extension;
import org.noear.solon.test.annotation.TestPropertySource;
@ExtendWith(SolonJUnit5Extension.class)

View File

@ -0,0 +1 @@
liteflow.rule-source=retry/flow.el.xml

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN( a, b ).retry(3);
</chain>
<chain name="chain2">
WHEN( a, b ).retry(3);
</chain>
<chain name="chain3">
THEN( a, b.retry(3) );
</chain>
<chain name="chain4">
FOR(c).DO(a).retry(3);
</chain>
<chain name="chain5">
SWITCH(d).TO(a).retry(3);
</chain>
<chain name="chain6">
IF(f, a).retry(3);
</chain>
<chain name="chain7">
WHILE(n).DO(a).retry(3);
</chain>
<chain name="chain8">
ITERATOR(i).DO(a).retry(3);
</chain>
<chain name="chain9">
THEN( a, b ).retry(1);
</chain>
<chain name="chain10">
THEN( a, FINALLY(b, a).retry(3) );
</chain>
<chain name="chain11">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
</chain>
<chain name="chain12">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
</chain>
</flow>

View File

@ -17,5 +17,4 @@ public class ACmp extends NodeComponent {
public void process() {
System.out.println("ACmp executed!");
}
}

View File

@ -18,4 +18,9 @@ public class CCmp extends NodeComponent {
System.out.println("CCmp executed!");
}
@Override
public boolean isAccess() {
System.out.println("hello");
return true;
}
}

View File

@ -0,0 +1,38 @@
package com.yomahub.liteflow.test.contextBean;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.contextBean.context.TestContext;
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;
/**
* ContextBean测试
*
* @author Bryan.Zhang
*/
@TestPropertySource(value = "classpath:/contextBean/application.properties")
@SpringBootTest(classes = ContextBeanSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({ "com.yomahub.liteflow.test.contextBean.cmp" })
public class ContextBeanSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 最简单的情况
@Test
public void testContextBean1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg", TestContext.class);
Assertions.assertTrue(response.isSuccess());
TestContext context = response.getContextBean("skuContext");
Assertions.assertEquals("J001", context.getSkuCode());
}
}

View File

@ -0,0 +1,23 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.contextBean.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.test.contextBean.context.TestContext;
import org.springframework.stereotype.Component;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
TestContext context = this.getContextBean("skuContext");
context.setSkuCode("J001");
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,21 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.contextBean.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!");
}
}

View File

@ -0,0 +1,32 @@
package com.yomahub.liteflow.test.contextBean.context;
import com.yomahub.liteflow.context.ContextBean;
@ContextBean("skuContext")
public class TestContext {
private String skuCode;
private String skuName;
public TestContext(String skuCode, String skuName) {
this.skuCode = skuCode;
this.skuName = skuName;
}
public String getSkuCode() {
return skuCode;
}
public void setSkuCode(String skuCode) {
this.skuCode = skuCode;
}
public String getSkuName() {
return skuName;
}
public void setSkuName(String skuName) {
this.skuName = skuName;
}
}

View File

@ -182,6 +182,16 @@ public class MaxWaitSecondsELSpringbootTest extends BaseTest {
assertNotTimeout("chain2");
}
// 测试超时情况下组件还在运行的场景是否会报错
@Test
public void testChain3() {
DefaultContext context = new DefaultContext();
context.setData("test", "123");
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg", context);
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals(TimeoutException.class, response.getCause().getClass());
}
private void assertTimeout(String chainId) {
LiteflowResponse response = flowExecutor.execute2Resp(chainId, "arg");
Assertions.assertFalse(response.isSuccess());

View File

@ -0,0 +1,20 @@
package com.yomahub.liteflow.test.maxWaitSeconds.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DefaultContext;
@LiteflowComponent("e")
public class ECmp extends NodeComponent {
@Override
public void process() throws Exception{
DefaultContext context = this.getFirstContextBean();
for (int i = 0; i < 10; i++) {
String str = context.getData("test");
System.out.println(str);
Thread.sleep(1000);
}
System.out.println("ECmp executed!");
}
}

View File

@ -0,0 +1,117 @@
package com.yomahub.liteflow.test.retry;
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;
@TestPropertySource(value = "classpath:/retry/application.properties")
@SpringBootTest(classes = RetrySpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.retry.cmp"})
public class RetrySpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.retry.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!");
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
@LiteflowComponent("b")
public class BCmp extends NodeComponent {
int flag = 0;
@Override
public void process() {
flag ++;
System.out.println("BCmp executed!");
if(flag < 4) throw new RuntimeException();
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeForComponent;
@LiteflowComponent("c")
public class CCmp extends NodeForComponent {
int flag = 0;
@Override
public int processFor() throws Exception {
flag ++;
System.out.println("CCmp executed!");
if(flag < 4) throw new RuntimeException();
else return 1;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
@LiteflowComponent("d")
public class DCmp extends NodeSwitchComponent {
int flag = 0;
@Override
public String processSwitch() throws Exception {
flag ++;
System.out.println("DCmp executed!");
if(flag < 4) throw new RuntimeException();
else return "a";
}
}

View File

@ -0,0 +1,16 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
@LiteflowComponent("f")
public class FCmp extends NodeIfComponent {
int flag = 0;
@Override
public boolean processIf() throws Exception {
System.out.println("FCmp executed!");
flag ++;
if(flag < 4) throw new RuntimeException();
else return true;
}
}

View File

@ -0,0 +1,23 @@
package com.yomahub.liteflow.test.retry.cmp;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeIteratorComponent;
import java.util.Iterator;
import java.util.List;
@LiteflowComponent("i")
public class ICmp extends NodeIteratorComponent {
int flag = 0;
@Override
public Iterator<?> processIterator() throws Exception {
flag ++;
if(flag < 4) throw new RuntimeException();
else {
List<String> list = ListUtil.toList("jack");
return list.iterator();
}
}
}

View File

@ -0,0 +1,18 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.exception.ELParseException;
@LiteflowComponent("m")
public class MCmp extends NodeComponent {
int flag = 0;
@Override
public void process() throws Exception {
flag ++;
System.out.println("MCmp executed!");
if(flag < 4) throw new ELParseException("MCmp error!");
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeWhileComponent;
@LiteflowComponent("n")
public class NCmp extends NodeWhileComponent {
int flag = 0;
@Override
public boolean processWhile() throws Exception {
flag ++;
System.out.println("NCmp executed!");
if(flag < 4) throw new RuntimeException();
else return flag == 4 ? true : false;
}
}

View File

@ -0,0 +1 @@
liteflow.rule-source=contextBean/flow.xml

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "liteflow" "liteflow.dtd">
<flow>
<chain name="chain1">
THEN(a,b);
</chain>
</flow>

View File

@ -107,4 +107,8 @@
<!-- 不超时 -->
testChain.maxWaitSeconds(3);
</chain>
<chain name="chain3">
THEN(a, b, e).maxWaitSeconds(8);
</chain>
</flow>

View File

@ -0,0 +1 @@
liteflow.rule-source=retry/flow.el.xml

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
THEN( a, b ).retry(3);
</chain>
<chain name="chain2">
WHEN( a, b ).retry(3);
</chain>
<chain name="chain3">
THEN( a, b.retry(3) );
</chain>
<chain name="chain4">
FOR(c).DO(a).retry(3);
</chain>
<chain name="chain5">
SWITCH(d).TO(a).retry(3);
</chain>
<chain name="chain6">
IF(f, a).retry(3);
</chain>
<chain name="chain7">
WHILE(n).DO(a).retry(3);
</chain>
<chain name="chain8">
ITERATOR(i).DO(a).retry(3);
</chain>
<chain name="chain9">
THEN( a, b ).retry(1);
</chain>
<chain name="chain10">
THEN( a, FINALLY(b, a).retry(3) );
</chain>
<chain name="chain11">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.ELParseException", "com.yomahub.liteflow.exception.FlowSystemException");
</chain>
<chain name="chain12">
THEN( a, m ).retry(3, "com.yomahub.liteflow.exception.AndOrConditionException");
</chain>
</flow>

View File

@ -0,0 +1,114 @@
package com.yomahub.liteflow.test.retry;
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;
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:/retry/application.xml")
public class RetrySpringTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// THEN测试
@Test
public void testThen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b==>a==>b==>a==>b", response.getExecuteStepStr());
}
// WHEN测试
@Test
public void testWhen() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assertions.assertTrue(response.isSuccess());
}
// node测试
@Test
public void testNode() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assertions.assertTrue(response.isSuccess());
}
// FOR测试
@Test
public void testFor() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("c==>c==>c==>c==>a", response.getExecuteStepStr());
}
// SWITCH测试
@Test
public void testSwitch() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("d==>d==>d==>d==>a", response.getExecuteStepStr());
}
// IF测试
@Test
public void testIf() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("f==>f==>f==>f==>a", response.getExecuteStepStr());
}
// WHILE测试
@Test
public void testWhile() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("n==>n==>n==>n==>a==>n", response.getExecuteStepStr());
}
// ITERATOR测试
@Test
public void testIterator() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "arg");
Assertions.assertTrue(response.isSuccess());
Assertions.assertEquals("i==>i==>i==>i==>a", response.getExecuteStepStr());
}
// 重试失败提示信息测试
@Test
public void testRetryFail() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain9", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b==>a==>b", response.getExecuteStepStr());
}
// FINALLY测试
@Test
public void testFinally() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain10", "arg");
Assertions.assertFalse(response.isSuccess());
Assertions.assertEquals("a==>b", response.getExecuteStepStr());
}
// 指定异常重试测试1
@Test
public void testException1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain11", "arg");
Assertions.assertTrue(response.isSuccess());
}
// 指定异常重试测试2
@Test
public void testException2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain12", "arg");
Assertions.assertFalse(response.isSuccess());
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.retry.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!");
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("b")
public class BCmp extends NodeComponent {
int flag = 0;
@Override
public void process() {
flag ++;
System.out.println("BCmp executed!");
if(flag < 4) throw new RuntimeException();
else flag = 0;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeForComponent;
import org.springframework.stereotype.Component;
@Component("c")
public class CCmp extends NodeForComponent {
int flag = 0;
@Override
public int processFor() throws Exception {
flag ++;
System.out.println("CCmp executed!");
if(flag < 4) throw new RuntimeException();
else return 1;
}
}

View File

@ -0,0 +1,17 @@
package com.yomahub.liteflow.test.retry.cmp;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import org.springframework.stereotype.Component;
@Component("d")
public class DCmp extends NodeSwitchComponent {
int flag = 0;
@Override
public String processSwitch() throws Exception {
flag ++;
System.out.println("DCmp executed!");
if(flag < 4) throw new RuntimeException();
else return "a";
}
}

Some files were not shown because too many files have changed in this diff Show More