Merge tag 'v2.6.8' of https://gitee.com/sikadai/liteFlow into v2.6.8

This commit is contained in:
daiqi
2022-01-21 22:26:53 +08:00
35 changed files with 529 additions and 66 deletions

View File

@@ -17,14 +17,23 @@ public class LiteFlowChainBuilder {
private Chain chain;
//声明这个变量而不是用chain.getConditionList的目的是为了辅助平滑加载
//虽然FlowBus里面的map都是CopyOnWrite类型的但是在buildCondition的时候为了平滑加载所以不能事先把chain.getConditionList给设为空List
//所以在这里做一个缓存等conditionList全部build完毕后再去一次性替换chain里面的conditionList
private final List<Condition> conditionList;
public static LiteFlowChainBuilder createChain(){
return new LiteFlowChainBuilder();
}
public LiteFlowChainBuilder(){
chain = new Chain();
conditionList = new ArrayList<>();
}
//在parser中chain的build是2段式的因为涉及到依赖问题以前是递归parser
//2.6.8之后取消了递归的模式两段式组装先把带有chainName的chain对象放进去第二段再组装chain里面的condition
//所以这里setChainName的时候需要判断下
public LiteFlowChainBuilder setChainName(String chainName){
if (FlowBus.containChain(chainName)){
this.chain = FlowBus.getChain(chainName);
@@ -41,6 +50,7 @@ public class LiteFlowChainBuilder {
}
public void build(){
this.chain.setConditionList(this.conditionList);
FlowBus.addChain(this.chain);
}
@@ -49,29 +59,29 @@ public class LiteFlowChainBuilder {
//对于then来说相邻的2个then会合并成一个condition
//对于when来说相同组的when会合并成一个condition不同组的when还是会拆开
if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_PRE)) {
this.chain.getConditionList().add(new PreCondition(condition));
this.conditionList.add(new PreCondition(condition));
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_THEN)) {
if (this.chain.getConditionList().size() >= 1 &&
CollectionUtil.getLast(this.chain.getConditionList()) instanceof ThenCondition) {
CollectionUtil.getLast(this.chain.getConditionList()).getNodeList().addAll(condition.getNodeList());
if (this.conditionList.size() >= 1 &&
CollectionUtil.getLast(this.conditionList) instanceof ThenCondition) {
CollectionUtil.getLast(this.conditionList).getNodeList().addAll(condition.getNodeList());
} else {
this.chain.getConditionList().add(new ThenCondition(condition));
this.conditionList.add(new ThenCondition(condition));
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_WHEN)) {
if (this.chain.getConditionList().size() > 1 &&
CollectionUtil.getLast(this.chain.getConditionList()) instanceof WhenCondition &&
CollectionUtil.getLast(this.chain.getConditionList()).getGroup().equals(condition.getGroup())) {
CollectionUtil.getLast(this.chain.getConditionList()).getNodeList().addAll(condition.getNodeList());
if (this.conditionList.size() > 1 &&
CollectionUtil.getLast(this.conditionList) instanceof WhenCondition &&
CollectionUtil.getLast(this.conditionList).getGroup().equals(condition.getGroup())) {
CollectionUtil.getLast(this.conditionList).getNodeList().addAll(condition.getNodeList());
} else {
this.chain.getConditionList().add(new WhenCondition(condition));
this.conditionList.add(new WhenCondition(condition));
}
} else if (condition.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)) {
this.chain.getConditionList().add(new FinallyCondition(condition));
this.conditionList.add(new FinallyCondition(condition));
}
//每一次build之后对conditionList进行排序pre最前面finally最后
//这里为什么要排序因为在声明的时候哪怕有人不把pre放最前finally放最后但最终也要确保是正确的顺序
CollectionUtil.sort(this.chain.getConditionList(), (o1, o2) -> {
CollectionUtil.sort(this.conditionList, (o1, o2) -> {
if (o1.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o2.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){
return -1;
} else if (o2.getConditionType().equals(ConditionTypeEnum.TYPE_PRE) || o1.getConditionType().equals(ConditionTypeEnum.TYPE_FINALLY)){

View File

@@ -2,6 +2,7 @@ package com.yomahub.liteflow.builder;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.entity.data.DataBus;
import com.yomahub.liteflow.entity.flow.Node;
import com.yomahub.liteflow.enums.NodeTypeEnum;
import com.yomahub.liteflow.exception.NodeBuildException;
@@ -19,10 +20,31 @@ public class LiteFlowNodeBuilder {
return new LiteFlowNodeBuilder();
}
public static LiteFlowNodeBuilder createCommonNode(){
return new LiteFlowNodeBuilder(NodeTypeEnum.COMMON);
}
public static LiteFlowNodeBuilder createCommonCondNode(){
return new LiteFlowNodeBuilder(NodeTypeEnum.COMMON);
}
public static LiteFlowNodeBuilder createScriptNode(){
return new LiteFlowNodeBuilder(NodeTypeEnum.SCRIPT);
}
public static LiteFlowNodeBuilder createScriptCondNode(){
return new LiteFlowNodeBuilder(NodeTypeEnum.COND_SCRIPT);
}
public LiteFlowNodeBuilder() {
this.node = new Node();
}
public LiteFlowNodeBuilder(NodeTypeEnum type) {
this.node = new Node();
this.node.setType(type);
}
public LiteFlowNodeBuilder setId(String nodeId) {
this.node.setId(nodeId);
return this;
@@ -57,6 +79,11 @@ public class LiteFlowNodeBuilder {
}
public void build() {
//这里也是一个防御性编程
//如果单独用builder进行构建的话那么flow.xml不一定存在不存在则不会进行FlowExecutor的init也就不会进行DataBus.init
//所以这里多加一步DataBus.init()事实上只会执行一遍,不会因为之前执行了,重复执行。因为里面有判断
DataBus.init();
try {
if (this.node.getType().equals(NodeTypeEnum.COMMON)) {
FlowBus.addCommonNode(this.node.getId(), this.node.getName(), this.node.getClazz());

View File

@@ -64,9 +64,14 @@ public class FlowExecutor {
* FlowExecutor的初始化化方式主要用于parse规则文件
*/
public void init() {
if (ObjectUtil.isNull(liteflowConfig) || StrUtil.isBlank(liteflowConfig.getRuleSource())) {
if (ObjectUtil.isNull(liteflowConfig)) {
throw new ConfigErrorException("config error, please check liteflow config property");
}
if (StrUtil.isBlank(liteflowConfig.getRuleSource())){
return;
}
List<String> sourceRulePathList = Lists.newArrayList(liteflowConfig.getRuleSource().split(",|;"));
FlowParser parser = null;

View File

@@ -34,6 +34,7 @@ import com.yomahub.liteflow.script.ScriptExecutorFactory;
import com.yomahub.liteflow.script.exception.ScriptSpiException;
import com.yomahub.liteflow.util.CopyOnWriteHashMap;
import com.yomahub.liteflow.util.SpringAware;
import org.checkerframework.checker.units.qual.C;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SerializationUtils;
@@ -61,14 +62,18 @@ public class FlowBus {
return chainMap.get(id);
}
public static void addChain(Chain chain) {
if (chainMap.containsKey(chain.getChainName()) && CollectionUtil.isEmpty(chain.getConditionList())){
chainMap.get(chain.getChainName()).setConditionList(chain.getConditionList());
}else{
chainMap.put(chain.getChainName(), chain);
//这一方法主要用于第一阶段chain的预装载
public static void addChain(String chainName){
if (!chainMap.containsKey(chainName)){
chainMap.put(chainName, new Chain(chainName));
}
}
//这个方法主要用于第二阶段的替换chain
public static void addChain(Chain chain) {
chainMap.put(chain.getChainName(), chain);
}
public static boolean containChain(String chainId) {
return chainMap.containsKey(chainId);
}
@@ -172,7 +177,6 @@ public class FlowBus {
}catch (ScriptSpiException ignored){}
}
//目前这种方式刷新不完全平滑
public static void refreshFlowMetaData(FlowParserTypeEnum type, String content) throws Exception {
if (type.equals(FlowParserTypeEnum.TYPE_XML)) {
new LocalXmlFlowParser().parse(content);

View File

@@ -78,7 +78,7 @@ public abstract class JsonFlowParser extends FlowParser {
//先在元数据里放上chain
chainArray.forEach(o -> {
JSONObject innerJsonObject = (JSONObject)o;
FlowBus.addChain(new Chain(innerJsonObject.getString("name")));
FlowBus.addChain(innerJsonObject.getString("name"));
});
});

View File

@@ -73,7 +73,7 @@ public abstract class XmlFlowParser extends FlowParser {
List<Element> chainList = document.getRootElement().elements("chain");
//先在元数据里放上chain
chainList.forEach(e -> FlowBus.addChain(new Chain(e.attributeValue("name"))));
chainList.forEach(e -> FlowBus.addChain(e.attributeValue("name")));
});
for (Document document : documentList) {

View File

@@ -45,6 +45,8 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -56,27 +58,25 @@ import java.util.concurrent.atomic.AtomicBoolean;
*
* @author Paul.Sandoz@Oracle.Com
* @author pavel.bucek@oracle.com
* @author Bryan.Zhang
*/
public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
volatile Map<K, V> view;
public class CopyOnWriteHashMap<K,V> extends ConcurrentHashMap<K,V> {
private Map<K, V> duplicate(Map<K, V> original) {
Map<K, V> result = new HashMap();
volatile ConcurrentHashMap<K, V> view;
private ConcurrentHashMap<K, V> duplicate(ConcurrentHashMap<K, V> original) {
// SUBTLETY: note that original.entrySet() grabs the entire contents of the original Map in a
// single call. This means that if the original map is Thread-safe or another CopyOnWriteHashMap,
// we can safely iterate over the list of entries.
for (Map.Entry<K, V> entry : original.entrySet()) {
result.put(entry.getKey(), entry.getValue());
}
return result;
return new ConcurrentHashMap<>(original);
}
public CopyOnWriteHashMap(Map<K, V> that) {
public CopyOnWriteHashMap(ConcurrentHashMap<K, V> that) {
this.view = duplicate(that);
}
public CopyOnWriteHashMap() {
this(new HashMap<K, V>());
this(new ConcurrentHashMap<>());
}
@Override
@@ -114,7 +114,7 @@ public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
}
@Override
public Set<K> keySet() {
public KeySetView<K,V> keySet() {
return view.keySet();
}
@@ -149,7 +149,7 @@ public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
@Override
public V put(K key, V value) {
synchronized (this) {
Map<K, V> newCore = duplicate(view);
ConcurrentHashMap<K, V> newCore = duplicate(view);
V result = newCore.put(key, value);
view = newCore; // volatile write
return result;
@@ -159,18 +159,17 @@ public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
@Override
public V remove(Object key) {
synchronized (this) {
Map<K, V> newCore = duplicate(view);
ConcurrentHashMap<K, V> newCore = duplicate(view);
V result = newCore.remove(key);
view = newCore; // volatile write
return result;
}
}
@Override
public void putAll(Map<? extends K, ? extends V> t) {
synchronized (this) {
Map<K, V> newCore = duplicate(view);
ConcurrentHashMap<K, V> newCore = duplicate(view);
newCore.putAll(t);
view = newCore; // volatile write
}
@@ -179,8 +178,7 @@ public class CopyOnWriteHashMap<K,V> implements Map<K,V> {
@Override
public void clear() {
synchronized (this) {
Map<K, V> newCore = new HashMap<K, V>();
view = newCore; // volatile write
view = new ConcurrentHashMap<>(); // volatile write
}
}
}

View File

@@ -11,6 +11,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import javax.swing.*;
import java.util.concurrent.ExecutorService;
/**
@@ -25,15 +26,16 @@ import java.util.concurrent.ExecutorService;
@Import(SpringAware.class)
public class LiteflowExecutorAutoConfiguration {
//这里参数加上这2个为了先行注入
@Bean("whenExecutors")
public ExecutorService executorService(LiteflowConfig liteflowConfig) {
public ExecutorService executorService(SpringAware springAware, LiteflowConfig liteflowConfig) {
return ExecutorHelper.loadInstance().buildExecutor();
}
//为什么要注释掉这个@Bean
//LiteFlowExecutorPoolShutdown这个类会在spring上下文移除这个bean的时候执行也就是应用被停止或者kill的时候
//这个类主要用于卸载掉线程池,会等待线程池中的线程执行完,再卸载掉,相当于一个钩子
//但这段代码在实际中并没有太多用,就算结束掉应用进程时很多公司也会优雅停机。就显得这段代码很鸡肋
//但这段代码在实际中并没有太多用,就算结束掉应用进程时很多公司也会优雅停机。就显得这段代码很鸡肋
//之所以注释掉是因为在单元测试中每一个testcase结束时都会调这个方法。
//当异步线程配置超时的时候。由于这个方法会去关闭掉线程池,会导致单元测试在所有一起运行时(单个运行没有问题)会出错
//按理说这个方法会等待线程池里的全部线程执行完再销毁,但是事实上在单元测试中的确会报错。具体原因还没深究,由于这个类比较鸡肋,就干脆不注册了。

View File

@@ -35,13 +35,9 @@ public class LiteflowMainAutoConfiguration {
@Bean
public FlowExecutor flowExecutor(LiteflowConfig liteflowConfig) {
if (StrUtil.isNotBlank(liteflowConfig.getRuleSource())) {
FlowExecutor flowExecutor = new FlowExecutor();
flowExecutor.setLiteflowConfig(liteflowConfig);
return flowExecutor;
} else {
return null;
}
FlowExecutor flowExecutor = new FlowExecutor();
flowExecutor.setLiteflowConfig(liteflowConfig);
return flowExecutor;
}
@Bean

View File

@@ -1,6 +1,5 @@
liteflow.enable=true
liteflow.print-banner=true
liteflow.rule-source=config/flow.xml
liteflow.zk-node=/lite-flow/flow
liteflow.slot-size=1024
liteflow.thread-executor-class=com.yomahub.liteflow.thread.LiteFlowDefaultExecutorBuilder

View File

@@ -16,7 +16,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
/**
* springboot环境下pre节点和finally节点的测试
* springboot环境最普通的例子测试
* @author Bryan.Zhang
* @since 2.6.4
*/
@@ -30,9 +30,8 @@ public class BaseSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试普通的pre和finally节点
@Test
public void testPreAndFinally1() throws Exception{
public void testBase() throws Exception{
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}

View File

@@ -0,0 +1,86 @@
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.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.LiteflowResponse;
import com.yomahub.liteflow.enums.NodeTypeEnum;
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模式的单元测试
//这里只是最基本的builder模式的测试只是为了验证在springboot模式下的正常性
//更详细的builder模式测试用例会单独拉testcase去做
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BuilderSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.builder.cmp"})
public class BuilderSpringbootTest 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.cmp.ACmp")
.build();
LiteFlowNodeBuilder.createNode().setId("b")
.setName("组件B")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.BCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("c")
.setName("组件C")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.CCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("d")
.setName("组件D")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.DCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("e")
.setName("组件E")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.ECmp")
.build();
LiteFlowNodeBuilder.createNode().setId("f")
.setName("组件F")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.FCmp")
.build();
LiteFlowNodeBuilder.createNode().setId("g")
.setName("组件G")
.setType(NodeTypeEnum.COMMON)
.setClazz("com.yomahub.liteflow.test.builder.cmp.GCmp")
.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<DefaultSlot> response = flowExecutor.execute2Resp("chain1");
Assert.assertTrue(response.isSuccess());
}
}

View File

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

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.builder.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
public class BCmp extends NodeComponent {
@Override
public void process() {
System.out.println("BCmp executed!");
}
}

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.builder.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
public class CCmp extends NodeComponent {
@Override
public void process() {
System.out.println("CCmp executed!");
}
}

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.builder.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
public class DCmp extends NodeComponent {
@Override
public void process() {
System.out.println("DCmp executed!");
}
}

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.builder.cmp;
import com.yomahub.liteflow.core.NodeCondComponent;
public class ECmp extends NodeCondComponent {
@Override
public String processCond() throws Exception {
System.out.println("ECmp executed!");
return "chain2";
}
}

View File

@@ -0,0 +1,19 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class FCmp extends NodeComponent {
@Override
public void process() {
System.out.println("FCmp executed!");
}
}

View File

@@ -0,0 +1,19 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.builder.cmp;
import com.yomahub.liteflow.core.NodeComponent;
public class GCmp extends NodeComponent {
@Override
public void process() {
System.out.println("GCmp executed!");
}
}

View File

@@ -45,8 +45,7 @@ public class FlowExecutorSpringBootTest extends BaseTest {
@Test(expected = ConfigErrorException.class)
public void testConfigErrorException() {
LiteflowConfig config = context.getBean(LiteflowConfig.class);
config.setRuleSource("");
flowExecutor.setLiteflowConfig(null);
flowExecutor.init();
}

View File

@@ -0,0 +1,41 @@
package com.yomahub.liteflow.test.monitor;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.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:/monitor/application.properties")
@SpringBootTest(classes = MonitorSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.monitor.cmp"})
public class MonitorSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
@Test
public void testMonitor() throws Exception{
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
Thread.sleep(20000);
}
}

View File

@@ -0,0 +1,28 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitor.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("a")
public class ACmp extends NodeComponent {
@Override
public void process() {
try {
Thread.sleep(new Random().nextInt(2000));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("ACmp executed!");
}
}

View File

@@ -0,0 +1,28 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitor.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("b")
public class BCmp extends NodeComponent {
@Override
public void process() {
try {
Thread.sleep(new Random().nextInt(2000));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("BCmp executed!");
}
}

View File

@@ -0,0 +1,28 @@
/**
* <p>Title: liteflow</p>
* <p>Description: 轻量级的组件式流程框架</p>
* @author Bryan.Zhang
* @email weenyc31@163.com
* @Date 2020/4/1
*/
package com.yomahub.liteflow.test.monitor.cmp;
import com.yomahub.liteflow.core.NodeComponent;
import org.springframework.stereotype.Component;
import java.util.Random;
@Component("c")
public class CCmp extends NodeComponent {
@Override
public void process() {
try {
Thread.sleep(new Random().nextInt(2000));
}catch (Exception e){
e.printStackTrace();
}
System.out.println("CCmp executed!");
}
}

View File

@@ -22,10 +22,9 @@ import javax.annotation.Resource;
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/parser/application-json.properties")
@SpringBootTest(classes = LFParserJsonSpringbootTest.class)
@SpringBootTest(classes = JsonParserSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.parser.cmp"})
public class LFParserJsonSpringbootTest extends BaseTest {
public class JsonParserSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;

View File

@@ -0,0 +1,32 @@
package com.yomahub.liteflow.test.parser;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.entity.data.DefaultSlot;
import com.yomahub.liteflow.entity.data.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.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/parser/application-springEL.properties")
@SpringBootTest(classes = SpringELSupportSpringbootTest.class)
@EnableAutoConfiguration
public class SpringELSupportSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;
//测试springEL的解析情况
@Test
public void testSpringELParser() {
LiteflowResponse<DefaultSlot> response = flowExecutor.execute2Resp("chain1", "arg");
Assert.assertTrue(response.isSuccess());
}
}

View File

@@ -22,10 +22,9 @@ import javax.annotation.Resource;
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/parser/application-xml.properties")
@SpringBootTest(classes = LFParserXmlSpringbootTest.class)
@SpringBootTest(classes = XmlParserSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.parser.cmp"})
public class LFParserXmlSpringbootTest extends BaseTest {
public class XmlParserSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;

View File

@@ -22,10 +22,9 @@ import javax.annotation.Resource;
*/
@RunWith(SpringRunner.class)
@TestPropertySource(value = "classpath:/parser/application-yml.properties")
@SpringBootTest(classes = LFParserYmlSpringbootTest.class)
@SpringBootTest(classes = YmlParserSpringbootTest.class)
@EnableAutoConfiguration
@ComponentScan({"com.yomahub.liteflow.test.parser.cmp"})
public class LFParserYmlSpringbootTest extends BaseTest {
public class YmlParserSpringbootTest extends BaseTest {
@Resource
private FlowExecutor flowExecutor;

View File

@@ -17,10 +17,6 @@ public class ACmp extends NodeComponent {
@Override
public void process() {
String str = this.getSlot().getRequestData();
if(StrUtil.isNotBlank(str) && str.equals("exception")) {
throw new FlowSystemException("chain execute execption");
}
System.out.println("ACmp executed!");
}
}

View File

@@ -0,0 +1,5 @@
liteflow.rule-source=monitor/flow.xml
liteflow.monitor.enable-log=true
liteflow.monitor.queue-limit=200
liteflow.monitor.delay=5000
liteflow.monitor.period=5000

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
<then value="a"/>
<when value="b,c"/>
</chain>
</flow>

View File

@@ -0,0 +1 @@
liteflow.rule-source=parser/**/*.xml

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<nodes>
<node id="a" class="com.yomahub.liteflow.test.parser.cmp.ACmp"/>
<node id="b" class="com.yomahub.liteflow.test.parser.cmp.BCmp"/>
<node id="c" class="com.yomahub.liteflow.test.parser.cmp.CCmp"/>
<node id="d" class="com.yomahub.liteflow.test.parser.cmp.DCmp"/>
<node id="e" class="com.yomahub.liteflow.test.parser.cmp.ECmp"/>
<node id="f" class="com.yomahub.liteflow.test.parser.cmp.FCmp"/>
<node id="g" class="com.yomahub.liteflow.test.parser.cmp.GCmp"/>
</nodes>
<chain name="chain1">
<then value="a,c"/>
<when value="b,d,e(f|g)"/>
<then value="chain2"/>
</chain>
<chain name="chain2">
<then value="c,g,f"/>
</chain>
</flow>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain1">
<then value="a,c"/>
<when value="b,d,e(f|g)"/>
<then value="chain2"/>
</chain>
</flow>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<flow>
<chain name="chain2">
<then value="c,g,f"/>
</chain>
</flow>