Browse Source

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

安浩浩 7 months ago
parent
commit
0af6d5a758

BIN
plugins/yudao-module-iot-http-plugin-2.2.0-snapshot.jar


+ 4 - 1
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugin/vo/PluginInfoPageReqVO.java

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

+ 51 - 0
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/PluginStart.java

@@ -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. 记录启动失败的日志
+                }
+            });
+        });
+
+    }
+
+}

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

+ 3 - 2
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;
 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);

+ 0 - 1
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 {
 
         // 3 上传新的插件文件,更新插件启用状态文件
         String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
-        pluginInstanceService.updatePluginStatusFile(pluginKeyNew, false);
 
         // 4. 更新插件信息
         updatePluginInfo(pluginInfoDo, pluginKeyNew, file);

+ 0 - 8
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/PluginInstanceService.java

@@ -37,14 +37,6 @@ public interface PluginInstanceService {
      */
     String uploadAndLoadNewPlugin(MultipartFile file);
 
-    /**
-     * 更新插件状态文件
-     *
-     * @param pluginKeyNew 插件标识符
-     * @param isEnabled    是否启用
-     */
-    void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled);
-
     /**
      * 更新插件状态
      *

+ 1 - 45
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;
 
 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;
             }
 

+ 14 - 2
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;
 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;
+    }
 }