初步完成 liteflow-solon-plugin 适配

This commit is contained in:
noear
2022-10-10 16:10:57 +08:00
parent 4fcbcf4c90
commit fea977730e
27 changed files with 874 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
package com.yomahub.liteflow.annotation;
import java.lang.annotation.*;
/**
* LiteFlow的组件标识注解
*
* @author Bryan.Zhang
* @since 2.6.0
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface LiteflowComponent {
@AliasFor("id")
String value() default "";
@AliasFor("value")
String id() default "";
String name() default "";
}

View File

@@ -0,0 +1,61 @@
package com.yomahub.liteflow.solon;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
/**
* 监控器的基础参数类
*
* @author Bryan.Zhang
* @author noear
* @since 2.9
*/
@Inject("${liteflow.monitor}")
@Configuration
public class LiteflowMonitorProperty {
//是否打印监控日志
private boolean enableLog;
//监控队列存储的最大数量
private int queueLimit;
//延迟多少毫秒打印
private long delay;
//每隔多少毫秒打印
private long period;
public boolean isEnableLog() {
return enableLog;
}
public void setEnableLog(boolean enableLog) {
this.enableLog = enableLog;
}
public int getQueueLimit() {
return queueLimit;
}
public void setQueueLimit(int queueLimit) {
this.queueLimit = queueLimit;
}
public long getDelay() {
return delay;
}
public void setDelay(long delay) {
this.delay = delay;
}
public long getPeriod() {
return period;
}
public void setPeriod(long period) {
this.period = period;
}
}

View File

@@ -0,0 +1,217 @@
package com.yomahub.liteflow.solon;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
/**
* 执行流程主要的参数类
*
* @author Bryan.Zhang
* @author noear
* @since 2.9
*/
@Inject("${liteflow}")
@Configuration
public class LiteflowProperty {
//是否装配liteflow
private boolean enable;
//流程定义资源地址
private String ruleSource;
//流程资源扩展数据
private String ruleSourceExtData;
//slot的数量
private int slotSize;
//FlowExecutor的execute2Future的线程数
private int mainExecutorWorks;
//FlowExecutor的execute2Future的自定义线程池
private String mainExecutorClass;
//并行线程执行器class路径
private String threadExecutorClass;
//异步线程最大等待描述
private int whenMaxWaitSeconds;
//异步线程池最大线程数
private int whenMaxWorkers;
//异步线程池最大队列数量
private int whenQueueLimit;
//是否在启动时解析规则文件
//这个参数主要给编码式注册元数据的场景用的结合FlowBus.addNode一起用
private boolean parseOnStart;
//这个属性为true则支持多种不同的类型的配置
//但是要注意,不能将主流程和子流程分配在不同类型配置文件中
private boolean supportMultipleType;
//重试次数
private int retryCount;
//是否打印liteflow banner
private boolean printBanner;
// 节点执行器class全名
private String nodeExecutorClass;
// requestId 生成器
private String requestIdGeneratorClass;
//是否打印执行过程中的日志
private boolean printExecutionLog;
//替补组件的class路径
private String substituteCmpClass;
public boolean isEnable() {
return enable;
}
public void setEnable(boolean enable) {
this.enable = enable;
}
public String getRuleSource() {
return ruleSource;
}
public void setRuleSource(String ruleSource) {
this.ruleSource = ruleSource;
}
public int getSlotSize() {
return slotSize;
}
public void setSlotSize(int slotSize) {
this.slotSize = slotSize;
}
public int getWhenMaxWaitSeconds() {
return whenMaxWaitSeconds;
}
public void setWhenMaxWaitSeconds(int whenMaxWaitSeconds) {
this.whenMaxWaitSeconds = whenMaxWaitSeconds;
}
public int getWhenMaxWorkers() {
return whenMaxWorkers;
}
public void setWhenMaxWorkers(int whenMaxWorkers) {
this.whenMaxWorkers = whenMaxWorkers;
}
public int getWhenQueueLimit() {
return whenQueueLimit;
}
public void setWhenQueueLimit(int whenQueueLimit) {
this.whenQueueLimit = whenQueueLimit;
}
public boolean isParseOnStart() {
return parseOnStart;
}
public void setParseOnStart(boolean parseOnStart) {
this.parseOnStart = parseOnStart;
}
public boolean isSupportMultipleType() {
return supportMultipleType;
}
public void setSupportMultipleType(boolean supportMultipleType) {
this.supportMultipleType = supportMultipleType;
}
public int getRetryCount() {
return retryCount;
}
public void setRetryCount(int retryCount) {
this.retryCount = retryCount;
}
public boolean isPrintBanner() {
return printBanner;
}
public void setPrintBanner(boolean printBanner) {
this.printBanner = printBanner;
}
public String getThreadExecutorClass() {
return threadExecutorClass;
}
public void setThreadExecutorClass(String threadExecutorClass) {
this.threadExecutorClass = threadExecutorClass;
}
public String getNodeExecutorClass() {
return nodeExecutorClass;
}
public void setNodeExecutorClass(String nodeExecutorClass) {
this.nodeExecutorClass = nodeExecutorClass;
}
public int getMainExecutorWorks() {
return mainExecutorWorks;
}
public void setMainExecutorWorks(int mainExecutorWorks) {
this.mainExecutorWorks = mainExecutorWorks;
}
public String getMainExecutorClass() {
return mainExecutorClass;
}
public void setMainExecutorClass(String mainExecutorClass) {
this.mainExecutorClass = mainExecutorClass;
}
public boolean isPrintExecutionLog() {
return printExecutionLog;
}
public void setPrintExecutionLog(boolean printExecutionLog) {
this.printExecutionLog = printExecutionLog;
}
public String getRequestIdGeneratorClass() {
return requestIdGeneratorClass;
}
public void setRequestIdGeneratorClass(String requestIdGeneratorClass) {
this.requestIdGeneratorClass = requestIdGeneratorClass;
}
public String getSubstituteCmpClass() {
return substituteCmpClass;
}
public void setSubstituteCmpClass(String substituteCmpClass) {
this.substituteCmpClass = substituteCmpClass;
}
public String getRuleSourceExtData() {
return ruleSourceExtData;
}
public void setRuleSourceExtData(String ruleSourceExtData) {
this.ruleSourceExtData = ruleSourceExtData;
}
}

