Browse Source

!165 绝对路径的目录位置及其所有子目录下规则配置文件的侦听
Merge pull request !165 from 与或非/issues/I6BDLN

铂赛东 2 years ago
parent
commit
394136b988
48 changed files with 1052 additions and 58 deletions
  1. 5 0
      liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java
  2. 50 24
      liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java
  3. 74 0
      liteflow-core/src/main/java/com/yomahub/liteflow/monitor/MonitorFile.java
  4. 1 0
      liteflow-core/src/main/java/com/yomahub/liteflow/parser/el/LocalJsonFlowELParser.java
  5. 11 0
      liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java
  6. 17 1
      liteflow-core/src/main/java/com/yomahub/liteflow/spi/PathContentParser.java
  7. 34 4
      liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java
  8. 33 15
      liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonPathContentParser.java
  9. 11 0
      liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java
  10. 1 0
      liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java
  11. 7 0
      liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  12. 1 0
      liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties
  13. 40 14
      liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java
  14. 42 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELDeclMultiSpringbootTest.java
  15. 44 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CmpConfig.java
  16. 2 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/monitorFile/application.properties
  17. 7 0
      liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/monitorFile/flow.el.xml
  18. 42 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELDeclSpringbootTest.java
  19. 30 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java
  20. 30 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java
  21. 30 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java
  22. 2 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/monitorFile/application.properties
  23. 7 0
      liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/monitorFile/flow.el.xml
  24. 42 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/LiteflowMonitorFileTest.java
  25. 18 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java
  26. 19 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java
  27. 19 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java
  28. 13 0
      liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/monitorFile/flow.el.xml
  29. 47 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/MonitorFileGroovyELTest.java
  30. 20 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/ACmp.java
  31. 21 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/BCmp.java
  32. 21 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/CCmp.java
  33. 29 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/DCmp.java
  34. 2 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/application.properties
  35. 12 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/flow.el.xml
  36. 3 0
      liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/s1.groovy
  37. 36 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java
  38. 28 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java
  39. 28 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java
  40. 28 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java
  41. 2 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/monitorFile/application.properties
  42. 7 0
      liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/monitorFile/flow.el.xml
  43. 43 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java
  44. 28 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java
  45. 28 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java
  46. 28 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java
  47. 2 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties
  48. 7 0
      liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/flow.el.xml

+ 5 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/builder/LiteFlowNodeBuilder.java

@@ -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);

+ 50 - 24
liteflow-core/src/main/java/com/yomahub/liteflow/core/FlowExecutor.java

@@ -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);
+    }
 }

+ 74 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/monitor/MonitorFile.java

@@ -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();
+        }
+    }
+
+}

+ 1 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/parser/el/LocalJsonFlowELParser.java

@@ -6,6 +6,7 @@ import java.util.List;
 
 /**
  * 基于本地的json方式EL表达式解析器
+ *
  * @author Bryan.Zhang
  * @since 2.8.0
  */

+ 11 - 0
liteflow-core/src/main/java/com/yomahub/liteflow/property/LiteflowConfig.java

@@ -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;

+ 17 - 1
liteflow-core/src/main/java/com/yomahub/liteflow/spi/PathContentParser.java

@@ -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;
 }

+ 34 - 4
liteflow-core/src/main/java/com/yomahub/liteflow/spi/local/LocalPathContentParser.java

@@ -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;

+ 33 - 15
liteflow-solon-plugin/src/main/java/com/yomahub/liteflow/spi/solon/SolonPathContentParser.java

@@ -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

+ 11 - 0
liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/LiteflowProperty.java

@@ -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;
     }

+ 1 - 0
liteflow-spring-boot-starter/src/main/java/com/yomahub/liteflow/springboot/config/LiteflowPropertyAutoConfiguration.java

@@ -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;
     }
 }

+ 7 - 0
liteflow-spring-boot-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@@ -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
     }
   ]
 }

+ 1 - 0
liteflow-spring-boot-starter/src/main/resources/META-INF/liteflow-default.properties

@@ -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

+ 40 - 14
liteflow-spring/src/main/java/com/yomahub/liteflow/spi/spring/SpringPathContentParser.java

@@ -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;

+ 42 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELDeclMultiSpringbootTest.java

@@ -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());
+
+    }
+}

+ 44 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CmpConfig.java

@@ -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!");
+    }
+}
+
+

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/monitorFile/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=monitorFile/flow.el.xml
+liteflow.enable-monitor-file=true

+ 7 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-multi-springboot/src/test/resources/monitorFile/flow.el.xml

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

+ 42 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELDeclSpringbootTest.java

@@ -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());
+    }
+}

+ 30 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java

@@ -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!");
+	}
+}

+ 30 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java

@@ -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!");
+	}
+
+}

+ 30 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java

@@ -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!");
+	}
+
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/monitorFile/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=monitorFile/flow.el.xml
+liteflow.enable-monitor-file=true

+ 7 - 0
liteflow-testcase-el/liteflow-testcase-el-declare-springboot/src/test/resources/monitorFile/flow.el.xml

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

+ 42 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/LiteflowMonitorFileTest.java

@@ -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());
+    }
+
+}

+ 18 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java

@@ -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!");
+	}
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java

@@ -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!");
+	}
+
+}

+ 19 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java

@@ -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!");
+	}
+
+}

+ 13 - 0
liteflow-testcase-el/liteflow-testcase-el-nospring/src/test/resources/monitorFile/flow.el.xml

@@ -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>

+ 47 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/MonitorFileGroovyELTest.java

@@ -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"));
+
+    }
+}

+ 20 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/ACmp.java

@@ -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!");
+	}
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/BCmp.java

@@ -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!");
+	}
+
+}

+ 21 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/CCmp.java

@@ -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!");
+	}
+
+}

+ 29 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/java/com/yomahub/liteflow/test/script/groovy/monitorFile/cmp/DCmp.java

@@ -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);
+		}
+	}
+
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=monitorFile/flow.el.xml
+liteflow.enable-monitor-file=true

+ 12 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/flow.el.xml

@@ -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>

+ 3 - 0
liteflow-testcase-el/liteflow-testcase-el-script-groovy-springboot/src/test/resources/monitorFile/s1.groovy

@@ -0,0 +1,3 @@
+Integer a=3
+Integer b=2
+defaultContext.setData("s1",a*b)

+ 36 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java

@@ -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());
+    }
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java

@@ -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!");
+	}
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java

@@ -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!");
+	}
+
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java

@@ -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!");
+	}
+
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/monitorFile/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=monitorFile/flow.el.xml
+liteflow.enable-monitor-file=true

+ 7 - 0
liteflow-testcase-el/liteflow-testcase-el-solon/src/test/resources/monitorFile/flow.el.xml

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

+ 43 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/MonitorFileELSpringbootTest.java

@@ -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());
+    }
+
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/ACmp.java

@@ -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!");
+	}
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/BCmp.java

@@ -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!");
+	}
+
+}

+ 28 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/java/com/yomahub/liteflow/test/monitorFile/cmp/CCmp.java

@@ -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!");
+	}
+
+}

+ 2 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/application.properties

@@ -0,0 +1,2 @@
+liteflow.rule-source=monitorFile/flow.el.xml
+liteflow.enable-monitor-file=true

+ 7 - 0
liteflow-testcase-el/liteflow-testcase-el-springboot/src/test/resources/monitorFile/flow.el.xml

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