Commit 290fcd94d56334e4706935a5b250c1d925a5eaee

Authored by 安浩浩
1 parent 92c2717d

【功能新增】IoT:增加插件支持,包含插件API和示例控制器

Showing 18 changed files with 637 additions and 92 deletions
yudao-module-iot/pom.xml
... ... @@ -11,6 +11,7 @@
11 11 <module>yudao-module-iot-api</module>
12 12 <module>yudao-module-iot-biz</module>
13 13 <module>yudao-module-iot-plugin</module>
  14 + <module>yudao-module-iot-plugin-api</module>
14 15 </modules>
15 16 <modelVersion>4.0.0</modelVersion>
16 17  
... ...
yudao-module-iot/yudao-module-iot-biz/pom.xml
... ... @@ -27,6 +27,13 @@
27 27  
28 28 <dependency>
29 29 <groupId>cn.iocoder.boot</groupId>
  30 + <artifactId>yudao-module-iot-plugin-api</artifactId>
  31 + <version>0.0.1</version>
  32 + <scope>compile</scope>
  33 + </dependency>
  34 +
  35 + <dependency>
  36 + <groupId>cn.iocoder.boot</groupId>
30 37 <artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
31 38 </dependency>
32 39  
... ... @@ -75,6 +82,7 @@
75 82 <groupId>org.pf4j</groupId>
76 83 <artifactId>pf4j-spring</artifactId>
77 84 </dependency>
  85 +
78 86 </dependencies>
79 87  
80 88 </project>
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/Greetings.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2012-present the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
  17 +
  18 +import cn.iocoder.yudao.module.iot.api.Greeting;
  19 +import org.springframework.beans.factory.annotation.Autowired;
  20 +import org.springframework.stereotype.Component;
  21 +
  22 +import java.util.List;
  23 +
  24 +/**
  25 + * 打招呼 测试用例
  26 + */
  27 +@Component
  28 +public class Greetings {
  29 +
  30 + @Autowired
  31 + private List<Greeting> greetings;
  32 +
  33 + public void printGreetings() {
  34 + System.out.printf("找到扩展点的 %d 个扩展 '%s'%n", greetings.size(), Greeting.class.getName());
  35 + for (Greeting greeting : greetings) {
  36 + System.out.println(">>> " + greeting.getGreeting());
  37 + }
  38 + }
  39 +
  40 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/PluginController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
  2 +
  3 +import jakarta.annotation.Resource;
  4 +import org.pf4j.spring.SpringPluginManager;
  5 +import org.springframework.beans.factory.annotation.Value;
  6 +import org.springframework.context.ApplicationContext;
  7 +import org.springframework.http.HttpStatus;
  8 +import org.springframework.http.ResponseEntity;
  9 +import org.springframework.web.bind.annotation.*;
  10 +import org.springframework.web.multipart.MultipartFile;
  11 +
  12 +import javax.annotation.security.PermitAll;
  13 +import java.io.IOException;
  14 +import java.nio.file.Files;
  15 +import java.nio.file.Path;
  16 +import java.nio.file.Paths;
  17 +import java.nio.file.StandardCopyOption;
  18 +import java.util.List;
  19 +import java.util.stream.Collectors;
  20 +
  21 +/**
  22 + * 插件 Controller 测试用例
  23 + */
  24 +@RestController
  25 +@RequestMapping("/iot/plugins")
  26 +public class PluginController {
  27 +
  28 + @Resource
  29 + private ApplicationContext applicationContext;
  30 + @Resource
  31 + private SpringPluginManager springPluginManager;
  32 + @Resource
  33 + private Greetings greetings;
  34 +
  35 + @Value("${pf4j.pluginsDir}")
  36 + private String pluginsDir;
  37 +
  38 + /**
  39 + * 上传插件 JAR 文件并加载插件
  40 + *
  41 + * @param file 上传的 JAR 文件
  42 + * @return 上传结果
  43 + */
  44 + @PermitAll
  45 + @PostMapping("/upload")
  46 + public ResponseEntity<String> uploadPlugin(@RequestParam("file") MultipartFile file) {
  47 + if (file.isEmpty()) {
  48 + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件为空");
  49 + }
  50 +
  51 + // 确保插件目录存在
  52 + Path pluginsPath = Paths.get(pluginsDir);
  53 + try {
  54 + if (!Files.exists(pluginsPath)) {
  55 + Files.createDirectories(pluginsPath);
  56 + }
  57 +
  58 + // 保存上传的 JAR 文件到插件目录
  59 + String filename = file.getOriginalFilename();
  60 + if (filename == null || (!filename.endsWith(".jar") && !filename.endsWith(".zip"))) {
  61 + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("上传的文件不是 JAR 或 ZIP 文件");
  62 + }
  63 +
  64 + Path jarPath = pluginsPath.resolve(filename);
  65 +
  66 + Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING);
  67 +
  68 + // 加载插件
  69 + String pluginId = springPluginManager.loadPlugin(jarPath.toAbsolutePath());
  70 +
  71 + // 启动插件
  72 + springPluginManager.startPlugin(pluginId);
  73 +
  74 + return ResponseEntity.ok("插件上传并加载成功");
  75 + } catch (IOException e) {
  76 + e.printStackTrace();
  77 + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("上传插件时发生错误: " + e.getMessage());
  78 + } catch (Exception e) {
  79 + e.printStackTrace();
  80 + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("加载插件时发生错误: " + e.getMessage());
  81 + }
  82 + }
  83 +
  84 + /**
  85 + * 卸载指定插件
  86 + *
  87 + * @param pluginId 插件 ID
  88 + * @return 卸载结果
  89 + */
  90 + @PermitAll
  91 + @DeleteMapping("/unload/{pluginId}")
  92 + public ResponseEntity<String> unloadPlugin(@PathVariable String pluginId) {
  93 + if (springPluginManager.getPlugins().stream().noneMatch(plugin -> plugin.getDescriptor().getPluginId().equals(pluginId))) {
  94 + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("插件未加载: " + pluginId);
  95 + }
  96 +
  97 + springPluginManager.stopPlugin(pluginId);
  98 + springPluginManager.unloadPlugin(pluginId);
  99 +
  100 + // 删除插件 JAR 文件(可选)
  101 +// PluginWrapper plugin = pluginManager.getPlugin(pluginId);
  102 +// PluginDescriptor descriptor = plugin.getDescriptor();
  103 +// Path jarPath = Paths.get(pluginsDir).resolve(descriptor.getPluginId() + ".jar");
  104 +// Files.deleteIfExists(jarPath);
  105 +
  106 + return ResponseEntity.ok("插件卸载成功: " + pluginId);
  107 + }
  108 +
  109 + /**
  110 + * 列出所有已加载的插件
  111 + *
  112 + * @return 插件列表
  113 + */
  114 + @PermitAll
  115 + @GetMapping("/list")
  116 + public ResponseEntity<List<String>> listPlugins() {
  117 + List<String> plugins = springPluginManager.getPlugins().stream()
  118 + .map(plugin -> plugin.getDescriptor().getPluginId())
  119 + .collect(Collectors.toList());
  120 + return ResponseEntity.ok(plugins);
  121 + }
  122 +
  123 + /**
  124 + * 打印问候语
  125 + *
  126 + * @return 1
  127 + */
  128 + @PermitAll
  129 + @GetMapping("/printGreetings")
  130 + public ResponseEntity<Integer> printGreetings() {
  131 + greetings.printGreetings();
  132 + return ResponseEntity.ok(1);
  133 + }
  134 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/plugininfo/WhazzupGreeting.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2012-present the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package cn.iocoder.yudao.module.iot.controller.admin.plugininfo;
  17 +
  18 +import cn.iocoder.yudao.module.iot.api.Greeting;
  19 +import org.pf4j.Extension;
  20 +import org.springframework.stereotype.Component;
  21 +
  22 +/**
  23 + * 打招呼 测试用例
  24 + */
  25 +@Extension
  26 +@Component
  27 +public class WhazzupGreeting implements Greeting {
  28 +
  29 + @Override
  30 + public String getGreeting() {
  31 + return "Whazzup";
  32 + }
  33 +
  34 +}
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/plugin/SpringConfiguration.java
1 1 package cn.iocoder.yudao.module.iot.framework.plugin;
2 2  
  3 +import cn.iocoder.yudao.module.iot.controller.admin.plugininfo.Greetings;
3 4 import org.pf4j.spring.SpringPluginManager;
4 5 import org.springframework.context.annotation.Bean;
5 6 import org.springframework.context.annotation.Configuration;
  7 +import org.springframework.context.annotation.DependsOn;
6 8  
7 9 @Configuration
8 10 public class SpringConfiguration {
... ... @@ -12,4 +14,10 @@ public class SpringConfiguration {
12 14 return new SpringPluginManager();
13 15 }
14 16  
  17 + @Bean
  18 + @DependsOn("pluginManager")
  19 + public Greetings greetings() {
  20 + return new Greetings();
  21 + }
  22 +
15 23 }
16 24 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoService.java
... ... @@ -7,8 +7,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.plugininfo.PluginInfoDO;
7 7 import jakarta.validation.Valid;
8 8 import org.springframework.web.multipart.MultipartFile;
9 9  
10   -import java.io.InputStream;
11   -
12 10 /**
13 11 * IoT 插件信息 Service 接口
14 12 *
... ... @@ -57,7 +55,7 @@ public interface PluginInfoService {
57 55 /**
58 56 * 上传插件的 JAR 包
59 57 *
60   - * @param id 插件id
  58 + * @param id 插件id
61 59 * @param file 文件
62 60 */
63 61 void uploadJar(Long id, MultipartFile file);
... ... @@ -65,7 +63,7 @@ public interface PluginInfoService {
65 63 /**
66 64 * 更新插件的状态
67 65 *
68   - * @param id 插件id
  66 + * @param id 插件id
69 67 * @param status 状态
70 68 */
71 69 void updatePluginStatus(Long id, Integer status);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugininfo/PluginInfoServiceImpl.java
... ... @@ -17,13 +17,18 @@ import org.pf4j.PluginDescriptor;
17 17 import org.pf4j.PluginState;
18 18 import org.pf4j.PluginWrapper;
19 19 import org.pf4j.spring.SpringPluginManager;
  20 +import org.springframework.beans.factory.annotation.Value;
20 21 import org.springframework.stereotype.Service;
21 22 import org.springframework.validation.annotation.Validated;
22 23 import org.springframework.web.multipart.MultipartFile;
23 24  
  25 +import java.nio.file.Files;
24 26 import java.nio.file.Path;
  27 +import java.nio.file.Paths;
25 28 import java.util.concurrent.Executors;
26 29 import java.util.concurrent.TimeUnit;
  30 +import java.util.jar.JarEntry;
  31 +import java.util.jar.JarFile;
27 32  
28 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
29 34 import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
... ... @@ -47,6 +52,9 @@ public class PluginInfoServiceImpl implements PluginInfoService {
47 52 @Resource
48 53 private FileApi fileApi;
49 54  
  55 + @Value("${pf4j.pluginsDir}")
  56 + private String pluginsDir;
  57 +
50 58 @Override
51 59 public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
52 60 // 插入
... ... @@ -132,7 +140,7 @@ public class PluginInfoServiceImpl implements PluginInfoService {
132 140 // 4. 上传插件
133 141 String pluginIdNew;
134 142 try {
135   - String path = fileApi.createFile(IoUtil.readBytes(file.getInputStream()));
  143 + String path = fileApi.createFile(pluginsDir, IoUtil.readBytes(file.getInputStream()));
136 144 Path pluginPath = Path.of(path);
137 145 pluginIdNew = pluginManager.loadPlugin(pluginPath);
138 146 } catch (Exception e) {
... ... @@ -144,10 +152,31 @@ public class PluginInfoServiceImpl implements PluginInfoService {
144 152 throw exception(PLUGIN_INSTALL_FAILED);
145 153 }
146 154  
147   -
148 155 // 5. 读取配置文件和脚本
149 156 String configJson = "";
150 157 String script = "";
  158 + try (JarFile jarFile = new JarFile(pluginWrapper.getPluginPath().toFile())) {
  159 + // 5.1 获取config文件在jar包中的路径
  160 + String configFile = "classes/config.json";
  161 + JarEntry configEntry = jarFile.getJarEntry(configFile);
  162 +
  163 + if (configEntry != null) {
  164 + // 5.2 读取配置文件
  165 + configJson = IoUtil.readUtf8(jarFile.getInputStream(configEntry));
  166 + log.info("configJson:{}", configJson);
  167 + }
  168 +
  169 + // 5.3 读取script.js脚本
  170 + String scriptFile = "classes/script.js";
  171 + JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
  172 + if (scriptEntity != null) {
  173 + // 5.4 读取脚本文件
  174 + script = IoUtil.readUtf8(jarFile.getInputStream(scriptEntity));
  175 + log.info("script:{}", script);
  176 + }
  177 + } catch (Exception e) {
  178 + throw exception(PLUGIN_INSTALL_FAILED);
  179 + }
151 180  
152 181 pluginInfoDo.setPluginId(pluginIdNew);
153 182 pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
... ... @@ -159,48 +188,6 @@ public class PluginInfoServiceImpl implements PluginInfoService {
159 188 pluginInfoDo.setVersion(pluginDescriptor.getVersion());
160 189 pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
161 190 pluginInfoMapper.updateById(pluginInfoDo);
162   -
163   -
164   - // 5. 读取配置文件和脚本
165   -// String configJson = "";
166   - // String script = "";
167   -// try (JarFile jarFile = new JarFile(pluginInfoUpdate.getPluginPath())) {
168   -// // 5.1 获取config文件在jar包中的路径
169   -// String configFile = "classes/config.json";
170   -// JarEntry configEntry = jarFile.getJarEntry(configFile);
171   -//
172   -// if (configEntry != null) {
173   -// // 5.2 读取配置文件
174   -// configJson = IoUtil.read(jarFile.getInputStream(configEntry), Charset.defaultCharset());
175   -// log.info("configJson:{}", configJson);
176   -// }
177   -//
178   -// // 5.3 读取script.js脚本
179   -// String scriptFile = "classes/script.js";
180   -// JarEntry scriptEntity = jarFile.getJarEntry(scriptFile);
181   -// if (scriptEntity != null) {
182   -// // 5.4 读取脚本文件
183   -// script = IoUtil.read(jarFile.getInputStream(scriptEntity), Charset.defaultCharset());
184   -// log.info("script:{}", script);
185   -// }
186   -// } catch (Exception e) {
187   -// throw exception(PLUGIN_INSTALL_FAILED);
188   -// }
189   -
190   -
191   -// PluginState pluginState = pluginInfoUpdate.getPluginState();
192   -// if (pluginState == PluginState.STARTED) {
193   -// pluginInfoDo.setStatus(IotPluginStatusEnum.RUNNING.getStatus());
194   -// }
195   -// pluginInfoDo.setPluginId(pluginInfoUpdate.getPluginId());
196   -// pluginInfoDo.setFile(file.getOriginalFilename());
197   -// pluginInfoDo.setConfigSchema(configJson);
198   -// pluginInfoDo.setScript(script);
199   -//
200   -// PluginDescriptor pluginDescriptor = pluginInfoUpdate.getPluginDescriptor();
201   -// pluginInfoDo.setVersion(pluginDescriptor.getPluginVersion());
202   -// pluginInfoDo.setDescription(pluginDescriptor.getDescription());
203   -// pluginInfoMapper.updateById(pluginInfoDo);
204 191 }
205 192  
206 193 @Override
... ... @@ -213,26 +200,22 @@ public class PluginInfoServiceImpl implements PluginInfoService {
213 200 throw exception(PLUGIN_STATUS_INVALID);
214 201 }
215 202  
216   - // 插件包为空
217   -// String pluginId = pluginInfoDo.getPluginId();
218   -// if (StrUtil.isBlank(pluginId)) {
219   -// throw exception(PLUGIN_INFO_NOT_EXISTS);
220   -// }
221   -// com.gitee.starblues.core.PluginInfo pluginInfo = pluginOperator.getPluginInfo(pluginId);
222   -// if (pluginInfo != null) {
223   -// if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.RUNNING.getStatus()) && pluginInfo.getPluginState() != PluginState.STARTED) {
224   -// // 启动插件
225   -// pluginOperator.start(pluginId);
226   -// } else if (pluginInfoDo.getStatus().equals(IotPluginStatusEnum.STOPPED.getStatus()) && pluginInfo.getPluginState() == PluginState.STARTED) {
227   -// // 停止插件
228   -// pluginOperator.stop(pluginId);
229   -// }
230   -// } else {
231   -// // 已经停止,未获取到插件
232   -// if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
233   -// throw exception(PLUGIN_STATUS_INVALID);
234   -// }
235   -// }
  203 + String pluginId = pluginInfoDo.getPluginId();
  204 + PluginWrapper plugin = pluginManager.getPlugin(pluginId);
  205 + if (plugin != null) {
  206 + if (status.equals(IotPluginStatusEnum.RUNNING.getStatus()) && plugin.getPluginState() != PluginState.STARTED) {
  207 + // 启动插件
  208 + pluginManager.startPlugin(pluginId);
  209 + } else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus()) && plugin.getPluginState() == PluginState.STARTED) {
  210 + // 停止插件
  211 + pluginManager.stopPlugin(pluginId);
  212 + }
  213 + } else {
  214 + // 已经停止,未获取到插件
  215 + if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
  216 + throw exception(PLUGIN_STATUS_INVALID);
  217 + }
  218 + }
236 219 pluginInfoDo.setStatus(status);
237 220 pluginInfoMapper.updateById(pluginInfoDo);
238 221 }
... ... @@ -244,20 +227,13 @@ public class PluginInfoServiceImpl implements PluginInfoService {
244 227  
245 228 @SneakyThrows
246 229 private void startPlugins() {
247   -// while (!pluginOperator.inited()) {
248   -// Thread.sleep(1000L);
249   -// }
250   -
251 230 for (PluginInfoDO pluginInfoDO : pluginInfoMapper.selectList()) {
252 231 if (!IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
253 232 continue;
254 233 }
255 234 log.info("start plugin:{}", pluginInfoDO.getPluginId());
256 235 try {
257   -// com.gitee.starblues.core.PluginInfo plugin = pluginOperator.getPluginInfo(pluginInfoDO.getPluginId());
258   -// if (plugin != null) {
259   -// pluginOperator.start(plugin.getPluginId());
260   -// }
  236 + pluginManager.startPlugin(pluginInfoDO.getPluginId());
261 237 } catch (Exception e) {
262 238 log.error("start plugin error", e);
263 239 }
... ...
yudao-module-iot/yudao-module-iot-plugin-api/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <modelVersion>4.0.0</modelVersion>
  6 + <groupId>cn.iocoder.boot</groupId>
  7 + <artifactId>yudao-module-iot-plugin-api</artifactId>
  8 + <version>0.0.1</version>
  9 + <packaging>jar</packaging>
  10 +
  11 + <name>${project.artifactId}</name>
  12 + <description>
  13 + 物联网 模块插件 API,暴露给其它模块调用
  14 + </description>
  15 +
  16 + <properties>
  17 + <pf4j-spring.version>0.9.0</pf4j-spring.version>
  18 + </properties>
  19 +
  20 + <dependencies>
  21 + <!-- PF4J Spring 集成 -->
  22 + <dependency>
  23 + <groupId>org.pf4j</groupId>
  24 + <artifactId>pf4j-spring</artifactId>
  25 + <version>${pf4j-spring.version}</version>
  26 + <scope>provided</scope>
  27 + </dependency>
  28 + </dependencies>
  29 +
  30 +</project>
... ...
yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/Greeting.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2012-present the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package cn.iocoder.yudao.module.iot.api;
  17 +
  18 +import org.pf4j.ExtensionPoint;
  19 +
  20 +/**
  21 + * @author Decebal Suiu
  22 + */
  23 +public interface Greeting extends ExtensionPoint {
  24 +
  25 + String getGreeting();
  26 +
  27 +}
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/package-info.java renamed to yudao-module-iot/yudao-module-iot-plugin-api/src/main/java/cn/iocoder/yudao/module/iot/api/package-info.java
... ... @@ -3,4 +3,4 @@
3 3 *
4 4 * TODO 芋艿:后续删除
5 5 */
6   -package cn.iocoder.yudao.module.iot.plugin;
  6 +package cn.iocoder.yudao.module.iot.api;
... ...
yudao-module-iot/yudao-module-iot-plugin/plugin.properties 0 → 100644
  1 +plugin.id=iot-plugin-hppt
  2 +plugin.class=cn.iocoder.yudao.module.iot.plugin.WelcomePlugin
  3 +plugin.version=0.0.1
  4 +plugin.provider=ahh
  5 +plugin.dependencies=
... ...
yudao-module-iot/yudao-module-iot-plugin/pom.xml
1 1 <?xml version="1.0" encoding="UTF-8"?>
2   -<project xmlns="http://maven.apache.org/POM/4.0.0"
3   - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4   - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5   - <parent>
6   - <artifactId>yudao-module-iot</artifactId>
7   - <groupId>cn.iocoder.boot</groupId>
8   - <version>${revision}</version>
9   - </parent>
  2 +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3 + xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="
  4 + http://maven.apache.org/POM/4.0.0
  5 + http://maven.apache.org/xsd/maven-4.0.0.xsd">
10 6 <modelVersion>4.0.0</modelVersion>
  7 + <groupId>cn.iocoder.boot</groupId>
11 8 <artifactId>yudao-module-iot-plugin</artifactId>
  9 + <version>0.0.1</version>
12 10 <packaging>jar</packaging>
13 11  
14 12 <name>${project.artifactId}</name>
15   - <description>
16   - 物联网 模块 - 插件
17   - </description>
  13 + <description>物联网 模块 - 插件</description>
  14 +
  15 + <properties>
  16 + <!-- 插件相关 -->
  17 + <plugin.id>iot-plugin-http</plugin.id>
  18 + <plugin.class>cn.iocoder.yudao.module.iot.plugin.WelcomePlugin</plugin.class>
  19 + <plugin.version>0.0.1</plugin.version>
  20 + <plugin.provider>ahh</plugin.provider>
  21 + <plugin.dependencies/>
  22 +
  23 + <!-- Maven 相关 -->
  24 + <java.version>17</java.version>
  25 + <maven.compiler.source>${java.version}</maven.compiler.source>
  26 + <maven.compiler.target>${java.version}</maven.compiler.target>
  27 + <maven-antrun-plugin.version>1.6</maven-antrun-plugin.version>
  28 + <maven-assembly-plugin.version>2.3</maven-assembly-plugin.version>
  29 + <maven-jar-plugin.version>2.4</maven-jar-plugin.version>
  30 + <pf4j-spring.version>0.9.0</pf4j-spring.version>
  31 + <!-- 看看咋放到 bom 里 -->
  32 + <spring.boot.version>3.3.1</spring.boot.version>
  33 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  34 + </properties>
18 35  
19 36 <dependencies>
  37 + <!-- 其他依赖项 -->
20 38 <dependency>
21   - <groupId>cn.iocoder.boot</groupId>
22   - <artifactId>yudao-common</artifactId>
  39 + <groupId>org.springframework.boot</groupId>
  40 + <artifactId>spring-boot-starter-web</artifactId>
  41 + <version>${spring.boot.version}</version>
  42 + <scope>provided</scope>
23 43 </dependency>
24   -
  44 + <!-- PF4J Spring 集成 -->
25 45 <dependency>
26 46 <groupId>org.pf4j</groupId>
27 47 <artifactId>pf4j-spring</artifactId>
  48 + <version>${pf4j-spring.version}</version>
  49 + <scope>provided</scope>
  50 + </dependency>
  51 + <!-- 项目依赖 -->
  52 + <dependency>
  53 + <groupId>cn.iocoder.boot</groupId>
  54 + <artifactId>yudao-module-iot-plugin-api</artifactId>
  55 + <version>${project.version}</version>
28 56 </dependency>
29 57 </dependencies>
30 58  
31   -</project>
  59 + <build>
  60 + <plugins>
  61 + <!-- DOESN'T WORK WITH MAVEN 3 (I defined the plugin metadata in properties section)
  62 + <plugin>
  63 + <groupId>org.codehaus.mojo</groupId>
  64 + <artifactId>properties-maven-plugin</artifactId>
  65 + <version>1.0-alpha-2</version>
  66 + <executions>
  67 + <execution>
  68 + <phase>initialize</phase>
  69 + <goals>
  70 + <goal>read-project-properties</goal>
  71 + </goals>
  72 + <configuration>
  73 + <files>
  74 + <file>plugin.properties</file>
  75 + </files>
  76 + </configuration>
  77 + </execution>
  78 + </executions>
  79 + </plugin>
  80 + -->
  81 +
  82 + <plugin>
  83 + <groupId>org.apache.maven.plugins</groupId>
  84 + <artifactId>maven-antrun-plugin</artifactId>
  85 + <version>${maven-antrun-plugin.version}</version>
  86 + <executions>
  87 + <execution>
  88 + <id>unzip jar file</id>
  89 + <phase>package</phase>
  90 + <configuration>
  91 + <target>
  92 + <unzip src="target/${project.artifactId}-${project.version}.${project.packaging}" dest="target/plugin-classes" />
  93 + </target>
  94 + </configuration>
  95 + <goals>
  96 + <goal>run</goal>
  97 + </goals>
  98 + </execution>
  99 + </executions>
  100 + </plugin>
  101 +
  102 + <plugin>
  103 + <artifactId>maven-assembly-plugin</artifactId>
  104 + <version>${maven-assembly-plugin.version}</version>
  105 + <configuration>
  106 + <descriptors>
  107 + <descriptor>
  108 + src/main/assembly/assembly.xml
  109 + </descriptor>
  110 + </descriptors>
  111 + <appendAssemblyId>false</appendAssemblyId>
  112 + </configuration>
  113 + <executions>
  114 + <execution>
  115 + <id>make-assembly</id>
  116 + <phase>package</phase>
  117 + <goals>
  118 + <goal>attached</goal>
  119 + </goals>
  120 + </execution>
  121 + </executions>
  122 + </plugin>
  123 +
  124 + <plugin>
  125 + <groupId>org.apache.maven.plugins</groupId>
  126 + <artifactId>maven-jar-plugin</artifactId>
  127 + <version>${maven-jar-plugin.version}</version>
  128 + <configuration>
  129 + <archive>
  130 + <manifestEntries>
  131 + <Plugin-Id>${plugin.id}</Plugin-Id>
  132 + <Plugin-Class>${plugin.class}</Plugin-Class>
  133 + <Plugin-Version>${plugin.version}</Plugin-Version>
  134 + <Plugin-Provider>${plugin.provider}</Plugin-Provider>
  135 + <Plugin-Dependencies>${plugin.dependencies}</Plugin-Dependencies>
  136 + </manifestEntries>
  137 + </archive>
  138 + </configuration>
  139 + </plugin>
  140 +
  141 + <plugin>
  142 + <artifactId>maven-deploy-plugin</artifactId>
  143 + <configuration>
  144 + <skip>true</skip>
  145 + </configuration>
  146 + </plugin>
  147 + </plugins>
  148 + </build>
  149 +</project>
32 150 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/assembly/assembly.xml 0 → 100644
  1 +<!--
  2 + Describes the plugin archive
  3 +
  4 + @author Decebal Suiu
  5 + @version 1.0
  6 +-->
  7 +<assembly>
  8 + <id>plugin</id>
  9 + <formats>
  10 + <format>zip</format>
  11 + </formats>
  12 + <includeBaseDirectory>false</includeBaseDirectory>
  13 + <dependencySets>
  14 + <dependencySet>
  15 + <useProjectArtifact>false</useProjectArtifact>
  16 + <scope>runtime</scope>
  17 + <outputDirectory>lib</outputDirectory>
  18 + <includes>
  19 + <include>*:jar:*</include>
  20 + </includes>
  21 + </dependencySet>
  22 + </dependencySets>
  23 + <!--
  24 + <fileSets>
  25 + <fileSet>
  26 + <directory>target/classes</directory>
  27 + <outputDirectory>classes</outputDirectory>
  28 + </fileSet>
  29 + </fileSets>
  30 + -->
  31 + <fileSets>
  32 + <fileSet>
  33 + <directory>target/plugin-classes</directory>
  34 + <outputDirectory>classes</outputDirectory>
  35 + </fileSet>
  36 + </fileSets>
  37 +</assembly>
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTHttpPluginController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin;
  2 +
  3 +
  4 +import org.pf4j.Extension;
  5 +import org.pf4j.ExtensionPoint;
  6 +import org.springframework.web.bind.annotation.*;
  7 +
  8 +@RestController
  9 +@RequestMapping("/iot/plugin-demo")
  10 +@Extension
  11 +public class IoTHttpPluginController implements ExtensionPoint {
  12 +
  13 + @GetMapping("/greet")
  14 + public String greet() {
  15 + return "Hello from MyPlugin!";
  16 + }
  17 +
  18 + @PostMapping("/message")
  19 + public void receiveMessage(@RequestBody String message) {
  20 + System.out.println("Received message: " + message);
  21 + }
  22 +}
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IoTPlugin.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin;
  2 +
  3 +import org.pf4j.PluginWrapper;
  4 +import org.pf4j.spring.SpringPlugin;
  5 +import org.springframework.context.ApplicationContext;
  6 +import org.springframework.context.annotation.AnnotationConfigApplicationContext;
  7 +
  8 +public class IoTPlugin extends SpringPlugin {
  9 + public IoTPlugin(PluginWrapper wrapper) {
  10 + super(wrapper);
  11 + }
  12 +
  13 + @Override
  14 + public void start() {
  15 + System.out.println("IoTPlugin 启动");
  16 + }
  17 +
  18 + @Override
  19 + public void stop() {
  20 + System.out.println("IoTPlugin 停止");
  21 + super.stop(); // to close applicationContext
  22 + }
  23 +
  24 + @Override
  25 + protected ApplicationContext createApplicationContext() {
  26 + AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
  27 + applicationContext.setClassLoader(getWrapper().getPluginClassLoader());
  28 + applicationContext.register(IoTHttpPluginController.class); // 注册 IoTPluginConfig
  29 + applicationContext.refresh();
  30 + System.out.println("IoTPlugin 加载完成");
  31 + return applicationContext;
  32 + }
  33 +}
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/IotPluginConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.module.iot.plugin;
  2 +
  3 +import org.springframework.context.annotation.ComponentScan;
  4 +import org.springframework.context.annotation.Configuration;
  5 +import org.springframework.boot.web.server.WebServerFactoryCustomizer;
  6 +import org.springframework.boot.web.server.ConfigurableWebServerFactory;
  7 +import org.springframework.context.annotation.Bean;
  8 +
  9 +@Configuration
  10 +public class IoTPluginConfig {
  11 +
  12 + @Bean
  13 + public IoTHttpPluginController ioTHttpPluginController() {
  14 + return new IoTHttpPluginController();
  15 + }
  16 +}
... ...
yudao-module-iot/yudao-module-iot-plugin/src/main/java/cn/iocoder/yudao/module/iot/plugin/WelcomePlugin.java 0 → 100644
  1 +/*
  2 + * Copyright (C) 2012-present the original author or authors.
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package cn.iocoder.yudao.module.iot.plugin;
  17 +
  18 +import cn.iocoder.yudao.module.iot.api.Greeting;
  19 +import org.apache.commons.lang.StringUtils;
  20 +import org.pf4j.Extension;
  21 +import org.pf4j.Plugin;
  22 +import org.pf4j.PluginWrapper;
  23 +import org.pf4j.RuntimeMode;
  24 +
  25 +/**
  26 + * 打招呼 测试用例
  27 + */
  28 +public class WelcomePlugin extends Plugin {
  29 +
  30 + public WelcomePlugin(PluginWrapper wrapper) {
  31 + super(wrapper);
  32 + }
  33 +
  34 + @Override
  35 + public void start() {
  36 + System.out.println("WelcomePlugin.start()");
  37 + // for testing the development mode
  38 + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) {
  39 + System.out.println(StringUtils.upperCase("WelcomePlugin"));
  40 + }
  41 + }
  42 +
  43 + @Override
  44 + public void stop() {
  45 + System.out.println("WelcomePlugin.stop()");
  46 + }
  47 +
  48 + @Extension
  49 + public static class WelcomeGreeting implements Greeting {
  50 +
  51 + @Override
  52 + public String getGreeting() {
  53 + return "Welcome to PF4J";
  54 + }
  55 +
  56 + }
  57 +
  58 +}
0 59 \ No newline at end of file
... ...