View File

@@ -0,0 +1,50 @@
package com.yomahub.liteflow.solon.config;
import com.yomahub.liteflow.core.FlowExecutor;
import com.yomahub.liteflow.monitor.MonitorBus;
import com.yomahub.liteflow.property.LiteflowConfig;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
/**
* 主要的业务装配器
* 在这个装配器里装配了执行器,执行器初始化类,监控器
* 这个装配前置条件是需要LiteflowConfigLiteflowPropertyAutoConfiguration以及SpringAware
*
* @author Bryan.Zhang
* @author noear
* @since 2.9
*/
@Configuration
public class LiteflowMainAutoConfiguration {
@Inject("${liteflow.parse-on-start}")
boolean parseOnStart;
@Inject("${liteflow.monitor.enable-log}")
boolean enableLog;
//实例化FlowExecutor
@Bean
public FlowExecutor flowExecutor(LiteflowConfig liteflowConfig) {
FlowExecutor flowExecutor = new FlowExecutor();
flowExecutor.setLiteflowConfig(liteflowConfig);
if (parseOnStart) {
flowExecutor.init();
}
return flowExecutor;
}
@Bean
public MonitorBus monitorBus(LiteflowConfig liteflowConfig) {
if (enableLog) {
return new MonitorBus(liteflowConfig);
} else {
return null; //null 即是没创建
}
}
}

View File

@@ -0,0 +1,48 @@
package com.yomahub.liteflow.solon.config;
import com.yomahub.liteflow.property.LiteflowConfig;
import com.yomahub.liteflow.solon.LiteflowMonitorProperty;
import com.yomahub.liteflow.solon.LiteflowProperty;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
/**
* LiteflowConfig的装配类
*
* 这个装配类主要是把监控器的配置参数类和流程配置参数类作一个合并,转换成统一的配置参数类。
* 同时这里设置了默认的参数路径,如果在 solon 的 app.properties/yml 里没取到的话,就取默认值
* @author Bryan.Zhang
* @author noear
* @since 2.9
*/
@Configuration
public class LiteflowPropertyAutoConfiguration {
@Bean
public LiteflowConfig liteflowConfig(LiteflowProperty property, LiteflowMonitorProperty liteflowMonitorProperty){
LiteflowConfig liteflowConfig = new LiteflowConfig();
liteflowConfig.setRuleSource(property.getRuleSource());
liteflowConfig.setRuleSourceExtData(property.getRuleSourceExtData());
liteflowConfig.setSlotSize(property.getSlotSize());
liteflowConfig.setThreadExecutorClass(property.getThreadExecutorClass());
liteflowConfig.setWhenMaxWaitSeconds(property.getWhenMaxWaitSeconds());
liteflowConfig.setEnableLog(liteflowMonitorProperty.isEnableLog());
liteflowConfig.setQueueLimit(liteflowMonitorProperty.getQueueLimit());
liteflowConfig.setDelay(liteflowMonitorProperty.getDelay());
liteflowConfig.setPeriod(liteflowMonitorProperty.getPeriod());
liteflowConfig.setWhenMaxWorkers(property.getWhenMaxWorkers());
liteflowConfig.setWhenQueueLimit(property.getWhenQueueLimit());
liteflowConfig.setParseOnStart(property.isParseOnStart());
liteflowConfig.setEnable(property.isEnable());
liteflowConfig.setSupportMultipleType(property.isSupportMultipleType());
liteflowConfig.setRetryCount(property.getRetryCount());
liteflowConfig.setPrintBanner(property.isPrintBanner());
liteflowConfig.setNodeExecutorClass(property.getNodeExecutorClass());
liteflowConfig.setRequestIdGeneratorClass(property.getRequestIdGeneratorClass());
liteflowConfig.setMainExecutorWorks(property.getMainExecutorWorks());
liteflowConfig.setMainExecutorClass(property.getMainExecutorClass());
liteflowConfig.setPrintExecutionLog(property.isPrintExecutionLog());
liteflowConfig.setSubstituteCmpClass(property.getSubstituteCmpClass());
return liteflowConfig;
}
}

