Merge remote-tracking branch 'origin/dev' into dev

# Conflicts:
#	liteflow-rule-plugin/pom.xml
#	liteflow-testcase-el/pom.xml
This commit is contained in:
mll 2022-09-19 17:08:47 +08:00
commit 73084b8da3
282 changed files with 7745 additions and 254 deletions

View File

@ -1,5 +1,6 @@
package com.yomahub.liteflow.annotation;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import java.lang.annotation.*;
@ -11,4 +12,14 @@ import java.lang.annotation.*;
public @interface LiteflowMethod {
LiteFlowMethodEnum value();
// 节点ID用于区分节点
// 默认为空 则按照Spring模式下BeanName为准
String nodeId() default "";
/**
* CMP类型定义
* @return AnnotationNodeTypeEnum
*/
AnnotationNodeTypeEnum nodeType() default AnnotationNodeTypeEnum.COMMON;
}

View File

@ -8,7 +8,7 @@ import java.lang.annotation.*;
* @author Bryan.Zhang
* @since 2.6.0
*/
@Target({ElementType.TYPE})
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

View File

@ -122,7 +122,7 @@ public class LiteFlowNodeBuilder {
FlowBus.addIfScriptNode(this.node.getId(), this.node.getName(), this.node.getScript());
}
} catch (Exception e) {
String errMsg = StrUtil.format("An exception occurred while building the node[{}]", this.node.getId());
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(),e.getMessage());
LOG.error(errMsg, e);
throw new NodeBuildException(errMsg);
}

View File

@ -6,21 +6,22 @@ import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.builder.el.operator.*;
import com.yomahub.liteflow.exception.DataNofFoundException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.exception.FlowSystemException;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Executable;
import com.yomahub.liteflow.flow.element.condition.Condition;
import com.yomahub.liteflow.flow.element.condition.FinallyCondition;
import com.yomahub.liteflow.flow.element.condition.PreCondition;
import com.yomahub.liteflow.flow.element.condition.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Chain基于代码形式的组装器
* EL表达式规则专属组装器
*
* @author Bryan.Zhang
* @since 2.8.0
*/
@ -41,7 +42,7 @@ public class LiteFlowChainELBuilder {
private final List<Condition> finallyConditionList;
//EL解析引擎
private final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();;
private final static ExpressRunner EXPRESS_RUNNER = new ExpressRunner();
static {
//初始化QLExpress的Runner
@ -86,13 +87,13 @@ public class LiteFlowChainELBuilder {
}
public LiteFlowChainELBuilder setEL(String elStr) {
if (StrUtil.isBlank(elStr)){
if (StrUtil.isBlank(elStr)) {
String errMsg = StrUtil.format("no conditionList in this chain[{}]", chain.getChainName());
throw new FlowSystemException(errMsg);
}
List<String> errorList = new ArrayList<>();
try{
try {
DefaultContext<String, Object> context = new DefaultContext<>();
//这里一定要先放chain再放node因为node优先于chain所以当重名时node会覆盖掉chain
@ -111,8 +112,8 @@ public class LiteFlowChainELBuilder {
//为什么只寻找第一层而不往下寻找了呢
//因为这是一个规范如果在后面的层级中出现pre和finally语义上也不好理解所以pre和finally只能定义在第一层
//如果硬是要在后面定义则执行的时候会忽略相关代码已做了判断
for (Executable executable : condition.getExecutableList()){
if (executable instanceof PreCondition){
for (Executable executable : condition.getExecutableList()) {
if (executable instanceof PreCondition) {
this.preConditionList.add((PreCondition) executable);
} else if (executable instanceof FinallyCondition) {
this.finallyConditionList.add((FinallyCondition) executable);
@ -122,9 +123,13 @@ public class LiteFlowChainELBuilder {
//把主要的condition加入
this.conditionList.add(condition);
return this;
}catch (QLException e){
} catch (QLException e) {
// EL 底层会包装异常这里是曲线处理
if (Objects.equals(e.getCause().getMessage(), DataNofFoundException.MSG)) {
throw new ELParseException(String.format("[node/chain is not exist or node/chain not register]elStr=%s", elStr));
}
throw new ELParseException(e.getCause().getMessage());
}catch (Exception e){
} catch (Exception e) {
throw new ELParseException(e.getMessage());
}
}

View File

@ -11,10 +11,10 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class AnyOperator extends BaseOperator {
public class AnyOperator extends BaseOperator<WhenCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);

View File

@ -15,10 +15,10 @@ import com.yomahub.liteflow.flow.element.condition.IfCondition;
* @author Bryan.Zhang
* @since 2.8.5
*/
public class ElifOperator extends BaseOperator {
public class ElifOperator extends BaseOperator<IfCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqThree(objects);
//解析caller

View File

@ -11,10 +11,10 @@ import com.yomahub.liteflow.flow.element.condition.IfCondition;
* @author Bryan.Zhang
* @since 2.8.5
*/
public class ElseOperator extends BaseOperator {
public class ElseOperator extends BaseOperator<IfCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public IfCondition build(Object[] objects) throws Exception {
// 参数只能是1个但这里为什么是2个呢第一个是caller第二个才是参数
OperatorHelper.checkObjectSizeEqTwo(objects);

View File

@ -12,10 +12,10 @@ import com.yomahub.liteflow.flow.element.condition.FinallyCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class FinallyOperator extends BaseOperator {
public class FinallyOperator extends BaseOperator<FinallyCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public FinallyCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
FinallyCondition finallyCondition = new FinallyCondition();

View File

@ -13,12 +13,12 @@ import org.slf4j.LoggerFactory;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class IdOperator extends BaseOperator {
public class IdOperator extends BaseOperator<Condition> {
private final Logger LOG = LoggerFactory.getLogger(this.getClass());
@Override
public Object buildCondition(Object[] objects) throws Exception {
public Condition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Condition condition = OperatorHelper.convert(objects[0], Condition.class);

View File

@ -15,10 +15,10 @@ import com.yomahub.liteflow.flow.element.condition.IfCondition;
* @author Bryan.Zhang
* @since 2.8.5
*/
public class IfOperator extends BaseOperator {
public class IfOperator extends BaseOperator<IfCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public IfCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEq(objects, 2, 3);
//解析第一个参数

View File

@ -11,10 +11,10 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class IgnoreErrorOperator extends BaseOperator {
public class IgnoreErrorOperator extends BaseOperator<WhenCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
WhenCondition condition = OperatorHelper.convert(objects[0], WhenCondition.class);

View File

@ -16,10 +16,10 @@ import com.yomahub.liteflow.property.LiteflowConfigGetter;
* @author Bryan.Zhang
* @since 2.8.3
*/
public class NodeOperator extends BaseOperator {
public class NodeOperator extends BaseOperator<Node> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeNeqOne(objects);
String nodeId;

View File

@ -12,10 +12,10 @@ import com.yomahub.liteflow.flow.element.condition.PreCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class PreOperator extends BaseOperator {
public class PreOperator extends BaseOperator<PreCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public PreCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
PreCondition preCondition = new PreCondition();

View File

@ -14,10 +14,10 @@ import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class SwitchOperator extends BaseOperator {
public class SwitchOperator extends BaseOperator<SwitchCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeNeqOne(objects);
Node switchNode;

View File

@ -12,10 +12,10 @@ import com.yomahub.liteflow.flow.element.Node;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class TagOperator extends BaseOperator {
public class TagOperator extends BaseOperator<Node> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public Node build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
Node node = OperatorHelper.convert(objects[0], Node.class);

View File

@ -12,10 +12,10 @@ import com.yomahub.liteflow.flow.element.condition.ThenCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ThenOperator extends BaseOperator {
public class ThenOperator extends BaseOperator<ThenCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public ThenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
ThenCondition thenCondition = new ThenCondition();

View File

@ -11,10 +11,10 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ThreadPoolOperator extends BaseOperator {
public class ThreadPoolOperator extends BaseOperator<WhenCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeEqTwo(objects);
WhenCondition whenCondition = OperatorHelper.convert(objects[0], WhenCondition.class);

View File

@ -12,10 +12,10 @@ import com.yomahub.liteflow.flow.element.condition.SwitchCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class ToOperator extends BaseOperator {
public class ToOperator extends BaseOperator<SwitchCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public SwitchCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtTwo(objects);
SwitchCondition switchCondition = OperatorHelper.convert(objects[0], SwitchCondition.class);

View File

@ -11,10 +11,10 @@ import com.yomahub.liteflow.flow.element.condition.WhenCondition;
* @author Bryan.Zhang
* @since 2.8.0
*/
public class WhenOperator extends BaseOperator {
public class WhenOperator extends BaseOperator<WhenCondition> {
@Override
public Object buildCondition(Object[] objects) throws Exception {
public WhenCondition build(Object[] objects) throws Exception {
OperatorHelper.checkObjectSizeGtZero(objects);
WhenCondition whenCondition = new WhenCondition();

View File

@ -3,6 +3,7 @@ package com.yomahub.liteflow.builder.el.operator.base;
import com.ql.util.express.Operator;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.ELParseException;
import com.yomahub.liteflow.flow.element.Executable;
/**
* BaseOperator 为了强化 executeInner 方法会捕获抛出的 QLException 错误输出友好的错误提示
@ -10,25 +11,27 @@ import com.yomahub.liteflow.exception.ELParseException;
* @author gaibu
* @since 2.8.6
*/
public abstract class BaseOperator extends Operator {
public abstract class BaseOperator<T extends Executable> extends Operator {
@Override
public Object executeInner(Object[] objects) throws Exception {
try {
return buildCondition(objects);
} catch (QLException e) {
throw e;
} catch (Exception e) {
throw new ELParseException("errors occurred in EL parsing");
}
}
@Override
public T executeInner(Object[] objects) throws Exception {
try {
// 检查 node chain 是否已经注册
OperatorHelper.checkNodeAndChainExist(objects);
return build(objects);
} catch (QLException e) {
throw e;
} catch (Exception e) {
throw new ELParseException("errors occurred in EL parsing");
}
}
/**
* 构建 EL 条件
*
* @param objects objects
* @return Condition
* @throws Exception Exception
*/
public abstract Object buildCondition(Object[] objects) throws Exception;
/**
* 构建 EL 条件
*
* @param objects objects
* @return Condition
* @throws Exception Exception
*/
public abstract T build(Object[] objects) throws Exception;
}

View File

@ -1,6 +1,9 @@
package com.yomahub.liteflow.builder.el.operator.base;
import com.ql.util.express.exception.QLException;
import com.yomahub.liteflow.exception.DataNofFoundException;
import java.util.Objects;
/**
* Operator 常用工具类
@ -9,121 +12,134 @@ import com.ql.util.express.exception.QLException;
*/
public class OperatorHelper {
/**
* 检查参数数量不等于1
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeNeqOne(Object[] objects) throws QLException {
checkObjectSizeNeq(objects, 1);
}
/**
* 检查参数数量不等于1
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeNeqOne(Object[] objects) throws QLException {
checkObjectSizeNeq(objects, 1);
}
/**
* 检查参数数量不等于 size
*
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
*/
public static void checkObjectSizeNeq(Object[] objects, int size) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量不等于 size
*
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
*/
public static void checkObjectSizeNeq(Object[] objects, int size) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量大于 0
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGtZero(Object[] objects) throws QLException {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
}
/**
* 检查参数数量大于 0
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGtZero(Object[] objects) throws QLException {
if (objects.length == 0) {
throw new QLException("parameter is empty");
}
}
/**
* 检查参数数量大于 2
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGtTwo(Object[] objects) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length <= 1) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量大于 2
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeGtTwo(Object[] objects) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length <= 1) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量等于 2
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqTwo(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 2);
}
/**
* 检查参数数量等于 2
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqTwo(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 2);
}
/**
* 检查参数数量等于 3
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqThree(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 3);
}
/**
* 检查参数数量等于 3
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkObjectSizeEqThree(Object[] objects) throws QLException {
checkObjectSizeEq(objects, 3);
}
/**
* 检查参数数量等于 size
*
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size) throws QLException {
checkObjectSizeGtZero(objects);
/**
* 检查参数数量等于 size
*
* @param objects objects
* @param size 参数数量
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size) {
throw new QLException("parameter error");
}
}
if (objects.length != size) {
throw new QLException("parameter error");
}
}
/**
* 检查参数数量等于 size1 size2
*
* @param objects objects
* @param size1 参数数量1
* @param size2 参数数量2
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size1, int size2) throws QLException {
checkObjectSizeGtZero(objects);
/**
* 检查参数数量等于 size1 size2
*
* @param objects objects
* @param size1 参数数量1
* @param size2 参数数量2
* @throws QLException QLException
*/
public static void checkObjectSizeEq(Object[] objects, int size1, int size2) throws QLException {
checkObjectSizeGtZero(objects);
if (objects.length != size1 && objects.length != size2) {
throw new QLException("parameter error");
}
}
if (objects.length != size1 && objects.length != size2) {
throw new QLException("parameter error");
}
}
/**
* 转换 object 为指定的类型
*
* @param object object
* @param tClass 指定类型
* @param <T> 返回类型
* @return T
* @throws QLException QLException
*/
public static <T> T convert(Object object, Class<T> tClass) throws QLException {
if (tClass.isInstance(object)) {
return (T) object;
}
/**
* 转换 object 为指定的类型
*
* @param object object
* @param tClass 指定类型
* @param <T> 返回类型
* @return T
* @throws QLException QLException
*/
public static <T> T convert(Object object, Class<T> tClass) throws QLException {
if (tClass.isInstance(object)) {
return (T) object;
}
throw new QLException("The caller must be " + tClass.getName() + " item");
}
throw new QLException("The caller must be " + tClass.getName() + " item");
}
/**
* 检查 node chain 是否已经注册
*
* @param objects objects
* @throws QLException QLException
*/
public static void checkNodeAndChainExist(Object[] objects) throws QLException {
for (Object object : objects) {
if (Objects.isNull(object)) {
throw new QLException(DataNofFoundException.MSG);
}
}
}
}

View File

@ -1,10 +1,18 @@
package com.yomahub.liteflow.core.proxy;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.*;
import cn.hutool.core.lang.Tuple;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.exception.ComponentMethodDefineErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import com.yomahub.liteflow.util.SerialsUtil;
import net.bytebuddy.ByteBuddy;
@ -12,13 +20,16 @@ import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
/**
@ -42,7 +53,7 @@ public class ComponentProxy {
this.clazz = clazz;
}
public Object getProxy() throws Exception{
public List<NodeComponent> getProxyList() throws Exception{
//这里要判断bean是否是spring代理过的bean如果是代理过的bean需要取到原class对象
Class<?> beanClazz;
if (LiteFlowProxyUtil.isCglibProxyClass(bean.getClass())){
@ -50,31 +61,104 @@ public class ComponentProxy {
}else{
beanClazz = bean.getClass();
}
//得到当前bean里所覆盖的组件方法(一定是被@LiteFlowMethod修饰的)自己定义的不算
List<String> methodStrList = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
//得到当前bean里所覆盖的LiteflowMethod(一定是被@LiteFlowMethod修饰的)自己定义的不算
Map<String, List<Method>> methodListMap = Arrays.stream(beanClazz.getDeclaredMethods()).filter(
m -> m.getAnnotation(LiteflowMethod.class) != null
).map(m -> {
LiteflowMethod liteflowMethod = m.getAnnotation(LiteflowMethod.class);
return liteflowMethod.value().getMethodName();
}).collect(Collectors.toList());
).collect(Collectors.groupingBy(
m -> m.getAnnotation(LiteflowMethod.class).nodeId()
));
//创建对象
//这里package进行了重设放到了被代理对象的所在目录
//生成的对象也加了上被代理对象拥有的注解
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
return new ByteBuddy().subclass(clazz)
.name(StrUtil.format("{}.ByteBuddy${}${}",
ClassUtil.getPackage(bean.getClass()),
nodeId,
SerialsUtil.generateShortUUID()))
.method(ElementMatchers.namedOneOf(methodStrList.toArray(new String[]{})))
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
.annotateType(bean.getClass().getAnnotations())
.make()
.load(ComponentProxy.class.getClassLoader())
.getLoaded()
.newInstance();
return methodListMap.entrySet().stream().map(entry -> {
// 获取当前节点的原有注解LiteFlowRetry 之类的规则注解
Annotation[] beanClassAnnotation = bean.getClass().getAnnotations();
// 如果entry的key为空字符串则是为了兼容老版本的写法没有指定nodeId的情况
// 判断是否是方法级创造节点
boolean isMethodCreate = !StrUtil.isEmpty(entry.getKey());
// 获取当前bean 真实的nodeId
String activeNodeId = isMethodCreate ? entry.getKey() : nodeId;
// 获取当前节点所有的@LiteflowRetry @LiteflowMethod注解对
List<Tuple> tupleList = entry.getValue().stream().map(m ->
new Tuple(m.getAnnotation(LiteflowRetry.class), m.getAnnotation(LiteflowMethod.class))
).collect(Collectors.toList());
// 获取当前节点的所有LiteFlowMethod注解
List<LiteflowMethod> methodList = tupleList.stream().map(tuple -> ((LiteflowMethod)tuple.get(1)))
.filter(Objects::nonNull)
.collect(Collectors.toList());
// nodeType去重
List<? extends Class<? extends NodeComponent>> classes = methodList.stream()
.map(LiteflowMethod::nodeType)
.map(AnnotationNodeTypeEnum::getCmpClass)
.distinct()
.collect(Collectors.toList());
// 相同nodeId里只能定义同一种的类型的NodeComponent
boolean legal = classes.size() == 1;
if (!legal){
throw new LiteFlowException("The cmpClass of the same nodeId must be the same,you declared nodeId:" + activeNodeId + ",cmpClass:" + classes);
}
// 当前节点实际LiteflowRetry注解
AtomicReference<LiteflowRetry> liteflowRetryAtomicReference = new AtomicReference<>(null);
// 相同nodeId只能有一个LiteflowRetry定义方法,且必须再Process方法上
boolean illegal = tupleList.stream().anyMatch(
tuple -> {
LiteflowRetry liteflowRetry = tuple.get(0);
LiteflowMethod liteflowMethod = tuple.get(1);
boolean existRetry = liteflowRetry != null;
boolean isProcess = liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_IF)
|| liteflowMethod.value().equals(LiteFlowMethodEnum.PROCESS_SWITCH);
// 如果是再Process方法上的liteflowRetry注解则默认为真实节点
if (isProcess && existRetry) {
liteflowRetryAtomicReference.set(liteflowRetry);
}
// 如果存在existRetry注解但是不是在Process方法上则为非法
return existRetry && !isProcess;
}
);
if (illegal){
throw new LiteFlowException("the retry annotation (@LiteflowRetry) must be declared on the PROCESS method");
}
// 生成nodeCmp的类型默认为全局定义的clazz
Class<?> cmpClazz;
cmpClazz = clazz;
// 判断是否是方法声明的组件
if (isMethodCreate){
cmpClazz = methodList.iterator().next().nodeType().getCmpClass();
LiteflowRetry liteflowRetry;
if ((liteflowRetry = liteflowRetryAtomicReference.get()) != null){
// 增加LiteFlowRetry注解到注解数组里
List<Annotation> annotations = Arrays.stream(beanClassAnnotation)
.filter(a -> !a.annotationType().equals(LiteflowRetry.class))
.collect(Collectors.toList());
annotations.add(liteflowRetry);
beanClassAnnotation = new Annotation[annotations.size()];
annotations.toArray(beanClassAnnotation);
}
}
try {
//创建对象
//这里package进行了重设放到了被代理对象的所在目录
//生成的对象也加了上被代理对象拥有的注解
//被拦截的对象也根据被代理对象根据@LiteFlowMethod所标注的进行了动态判断
Object instance = new ByteBuddy().subclass(cmpClazz)
.name(StrUtil.format("{}.ByteBuddy${}${}",
ClassUtil.getPackage(bean.getClass()),
activeNodeId,
SerialsUtil.generateShortUUID()))
.method(ElementMatchers.namedOneOf(methodList.stream().map(m -> m.value().getMethodName()).toArray(String[]::new)))
.intercept(InvocationHandlerAdapter.of(new AopInvocationHandler(bean)))
.annotateType(beanClassAnnotation)
.make()
.load(ComponentProxy.class.getClassLoader())
.getLoaded()
.newInstance();
NodeComponent nodeComponent = (NodeComponent) instance;
// 重设nodeId
nodeComponent.setNodeId(activeNodeId);
return nodeComponent;
} catch (Exception e) {
throw new LiteFlowException(e);
}
}).collect(Collectors.toList());
}
public class AopInvocationHandler implements InvocationHandler {
@ -93,6 +177,10 @@ public class ComponentProxy {
List<LiteFlowMethodBean> liteFlowMethodBeanList = Arrays.stream(ReflectUtil.getMethods(bean.getClass())).filter(m -> {
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return ObjectUtil.isNotNull(liteFlowMethod);
}).filter(m -> {
// 过滤不属于当前NodeComponent的方法
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return StrUtil.isEmpty(liteFlowMethod.nodeId())|| Objects.equals(liteFlowMethod.nodeId(),((NodeComponent) proxy).getNodeId());
}).map(m -> {
LiteflowMethod liteFlowMethod = m.getAnnotation(LiteflowMethod.class);
return new LiteFlowMethodBean(liteFlowMethod.value().getMethodName(), m);

View File

@ -0,0 +1,47 @@
package com.yomahub.liteflow.enums;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
/**
* 注解节点类型枚举
*
* @author Sorghum
* @since 2.9.0
*/
public enum AnnotationNodeTypeEnum {
/**
* 普通节点
*/
COMMON("普通", NodeComponent.class),
/**
* 选择节点
*/
SWITCH("选择", NodeSwitchComponent.class),
/**
* 条件节点
*/
IF("条件", NodeIfComponent.class),;
/**
* 描述
*/
final String desc;
/**
* cmp类
*/
final Class<? extends NodeComponent> cmpClass;
AnnotationNodeTypeEnum(String desc, Class<? extends NodeComponent> cmpClass) {
this.desc = desc;
this.cmpClass = cmpClass;
}
/**
* 得到Node定义类
* @return node定义类
*/
public Class<? extends NodeComponent> getCmpClass() {
return cmpClass;
}
}

View File

@ -0,0 +1,32 @@
package com.yomahub.liteflow.exception;
/**
* @author tangkc
*/
public class DataNofFoundException extends RuntimeException {
public static final String MSG = "DataNofFoundException";
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public DataNofFoundException() {
this.message = MSG;
}
public DataNofFoundException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -9,16 +9,17 @@
package com.yomahub.liteflow.flow;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.*;
import com.yomahub.liteflow.exception.NullNodeTypeException;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.enums.FlowParserTypeEnum;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.ComponentCannotRegisterException;
import com.yomahub.liteflow.exception.NullNodeTypeException;
import com.yomahub.liteflow.flow.element.Chain;
import com.yomahub.liteflow.flow.element.Node;
import com.yomahub.liteflow.parser.LocalJsonFlowParser;
import com.yomahub.liteflow.parser.LocalXmlFlowParser;
import com.yomahub.liteflow.parser.LocalYmlFlowParser;
@ -36,7 +37,10 @@ import com.yomahub.liteflow.util.LiteFlowProxyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 流程元数据类
@ -156,7 +160,7 @@ public class FlowBus {
try {
//判断此类是否是声明式的组件如果是声明式的组件就用动态代理生成实例
//如果不是声明式的就用传统的方式进行判断
NodeComponent cmpInstance = null;
List<NodeComponent> cmpInstances = new ArrayList<>();
if (LiteFlowProxyUtil.isDeclareCmp(cmpClazz)){
//这里的逻辑要仔细看下
//如果是spring体系把原始的类往spring上下文中进行注册那么会走到ComponentScanner中
@ -167,42 +171,50 @@ public class FlowBus {
ContextAware contextAware = ContextAwareHolder.loadContextAware();
Object bean = ContextAwareHolder.loadContextAware().registerBean(nodeId, cmpClazz);
if (LocalContextAware.class.isAssignableFrom(contextAware.getClass())){
cmpInstance = LiteFlowProxyUtil.proxy2NodeComponent(bean, nodeId);
cmpInstances = LiteFlowProxyUtil.proxy2NodeComponent(bean, nodeId);
}else {
cmpInstance = (NodeComponent) bean;
cmpInstances = ListUtil.toList((NodeComponent) bean);
}
}else{
//以node方式配置本质上是为了适配无spring的环境如果有spring环境其实不用这么配置
//这里的逻辑是判断是否能从spring上下文中取到如果没有spring则就是new instance了
//如果是script类型的节点因为class只有一个所以也不能注册进spring上下文注册的时候需要new Instance
if (!CollectionUtil.newArrayList(NodeTypeEnum.SCRIPT, NodeTypeEnum.SWITCH_SCRIPT, NodeTypeEnum.IF_SCRIPT).contains(type)){
cmpInstance = (NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz);
cmpInstances = ListUtil.toList((NodeComponent) ContextAwareHolder.loadContextAware().registerOrGet(nodeId, cmpClazz));
}
if (ObjectUtil.isNull(cmpInstance)) {
cmpInstance = (NodeComponent) cmpClazz.newInstance();
// 去除null元素
cmpInstances.remove(null);
// 如果为空
if (cmpInstances.isEmpty()) {
NodeComponent cmpInstance = (NodeComponent) cmpClazz.newInstance();
cmpInstances.add(cmpInstance);
}
}
//进行初始化
cmpInstance = ComponentInitializer.loadInstance().initComponent(cmpInstance, type, name, nodeId);
cmpInstances = cmpInstances.stream()
.map(
cmpInstance -> ComponentInitializer.loadInstance().initComponent(cmpInstance, type, name, cmpInstance.getNodeId() == null ? nodeId : cmpInstance.getNodeId())).collect(Collectors.toList());
//初始化Node
Node node = new Node(cmpInstance);
List<Node> nodes = cmpInstances.stream().map(Node::new).collect(Collectors.toList());
//如果是脚本节点(普通脚本节点/条件脚本节点)则还要加载script脚本
if (StrUtil.isNotBlank(script)){
node.setScript(script);
if (type.equals(NodeTypeEnum.SCRIPT)){
((ScriptComponent)cmpInstance).loadScript(script);
}else if(type.equals(NodeTypeEnum.SWITCH_SCRIPT)){
((ScriptSwitchComponent)cmpInstance).loadScript(script);
}else if(type.equals(NodeTypeEnum.IF_SCRIPT)){
((ScriptIfComponent)cmpInstance).loadScript(script);
for (int i = 0; i < nodes.size(); i++) {
Node node = nodes.get(i);
NodeComponent cmpInstance = cmpInstances.get(i);
if (StrUtil.isNotBlank(script)){
node.setScript(script);
if (type.equals(NodeTypeEnum.SCRIPT)){
((ScriptComponent)cmpInstance).loadScript(script);
}else if(type.equals(NodeTypeEnum.SWITCH_SCRIPT)){
((ScriptSwitchComponent)cmpInstance).loadScript(script);
}else if(type.equals(NodeTypeEnum.IF_SCRIPT)){
((ScriptIfComponent)cmpInstance).loadScript(script);
}
}
String activeNodeId = StrUtil.isEmpty(cmpInstance.getNodeId()) ? nodeId : cmpInstance.getNodeId();
//如果是脚本节点(普通脚本节点/条件脚本节点)则还要加载script脚本
nodeMap.put(activeNodeId, node);
}
nodeMap.put(nodeId, node);
} catch (Exception e) {
String error = StrUtil.format("component[{}] register error", StrUtil.isEmpty(name)?nodeId:StrUtil.format("{}({})",nodeId,name));
LOG.error(error);

View File

@ -331,8 +331,7 @@ public class ParserHelper {
}
/**
* 解析一个chain的过程
* <p>
* param e chain 节点
* @param e chain 节点
*/
public static void parseOneChain(Element e) {
String condValueStr;

View File

@ -1,21 +1,22 @@
package com.yomahub.liteflow.util;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowIfCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.core.NodeIfComponent;
import com.yomahub.liteflow.core.NodeSwitchComponent;
import com.yomahub.liteflow.core.proxy.ComponentProxy;
import com.yomahub.liteflow.exception.ComponentProxyErrorException;
import com.yomahub.liteflow.exception.LiteFlowException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
/**
* 组件代理类通用方法
@ -29,23 +30,6 @@ public class LiteFlowProxyUtil {
//判断一个bean是否是声明式组件
public static boolean isDeclareCmp(Class<?> clazz){
//判断bean是否标记了@LiteflowCmpDefine,@LiteflowCondCmpDefine,LiteflowIfCmpDefine这3个标注之一
boolean flag1 = clazz.getAnnotation(LiteflowCmpDefine.class) != null
|| clazz.getAnnotation(LiteflowSwitchCmpDefine.class) != null
|| clazz.getAnnotation(LiteflowIfCmpDefine.class) != null;
if (!flag1){
return false;
}
//看超类是否是NodeComponent,NodeCondComponent,NodeIfComponent中的一个如果不是则说明满足条件是的话也不满足
boolean flag2 = !ListUtil.toList(NodeComponent.class, NodeSwitchComponent.class, NodeIfComponent.class)
.contains(clazz.getSuperclass());
if (!flag2){
return false;
}
//查看bean里的method是否有方法标记了@LiteflowMethod标注
//这里的bean有可能是cglib加强过的class所以要先进行个判断
Class<?> targetClass;
@ -54,15 +38,14 @@ public class LiteFlowProxyUtil {
}else{
targetClass = clazz;
}
boolean flag3 = Arrays.stream(targetClass.getMethods()).anyMatch(
// 判断是否有方法标记了@LiteflowMethod标注有则为声明式组件
return Arrays.stream(targetClass.getMethods()).anyMatch(
method -> method.getAnnotation(LiteflowMethod.class) != null
);
return flag3;
}
//对一个满足声明式的bean进行代理
public static NodeComponent proxy2NodeComponent(Object bean, String nodeId){
//对一个满足声明式的bean进行代理,生成代理类数组
public static List<NodeComponent> proxy2NodeComponent(Object bean, String nodeId){
try{
LiteflowCmpDefine liteflowCmpDefine = bean.getClass().getAnnotation(LiteflowCmpDefine.class);
LiteflowSwitchCmpDefine liteflowSwitchCmpDefine = bean.getClass().getAnnotation(LiteflowSwitchCmpDefine.class);
@ -71,21 +54,23 @@ public class LiteFlowProxyUtil {
ComponentProxy proxy;
if (ObjectUtil.isNotNull(liteflowCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeComponent.class);
return (NodeComponent) proxy.getProxy();
return proxy.getProxyList();
}
if (ObjectUtil.isNotNull(liteflowSwitchCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeSwitchComponent.class);
return (NodeSwitchComponent) proxy.getProxy();
return proxy.getProxyList();
}
if (ObjectUtil.isNotNull(liteflowIfCmpDefine)){
proxy = new ComponentProxy(nodeId, bean, NodeIfComponent.class);
return (NodeIfComponent) proxy.getProxy();
return proxy.getProxyList();
}
throw new RuntimeException();
}catch (Exception e){
return new ComponentProxy(nodeId, bean, NodeIfComponent.class).getProxyList();
}catch (LiteFlowException liteFlowException){
throw liteFlowException;
}
catch (Exception e){
String errMsg = StrUtil.format("Error while proxying bean[{}]",bean.getClass().getName());
LOG.error(errMsg);
throw new ComponentProxyErrorException(errMsg);

View File

@ -55,6 +55,8 @@ public class SerialsUtil {
/**
* 生成一个12位随机数
* @param seed 种子值
* @return String 随机数
*/
public static String randomNum12(long seed) {
// 被除数
@ -64,6 +66,8 @@ public class SerialsUtil {
/**
* 生成一个8位随机数
* @param seed 种子值
* @return String 随机数
*/
public static String randomNum8(long seed) {
// 被除数

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-rule-sql</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-core</artifactId>
<version>${revision}</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.yomahub.liteflow.parser.spi.sql;
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
import com.yomahub.liteflow.parser.sql.SQLXmlELParser;
/**
* SQL 解析器 SPI 实现
*
* @author tangkc
* @since 2.9.0
*/
public class SQLParserClassNameSpi implements ParserClassNameSpi {
@Override
public String getSpiClassName() {
return SQLXmlELParser.class.getName();
}
}

View File

@ -0,0 +1,84 @@
package com.yomahub.liteflow.parser.sql;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.parser.el.ClassXmlFlowELParser;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.util.JDBCHelper;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.util.JsonUtil;
import java.util.Objects;
/**
* SQL 解析器实现只支持 EL 形式的 XML不支持其他的形式
*
* @author tangkc
* @since 2.9.0
*/
public class SQLXmlELParser extends ClassXmlFlowELParser {
private static final String ERROR_MSG_PATTERN = "rule-source-ext-data {} is blank";
private static final String ERROR_COMMON_MSG = "rule-source-ext-data is empty";
/**
* 构造函数
*/
public SQLXmlELParser() {
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData())) {
throw new ELSQLException(ERROR_COMMON_MSG);
}
try {
SQLParserVO sqlParserVO = JsonUtil.parseObject(liteflowConfig.getRuleSourceExtData(), SQLParserVO.class);
if (Objects.isNull(sqlParserVO)) {
throw new ELSQLException(ERROR_COMMON_MSG);
}
// 检查配置文件
checkParserVO(sqlParserVO);
// 初始化 JDBCHelper
JDBCHelper.init(sqlParserVO);
} catch (ELSQLException elsqlException) {
throw elsqlException;
} catch (Exception ex) {
throw new ELSQLException(ex.getMessage());
}
}
@Override
public String parseCustom() {
return JDBCHelper.getInstance().getElDataContent();
}
/**
* 检查配置文件并设置默认值
*
* @param sqlParserVO sqlParserVO
*/
private void checkParserVO(SQLParserVO sqlParserVO) {
if (StrUtil.isEmpty(sqlParserVO.getUrl())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "url"));
}
if (StrUtil.isEmpty(sqlParserVO.getDriverClassName())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "driverClassName"));
}
if (Objects.isNull(sqlParserVO.getUsername())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "username"));
}
if (Objects.isNull(sqlParserVO.getPassword())) {
throw new ELSQLException(StrFormatter.format(ERROR_MSG_PATTERN, "password"));
}
if (Objects.isNull(sqlParserVO.getElTable())) {
sqlParserVO.setElTable(new SQLParserVO.ElTable());
}
}
}

View File

@ -0,0 +1,30 @@
package com.yomahub.liteflow.parser.sql.exception;
/**
* SQL 相关业务异常
*
* @author tangkc
* @since 2.9.0
*/
public class ELSQLException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 异常信息
*/
private String message;
public ELSQLException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

View File

@ -0,0 +1,157 @@
package com.yomahub.liteflow.parser.sql.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.parser.sql.exception.ELSQLException;
import com.yomahub.liteflow.parser.sql.vo.SQLParserVO;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* jdbc 工具类
*
* @author tangkc
* @since 2.9.0
*/
public class JDBCHelper {
private static final String SQL_PATTERN = "SELECT {},{} FROM {} ";
private static final String CHAIN_XML_PATTERN = "<chain name=\"{}\">{}</chain>";
private static final String XML_PATTERN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><flow>{}</flow>";
private static final Integer FETCH_SIZE_MAX = 1000;
private SQLParserVO sqlParserVO;
private static JDBCHelper INSTANCE;
/**
* 初始化 INSTANCE
*/
public static void init(SQLParserVO sqlParserVO) {
try {
INSTANCE = new JDBCHelper();
Class.forName(sqlParserVO.getDriverClassName());
INSTANCE.setSqlParserVO(sqlParserVO);
} catch (ClassNotFoundException e) {
throw new ELSQLException(e.getMessage());
}
}
/**
* 获取 INSTANCE
*/
public static JDBCHelper getInstance() {
return INSTANCE;
}
/**
* 获取链接
*/
public Connection getConn() {
Connection connection = null;
try {
connection = DriverManager.getConnection(sqlParserVO.getUrl(), sqlParserVO.getUsername(), sqlParserVO.getPassword());
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
return connection;
}
/**
* 获取 ElData 数据内容
*/
public String getElDataContent() {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
String elDataField = sqlParserVO.getElTable().getElDataField();
String chainNameField = sqlParserVO.getElTable().getChainNameField();
String tableName = sqlParserVO.getElTable().getTableName();
String sqlCmd = StrFormatter.format(SQL_PATTERN, chainNameField, elDataField, tableName);
List<String> result = new ArrayList<>();
try {
conn = getConn();
stmt = conn.prepareStatement(sqlCmd, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
// 设置游标拉取数量
stmt.setFetchSize(FETCH_SIZE_MAX);
rs = stmt.executeQuery();
while (rs.next()) {
String elData = rs.getString(elDataField);
if (StrUtil.isBlank(elData)) {
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
}
String chainName = rs.getString(chainNameField);
if (StrUtil.isBlank(elData)) {
throw new ELSQLException(StrFormatter.format("{} table exist {} field value is empty", tableName, elDataField));
}
result.add(StrFormatter.format(CHAIN_XML_PATTERN, chainName, elData));
}
} catch (Exception e) {
throw new ELSQLException(e.getMessage());
} finally {
// 关闭连接
close(conn, stmt, rs);
}
String chains = CollUtil.join(result, StrUtil.CRLF);
return StrFormatter.format(XML_PATTERN, chains);
}
/**
* 关闭连接
*
* @param conn conn
* @param stmt stmt
* @param rs rs
*/
private void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
// 关闭连接
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
// 关闭 statement
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
//关闭结果集
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new ELSQLException(e.getMessage());
}
}
}
//#region get set method
private SQLParserVO getSqlParserVO() {
return sqlParserVO;
}
private JDBCHelper setSqlParserVO(SQLParserVO sqlParserVO) {
this.sqlParserVO = sqlParserVO;
return this;
}
//#endregion
}

View File

@ -0,0 +1,116 @@
package com.yomahub.liteflow.parser.sql.vo;
/**
* 用于解析 RuleSourceExtData VO 用于 sql 模式中
*
* @author tangkc
* @since 2.9.0
*/
public class SQLParserVO {
/**
* 连接地址
*/
private String url;
/**
* 驱动
*/
private String driverClassName;
/**
* 账号名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* EL 表相关配置
*/
private ElTable elTable;
public static class ElTable {
/**
* 表名
*/
private String tableName = "el_table";
/**
* chainName
*/
private String chainNameField = "chain_name";
/**
* el 表达式相关数据
*/
private String elDataField = "el_data";
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getChainNameField() {
return chainNameField;
}
public void setChainNameField(String chainNameField) {
this.chainNameField = chainNameField;
}
public String getElDataField() {
return elDataField;
}
public void setElDataField(String elDataField) {
this.elDataField = elDataField;
}
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public ElTable getElTable() {
return elTable;
}
public void setElTable(ElTable elTable) {
this.elTable = elTable;
}
}

View File

@ -0,0 +1 @@
com.yomahub.liteflow.parser.spi.sql.SQLParserClassNameSpi

View File

@ -12,7 +12,7 @@
<packaging>pom</packaging>
<modules>
<module>liteflow-rule-zk</module>
<module>liteflow-rule-nacos</module>
<module>liteflow-rule-sql</module>
</modules>
<artifactId>liteflow-rule-plugin</artifactId>

View File

@ -8,6 +8,7 @@
*/
package com.yomahub.liteflow.spring;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.aop.ICmpAroundAspect;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.property.LiteflowConfig;
@ -19,6 +20,7 @@ import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -61,9 +63,19 @@ public class ComponentScanner implements BeanPostProcessor {
//如果是就缓存到类属性的map中
if (LiteFlowProxyUtil.isDeclareCmp(bean.getClass())){
LOG.info("proxy component[{}] has been found", beanName);
NodeComponent nodeComponent = LiteFlowProxyUtil.proxy2NodeComponent(bean, beanName);
nodeComponentMap.put(beanName, nodeComponent);
return nodeComponent;
List<NodeComponent> nodeComponents = LiteFlowProxyUtil.proxy2NodeComponent(bean, beanName);
nodeComponents.forEach(
nodeComponent -> {
String nodeId = nodeComponent.getNodeId();
nodeId = StrUtil.isEmpty(nodeId) ? beanName : nodeId;
nodeComponentMap.put(nodeId, nodeComponent);
}
);
// 只有注解支持单bean多Node,所以一个直接返回
if (nodeComponents.size() == 1){
return nodeComponents.get(0);
}
return bean;
}
// 组件的扫描发现扫到之后缓存到类属性map中

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>liteflow-testcase-el</artifactId>
<groupId>com.yomahub</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>liteflow-testcase-el-declare-multi-springboot</artifactId>
<dependencies>
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${springboot.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,21 @@
package com.yomahub.liteflow.test;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.spi.holder.SpiFactoryCleaner;
import com.yomahub.liteflow.spring.ComponentScanner;
import com.yomahub.liteflow.thread.ExecutorHelper;
import org.junit.AfterClass;
public class BaseTest {
@AfterClass
public static void cleanScanCache(){
ComponentScanner.cleanCache();
FlowBus.cleanCache();
ExecutorHelper.loadInstance().clearExecutorServiceMap();
SpiFactoryCleaner.clean();
LiteflowConfigGetter.clean();
}
}

View File

@ -0,0 +1,41 @@
package com.yomahub.liteflow.test.absoluteConfigPath;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境下异步线程超时日志打印测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/absoluteConfigPath/application.properties")
@SpringBootTest(classes = AbsoluteConfigPathELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.absoluteConfigPath.cmp"})
public class AbsoluteConfigPathELDeclMultiSpringbootTest extends BaseTest {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Resource
private FlowExecutor flowExecutor;
@Test
public void testAbsoluteConfig() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,33 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.absoluteConfigPath.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpMultiDefine{
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a",nodeType = AnnotationNodeTypeEnum.COMMON)
public void processA(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,79 @@
package com.yomahub.liteflow.test.aop;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.spring.ComponentScanner;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.aop.aspect.CmpAspect;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 切面场景单元测试
* @author Bryan.Zhang
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/aop/application.properties")
@SpringBootTest(classes = GlobalAOPELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@Import(CmpAspect.class)
@ComponentScan({"com.yomahub.liteflow.test.aop.cmp1","com.yomahub.liteflow.test.aop.cmp2"})
public class GlobalAOPELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试全局AOP串行场景
@Test
public void testGlobalAopS() {
LiteflowResponse response= flowExecutor.execute2Resp("chain1", "it's a request");
DefaultContext context = response.getFirstContextBean();
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("before_after", context.getData("a"));
Assert.assertEquals("before_after", context.getData("b"));
Assert.assertEquals("before_after", context.getData("c"));
Assert.assertEquals("before_after", context.getData("d"));
Assert.assertEquals("before_after", context.getData("e"));
}
//测试全局AOP并行场景
@Test
public void testGlobalAopP() {
LiteflowResponse response= flowExecutor.execute2Resp("chain2", "it's a request");
DefaultContext context = response.getFirstContextBean();
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("before_after", context.getData("a"));
Assert.assertEquals("before_after", context.getData("b"));
Assert.assertEquals("before_after", context.getData("c"));
Assert.assertEquals("before_after", context.getData("d"));
Assert.assertEquals("before_after", context.getData("e"));
}
@Test
public void testGlobalAopException() {
LiteflowResponse response= flowExecutor.execute2Resp("chain3", "it's a request");
DefaultContext context = response.getFirstContextBean();
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("before_after", context.getData("a"));
Assert.assertEquals("before_after", context.getData("b"));
Assert.assertEquals("before_after", context.getData("c"));
Assert.assertEquals("before_after", context.getData("f"));
}
@AfterClass
public static void cleanScanCache(){
BaseTest.cleanScanCache();
ComponentScanner.cmpAroundAspect = null;
}
}

View File

@ -0,0 +1,20 @@
package com.yomahub.liteflow.test.aop.aspect;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.aop.ICmpAroundAspect;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.slot.Slot;
public class CmpAspect implements ICmpAroundAspect {
@Override
public void beforeProcess(String nodeId, Slot slot) {
DefaultContext context = slot.getFirstContextBean();
context.setData(nodeId, "before");
}
@Override
public void afterProcess(String nodeId, Slot slot) {
DefaultContext context = slot.getFirstContextBean();
context.setData(nodeId, StrUtil.format("{}_{}", context.getData(nodeId), "after"));
}
}

View File

@ -0,0 +1,27 @@
package com.yomahub.liteflow.test.aop.aspect;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.slot.DefaultContext;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class CustomAspect {
@Pointcut("execution(* com.yomahub.liteflow.test.aop.cmp1.*.process(*))")
public void cut() {
}
@Around("cut()")
public Object around(ProceedingJoinPoint jp) throws Throwable {
NodeComponent cmp = (NodeComponent) jp.getThis();
DefaultContext context = cmp.getFirstContextBean();
context.setData(cmp.getNodeId(), "before");
Object returnObj = jp.proceed();
context.setData(cmp.getNodeId(), StrUtil.format("{}_{}", context.getData(cmp.getNodeId()), "after"));
return returnObj;
}
}

View File

@ -0,0 +1,25 @@
package com.yomahub.liteflow.test.aop.cmp1;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class Cmp1Config {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
System.out.println("Acomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("Bcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("Ccomp executed!");
}
}

View File

@ -0,0 +1,25 @@
package com.yomahub.liteflow.test.aop.cmp2;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class Cmp2Config {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
System.out.println("Dcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) {
System.out.println("Ecomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "f")
public void processF(NodeComponent bindCmp) {
throw new RuntimeException("test error");
}
}

View File

@ -0,0 +1,139 @@
package com.yomahub.liteflow.test.asyncNode;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.asyncNode.exception.TestException;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 测试隐式调用子流程
* 单元测试
*
* @author ssss
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/asyncNode/application.properties")
@SpringBootTest(classes = AsyncNodeELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.asyncNode.cmp"})
public class AsyncNodeELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
/*****
* 标准chain 嵌套选择 嵌套子chain进行执行
* 验证了when情况下 多个node是并行执行
* 验证了默认参数情况下 when可以加载执行
* **/
@Test
public void testAsyncFlow1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "it's a base request");
Assert.assertTrue(response.isSuccess());
System.out.println(response.getExecuteStepStr());
}
//这个和test1有点类似只不过进一步验证了步骤
@Test
public void testAsyncFlow2() {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "it's a base request");
Assert.assertTrue(ListUtil.toList("b==>j==>g==>f==>h","b==>j==>g==>h==>f",
"b==>j==>h==>g==>f","b==>j==>h==>f==>g",
"b==>j==>f==>h==>g","b==>j==>f==>g==>h"
).contains(response.getExecuteStepStr()));
}
//测试errorResume,默认的errorResume为false这里测试默认的
@Test
public void testAsyncFlow3_1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain3-1", "it's a base request");
Assert.assertFalse(response.isSuccess());
Assert.assertEquals(response.getSlot().getException().getClass(), TestException.class);
}
//测试errorResume,默认的errorResume为false这里设置为true
@Test
public void testAsyncFlow3_2() {
LiteflowResponse response = flowExecutor.execute2Resp("chain3-2", "it's a base request");
Assert.assertTrue(response.isSuccess());
}
//相同group的并行组会合并并且errorResume根据第一个when来这里第一个when配置了不抛错
@Test
public void testAsyncFlow4() {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "it's a base request");
DefaultContext context = response.getFirstContextBean();
//因为不记录错误所以最终结果是true
Assert.assertTrue(response.isSuccess());
//因为是并行组所以即便抛错了其他组件也会执行i在流程里配置了2遍i抛错但是也执行了2遍这里验证下
Integer count = context.getData("count");
Assert.assertEquals(new Integer(2), count);
//因为配置了不抛错所以response里的cause应该为null
Assert.assertNull(response.getCause());
}
//相同group的并行组会合并并且errorResume根据第一个when来这里第一个when配置了会抛错
@Test
public void testAsyncFlow5() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "it's a base request");
DefaultContext context = response.getFirstContextBean();
//整个并行组是报错的所以最终结果是false
Assert.assertFalse(response.isSuccess());
//因为是并行组所以即便抛错了其他组件也会执行i在流程里配置了2遍i抛错但是也执行了2遍这里验证下
Integer count = context.getData("count");
Assert.assertEquals(new Integer(2), count);
//因为第一个when配置了会报错所以response里的cause里应该会有TestException
Assert.assertEquals(TestException.class, response.getCause().getClass());
}
//不同group的并行组不会合并第一个when的errorResume是false会抛错那第二个when就不会执行
@Test
public void testAsyncFlow6() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "it's a base request");
DefaultContext context = response.getFirstContextBean();
//第一个when会抛错所以最终结果是false
Assert.assertFalse(response.isSuccess());
//因为是不同组并行组第一组的when里的i就抛错了所以i就执行了1遍
Integer count = context.getData("count");
Assert.assertEquals(new Integer(1), count);
//第一个when会报错所以最终response的cause里应该会有TestException
Assert.assertEquals(TestException.class, response.getCause().getClass());
}
//不同group的并行组不会合并第一个when的errorResume是true不会报错那第二个when还会继续执行但是第二个when的errorResume是false所以第二个when会报错
@Test
public void testAsyncFlow7() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", "it's a base request");
DefaultContext context = response.getFirstContextBean();
//第二个when会抛错所以最终结果是false
Assert.assertFalse(response.isSuccess());
// 传递了slotIndex则set的size==2
Integer count = context.getData("count");
Assert.assertEquals(new Integer(2), count);
//第一个when会报错所以最终response的cause里应该会有TestException
Assert.assertEquals(TestException.class, response.getCause().getClass());
}
//测试任意异步一个执行完即继续的场景
//d g h并行配置了any=true其中d耗时1秒g耗时0.5秒其他都不设耗时
//最终执行效果应该是h先返回然后执行abc,最后gd
//这里要注意的是由于step是先加入所以step的打印顺序并不是这样的但是实际执行是正确的
@Test
public void testAsyncFlow8() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain8", "it's a base request");
DefaultContext context = response.getFirstContextBean();
Assert.assertTrue(response.isSuccess());
Assert.assertTrue(context.getData("check").toString().startsWith("habc"));
}
}

View File

@ -0,0 +1,138 @@
package com.yomahub.liteflow.test.asyncNode.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.asyncNode.exception.TestException;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Acomp executed!");
}
@LiteflowMethod(value =LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Bcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Ccomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) throws Exception {
Thread.sleep(1000);
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Dcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeId = "e",nodeType = AnnotationNodeTypeEnum.SWITCH)
public String processSwitchE(NodeComponent bindCmp) throws Exception {
System.out.println("Ecomp executed!");
return "g";
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "f")
public void processF(NodeComponent bindCmp) throws Exception {
System.out.println("Fcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "g")
public void processG(NodeComponent bindCmp) throws Exception {
Thread.sleep(500);
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Gcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "h")
public void processH(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (NodeComponent.class){
if (context.hasData("check")){
String str = context.getData("check");
str += bindCmp.getNodeId();
context.setData("check", str);
}else{
context.setData("check", bindCmp.getNodeId());
}
}
System.out.println("Hcomp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "i")
public void processI(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
synchronized (this){
if (context.hasData("count")){
Integer count = context.getData("count");
context.setData("count", ++count);
} else{
context.setData("count", 1);
}
}
System.out.println("Icomp executed! throw Exception!");
throw new TestException();
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeId = "j",nodeType = AnnotationNodeTypeEnum.SWITCH)
public String processSwitchJ(NodeComponent bindCmp) throws Exception {
System.out.println("Jcomp executed!");
return "chain3";
}
}

View File

@ -0,0 +1,4 @@
package com.yomahub.liteflow.test.asyncNode.exception;
public class TestException extends Exception{
}

View File

@ -0,0 +1,38 @@
package com.yomahub.liteflow.test.base;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境最普通的例子测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/base/application.properties")
@SpringBootTest(classes = BaseELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.base.cmp"})
public class BaseELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testBase() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,40 @@
package com.yomahub.liteflow.test.base.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import javax.annotation.Resource;
@LiteflowComponent
public class CmpConfig {
@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) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
@Resource
private TestDomain testDomain;
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
testDomain.sayHi();
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,10 @@
package com.yomahub.liteflow.test.base.cmp;
import org.springframework.stereotype.Component;
@Component
public class TestDomain {
public void sayHi(){
System.out.println("hello");
}
}

View File

@ -0,0 +1,212 @@
package com.yomahub.liteflow.test.builder;
import com.yomahub.liteflow.builder.LiteFlowChainBuilder;
import com.yomahub.liteflow.builder.LiteFlowConditionBuilder;
import com.yomahub.liteflow.builder.LiteFlowNodeBuilder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.builder.entity.ExecutableEntity;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import com.yomahub.liteflow.test.builder.cmp1.*;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
//基于builder模式的单元测试
//这里只是最基本的builder模式的测试只是为了验证在springboot模式下的正常性
//更详细的builder模式测试用例会单独拉testcase去做
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BuilderELDeclMultiSpringbootTest1.class)
@EnableAutoConfiguration
public class BuilderELDeclMultiSpringbootTest1 extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//基于普通组件的builder模式测试
@Test
public void testBuilder() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.ACmp")
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.BCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.CCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.DCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setType(NodeTypeEnum.SWITCH)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.ECmp")
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.FCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp1.GCmp")
.build();
LiteFlowChainELBuilder.createChain().setChainName("chain2").setEL(
"THEN(c, d)"
).build();
LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL(
"THEN(a, b, WHEN(SWITCH(e).to(f, g, chain2)))"
).build();
LiteflowResponse response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getExecuteStepStr());
}
//基于普通组件的builder模式测试
@Test
public void testBuilderForClassAndCode() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setType(NodeTypeEnum.COMMON)
.setClazz(ACmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setType(NodeTypeEnum.COMMON)
.setClazz(BCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setType(NodeTypeEnum.COMMON)
.setClazz(CCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setType(NodeTypeEnum.COMMON)
.setClazz(DCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setType(NodeTypeEnum.SWITCH)
.setClazz(ECmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setType(NodeTypeEnum.COMMON)
.setClazz(FCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setType(NodeTypeEnum.COMMON)
.setClazz(GCmp.class)
.build();
LiteFlowChainBuilder.createChain().setChainName("chain2").setCondition(
LiteFlowConditionBuilder.createThenCondition().setValue("c,d").build()
).build();
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createThenCondition()
.setValue("a,b").build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setValue("e(f|g|chain2)").build()
).build();
LiteflowResponse response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getExecuteStepStr());
}
//基于普通组件的builder模式测试
@Test
public void testBuilderForConditionNode() throws Exception {
LiteFlowNodeBuilder.createNode().setId("a")
.setName("组件A")
.setType(NodeTypeEnum.COMMON)
.setClazz(ACmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setType(NodeTypeEnum.COMMON)
.setClazz(BCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setType(NodeTypeEnum.COMMON)
.setClazz(CCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setType(NodeTypeEnum.COMMON)
.setClazz(DCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setType(NodeTypeEnum.SWITCH)
.setClazz(ECmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setType(NodeTypeEnum.COMMON)
.setClazz(FCmp.class)
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setType(NodeTypeEnum.COMMON)
.setClazz(GCmp.class)
.build();
LiteFlowChainBuilder.createChain().setChainName("chain2").setCondition(
LiteFlowConditionBuilder.createThenCondition()
.setExecutable(new ExecutableEntity().setId("c"))
.setExecutable(new ExecutableEntity().setId("d"))
.build()
).build();
LiteFlowChainBuilder.createChain().setChainName("chain1").setCondition(
LiteFlowConditionBuilder
.createThenCondition()
.setExecutable(new ExecutableEntity().setId("a").setTag("hello"))
.setExecutable(new ExecutableEntity().setId("b"))
.build()
).setCondition(
LiteFlowConditionBuilder.createWhenCondition()
.setExecutable(
new ExecutableEntity().setId("e")
.addNodeCondComponent(new ExecutableEntity().setId("f").setTag("FHello"))
.addNodeCondComponent(new ExecutableEntity().setId("g"))
.addNodeCondComponent(new ExecutableEntity().setId("chain2")
)).build()
).build();
LiteflowResponse response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a[组件A]==>b[组件B]==>e[组件E]==>c[组件C]==>d[组件D]", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,39 @@
package com.yomahub.liteflow.test.builder;
import com.yomahub.liteflow.builder.el.LiteFlowChainELBuilder;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
//基于builder模式的单元测试
//这里测试的是通过spring去扫描但是通过代码去构建chain的用例
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BuilderELDeclMultiSpringbootTest2.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.builder.cmp2"})
public class BuilderELDeclMultiSpringbootTest2 extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//通过spring去扫描组件通过代码去构建chain
@Test
public void testBuilder() throws Exception {
LiteFlowChainELBuilder.createChain().setChainName("chain1").setEL(
"THEN(h, i, j)"
).build();
LiteflowResponse response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("h==>i==>j", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class ACmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
}

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.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class BCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
}

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.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class CCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

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.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class DCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("DCmp executed!");
}
}

View File

@ -0,0 +1,24 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowSwitchCmpDefine;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowSwitchCmpDefine
public class ECmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS_SWITCH)
public String processSwitch(NodeComponent bindCmp) throws Exception {
System.out.println("ECmp executed!");
return "chain2";
}
}

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.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class FCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("FCmp executed!");
}
}

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.builder.cmp1;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class GCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("GCmp executed!");
}
}

View File

@ -0,0 +1,25 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp2;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent("h")
@LiteflowCmpDefine
public class HCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("HCmp executed!");
}
}

View File

@ -0,0 +1,25 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp2;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent("i")
@LiteflowCmpDefine
public class ICmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("ICmp executed!");
}
}

View File

@ -0,0 +1,25 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp2;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent("j")
@LiteflowCmpDefine
public class JCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("JCmp executed!");
}
}

View File

@ -0,0 +1,63 @@
package com.yomahub.liteflow.test.cmpRetry;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 测试springboot下的节点执行器
* @author Bryan.Zhang
* @since 2.5.10
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/cmpRetry/application.properties")
@SpringBootTest(classes = LiteflowRetryELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.cmpRetry.cmp"})
public class LiteflowRetryELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//全局重试配置测试
@Test
public void testRetry1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a==>b==>b==>b", response.getExecuteStepStr());
}
//单个组件重试配置测试
@Test
public void testRetry2() {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("c==>c==>c==>c==>c==>c", response.getExecuteStepStr());
}
//单个组件指定异常但抛出的并不是指定异常的场景测试
@Test
public void testRetry3() {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assert.assertFalse(response.isSuccess());
}
//单个组件指定异常重试抛出的是指定异常或者
@Test
public void testRetry4() {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "arg");
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("e==>e==>e==>e==>e==>e", response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,69 @@
package com.yomahub.liteflow.test.cmpRetry.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.annotation.LiteflowRetry;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
private int flag = 0;
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
if (flag < 2){
flag++;
throw new RuntimeException("demo exception");
}
}
@LiteflowRetry(5)
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
throw new RuntimeException("demo exception");
}
@LiteflowRetry(retry = 5, forExceptions = {NullPointerException.class})
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
System.out.println("DCmp executed!");
throw new RuntimeException("demo exception");
}
@LiteflowRetry(retry = 5, forExceptions = {NullPointerException.class})
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) {
System.out.println("ECmp executed!");
throw new NullPointerException("demo null exception");
}
@LiteflowRetry(retry = 5, forExceptions = {NullPointerException.class})
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "f")
public void processF(NodeComponent bindCmp) {
System.out.println("ECmp executed!");
throw new NullPointerException("demo null exception");
}
@LiteflowMethod(value = LiteFlowMethodEnum.AFTER_PROCESS,nodeId = "f")
public void after(NodeComponent bindCmp) {
System.out.println("ECmp executed!");
throw new NullPointerException("demo null exception");
}
}

View File

@ -0,0 +1,74 @@
package com.yomahub.liteflow.test.cmpStep;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.flow.entity.CmpStep;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.HashSet;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
/**
* springboot环境最普通的例子测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/cmpStep/application.properties")
@SpringBootTest(classes = CmpStepELDeclSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.cmpStep.cmp"})
public class CmpStepELDeclSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testStep() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertFalse(response.isSuccess());
Assert.assertTrue(response.getExecuteSteps().get("a").isSuccess());
Assert.assertTrue(response.getExecuteSteps().get("b").isSuccess());
Assert.assertFalse(response.getExecuteSteps().get("c").isSuccess());
Assert.assertFalse(response.getExecuteSteps().get("d").isSuccess());
Assert.assertTrue(response.getExecuteSteps().get("c").getTimeSpent() >= 2000);
Assert.assertEquals(RuntimeException.class, response.getExecuteSteps().get("c").getException().getClass());
Assert.assertEquals(RuntimeException.class, response.getExecuteSteps().get("d").getException().getClass());
}
@Test
public void testStep2() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("a==>b", response.getExecuteStepStrWithoutTime());
}
@Test
public void testStep3() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
Assert.assertTrue(response.isSuccess());
Map<String, CmpStep> stepMap = response.getExecuteSteps();
Assert.assertEquals(2, stepMap.size());
Queue<CmpStep> queue = response.getExecuteStepQueue();
Assert.assertEquals(5, queue.size());
Set<String> tagSet = new HashSet<>();
response.getExecuteStepQueue().stream().filter(
cmpStep -> cmpStep.getNodeId().equals("a")
).forEach(cmpStep -> tagSet.add(cmpStep.getTag()));
Assert.assertEquals(3, tagSet.size());
}
}

View File

@ -0,0 +1,49 @@
package com.yomahub.liteflow.test.cmpStep.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) throws Exception{
Thread.sleep(5000L);
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) throws Exception{
System.out.println("CCmp executed!");
Thread.sleep(2000);
throw new RuntimeException("test error c");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
throw new RuntimeException("test error d");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) {
System.out.println("ECmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS,nodeId = "e")
public boolean isAccessE(NodeComponent bindCmp) {
return false;
}
}

View File

@ -0,0 +1,35 @@
package com.yomahub.liteflow.test.comments;
import cn.hutool.core.collection.ListUtil;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/comments/application.properties")
@SpringBootTest(classes = LiteflowNodeELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.comments.cmp"})
public class LiteflowNodeELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
// 测试注释
@Test
public void testAsyncFlow1() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "it's a base request");
Assert.assertTrue(response.isSuccess());
Assert.assertTrue(ListUtil.toList("a==>b==>c==>b","a==>b==>b==>c").contains(response.getExecuteStepStr()));
}
}

View File

@ -0,0 +1,26 @@
package com.yomahub.liteflow.test.comments.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig {
@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) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

View File

@ -0,0 +1,48 @@
package com.yomahub.liteflow.test.complex;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境EL复杂例子测试1
* @author Bryan.Zhang
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/complex/application1.properties")
@SpringBootTest(classes = ComplexELDeclMultiSpringbootTest1.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.complex.cmp1"})
public class ComplexELDeclMultiSpringbootTest1 extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试复杂例子优化前
//案例来自于文档中 EL规则写法/复杂编排例子/复杂例子一
//因为所有的组件都是空执行你可以在组件里加上Thread.sleep来模拟业务耗时再来看这个打出结果
@Test
public void testComplex1_1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1_1", "arg");
Assert.assertTrue(response.isSuccess());
}
//测试复杂例子优化后
//案例来自于文档中 EL规则写法/复杂编排例子/复杂例子一
//因为所有的组件都是空执行你可以在组件里加上Thread.sleep来模拟业务耗时再来看这个打出结果
@Test
public void testComplex1_2() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1_2", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,48 @@
package com.yomahub.liteflow.test.complex;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境EL复杂例子测试1
* @author Bryan.Zhang
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/complex/application2.properties")
@SpringBootTest(classes = ComplexELDeclMultiSpringbootTest2.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.complex.cmp2"})
public class ComplexELDeclMultiSpringbootTest2 extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试复杂例子优化前
//案例来自于文档中 EL规则写法/复杂编排例子/复杂例子二
//因为所有的组件都是空执行你可以在组件里加上Thread.sleep来模拟业务耗时再来看这个打出结果
@Test
public void testComplex2_1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain2_1", "arg");
Assert.assertTrue(response.isSuccess());
}
//测试复杂例子优化后
//案例来自于文档中 EL规则写法/复杂编排例子/复杂例子二
//因为所有的组件都是空执行你可以在组件里加上Thread.sleep来模拟业务耗时再来看这个打出结果
@Test
public void testComplex2_2() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain2_2", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,82 @@
package com.yomahub.liteflow.test.complex.cmp1;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig1 {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "A")
public void processA(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "B")
public void processB(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "C")
public void processC(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "D")
public void processD(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "E")
public void processE(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "F")
public void processF(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeId = "G",nodeType = AnnotationNodeTypeEnum.SWITCH)
public String processSwitchG(NodeComponent bindCmp) throws Exception {
return "t1";
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "H")
public void processH(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "J")
public void processJ(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "K")
public void processK(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "L")
public void processL(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "M")
public void processM(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "N")
public void processN(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "Z")
public void processZ(NodeComponent bindCmp) {
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.complex.cmp1;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("I")
public class ICmp extends NodeComponent {
@Override
public void process() {
}
}

View File

@ -0,0 +1,82 @@
package com.yomahub.liteflow.test.complex.cmp2;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowComponent
public class CmpConfig2 {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "A")
public void processA(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeType = AnnotationNodeTypeEnum.SWITCH,nodeId = "B")
public String processSwitchB(NodeComponent bindCmp) {
return "t3";
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "C")
public void processC(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "D")
public void processD(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "E")
public void processE(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "F")
public void processF(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeType = AnnotationNodeTypeEnum.SWITCH,nodeId = "G")
public String processSwitchG(NodeComponent bindCmp) throws Exception {
return "t2";
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "H")
public void processH(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "J")
public void processJ(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "K")
public void processK(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "L")
public void processL(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "M")
public void processM(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "N")
public void processN(NodeComponent bindCmp) {
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId ="Z")
public void processZ(NodeComponent bindCmp) {
}
}

View File

@ -0,0 +1,20 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.complex.cmp2;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
@Component("I")
public class ICmp extends NodeComponent {
@Override
public void process() {
}
}

View File

@ -0,0 +1,93 @@
package com.yomahub.liteflow.test.component;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ReflectionUtils;
import javax.annotation.Resource;
/**
* 组件功能点测试
* 单元测试
*
* @author donguo.tao
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/component/application.properties")
@SpringBootTest(classes = FlowExecutorELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.component.cmp1","com.yomahub.liteflow.test.component.cmp2"})
public class FlowExecutorELDeclMultiSpringbootTest extends BaseTest {
private static final Logger LOG = LoggerFactory.getLogger(FlowExecutorELDeclMultiSpringbootTest.class);
@Resource
private FlowExecutor flowExecutor;
//isAccess方法的功能测试
@Test
public void testIsAccess() {
LiteflowResponse response = flowExecutor.execute2Resp("chain1", 101);
Assert.assertTrue(response.isSuccess());
Assert.assertNotNull(response.getSlot().getResponseData());
}
//组件抛错的功能点测试
@Test(expected = ArithmeticException.class)
public void testComponentException() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain2", 0);
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("/ by zero", response.getMessage());
ReflectionUtils.rethrowException(response.getCause());
}
//isContinueOnError方法的功能点测试
@Test
public void testIsContinueOnError() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain3", 0);
Assert.assertTrue(response.isSuccess());
Assert.assertNull(response.getCause());
}
//isEnd方法的功能点测试
@Test
public void testIsEnd() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain4", 10);
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("d",response.getExecuteStepStr());
}
//setIsEnd方法的功能点测试
@Test
public void testSetIsEnd1() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain5", 10);
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("e",response.getExecuteStepStr());
}
//条件组件的功能点测试
@Test
public void testNodeCondComponent() {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", 0);
Assert.assertTrue(response.isSuccess());
}
//测试setIsEnd如果为truecontinueError也为true那不应该continue了
@Test
public void testSetIsEnd2() throws Exception {
LiteflowResponse response = flowExecutor.execute2Resp("chain7", 10);
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("g",response.getExecuteStepStr());
}
}

View File

@ -0,0 +1,116 @@
package com.yomahub.liteflow.test.component.cmp1;
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.util.JsonUtil;
import java.util.Objects;
@LiteflowComponent
public class CmpConfig1 {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
System.out.println("AComp executed!");
bindCmp.getSlot().setResponseData("AComp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS,nodeId = "a")
public boolean isAccessA(NodeComponent bindCmp) {
Integer requestData = bindCmp.getRequestData();
if (Objects.nonNull(requestData) && requestData > 100){
return true;
}
System.out.println("AComp isAccess false.");
return false;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BComp executed!");
Integer requestData = bindCmp.getRequestData();
Integer divisor = 130;
Integer result = divisor / requestData;
bindCmp.getSlot().setResponseData(result);
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS,nodeId = "b")
public boolean isAccessB(NodeComponent bindCmp) {
Integer requestData = bindCmp.getRequestData();
if (Objects.nonNull(requestData)){
return true;
}
return false;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CComp executed!");
Integer requestData = bindCmp.getRequestData();
Integer divisor = 130;
Integer result = divisor / requestData;
bindCmp.getSlot().setResponseData(result);
System.out.println("responseData="+Integer.parseInt(bindCmp.getSlot().getResponseData()));
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_CONTINUE_ON_ERROR,nodeId = "c")
public boolean isContinueOnErrorC(NodeComponent bindCmp) {
Integer requestData = bindCmp.getRequestData();
if (Objects.nonNull(requestData)){
return true;
}
return false;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) throws Exception {
System.out.println("DComp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_END,nodeId = "d")
public boolean isEndD(NodeComponent bindCmp) {
//组件的process执行完之后才会执行isEnd
Object requestData = bindCmp.getSlot().getResponseData();
if (Objects.isNull(requestData)){
System.out.println("DComp flow isEnd, because of responseData is null.");
return true;
}
return false;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) throws Exception {
System.out.println("EComp executed!");
Object responseData = bindCmp.getSlot().getResponseData();
if (Objects.isNull(responseData)){
System.out.println("EComp responseData flow must be set end .");
//执行到某个条件时手动结束流程
bindCmp.setIsEnd(true);
}
System.out.println("EComp responseData responseData=" + JsonUtil.toJsonString(responseData));
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "g")
public void processF(NodeComponent bindCmp) {
System.out.println("GCmp executed!");
bindCmp.setIsEnd(true);
}
@LiteflowMethod(LiteFlowMethodEnum.IS_CONTINUE_ON_ERROR)
public boolean isContinueOnError(NodeComponent bindCmp) {
return true;
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "h")
public void processH(NodeComponent bindCmp) {
System.out.println("HCmp executed!");
}
}

View File

@ -0,0 +1,25 @@
package com.yomahub.liteflow.test.component.cmp2;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import java.util.Objects;
@LiteflowComponent
public class FCondCmp{
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeId = "f",nodeType = AnnotationNodeTypeEnum.SWITCH)
public String processSwitchF(NodeComponent bindCmp) {
Integer requestData = bindCmp.getRequestData();
if (Objects.isNull(requestData)){
return "d";
} else if(requestData == 0){
return "c";
} else {
return "b";
}
}
}

View File

@ -0,0 +1,37 @@
package com.yomahub.liteflow.test.customMethodName;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 声明式组件自定义方法的测试用例
* @author Bryan.Zhang
* @since 2.7.2
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/customMethodName/application.properties")
@SpringBootTest(classes = CustomMethodNameELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.customMethodName.cmp"})
public class CustomMethodNameELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testCustomMethodName() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,54 @@
package com.yomahub.liteflow.test.customMethodName.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.slot.Slot;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processAcmp(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_ACCESS,nodeId = "a")
public boolean isAcmpAccess(NodeComponent bindCmp){
return true;
}
@LiteflowMethod(value = LiteFlowMethodEnum.BEFORE_PROCESS,nodeId = "a")
public void beforeAcmp(String nodeId, Slot slot){
System.out.println("before A");
}
@LiteflowMethod(value = LiteFlowMethodEnum.AFTER_PROCESS,nodeId = "a")
public void afterAcmp(String nodeId, Slot slot){
System.out.println("after A");
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_SUCCESS,nodeId ="a")
public void onAcmpSuccess(NodeComponent bindCmp){
System.out.println("Acmp success");
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_ERROR,nodeId = "a")
public void onAcmpError(NodeComponent bindCmp){
System.out.println("Acmp error");
}
@LiteflowMethod(value = LiteFlowMethodEnum.IS_END,nodeId = "a")
public boolean isAcmpEnd(NodeComponent bindCmp) {
System.out.println("Acmp end config");
return false;
}
///////////////////////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processBcmp(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
}

View File

@ -0,0 +1,44 @@
package com.yomahub.liteflow.test.customNodes;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境下自定义声明节点的测试
* 不通过spring扫描的方式通过在配置文件里定义nodes的方式
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/customNodes/application.properties")
@SpringBootTest(classes = CustomNodesELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.customNodes.domain"})
public class CustomNodesELDeclMultiSpringbootTest extends BaseTest {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Resource
private FlowExecutor flowExecutor;
@Test
public void testCustomNodes() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
response = flowExecutor.execute2Resp("chain2", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@ -0,0 +1,22 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class ACmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("ACmp executed!");
}
}

View File

@ -0,0 +1,29 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.test.customNodes.domain.DemoDomain;
import javax.annotation.Resource;
@LiteflowCmpDefine
public class BCmp{
@Resource
private DemoDomain demoDomain;
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
demoDomain.sayHi();
System.out.println("BCmp executed!");
}
}

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.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class CCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
}

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.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class DCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("DCmp executed!");
}
}

View File

@ -0,0 +1,30 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.test.customNodes.domain.DemoDomain;
import javax.annotation.Resource;
@LiteflowCmpDefine
public class ECmp{
@Resource
private DemoDomain demoDomain;
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
demoDomain.sayHi();
System.out.println("ECmp executed!");
}
}

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.customNodes.cmp;
import com.yomahub.liteflow.annotation.LiteflowCmpDefine;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
@LiteflowCmpDefine
public class FCmp{
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
public void process(NodeComponent bindCmp) {
System.out.println("FCmp executed!");
}
}

View File

@ -0,0 +1,11 @@
package com.yomahub.liteflow.test.customNodes.domain;
import org.springframework.stereotype.Component;
@Component
public class DemoDomain {
public void sayHi(){
System.out.println("hi");
}
}

View File

@ -0,0 +1,25 @@
package com.yomahub.liteflow.test.customWhenThreadPool;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.thread.ExecutorBuilder;
import java.util.concurrent.ExecutorService;
public class CustomThreadExecutor1 implements ExecutorBuilder {
@Override
public ExecutorService buildExecutor() {
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
//只有在非spring的场景下liteflowConfig才会为null
if (ObjectUtil.isNull(liteflowConfig)) {
liteflowConfig = new LiteflowConfig();
}
return buildDefaultExecutor(
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenQueueLimit(),
"customer-when-1-thead-");
}
}

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.customWhenThreadPool;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.thread.ExecutorBuilder;
import java.util.concurrent.ExecutorService;
public class CustomThreadExecutor2 implements ExecutorBuilder {
@Override
public ExecutorService buildExecutor() {
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
//只有在非spring的场景下liteflowConfig才会为null
if (ObjectUtil.isNull(liteflowConfig)) {
liteflowConfig = new LiteflowConfig();
}
return buildDefaultExecutor(
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenQueueLimit(),
"customer-when-2-thead-");
}
}

View File

@ -0,0 +1,24 @@
package com.yomahub.liteflow.test.customWhenThreadPool;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.thread.ExecutorBuilder;
import java.util.concurrent.ExecutorService;
public class CustomThreadExecutor3 implements ExecutorBuilder {
@Override
public ExecutorService buildExecutor() {
LiteflowConfig liteflowConfig = LiteflowConfigGetter.get();
//只有在非spring的场景下liteflowConfig才会为null
if (ObjectUtil.isNull(liteflowConfig)) {
liteflowConfig = new LiteflowConfig();
}
return buildDefaultExecutor(
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenMaxWorkers(),
liteflowConfig.getWhenQueueLimit(),
"customer-when-3-thead-");
}
}

View File

@ -0,0 +1,75 @@
package com.yomahub.liteflow.test.customWhenThreadPool;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境下异步线程超时日志打印测试
*
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/customWhenThreadPool/application.properties")
@SpringBootTest(classes = CustomWhenThreadPoolELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.customWhenThreadPool.cmp"})
public class CustomWhenThreadPoolELDeclMultiSpringbootTest extends BaseTest {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Resource
private FlowExecutor flowExecutor;
/**
* 测试全局线程池配置
*/
@Test
public void testGlobalThreadPool() {
LiteflowResponse response = flowExecutor.execute2Resp("chain", "arg");
DefaultContext context = response.getFirstContextBean();
Assert.assertTrue(response.isSuccess());
Assert.assertTrue(context.getData("threadName").toString().startsWith("lf-when-thead"));
}
/**
* 测试全局和when上自定义线程池-优先以when上为准
*/
@Test
public void testGlobalAndCustomWhenThreadPool() {
LiteflowResponse response1 = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response1.getFirstContextBean();
Assert.assertTrue(response1.isSuccess());
Assert.assertTrue(context.getData("threadName").toString().startsWith("customer-when-1-thead"));
}
/**
* when配置的线程池可以共用
*/
@Test
public void testCustomWhenThreadPool() {
// 使用when - thread1
testGlobalAndCustomWhenThreadPool();
// chain配置同一个thead1
LiteflowResponse response2 = flowExecutor.execute2Resp("chain2", "arg");
DefaultContext context = response2.getFirstContextBean();
Assert.assertTrue(response2.isSuccess());
Assert.assertTrue(context.getData("threadName").toString().startsWith("customer-when-1-thead"));
}
}

View File

@ -0,0 +1,52 @@
package com.yomahub.liteflow.test.customWhenThreadPool.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.slot.DefaultContext;
@LiteflowComponent
public class CmpConfig {
@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) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("threadName", Thread.currentThread().getName());
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("threadName", Thread.currentThread().getName());
System.out.println("CCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("threadName", Thread.currentThread().getName());
System.out.println("DCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("threadName", Thread.currentThread().getName());
System.out.println("ECmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "f")
public void processF(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("threadName", Thread.currentThread().getName());
System.out.println("FCmp executed!");
}
}

View File

@ -0,0 +1,64 @@
package com.yomahub.liteflow.test.event;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.slot.DefaultContext;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境最普通的例子测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/event/application.properties")
@SpringBootTest(classes = EventELDeclMultiSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.event.cmp"})
public class EventELDeclMultiSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试组件成功事件
@Test
public void testEvent1() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
DefaultContext context = response.getFirstContextBean();
Assert.assertTrue(response.isSuccess());
Assert.assertEquals("abc", context.getData("test"));
}
//测试组件失败事件
@Test
public void testEvent2() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain2", "arg");
DefaultContext context = response.getFirstContextBean();
Assert.assertFalse(response.isSuccess());
Assert.assertEquals(NullPointerException.class, response.getCause().getClass());
Assert.assertEquals("ab", context.getData("test"));
Assert.assertEquals("error:d", context.getData("error"));
}
//测试组件失败事件本身抛出异常
@Test
public void testEvent3() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain3", "arg");
DefaultContext context = response.getFirstContextBean();
Assert.assertFalse(response.isSuccess());
Assert.assertEquals(NullPointerException.class, response.getCause().getClass());
Assert.assertEquals("a", context.getData("test"));
Assert.assertEquals("error:e", context.getData("error"));
}
}

View File

@ -0,0 +1,77 @@
package com.yomahub.liteflow.test.event.cmp;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.slot.DefaultContext;
@LiteflowComponent
public class CmpConfig {
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("test","");
System.out.println("ACmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_SUCCESS,nodeId = "a")
public void onSuccessA(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
String str = context.getData("test");
str += bindCmp.getNodeId();
context.setData("test", str);
}
////////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) {
System.out.println("BCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_SUCCESS,nodeId = "b")
public void onSuccessB(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
String str = context.getData("test");
str += bindCmp.getNodeId();
context.setData("test", str);
}
///////////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
System.out.println("CCmp executed!");
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_SUCCESS,nodeId = "c")
public void onSuccessC(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
String str = context.getData("test");
str += bindCmp.getNodeId();
context.setData("test", str);
}
///////////////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) throws Exception{
System.out.println("CCmp executed!");
throw new NullPointerException();
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_ERROR,nodeId = "d")
public void onErrorD(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("error","error:"+bindCmp.getNodeId());
}
///////////////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "e")
public void processE(NodeComponent bindCmp) throws Exception{
System.out.println("CCmp executed!");
throw new NullPointerException();
}
@LiteflowMethod(value = LiteFlowMethodEnum.ON_ERROR,nodeId = "e")
public void onErrorE(NodeComponent bindCmp) throws Exception {
DefaultContext context = bindCmp.getFirstContextBean();
context.setData("error","error:"+bindCmp.getNodeId());
throw new IllegalAccessException("错误事件回调本身抛出异常");
}
}

View File

@ -0,0 +1,12 @@
package com.yomahub.liteflow.test.exception;
import com.yomahub.liteflow.exception.LiteFlowException;
/**
* 用户自定义带状态码的异常
*/
public class CustomStatefulException extends LiteFlowException {
public CustomStatefulException(String code, String message) {
super(code, message);
}
}

View File

@ -0,0 +1,61 @@
package com.yomahub.liteflow.test.exception;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.ChainDuplicateException;
import com.yomahub.liteflow.exception.ConfigErrorException;
import com.yomahub.liteflow.exception.FlowExecutorNotInitException;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.property.LiteflowConfigGetter;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 流程执行异常
* 单元测试
*
* @author zendwang
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Exception1ELDeclMultiSpringBootTest.class)
@EnableAutoConfiguration
public class Exception1ELDeclMultiSpringBootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
/**
* 验证 chain 节点重复的异常
*/
@Test(expected = ChainDuplicateException.class)
public void testChainDuplicateException() {
LiteflowConfig config = LiteflowConfigGetter.get();
config.setRuleSource("exception/flow-exception.el.xml");
flowExecutor.init();
}
@Test(expected = ConfigErrorException.class)
public void testConfigErrorException() {
flowExecutor.setLiteflowConfig(null);
flowExecutor.init();
}
@Test(expected = FlowExecutorNotInitException.class)
public void testFlowExecutorNotInitException() {
LiteflowConfig config = LiteflowConfigGetter.get();
config.setRuleSource("error/flow.txt");
flowExecutor.init();
}
@Test(expected = FlowExecutorNotInitException.class)
public void testNoConditionInChainException() throws Exception {
LiteflowConfig config = LiteflowConfigGetter.get();
config.setRuleSource("exception/flow-blank.el.xml");
flowExecutor.init();
}
}

View File

@ -0,0 +1,77 @@
package com.yomahub.liteflow.test.exception;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.exception.ChainNotFoundException;
import com.yomahub.liteflow.exception.LiteFlowException;
import com.yomahub.liteflow.exception.NoSwitchTargetNodeException;
import com.yomahub.liteflow.flow.LiteflowResponse;
import com.yomahub.liteflow.test.BaseTest;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* 流程执行异常
* 单元测试
*
* @author zendwang
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/exception/application.properties")
@SpringBootTest(classes = Exception2ELDeclMultiSpringBootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.exception.cmp"})
public class Exception2ELDeclMultiSpringBootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test(expected = ChainNotFoundException.class)
public void testChainNotFoundException() throws Exception {
flowExecutor.execute("chain0", "it's a request");
}
@Test(expected = RuntimeException.class)
public void testComponentCustomException() throws Exception {
flowExecutor.execute("chain1", "exception");
}
@Test
public void testGetSlotFromResponseWhenException() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain4", "test");
Assert.assertFalse(response.isSuccess());
Assert.assertNotNull(response.getCause());
Assert.assertNotNull(response.getSlot());
}
@Test(expected = NoSwitchTargetNodeException.class)
public void testNoTargetFindException() throws Exception{
LiteflowResponse response = flowExecutor.execute2Resp("chain5", "test");
Assert.assertFalse(response.isSuccess());
throw response.getCause();
}
@Test
public void testInvokeCustomStatefulException() {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "custom-stateful-exception");
Assert.assertFalse(response.isSuccess());
Assert.assertEquals("300", response.getCode());
Assert.assertNotNull(response.getCause());
Assert.assertTrue(response.getCause() instanceof LiteFlowException);
Assert.assertNotNull(response.getSlot());
}
@Test
public void testNotInvokeCustomStatefulException() {
LiteflowResponse response = flowExecutor.execute2Resp("chain6", "test");
Assert.assertTrue(response.isSuccess());
Assert.assertNull(response.getCode());
}
}

View File

@ -0,0 +1,76 @@
package com.yomahub.liteflow.test.exception.cmp;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.annotation.LiteflowMethod;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.enums.AnnotationNodeTypeEnum;
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
import com.yomahub.liteflow.test.exception.CustomStatefulException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@LiteflowComponent
public class CmpConfig {
private static final Logger LOG = LoggerFactory.getLogger(CmpConfig.class);
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
public void processA(NodeComponent bindCmp) {
String str = bindCmp.getRequestData();
if(StrUtil.isNotBlank(str) && str.equals("exception")) {
throw new RuntimeException("chain execute execption");
}
LOG.info("Acomp executed!");
}
//////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
public void processB(NodeComponent bindCmp) throws InterruptedException {
String str = bindCmp.getRequestData();
if(StrUtil.isNotBlank(str) && str.equals("when")) {
try {
LOG.info("Bcomp sleep begin");
Thread.sleep(3000);
LOG.info("Bcomp sleep end");
} catch (InterruptedException e) {
throw e;
}
}
LOG.info("Bcomp executed!");
}
//////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
public void processC(NodeComponent bindCmp) {
LOG.info("Ccomp executed!");
}
//////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "d")
public void processD(NodeComponent bindCmp) {
if(1==1){
int a = 1/0;
}
LOG.info("Dcomp executed!");
}
////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS_SWITCH,nodeId = "e",nodeType = AnnotationNodeTypeEnum.SWITCH)
public String processSwitchE(NodeComponent bindCmp) throws Exception {
return "a";
}
//////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "f")
public void processF(NodeComponent bindCmp) {
String str = bindCmp.getRequestData();
if(StrUtil.isNotBlank(str) && str.equals("custom-stateful-exception")) {
throw new CustomStatefulException("300", "chain execute custom stateful execption");
}
LOG.info("Fcomp executed!");
}
////////
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "g")
public void processG(NodeComponent bindCmp) {
LOG.info("Gcomp executed!");
}
}

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