Commit 0af6d5a7581532b4658160a19680c09ffd427279
1 parent
d39e2c1b
【功能完善】IoT: 新增插件启动逻辑,重构插件状态管理,删除不再使用的状态文件更新方法
Showing
9 changed files
with
80 additions
and
60 deletions
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 |