View File

@@ -0,0 +1,50 @@
package com.yomahub.liteflow.solon.integration;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.flow.FlowBus;
import com.yomahub.liteflow.solon.LiteflowProperty;
import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.core.AopContext;
import org.noear.solon.core.Plugin;
import java.util.Properties;
/**
* @author noear
* @since 2.9
*/
public class XPluginImpl implements Plugin {
@Override
public void start(AopContext context) {
//加载默认配置
Properties defProps = Utils.loadProperties("META-INF/liteflow-default.properties");
if (defProps != null && defProps.size() > 0) {
defProps.forEach((k, v) -> {
Solon.cfg().putIfAbsent(k, v);
});
}
//是否启用
boolean enable = Solon.cfg().getBool("liteflow.enable", false);
if (!enable) {
return;
}
//订阅 NodeComponent 组件
context.subWrapsOfType(NodeComponent.class, bw -> {
NodeComponent node1 = bw.raw();
if (Utils.isNotEmpty(bw.name())) {
node1.setName(bw.name());
node1.setNodeId(bw.name());
}
FlowBus.addSpringScanNode(node1.getNodeId(), node1);
});
//扫描内部相关组件
context.beanScan(LiteflowProperty.class);
}
}

View File

@@ -0,0 +1,27 @@
package com.yomahub.liteflow.spi.solon;
/**
* @author noear 2022/10/10 created
*/
public final class ResourceUtils {
public static final String CLASSPATH_URL_PREFIX = "classpath:";
public static final String FILE_URL_PREFIX = "file:";
public static final String JAR_URL_PREFIX = "jar:";
public static final String WAR_URL_PREFIX = "war:";
public static final String URL_PROTOCOL_FILE = "file";
public static final String URL_PROTOCOL_JAR = "jar";
public static final String URL_PROTOCOL_WAR = "war";
public static final String URL_PROTOCOL_ZIP = "zip";
public static final String URL_PROTOCOL_WSJAR = "wsjar";
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
public static final String URL_PROTOCOL_VFSFILE = "vfsfile";
public static final String URL_PROTOCOL_VFS = "vfs";
public static final String JAR_FILE_EXTENSION = ".jar";
public static final String JAR_URL_SEPARATOR = "!/";
public static final String WAR_URL_SEPARATOR = "*/";
public static final String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
}

View File

@@ -0,0 +1,74 @@
package com.yomahub.liteflow.spi.solon;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.spi.ContextAware;
import org.noear.solon.Solon;
import org.noear.solon.core.BeanWrap;
/**
* 基于代码形式的 Solon 上下文工具类
* @author Bryan.Zhang
*/
public class SolonAware implements ContextAware {
@Override
public <T> T getBean(String name) {
try{
return Solon.context().getBean(name);
}catch (Exception e){
return null;
}
}
@Override
public <T> T getBean(Class<T> clazz) {
try{
return Solon.context().getBean(clazz);
}catch (Exception e){
return null;
}
}
private <T> T getBean(String beanName, Class<T> clazz) {
try{
return Solon.context().getBean(beanName);
}catch (Exception e){
return null;
}
}
@Override
public <T> T registerBean(String beanName, Class<T> c) {
BeanWrap beanWrap = new BeanWrap(Solon.context(), c, null, beanName);
Solon.context().putWrap(beanName, beanWrap);
return beanWrap.get();
}
@Override
public <T> T registerBean(Class<T> c) {
return registerBean(c.getName(), c);
}
@Override
public <T> T registerBean(String beanName, Object bean) {
BeanWrap beanWrap = new BeanWrap(Solon.context(), bean.getClass(), bean, beanName);
Solon.context().putWrap(beanName, beanWrap);
return beanWrap.get();
}
@Override
public <T> T registerOrGet(String beanName, Class<T> clazz) {
T t = getBean(beanName, clazz);
if (ObjectUtil.isNull(t)) {
t = registerBean(beanName, clazz);
}
return t;
}
@Override
public int priority() {
return 1;
}
}

