Commit 0af6d5a7581532b4658160a19680c09ffd427279

Authored by 安浩浩
1 parent d39e2c1b

【功能完善】IoT: 新增插件启动逻辑,重构插件状态管理,删除不再使用的状态文件更新方法

plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar
No preview for this file type
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java
1 package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo; 1 package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 6 import io.swagger.v3.oas.annotations.media.Schema;
5 import lombok.Data; 7 import lombok.Data;
6 8
@@ -11,7 +13,8 @@ public class PluginInfoPageReqVO extends PageParam { @@ -11,7 +13,8 @@ public class PluginInfoPageReqVO extends PageParam {
11 @Schema(description = "插件名称", example = "http") 13 @Schema(description = "插件名称", example = "http")
12 private String name; 14 private String name;
13 15
14 - @Schema(description = "状态") 16 + @Schema(description = "状态", example = "1")
  17 + @InEnum(IotPluginStatusEnum.class)
15 private Integer status; 18 private Integer status;
16 19
17 } 20 }
18 \ No newline at end of file 21 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.framework.plugin;
  2 +
  3 +import java.util.List;
  4 +
  5 +import javax.annotation.Resource;
  6 +
  7 +import org.pf4j.spring.SpringPluginManager;
  8 +import org.springframework.boot.ApplicationArguments;
  9 +import org.springframework.boot.ApplicationRunner;
  10 +import org.springframework.stereotype.Component;
  11 +
  12 +import lombok.extern.slf4j.Slf4j;
  13 +
  14 +import cn.iocoder.yudao.module.iot.service.plugin.PluginInfoService;
  15 +import cn.hutool.core.collection.CollUtil;
  16 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  17 +import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  18 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
  19 +
  20 +@Component
  21 +@Slf4j
  22 +public class PluginStart implements ApplicationRunner {
  23 +
  24 + @Resource
  25 + private PluginInfoService pluginInfoService;
  26 +
  27 + @Resource
  28 + private SpringPluginManager pluginManager;
  29 +
  30 + @Override
  31 + public void run(ApplicationArguments args) {
  32 + TenantUtils.executeIgnore(() -> { // 1. 忽略租户上下文执行
  33 + List<PluginInfoDO> pluginInfoList = pluginInfoService
  34 + .getPluginInfoListByStatus(IotPluginStatusEnum.RUNNING.getStatus()); // 2. 获取运行中的插件列表
  35 + if (CollUtil.isEmpty(pluginInfoList)) { // 3. 检查插件列表是否为空
  36 + log.info("[run] 没有需要启动的插件"); // 4. 日志记录没有插件需要启动
  37 + return;
  38 + }
  39 + pluginInfoList.forEach(pluginInfo -> { // 5. 使用lambda表达式遍历插件列表
  40 + try {
  41 + log.info("[run][启动插件] pluginKey = {}", pluginInfo.getPluginKey()); // 6. 日志记录插件启动信息
  42 + pluginManager.startPlugin(pluginInfo.getPluginKey()); // 7. 启动插件
  43 + } catch (Exception e) {
  44 + log.error("[run][启动插件失败] pluginKey = {}", pluginInfo.getPluginKey(), e); // 8. 记录启动失败的日志
  45 + }
  46 + });
  47 + });
  48 +
  49 + }
  50 +
  51 +}
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/UnifiedConfiguration.java
@@ -31,7 +31,13 @@ public class UnifiedConfiguration { @@ -31,7 +31,13 @@ public class UnifiedConfiguration {
31 @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER) 31 @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER)
32 public SpringPluginManager pluginManager() { 32 public SpringPluginManager pluginManager() {
33 log.info("[init][实例化 SpringPluginManager]"); 33 log.info("[init][实例化 SpringPluginManager]");
34 - SpringPluginManager springPluginManager = new SpringPluginManager(); 34 + SpringPluginManager springPluginManager = new SpringPluginManager() {
  35 + @Override
  36 + public void startPlugins() {
  37 + // 禁用插件启动,避免插件启动时,启动所有插件
  38 + log.info("[init][禁用默认启动所有插件]");
  39 + }
  40 + };
35 springPluginManager.addPluginStateListener(new CustomPluginStateListener()); 41 springPluginManager.addPluginStateListener(new CustomPluginStateListener());
36 return springPluginManager; 42 return springPluginManager;
37 } 43 }
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoService.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO; 4 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
5 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO; 5 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
6 import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO; 6 import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
7 import jakarta.validation.Valid; 8 import jakarta.validation.Valid;
8 import org.springframework.web.multipart.MultipartFile; 9 import org.springframework.web.multipart.MultipartFile;
9 10
@@ -66,7 +67,7 @@ public interface PluginInfoService { @@ -66,7 +67,7 @@ public interface PluginInfoService {
66 * 更新插件的状态 67 * 更新插件的状态
67 * 68 *
68 * @param id 插件id 69 * @param id 插件id
69 - * @param status 状态 70 + * @param status 状态 {@link IotPluginStatusEnum}
70 */ 71 */
71 void updatePluginStatus(Long id, Integer status); 72 void updatePluginStatus(Long id, Integer status);
72 73
@@ -80,7 +81,7 @@ public interface PluginInfoService { @@ -80,7 +81,7 @@ public interface PluginInfoService {
80 /** 81 /**
81 * 根据状态获得插件信息列表 82 * 根据状态获得插件信息列表
82 * 83 *
83 - * @param status 状态 84 + * @param status 状态 {@link IotPluginStatusEnum}
84 * @return 插件信息列表 85 * @return 插件信息列表
85 */ 86 */
86 List<PluginInfoDO> getPluginInfoListByStatus(Integer status); 87 List<PluginInfoDO> getPluginInfoListByStatus(Integer status);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInfoServiceImpl.java
@@ -102,7 +102,6 @@ public class PluginInfoServiceImpl implements PluginInfoService { @@ -102,7 +102,6 @@ public class PluginInfoServiceImpl implements PluginInfoService {
102 102
103 // 3 上传新的插件文件,更新插件启用状态文件 103 // 3 上传新的插件文件,更新插件启用状态文件
104 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file); 104 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
105 - pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false);  
106 105
107 // 4. 更新插件信息 106 // 4. 更新插件信息
108 updatePluginInfo(pluginInfoDo, pluginKeyNew, file); 107 updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java
@@ -38,14 +38,6 @@ public interface PluginInstanceService { @@ -38,14 +38,6 @@ public interface PluginInstanceService {
38 String uploadAndLoadNewPlugin(MultipartFile file); 38 String uploadAndLoadNewPlugin(MultipartFile file);
39 39
40 /** 40 /**
41 - * 更新插件状态文件  
42 - *  
43 - * @param pluginKeyNew 插件标识符  
44 - * @param isEnabled 是否启用  
45 - */  
46 - void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled);  
47 -  
48 - /**  
49 * 更新插件状态 41 * 更新插件状态
50 * 42 *
51 * @param pluginInfoDo 插件信息 43 * @param pluginInfoDo 插件信息
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceServiceImpl.java
@@ -21,11 +21,7 @@ import org.springframework.web.multipart.MultipartFile; @@ -21,11 +21,7 @@ import org.springframework.web.multipart.MultipartFile;
21 21
22 import java.io.File; 22 import java.io.File;
23 import java.io.IOException; 23 import java.io.IOException;
24 -import java.net.Inet4Address;  
25 -import java.net.InetAddress;  
26 import java.nio.file.*; 24 import java.nio.file.*;
27 -import java.util.ArrayList;  
28 -import java.util.LinkedHashSet;  
29 import java.util.List; 25 import java.util.List;
30 import java.util.Map; 26 import java.util.Map;
31 import java.util.concurrent.TimeUnit; 27 import java.util.concurrent.TimeUnit;
@@ -120,44 +116,6 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @@ -120,44 +116,6 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
120 } 116 }
121 117
122 @Override 118 @Override
123 - public void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) {  
124 - // TODO @haohao:疑问,这里写 enabled.txt 和 disabled.txt 的目的是啥哈?  
125 - // pf4j 的插件状态文件,需要 2 个文件,一个 enabled.txt 一个 disabled.txt  
126 - Path enabledFilePath = Paths.get(pluginsDir, "enabled.txt");  
127 - Path disabledFilePath = Paths.get(pluginsDir, "disabled.txt");  
128 - Path targetFilePath = isEnabled ? enabledFilePath : disabledFilePath;  
129 - Path oppositeFilePath = isEnabled ? disabledFilePath : enabledFilePath;  
130 -  
131 - try {  
132 - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginKeyNew);  
133 - if (pluginWrapper == null) {  
134 - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED);  
135 - }  
136 - List<String> targetLines = Files.exists(targetFilePath) ? Files.readAllLines(targetFilePath)  
137 - : new ArrayList<>();  
138 - List<String> oppositeLines = Files.exists(oppositeFilePath) ? Files.readAllLines(oppositeFilePath)  
139 - : new ArrayList<>();  
140 -  
141 - if (!targetLines.contains(pluginKeyNew)) {  
142 - targetLines.add(pluginKeyNew);  
143 - Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE,  
144 - StandardOpenOption.TRUNCATE_EXISTING);  
145 - log.info("已添加插件 {} 到 {}", pluginKeyNew, targetFilePath.getFileName());  
146 - }  
147 -  
148 - if (oppositeLines.contains(pluginKeyNew)) {  
149 - oppositeLines.remove(pluginKeyNew);  
150 - Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE,  
151 - StandardOpenOption.TRUNCATE_EXISTING);  
152 - log.info("已从 {} 移除插件 {}", oppositeFilePath.getFileName(), pluginKeyNew);  
153 - }  
154 - } catch (IOException e) {  
155 - log.error("[updatePluginStatusFile][更新插件状态文件失败]", e);  
156 - throw exception(ErrorCodeConstants.PLUGIN_INSTALL_FAILED, e);  
157 - }  
158 - }  
159 -  
160 - @Override  
161 public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) { 119 public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) {
162 String pluginKey = pluginInfoDo.getPluginKey(); 120 String pluginKey = pluginInfoDo.getPluginKey();
163 PluginWrapper plugin = pluginManager.getPlugin(pluginKey); 121 PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
@@ -167,14 +125,12 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @@ -167,14 +125,12 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
167 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) 125 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus())
168 && plugin.getPluginState() != PluginState.STARTED) { 126 && plugin.getPluginState() != PluginState.STARTED) {
169 pluginManager.startPlugin(pluginKey); 127 pluginManager.startPlugin(pluginKey);
170 - updatePluginStatusFile(pluginKey, true);  
171 log.info("已启动插件: {}", pluginKey); 128 log.info("已启动插件: {}", pluginKey);
172 } 129 }
173 // 停止插件 130 // 停止插件
174 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) 131 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus())
175 && plugin.getPluginState() == PluginState.STARTED) { 132 && plugin.getPluginState() == PluginState.STARTED) {
176 pluginManager.stopPlugin(pluginKey); 133 pluginManager.stopPlugin(pluginKey);
177 - updatePluginStatusFile(pluginKey, false);  
178 log.info("已停止插件: {}", pluginKey); 134 log.info("已停止插件: {}", pluginKey);
179 } 135 }
180 } else { 136 } else {
@@ -208,7 +164,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService { @@ -208,7 +164,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
208 PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey); 164 PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey);
209 if (pluginInfo == null) { 165 if (pluginInfo == null) {
210 // 4.2 插件信息不存在,记录错误并跳过 166 // 4.2 插件信息不存在,记录错误并跳过
211 - log.error("插件信息不存在,插件包标识符 = {}", pluginKey); 167 + log.error("插件信息不存在,pluginKey = {}", pluginKey);
212 continue; 168 continue;
213 } 169 }
214 170
yudao-module-iot/yudao-module-iot-plugin/yudao-module-iot-http-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/HttpVertxPlugin.java
@@ -5,12 +5,15 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi; @@ -5,12 +5,15 @@ import cn.iocoder.yudao.module.iot.api.device.DeviceDataApi;
5 import io.vertx.core.Vertx; 5 import io.vertx.core.Vertx;
6 import io.vertx.ext.web.Router; 6 import io.vertx.ext.web.Router;
7 import io.vertx.ext.web.handler.BodyHandler; 7 import io.vertx.ext.web.handler.BodyHandler;
8 -import org.pf4j.Plugin;  
9 import org.pf4j.PluginWrapper; 8 import org.pf4j.PluginWrapper;
  9 +import org.pf4j.spring.SpringPlugin;
  10 +import org.springframework.context.ApplicationContext;
  11 +import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  12 +
10 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
11 14
12 @Slf4j 15 @Slf4j
13 -public class HttpVertxPlugin extends Plugin { 16 +public class HttpVertxPlugin extends SpringPlugin {
14 17
15 private static final int PORT = 8092; 18 private static final int PORT = 8092;
16 private Vertx vertx; 19 private Vertx vertx;
@@ -67,4 +70,13 @@ public class HttpVertxPlugin extends Plugin { @@ -67,4 +70,13 @@ public class HttpVertxPlugin extends Plugin {
67 }); 70 });
68 } 71 }
69 } 72 }
  73 +
  74 + @Override
  75 + protected ApplicationContext createApplicationContext() {
  76 + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  77 + applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
  78 + applicationContext.refresh();
  79 +
  80 + return applicationContext;
  81 + }
70 } 82 }
71 \ No newline at end of file 83 \ No newline at end of file