!165 绝对路径的目录位置及其所有子目录下规则配置文件的侦听
Merge pull request !165 from 与或非/issues/I6BDLN
This commit is contained in:
commit
394136b988
|
@ -7,6 +7,7 @@ import com.yomahub.liteflow.enums.NodeTypeEnum;
|
|||
import com.yomahub.liteflow.exception.NodeBuildException;
|
||||
import com.yomahub.liteflow.flow.FlowBus;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.monitor.MonitorFile;
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -134,6 +135,10 @@ public class LiteFlowNodeBuilder {
|
|||
List<String> scriptList = PathContentParserHolder.loadContextAware().parseContent(ListUtil.toList(filePath));
|
||||
String script = CollUtil.getFirst(scriptList);
|
||||
setScript(script);
|
||||
|
||||
// 添加脚本文件监听
|
||||
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(ListUtil.toList(filePath));
|
||||
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
|
||||
} catch (Exception e) {
|
||||
String errMsg = StrUtil.format("An exception occurred while building the node[{}],{}", this.node.getId(), e.getMessage());
|
||||
throw new NodeBuildException(errMsg);
|
||||
|
|
|
@ -18,6 +18,7 @@ import com.yomahub.liteflow.flow.LiteflowResponse;
|
|||
import com.yomahub.liteflow.flow.element.Chain;
|
||||
import com.yomahub.liteflow.flow.element.Node;
|
||||
import com.yomahub.liteflow.flow.id.IdGeneratorHolder;
|
||||
import com.yomahub.liteflow.monitor.MonitorFile;
|
||||
import com.yomahub.liteflow.parser.base.FlowParser;
|
||||
import com.yomahub.liteflow.parser.factory.FlowParserProvider;
|
||||
import com.yomahub.liteflow.parser.spi.ParserClassNameSpi;
|
||||
|
@ -27,6 +28,7 @@ import com.yomahub.liteflow.slot.DataBus;
|
|||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.slot.Slot;
|
||||
import com.yomahub.liteflow.spi.holder.ContextCmpInitHolder;
|
||||
import com.yomahub.liteflow.spi.holder.PathContentParserHolder;
|
||||
import com.yomahub.liteflow.thread.ExecutorHelper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -89,11 +91,11 @@ public class FlowExecutor {
|
|||
//所有的Parser的SPI实现都是以custom形式放入的,且只支持xml形式
|
||||
ServiceLoader<ParserClassNameSpi> loader = ServiceLoader.load(ParserClassNameSpi.class);
|
||||
Iterator<ParserClassNameSpi> it = loader.iterator();
|
||||
if (it.hasNext()){
|
||||
if (it.hasNext()) {
|
||||
ParserClassNameSpi parserClassNameSpi = it.next();
|
||||
ruleSource = "el_xml:" + parserClassNameSpi.getSpiClassName();
|
||||
liteflowConfig.setRuleSource(ruleSource);
|
||||
}else{
|
||||
} else {
|
||||
//ruleSource为空,而且没有spi形式的扩展,那么说明真的没有ruleSource
|
||||
//这种情况有可能是基于代码动态构建的
|
||||
return;
|
||||
|
@ -124,6 +126,9 @@ public class FlowExecutor {
|
|||
|
||||
//支持多类型的配置文件,分别解析
|
||||
if (BooleanUtil.isTrue(liteflowConfig.isSupportMultipleType())) {
|
||||
// 添加监听文件路径
|
||||
addMonitorFilePaths(ListUtil.toList(path));
|
||||
// 解析文件
|
||||
parser.parseMain(ListUtil.toList(path));
|
||||
}
|
||||
} catch (CyclicDependencyException e) {
|
||||
|
@ -148,6 +153,9 @@ public class FlowExecutor {
|
|||
//进行多个配置文件的一起解析
|
||||
try {
|
||||
if (parser != null) {
|
||||
// 添加监听文件路径
|
||||
addMonitorFilePaths(rulePathList);
|
||||
// 解析文件
|
||||
parser.parseMain(rulePathList);
|
||||
} else {
|
||||
throw new ConfigErrorException("parse error, please check liteflow config property");
|
||||
|
@ -167,30 +175,37 @@ public class FlowExecutor {
|
|||
}
|
||||
|
||||
//如果是ruleSource方式的,最后判断下有没有解析出来,如果没有解析出来则报错
|
||||
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())){
|
||||
if (FlowBus.getChainMap().isEmpty()){
|
||||
if (StrUtil.isBlank(liteflowConfig.getRuleSourceExtData()) && MapUtil.isEmpty(liteflowConfig.getRuleSourceExtDataMap())) {
|
||||
if (FlowBus.getChainMap().isEmpty()) {
|
||||
String errMsg = StrUtil.format("no valid rule config found in rule path [{}]", liteflowConfig.getRuleSource());
|
||||
throw new ConfigErrorException(errMsg);
|
||||
}
|
||||
}
|
||||
|
||||
//执行钩子
|
||||
if(hook){
|
||||
if (hook) {
|
||||
FlowInitHook.executeHook();
|
||||
}
|
||||
|
||||
// 文件监听
|
||||
if (liteflowConfig.getEnableMonitorFile()) {
|
||||
MonitorFile.getInstance().create();
|
||||
}
|
||||
}
|
||||
|
||||
//此方法就是从原有的配置源主动拉取新的进行刷新
|
||||
//和FlowBus.refreshFlowMetaData的区别就是一个为主动拉取,一个为被动监听到新的内容进行刷新
|
||||
public void reloadRule() {
|
||||
long start = System.currentTimeMillis();
|
||||
init(false);
|
||||
LOG.info("reload rules takes {}ms", System.currentTimeMillis() - start);
|
||||
}
|
||||
|
||||
//隐式流程的调用方法
|
||||
@Deprecated
|
||||
public void invoke(String chainId, Object param, Integer slotIndex) throws Exception {
|
||||
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_SYNC);
|
||||
if (!response.isSuccess()){
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +213,7 @@ public class FlowExecutor {
|
|||
@Deprecated
|
||||
public void invokeInAsync(String chainId, Object param, Integer slotIndex) throws Exception {
|
||||
LiteflowResponse response = this.invoke2Resp(chainId, param, slotIndex, InnerChainTypeEnum.IN_ASYNC);
|
||||
if (!response.isSuccess()){
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}
|
||||
}
|
||||
|
@ -240,7 +255,7 @@ public class FlowExecutor {
|
|||
//调用一个流程并返回Future<LiteflowResponse>,允许多上下文的传入
|
||||
public Future<LiteflowResponse> execute2Future(String chainId, Object param, Class<?>... contextBeanClazzArray) {
|
||||
return ExecutorHelper.loadInstance().buildMainExecutor(liteflowConfig.getMainExecutorClass()).submit(()
|
||||
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray,null));
|
||||
-> FlowExecutorHolder.loadInstance().execute2Resp(chainId, param, contextBeanClazzArray, null));
|
||||
}
|
||||
|
||||
|
||||
|
@ -251,11 +266,11 @@ public class FlowExecutor {
|
|||
|
||||
//调用一个流程,返回默认的上下文,适用于简单的调用
|
||||
@Deprecated
|
||||
public DefaultContext execute(String chainId, Object param) throws Exception{
|
||||
public DefaultContext execute(String chainId, Object param) throws Exception {
|
||||
LiteflowResponse response = this.execute2Resp(chainId, param, DefaultContext.class);
|
||||
if (!response.isSuccess()){
|
||||
if (!response.isSuccess()) {
|
||||
throw response.getCause();
|
||||
}else{
|
||||
} else {
|
||||
return response.getFirstContextBean();
|
||||
}
|
||||
}
|
||||
|
@ -269,8 +284,8 @@ public class FlowExecutor {
|
|||
}
|
||||
|
||||
private LiteflowResponse invoke2Resp(String chainId,
|
||||
Object param,
|
||||
Integer slotIndex, InnerChainTypeEnum innerChainType) {
|
||||
Object param,
|
||||
Integer slotIndex, InnerChainTypeEnum innerChainType) {
|
||||
Slot slot = doExecute(chainId, param, null, null, slotIndex, innerChainType);
|
||||
return LiteflowResponse.newInnerResponse(chainId, slot);
|
||||
}
|
||||
|
@ -288,9 +303,9 @@ public class FlowExecutor {
|
|||
//如果不是隐式流程,那么需要分配Slot
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE) && ObjectUtil.isNull(slotIndex)) {
|
||||
//这里可以根据class分配,也可以根据bean去分配
|
||||
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)){
|
||||
if (ArrayUtil.isNotEmpty(contextBeanClazzArray)) {
|
||||
slotIndex = DataBus.offerSlotByClass(ListUtil.toList(contextBeanClazzArray));
|
||||
}else{
|
||||
} else {
|
||||
slotIndex = DataBus.offerSlotByBean(ListUtil.toList(contextBeanArray));
|
||||
}
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
|
||||
|
@ -311,7 +326,7 @@ public class FlowExecutor {
|
|||
//如果是隐式流程,事先把subException给置空,然后把隐式流程的chainId放入slot元数据中
|
||||
//我知道这在多线程调用隐式流程中会有问题。但是考虑到这种场景的不会多,也有其他的转换方式。
|
||||
//所以暂且这么做,以后再优化
|
||||
if (!innerChainType.equals(InnerChainTypeEnum.NONE)){
|
||||
if (!innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.removeSubException(chainId);
|
||||
slot.addSubChain(chainId);
|
||||
}
|
||||
|
@ -326,9 +341,9 @@ public class FlowExecutor {
|
|||
if (ObjectUtil.isNotNull(param)) {
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.setRequestData(param);
|
||||
} else if(innerChainType.equals(InnerChainTypeEnum.IN_SYNC)){
|
||||
} else if (innerChainType.equals(InnerChainTypeEnum.IN_SYNC)) {
|
||||
slot.setChainReqData(chainId, param);
|
||||
} else if(innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)){
|
||||
} else if (innerChainType.equals(InnerChainTypeEnum.IN_ASYNC)) {
|
||||
slot.setChainReqData2Queue(chainId, param);
|
||||
}
|
||||
}
|
||||
|
@ -351,15 +366,15 @@ public class FlowExecutor {
|
|||
} catch (Exception e) {
|
||||
if (ObjectUtil.isNotNull(chain)) {
|
||||
String errMsg = StrUtil.format("[{}]:chain[{}] execute error on slot[{}]", slot.getRequestId(), chain.getChainName(), slotIndex);
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
|
||||
LOG.error(errMsg, e);
|
||||
}else{
|
||||
} else {
|
||||
LOG.error(errMsg);
|
||||
}
|
||||
}else{
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())){
|
||||
} else {
|
||||
if (BooleanUtil.isTrue(liteflowConfig.getPrintExecutionLog())) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}else{
|
||||
} else {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
@ -368,7 +383,7 @@ public class FlowExecutor {
|
|||
//如果是隐式流程,则需要设置到隐式流程的exception属性里
|
||||
if (innerChainType.equals(InnerChainTypeEnum.NONE)) {
|
||||
slot.setException(e);
|
||||
}else{
|
||||
} else {
|
||||
slot.setSubException(chainId, e);
|
||||
}
|
||||
} finally {
|
||||
|
@ -389,4 +404,15 @@ public class FlowExecutor {
|
|||
//把liteFlowConfig设到LiteFlowGetter中去
|
||||
LiteflowConfigGetter.setLiteflowConfig(liteflowConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听文件路径
|
||||
*
|
||||
* @param pathList 文件路径
|
||||
*/
|
||||
private void addMonitorFilePaths(List<String> pathList) throws Exception {
|
||||
// 添加规则文件监听
|
||||
List<String> fileAbsolutePath = PathContentParserHolder.loadContextAware().getFileAbsolutePath(pathList);
|
||||
MonitorFile.getInstance().addMonitorFilePaths(fileAbsolutePath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.yomahub.liteflow.monitor;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.watch.SimpleWatcher;
|
||||
import cn.hutool.core.io.watch.WatchMonitor;
|
||||
import cn.hutool.core.io.watch.watchers.DelayWatcher;
|
||||
import cn.hutool.core.lang.Singleton;
|
||||
import com.yomahub.liteflow.core.FlowExecutorHolder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 规则文件监听器
|
||||
*
|
||||
* @author tangkc
|
||||
*/
|
||||
public class MonitorFile {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
private final List<String> PATH_LIST = new ArrayList<>();
|
||||
|
||||
public static MonitorFile getInstance() {
|
||||
return Singleton.get(MonitorFile.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听文件路径
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
*/
|
||||
public void addMonitorFilePath(String filePath) {
|
||||
PATH_LIST.add(filePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加监听文件路径
|
||||
*
|
||||
* @param filePaths 文件路径
|
||||
*/
|
||||
public void addMonitorFilePaths(List<String> filePaths) {
|
||||
PATH_LIST.addAll(filePaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件监听
|
||||
*/
|
||||
public void create() {
|
||||
for (String filePath : CollUtil.distinct(PATH_LIST)) {
|
||||
// 这里只监听两种类型,文件修改和文件覆盖
|
||||
WatchMonitor.createAll(filePath, new DelayWatcher(new SimpleWatcher() {
|
||||
|
||||
@Override
|
||||
public void onModify(WatchEvent<?> event, Path currentPath) {
|
||||
logger.info("file modify,filePath={}", filePath);
|
||||
FlowExecutorHolder.loadInstance().reloadRule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOverflow(WatchEvent<?> event, Path currentPath) {
|
||||
logger.info("file over flow,filePath={}", filePath);
|
||||
FlowExecutorHolder.loadInstance().reloadRule();
|
||||
}
|
||||
// 在监听目录或文件时,如果这个文件有修改操作,JDK会多次触发modify方法,为了解决这个问题
|
||||
// 合并 500 毫秒内相同的变化
|
||||
}, 500)).start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,7 @@ import java.util.List;
|
|||
|
||||
/**
|
||||
* 基于本地的json方式EL表达式解析器
|
||||
*
|
||||
* @author Bryan.Zhang
|
||||
* @since 2.8.0
|
||||
*/
|
||||
|
|
|
@ -94,6 +94,17 @@ public class LiteflowConfig {
|
|||
//替补组件class路径
|
||||
private String substituteCmpClass;
|
||||
|
||||
// 规则文件/脚本文件变更监听
|
||||
private Boolean enableMonitorFile = Boolean.FALSE;
|
||||
|
||||
public Boolean getEnableMonitorFile() {
|
||||
return enableMonitorFile;
|
||||
}
|
||||
|
||||
public void setEnableMonitorFile(Boolean enableMonitorFile) {
|
||||
this.enableMonitorFile = enableMonitorFile;
|
||||
}
|
||||
|
||||
public Boolean getEnable() {
|
||||
if (ObjectUtil.isNull(enable)) {
|
||||
return Boolean.TRUE;
|
||||
|
|
|
@ -2,7 +2,23 @@ package com.yomahub.liteflow.spi;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
public interface PathContentParser extends SpiPriority{
|
||||
public interface PathContentParser extends SpiPriority {
|
||||
|
||||
/**
|
||||
* 解析路径下的文件内容
|
||||
*
|
||||
* @param pathList 文件路径(支持 classpath 路径和 file 绝对路径,spring 环境支持 PathMatchingResourcePatternResolver 规则)
|
||||
* @return 返回文件内容
|
||||
* @throws Exception ex
|
||||
*/
|
||||
List<String> parseContent(List<String> pathList) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取文件路径的绝对路径
|
||||
*
|
||||
* @param pathList 文件路径(支持 classpath 路径和 file 绝对路径,spring 环境支持 PathMatchingResourcePatternResolver 规则)
|
||||
* @return 返回文件绝对路径
|
||||
* @throws Exception ex
|
||||
*/
|
||||
List<String> getFileAbsolutePath(List<String> pathList) throws Exception;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,10 @@ package com.yomahub.liteflow.spi.local;
|
|||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.FileResource;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.ClassLoaderUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.exception.ConfigErrorException;
|
||||
import com.yomahub.liteflow.spi.PathContentParser;
|
||||
|
@ -18,14 +21,14 @@ public class LocalPathContentParser implements PathContentParser {
|
|||
|
||||
@Override
|
||||
public List<String> parseContent(List<String> pathList) throws Exception {
|
||||
if(CollectionUtil.isEmpty(pathList)){
|
||||
if (CollectionUtil.isEmpty(pathList)) {
|
||||
throw new ConfigErrorException("rule source must not be null");
|
||||
}
|
||||
|
||||
List<String> contentList = new ArrayList<>();
|
||||
|
||||
for(String path : pathList){
|
||||
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)){
|
||||
for (String path : pathList) {
|
||||
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
|
||||
path = FILE_URL_PREFIX + path;
|
||||
} else {
|
||||
if (!path.startsWith(CLASSPATH_URL_PREFIX)) {
|
||||
|
@ -33,7 +36,7 @@ public class LocalPathContentParser implements PathContentParser {
|
|||
}
|
||||
}
|
||||
String content = ResourceUtil.readUtf8Str(path);
|
||||
if (StrUtil.isNotBlank(content)){
|
||||
if (StrUtil.isNotBlank(content)) {
|
||||
contentList.add(content);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +44,33 @@ public class LocalPathContentParser implements PathContentParser {
|
|||
return contentList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
|
||||
if (CollectionUtil.isEmpty(pathList)) {
|
||||
throw new ConfigErrorException("rule source must not be null");
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (String path : pathList) {
|
||||
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
|
||||
path = FILE_URL_PREFIX + path;
|
||||
result.add(new FileResource(path).getFile().getAbsolutePath());
|
||||
} else {
|
||||
if (!path.startsWith(CLASSPATH_URL_PREFIX)) {
|
||||
path = CLASSPATH_URL_PREFIX + path;
|
||||
|
||||
// 这里会有自定义解析器
|
||||
if(ClassLoaderUtil.isPresent(path)){
|
||||
result.add(new ClassPathResource(path).getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int priority() {
|
||||
return 2;
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.yomahub.liteflow.spi.solon;
|
|||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.stream.StreamUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.yomahub.liteflow.exception.ConfigErrorException;
|
||||
|
@ -10,17 +11,42 @@ import com.yomahub.liteflow.spi.PathContentParser;
|
|||
import org.noear.solon.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SolonPathContentParser implements PathContentParser {
|
||||
@Override
|
||||
public List<String> parseContent(List<String> pathList) throws Exception {
|
||||
if(CollectionUtil.isEmpty(pathList)){
|
||||
List<URL> allResource = getUrls(pathList);
|
||||
|
||||
//转换成内容List
|
||||
List<String> contentList = new ArrayList<>();
|
||||
for (URL resource : allResource) {
|
||||
String content = IoUtil.read(resource.openStream(), CharsetUtil.CHARSET_UTF_8);
|
||||
if (StrUtil.isNotBlank(content)) {
|
||||
contentList.add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return contentList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
|
||||
List<URL> allResource = getUrls(pathList);
|
||||
return StreamUtil.of(allResource)
|
||||
.map(URL::getPath)
|
||||
.filter(FileUtil::isFile)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private static List<URL> getUrls(List<String> pathList) throws MalformedURLException {
|
||||
if (CollectionUtil.isEmpty(pathList)) {
|
||||
throw new ConfigErrorException("rule source must not be null");
|
||||
}
|
||||
|
||||
|
@ -34,27 +60,19 @@ public class SolonPathContentParser implements PathContentParser {
|
|||
path = path.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
|
||||
}
|
||||
|
||||
allResource.add(Utils.getResource(path));
|
||||
if (Utils.getResource(path) != null) {
|
||||
allResource.add(Utils.getResource(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//如果有多个资源,检查资源都是同一个类型,如果出现不同类型的配置,则抛出错误提示
|
||||
Set<String> fileTypeSet = new HashSet<>();
|
||||
allResource.forEach(resource -> fileTypeSet.add(FileUtil.extName(resource.getPath())));
|
||||
if (fileTypeSet.size() != 1) {
|
||||
if (fileTypeSet.size() > 1) {
|
||||
throw new ConfigErrorException("config error,please use the same type of configuration");
|
||||
}
|
||||
|
||||
//转换成内容List
|
||||
List<String> contentList = new ArrayList<>();
|
||||
for (URL resource : allResource) {
|
||||
String content = IoUtil.read(resource.openStream(), CharsetUtil.CHARSET_UTF_8);
|
||||
if (StrUtil.isNotBlank(content)){
|
||||
contentList.add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return contentList;
|
||||
return allResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -70,6 +70,17 @@ public class LiteflowProperty {
|
|||
//替补组件的class路径
|
||||
private String substituteCmpClass;
|
||||
|
||||
// 规则文件/脚本文件变更监听
|
||||
private Boolean enableMonitorFile;
|
||||
|
||||
public Boolean getEnableMonitorFile() {
|
||||
return enableMonitorFile;
|
||||
}
|
||||
|
||||
public void setEnableMonitorFile(Boolean enableMonitorFile) {
|
||||
this.enableMonitorFile = enableMonitorFile;
|
||||
}
|
||||
|
||||
public boolean isEnable() {
|
||||
return enable;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public class LiteflowPropertyAutoConfiguration {
|
|||
liteflowConfig.setMainExecutorClass(property.getMainExecutorClass());
|
||||
liteflowConfig.setPrintExecutionLog(property.isPrintExecutionLog());
|
||||
liteflowConfig.setSubstituteCmpClass(property.getSubstituteCmpClass());
|
||||
liteflowConfig.setEnableMonitorFile(property.getEnableMonitorFile());
|
||||
return liteflowConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,6 +158,13 @@
|
|||
"description": "Set period time to print monitor log.",
|
||||
"sourceType": "com.yomahub.liteflow.springboot.LiteflowMonitorProperty",
|
||||
"defaultValue": 300000
|
||||
},
|
||||
{
|
||||
"name": "liteflow.enable-monitor-file",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "Set file change monitoring.",
|
||||
"sourceType": "com.yomahub.liteflow.springboot.LiteflowMonitorProperty",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
|
@ -18,3 +18,4 @@ liteflow.monitor.enable-log=false
|
|||
liteflow.monitor.queue-limit=200
|
||||
liteflow.monitor.delay=300000
|
||||
liteflow.monitor.period=300000
|
||||
liteflow.enable-monitor-file=false
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollectionUtil;
|
|||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.stream.StreamUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
@ -13,15 +14,49 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SpringPathContentParser implements PathContentParser {
|
||||
@Override
|
||||
public List<String> parseContent(List<String> pathList) throws Exception {
|
||||
if(CollectionUtil.isEmpty(pathList)){
|
||||
List<Resource> allResource = getResources(pathList);
|
||||
|
||||
//转换成内容List
|
||||
List<String> contentList = new ArrayList<>();
|
||||
for (Resource resource : allResource) {
|
||||
String content = IoUtil.read(resource.getInputStream(), CharsetUtil.CHARSET_UTF_8);
|
||||
if (StrUtil.isNotBlank(content)) {
|
||||
contentList.add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return contentList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getFileAbsolutePath(List<String> pathList) throws Exception {
|
||||
List<Resource> allResource = getResources(pathList);
|
||||
|
||||
return StreamUtil.of(allResource)
|
||||
// 过滤非 file 类型 Resource
|
||||
.filter(Resource::isFile)
|
||||
.map(r -> {
|
||||
try {
|
||||
return r.getFile().getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<Resource> getResources(List<String> pathList) throws IOException {
|
||||
if (CollectionUtil.isEmpty(pathList)) {
|
||||
throw new ConfigErrorException("rule source must not be null");
|
||||
}
|
||||
|
||||
|
@ -30,12 +65,12 @@ public class SpringPathContentParser implements PathContentParser {
|
|||
String locationPattern;
|
||||
|
||||
//如果path是绝对路径且这个文件存在时,我们认为这是一个本地文件路径,而并非classpath路径
|
||||
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)){
|
||||
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
|
||||
locationPattern = ResourceUtils.FILE_URL_PREFIX + path;
|
||||
} else {
|
||||
if (!path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX) && !path.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX)) {
|
||||
locationPattern = ResourceUtils.CLASSPATH_URL_PREFIX + path;
|
||||
}else{
|
||||
} else {
|
||||
locationPattern = path;
|
||||
}
|
||||
}
|
||||
|
@ -53,19 +88,10 @@ public class SpringPathContentParser implements PathContentParser {
|
|||
if (fileTypeSet.size() > 1) {
|
||||
throw new ConfigErrorException("config error,please use the same type of configuration");
|
||||
}
|
||||
|
||||
//转换成内容List
|
||||
List<String> contentList = new ArrayList<>();
|
||||
for (Resource resource : allResource) {
|
||||
String content = IoUtil.read(resource.getInputStream(), CharsetUtil.CHARSET_UTF_8);
|
||||
if (StrUtil.isNotBlank(content)){
|
||||
contentList.add(content);
|
||||
}
|
||||
}
|
||||
|
||||
return contentList;
|
||||
return allResource;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int priority() {
|
||||
return 1;
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package com.yomahub.liteflow.test.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
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.io.File;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
|
||||
@SpringBootTest(classes = MonitorFileELDeclMultiSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.monitorFile.cmp"})
|
||||
public class MonitorFileELDeclMultiSpringbootTest {
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testMonitor() throws Exception{
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
|
||||
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.yomahub.liteflow.test.monitorFile.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 java.util.Random;
|
||||
|
||||
@LiteflowComponent
|
||||
public class CmpConfig {
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "a")
|
||||
public void processA(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("ACmp executed!");
|
||||
}
|
||||
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "b")
|
||||
public void processB(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
@LiteflowMethod(value = LiteFlowMethodEnum.PROCESS,nodeId = "c")
|
||||
public void process(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
liteflow.rule-source=monitorFile/flow.el.xml
|
||||
liteflow.enable-monitor-file=true
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c);
|
||||
</chain>
|
||||
|
||||
</flow>
|
|
@ -0,0 +1,42 @@
|
|||
package com.yomahub.liteflow.test.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
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;
|
||||
import java.io.File;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
|
||||
@SpringBootTest(classes = MonitorFileELDeclSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.monitorFile.cmp"})
|
||||
public class MonitorFileELDeclSpringbootTest extends BaseTest {
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testMonitor() throws Exception{
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
|
||||
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Component("a")
|
||||
public class ACmp{
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
|
||||
public void process(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("ACmp executed!");
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Component("b")
|
||||
public class BCmp{
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
|
||||
public void process(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.annotation.LiteflowMethod;
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.enums.LiteFlowMethodEnum;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Component("c")
|
||||
public class CCmp{
|
||||
|
||||
@LiteflowMethod(LiteFlowMethodEnum.PROCESS)
|
||||
public void process(NodeComponent bindCmp) {
|
||||
try {
|
||||
Thread.sleep(new Random().nextInt(2000));
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("CCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
liteflow.rule-source=monitorFile/flow.el.xml
|
||||
liteflow.enable-monitor-file=true
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c);
|
||||
</chain>
|
||||
|
||||
</flow>
|
|
@ -0,0 +1,42 @@
|
|||
package com.yomahub.liteflow.test.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.core.FlowExecutorHolder;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.property.LiteflowConfig;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class LiteflowMonitorFileTest extends BaseTest {
|
||||
|
||||
private static FlowExecutor flowExecutor;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
LiteflowConfig config = new LiteflowConfig();
|
||||
config.setRuleSource("monitorFile/flow.el.xml");
|
||||
config.setEnableMonitorFile(true);
|
||||
flowExecutor = FlowExecutorHolder.loadInstance(config);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleType() throws InterruptedException {
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
|
||||
FileUtil.writeString(newContent, new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
|
||||
public class ACmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("ACmp executed!");
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
|
||||
public class BCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
|
||||
public class CCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("CCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<nodes>
|
||||
<node id="a" class="com.yomahub.liteflow.test.multipleType.cmp.ACmp"/>
|
||||
<node id="b" class="com.yomahub.liteflow.test.multipleType.cmp.BCmp"/>
|
||||
<node id="c" class="com.yomahub.liteflow.test.multipleType.cmp.CCmp"/>
|
||||
</nodes>
|
||||
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c);
|
||||
</chain>
|
||||
|
||||
</flow>
|
|
@ -0,0 +1,47 @@
|
|||
package com.yomahub.liteflow.test.script.groovy.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import com.yomahub.liteflow.test.BaseTest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.File;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
|
||||
@SpringBootTest(classes = MonitorFileGroovyELTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.script.groovy.monitorFile.cmp"})
|
||||
public class MonitorFileGroovyELTest extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testMonitor() throws Exception{
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/s1.groovy").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("a=3", "a=2");
|
||||
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
DefaultContext context = response.getFirstContextBean();
|
||||
Assert.assertTrue(response.isSuccess());
|
||||
Assert.assertEquals(Integer.valueOf(4), context.getData("s1"));
|
||||
|
||||
}
|
||||
}
|
|
@ -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.script.groovy.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("a")
|
||||
public class ACmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("ACmp executed!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.script.groovy.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("b")
|
||||
public class BCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("BCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* <p>Title: liteflow</p>
|
||||
* <p>Description: 轻量级的组件式流程框架</p>
|
||||
* @author Bryan.Zhang
|
||||
* @email weenyc31@163.com
|
||||
* @Date 2020/4/1
|
||||
*/
|
||||
package com.yomahub.liteflow.test.script.groovy.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("c")
|
||||
public class CCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
System.out.println("CCmp executed!");
|
||||
}
|
||||
|
||||
}
|
|
@ -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.script.groovy.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import com.yomahub.liteflow.slot.DefaultContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component("d")
|
||||
public class DCmp extends NodeComponent {
|
||||
|
||||
@Override
|
||||
public void process() {
|
||||
DefaultContext context = this.getFirstContextBean();
|
||||
String key = "test";
|
||||
if (context.hasData(key)){
|
||||
int count = context.getData(key);
|
||||
context.setData(key, ++count);
|
||||
}else{
|
||||
context.setData(key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
liteflow.rule-source=monitorFile/flow.el.xml
|
||||
liteflow.enable-monitor-file=true
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
|
||||
<nodes>
|
||||
<node id="s1" name="普通脚本" type="script" file="monitorFile/s1.groovy"/>
|
||||
</nodes>
|
||||
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c, s1);
|
||||
</chain>
|
||||
|
||||
</flow>
|
|
@ -0,0 +1,3 @@
|
|||
Integer a=3
|
||||
Integer b=2
|
||||
defaultContext.setData("s1",a*b)
|
|
@ -0,0 +1,36 @@
|
|||
package com.yomahub.liteflow.test.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.test.SolonJUnit4ClassRunner;
|
||||
import org.noear.solon.test.annotation.TestPropertySource;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@RunWith(SolonJUnit4ClassRunner.class)
|
||||
@TestPropertySource("classpath:/monitorFile/application.properties")
|
||||
public class MonitorFileELSpringbootTest {
|
||||
|
||||
@Inject
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testMonitor() throws Exception{
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
|
||||
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.noear.solon.annotation.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!");
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.noear.solon.annotation.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!");
|
||||
}
|
||||
|
||||
}
|
|
@ -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.monitorFile.cmp;
|
||||
|
||||
import com.yomahub.liteflow.core.NodeComponent;
|
||||
import org.noear.solon.annotation.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!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
liteflow.rule-source=monitorFile/flow.el.xml
|
||||
liteflow.enable-monitor-file=true
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c);
|
||||
</chain>
|
||||
|
||||
</flow>
|
|
@ -0,0 +1,43 @@
|
|||
package com.yomahub.liteflow.test.monitorFile;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import com.yomahub.liteflow.core.FlowExecutor;
|
||||
import com.yomahub.liteflow.flow.LiteflowResponse;
|
||||
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.io.File;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@TestPropertySource(value = "classpath:/monitorFile/application.properties")
|
||||
@SpringBootTest(classes = MonitorFileELSpringbootTest.class)
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan({"com.yomahub.liteflow.test.monitorFile.cmp"})
|
||||
public class MonitorFileELSpringbootTest {
|
||||
|
||||
@Resource
|
||||
private FlowExecutor flowExecutor;
|
||||
|
||||
@Test
|
||||
public void testMonitor() throws Exception{
|
||||
String absolutePath = new ClassPathResource("classpath:/monitorFile/flow.el.xml").getAbsolutePath();
|
||||
String content = FileUtil.readUtf8String(absolutePath);
|
||||
String newContent = content.replace("THEN(a, b, c);", "THEN(a, c, b);");
|
||||
FileUtil.writeString(newContent,new File(absolutePath), CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Thread.sleep(1000);
|
||||
|
||||
LiteflowResponse response = flowExecutor.execute2Resp("chain1", "arg");
|
||||
Assert.assertEquals("a==>c==>b", response.getExecuteStepStr());
|
||||
}
|
||||
|
||||
}
|
|
@ -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.monitorFile.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!");
|
||||
}
|
||||
}
|
|
@ -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.monitorFile.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!");
|
||||
}
|
||||
|
||||
}
|
|
@ -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.monitorFile.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!");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
liteflow.rule-source=monitorFile/flow.el.xml
|
||||
liteflow.enable-monitor-file=true
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<flow>
|
||||
<chain name="chain1">
|
||||
THEN(a, b, c);
|
||||
</chain>
|
||||
|
||||
</flow>
|
Loading…
Reference in New Issue