View File

@@ -0,0 +1,42 @@
package com.yomahub.liteflow.spi.solon;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.aop.ICmpAroundAspect;
import com.yomahub.liteflow.slot.Slot;
import com.yomahub.liteflow.spi.CmpAroundAspect;
import org.noear.solon.Solon;
/**
* Solon 环境全局组件切面实现
* @author Bryan.Zhang
* @since 2.6.11
*/
public class SolonCmpAroundAspect implements CmpAroundAspect {
public static ICmpAroundAspect cmpAroundAspect;
static {
Solon.context().getBeanAsyn(ICmpAroundAspect.class, bean -> {
cmpAroundAspect = bean;
});
}
@Override
public void beforeProcess(String nodeId, Slot slot) {
if (ObjectUtil.isNotNull(cmpAroundAspect)) {
cmpAroundAspect.beforeProcess(nodeId, slot);
}
}
@Override
public void afterProcess(String nodeId, Slot slot) {
if (ObjectUtil.isNotNull(cmpAroundAspect)) {
cmpAroundAspect.afterProcess(nodeId, slot);
}
}
@Override
public int priority() {
return 1;
}
}

View File

@@ -0,0 +1,21 @@
package com.yomahub.liteflow.spi.solon;
import com.yomahub.liteflow.spi.ContextCmpInit;
/**
* Solon 环境容器上下文组件初始化实现
* @author Bryan.Zhang
* @since 2.6.11
*/
public class SolonContextCmpInit implements ContextCmpInit {
@Override
public void initCmp() {
//todo: 转到 FlowBus.addSpringScanNode 不需要了
}
@Override
public int priority() {
return 1;
}
}

View File

@@ -0,0 +1,30 @@
package com.yomahub.liteflow.spi.solon;
import cn.hutool.core.util.ObjectUtil;
import com.yomahub.liteflow.annotation.LiteflowComponent;
import com.yomahub.liteflow.core.NodeComponent;
import com.yomahub.liteflow.spi.LiteflowComponentSupport;
/**
* Solon 环境 LiteflowComponent 注解的处理器
* @author Bryan.Zhang
* @since 2.6.11
*/
public class SolonLiteflowComponentSupport implements LiteflowComponentSupport {
@Override
public String getCmpName(NodeComponent nodeComponent) {
//判断NodeComponent是否是标识了@LiteflowComponent的标注
//如果标注了那么要从中取到name字段
LiteflowComponent liteflowComponent = nodeComponent.getClass().getAnnotation(LiteflowComponent.class);
if (ObjectUtil.isNotNull(liteflowComponent)) {
return liteflowComponent.name();
}else{
return null;
}
}
@Override
public int priority() {
return 1;
}
}

View File

@@ -0,0 +1,64 @@
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.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.yomahub.liteflow.exception.ConfigErrorException;
import com.yomahub.liteflow.spi.PathContentParser;
import org.noear.solon.Utils;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SolonPathContentParser implements PathContentParser {
@Override
public List<String> parseContent(List<String> pathList) throws Exception {
if(CollectionUtil.isEmpty(pathList)){
throw new ConfigErrorException("rule source must not be null");
}
List<URL> allResource = new ArrayList<>();
for (String path : pathList) {
//如果 path 是绝对路径且这个文件存在时我们认为这是一个本地文件路径而并非classpath路径
if (FileUtil.isAbsolutePath(path) && FileUtil.isFile(path)) {
allResource.add(new File(path).toURI().toURL());
} else {
if (path.startsWith(ResourceUtils.CLASSPATH_URL_PREFIX)) {
path = path.substring(ResourceUtils.CLASSPATH_URL_PREFIX.length());
}
allResource.add(Utils.getResource(path));
}
}
//如果有多个资源,检查资源都是同一个类型,如果出现不同类型的配置,则抛出错误提示
Set<String> fileTypeSet = new HashSet<>();
allResource.forEach(resource -> fileTypeSet.add(FileUtil.extName(resource.getPath())));
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;
}
@Override
public int priority() {
return 1;
}
}