【功能完善】IoT: 新增插件启动逻辑,重构插件状态管理,删除不再使用的状态文件更新方法
This commit is contained in:
parent
d39e2c1bc4
commit
0af6d5a758
Binary file not shown.
|
@ -1,6 +1,8 @@
|
|||
package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
@ -11,7 +13,8 @@ public class PluginInfoPageReqVO extends PageParam {
|
|||
@Schema(description = "插件名称", example = "http")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态")
|
||||
@Schema(description = "状态", example = "1")
|
||||
@InEnum(IotPluginStatusEnum.class)
|
||||
private Integer status;
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package cn.iocoder.yudao.module.iot.framework.plugin;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import org.pf4j.spring.SpringPluginManager;
|
||||
import org.springframework.boot.ApplicationArguments;
|
||||
import org.springframework.boot.ApplicationRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class PluginStart implements ApplicationRunner {
|
||||
|
||||
@Resource
|
||||
private PluginInfoService pluginInfoService;
|
||||
|
||||
@Resource
|
||||
private SpringPluginManager pluginManager;
|
||||
|
||||
@Override
|
||||
public void run(ApplicationArguments args) {
|
||||
TenantUtils.executeIgnore(() -> { // 1. 忽略租户上下文执行
|
||||
List<PluginInfoDO> pluginInfoList = pluginInfoService
|
||||
.getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表
|
||||
if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空
|
||||
log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动
|
||||
return;
|
||||
}
|
||||
pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表
|
||||
try {
|
||||
log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息
|
||||
pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件
|
||||
} catch (Exception e) {
|
||||
log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -31,7 +31,13 @@ public class UnifiedConfiguration {
|
|||
@DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER)
|
||||
public SpringPluginManager pluginManager() {
|
||||
log.info("[init][实例化 SpringPluginManager]");
|
||||
SpringPluginManager springPluginManager = new SpringPluginManager();
|
||||
SpringPluginManager springPluginManager = new SpringPluginManager() {
|
||||
@Override
|
||||
public void startPlugins() {
|
||||
// 禁用插件启动,避免插件启动时,启动所有插件
|
||||
log.info("[init][禁用默认启动所有插件]");
|
||||
}
|
||||
};
|
||||
springPluginManager.addPluginStateListener(new CustomPluginStateListener());
|
||||
return springPluginManager;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|||
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
|
@ -66,7 +67,7 @@ public interface PluginInfoService {
|
|||
* 更新插件的状态
|
||||
*
|
||||
* @param id 插件id
|
||||
* @param status 状态
|
||||
* @param status 状态 {@link IotPluginStatusEnum}
|
||||
*/
|
||||
void updatePluginStatus(Long id, Integer status);
|
||||
|
||||
|
@ -80,7 +81,7 @@ public interface PluginInfoService {
|
|||
/**
|
||||
* 根据状态获得插件信息列表
|
||||
*
|
||||
* @param status 状态
|
||||
* @param status 状态 {@link IotPluginStatusEnum}
|
||||
* @return 插件信息列表
|
||||
*/
|
||||
List<PluginInfoDO> getPluginInfoListByStatus(Integer status);
|
||||
|
|
|
@ -102,7 +102,6 @@ public class PluginInfoServiceImpl implements PluginInfoService {
|
|||
|
||||
// 3 上传新的插件文件,更新插件启用状态文件
|
||||
String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
|
||||
pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false);
|
||||
|
||||
// 4. 更新插件信息
|
||||
updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
|
||||
|
|
|
@ -37,14 +37,6 @@ public interface PluginInstanceService {
|
|||
*/
|
||||
String uploadAndLoadNewPlugin(MultipartFile file);
|
||||
|
||||
/**
|
||||
* 更新插件状态文件
|
||||
*
|
||||
* @param pluginKeyNew 插件标识符
|
||||
* @param isEnabled 是否启用
|
||||
*/
|
||||
void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled);
|
||||
|
||||
/**
|
||||
* 更新插件状态
|
||||
*
|
||||
|
|
|
@ -21,11 +21,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.file.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -119,44 +115,6 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
|
|||
return pluginKeyNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) {
|
||||
// TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈?
|
||||
// pf4j 的插件状态文件,需要 2 个文件,一个 enabled.txt 一个 disabled.txt
|
||||
Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt");
|
||||
Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt");
|
||||
Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath;
|
||||
Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath;
|
||||
|
||||
try {
|
||||
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew);
|
||||
if (pluginWrapper == null) {
|
||||
throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED);
|
||||
}
|
||||
List<String> targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath)
|
||||
: new ArrayList<>();
|
||||
List<String> oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath)
|
||||
: new ArrayList<>();
|
||||
|
||||
if (!targetLines.contains(pluginKeyNew)) {
|
||||
targetLines.add(pluginKeyNew);
|
||||
Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
log.info("已添加插件 {} 到 {}", pluginKeyNew, targetFilePath.getFileName());
|
||||
}
|
||||
|
||||
if (oppositeLines.contains(pluginKeyNew)) {
|
||||
oppositeLines.remove(pluginKeyNew);
|
||||
Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
log.info("已从 {} 移除插件 {}", oppositeFilePath.getFileName(), pluginKeyNew);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("[updatePluginStatusFile][更新插件状态文件失败]", e);
|
||||
throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) {
|
||||
String pluginKey = pluginInfoDo.getPluginKey();
|
||||
|
@ -167,14 +125,12 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
|
|||
if (status.equals(IotPluginStatusEnum.RUNNING.getStatus())
|
||||
&& plugin.getPluginState() != PluginState.STARTED) {
|
||||
pluginManager.startPlugin(pluginKey);
|
||||
updatePluginStatusFile(pluginKey, true);
|
||||
log.info("已启动插件: {}", pluginKey);
|
||||
}
|
||||
// 停止插件
|
||||
else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus())
|
||||
&& plugin.getPluginState() == PluginState.STARTED) {
|
||||
pluginManager.stopPlugin(pluginKey);
|
||||
updatePluginStatusFile(pluginKey, false);
|
||||
log.info("已停止插件: {}", pluginKey);
|
||||
}
|
||||
} else {
|
||||
|
@ -208,7 +164,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
|
|||
PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey);
|
||||
if (pluginInfo == null) {
|
||||
// 4.2 插件信息不存在,记录错误并跳过
|
||||
log.error("插件信息不存在,插件包标识符 = {}", pluginKey);
|
||||
log.error("插件信息不存在,pluginKey = {}", pluginKey);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
|
|||
import io.vertx.core.Vertx;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.handler.BodyHandler;
|
||||
import org.pf4j.Plugin;
|
||||
import org.pf4j.PluginWrapper;
|
||||
import org.pf4j.spring.SpringPlugin;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class HttpVertxPlugin extends Plugin {
|
||||
public class HttpVertxPlugin extends SpringPlugin {
|
||||
|
||||
private static final int PORT = 8092;
|
||||
private Vertx vertx;
|
||||
|
@ -67,4 +70,13 @@ public class HttpVertxPlugin extends Plugin {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApplicationContext createApplicationContext() {
|
||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
||||
applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
|
||||
applicationContext.refresh();
|
||||
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue