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 1 package cn.iocoder.yudao.module.iot.controller.admin.plugin.vo;
2 2  
3 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 6 import io.swagger.v3.oas.annotations.media.Schema;
5 7 import lombok.Data;
6 8  
... ... @@ -11,7 +13,8 @@ public class PluginInfoPageReqVO extends PageParam {
11 13 @Schema(description = "插件名称", example = "http")
12 14 private String name;
13 15  
14   - @Schema(description = "状态")
  16 + @Schema(description = "状态", example = "1")
  17 + @InEnum(IotPluginStatusEnum.class)
15 18 private Integer status;
16 19  
17 20 }
18 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 31 @DependsOn(SERVICE_REGISTRY_INITIALIZED_MARKER)
32 32 public SpringPluginManager pluginManager() {
33 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 41 springPluginManager.addPluginStateListener(new CustomPluginStateListener());
36 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 4 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoPageReqVO;
5 5 import cn.iocoder.yudao.module.iot.controller.admin.plugin.vo.PluginInfoSaveReqVO;
6 6 import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
  7 +import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
7 8 import jakarta.validation.Valid;
8 9 import org.springframework.web.multipart.MultipartFile;
9 10  
... ... @@ -66,7 +67,7 @@ public interface PluginInfoService {
66 67 * 更新插件的状态
67 68 *
68 69 * @param id 插件id
69   - * @param status 状态
  70 + * @param status 状态 {@link IotPluginStatusEnum}
70 71 */
71 72 void updatePluginStatus(Long id, Integer status);
72 73  
... ... @@ -80,7 +81,7 @@ public interface PluginInfoService {
80 81 /**
81 82 * 根据状态获得插件信息列表
82 83 *
83   - * @param status 状态
  84 + * @param status 状态 {@link IotPluginStatusEnum}
84 85 * @return 插件信息列表
85 86 */
86 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 102  
103 103 // 3 上传新的插件文件,更新插件启用状态文件
104 104 String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
105   - pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false);
106 105  
107 106 // 4. 更新插件信息
108 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 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 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 21  
22 22 import java.io.File;
23 23 import java.io.IOException;
24   -import java.net.Inet4Address;
25   -import java.net.InetAddress;
26 24 import java.nio.file.*;
27   -import java.util.ArrayList;
28   -import java.util.LinkedHashSet;
29 25 import java.util.List;
30 26 import java.util.Map;
31 27 import java.util.concurrent.TimeUnit;
... ... @@ -120,44 +116,6 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
120 116 }
121 117  
122 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 119 public void updatePluginStatus(PluginInfoDO pluginInfoDo, Integer status) {
162 120 String pluginKey = pluginInfoDo.getPluginKey();
163 121 PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
... ... @@ -167,14 +125,12 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
167 125 if (status.equals(IotPluginStatusEnum.RUNNING.getStatus())
168 126 && plugin.getPluginState() != PluginState.STARTED) {
169 127 pluginManager.startPlugin(pluginKey);
170   - updatePluginStatusFile(pluginKey, true);
171 128 log.info("已启动插件: {}", pluginKey);
172 129 }
173 130 // 停止插件
174 131 else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus())
175 132 && plugin.getPluginState() == PluginState.STARTED) {
176 133 pluginManager.stopPlugin(pluginKey);
177   - updatePluginStatusFile(pluginKey, false);
178 134 log.info("已停止插件: {}", pluginKey);
179 135 }
180 136 } else {
... ... @@ -208,7 +164,7 @@ public class PluginInstanceServiceImpl implements PluginInstanceService {
208 164 PluginInfoDO pluginInfo = pluginInfoMap.get(pluginKey);
209 165 if (pluginInfo == null) {
210 166 // 4.2 插件信息不存在,记录错误并跳过
211   - log.error("插件信息不存在,插件包标识符 = {}", pluginKey);
  167 + log.error("插件信息不存在,pluginKey = {}", pluginKey);
212 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 5 import io.vertx.core.Vertx;
6 6 import io.vertx.ext.web.Router;
7 7 import io.vertx.ext.web.handler.BodyHandler;
8   -import org.pf4j.Plugin;
9 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 13 import lombok.extern.slf4j.Slf4j;
11 14  
12 15 @Slf4j
13   -public class HttpVertxPlugin extends Plugin {
  16 +public class HttpVertxPlugin extends SpringPlugin {
14 17  
15 18 private static final int PORT = 8092;
16 19 private Vertx vertx;
... ... @@ -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 83 \ No newline at end of file
... ...