|
@@ -9,25 +9,16 @@ import cn.iocoder.yudao.module.iot.dal.mysql.plugin.PluginInfoMapper;
|
|
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
|
import cn.iocoder.yudao.module.iot.enums.plugin.IotPluginStatusEnum;
|
|
import jakarta.annotation.Resource;
|
|
import jakarta.annotation.Resource;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
-import org.pf4j.PluginDescriptor;
|
|
|
|
-import org.pf4j.PluginState;
|
|
|
|
-import org.pf4j.PluginWrapper;
|
|
|
|
import org.pf4j.spring.SpringPluginManager;
|
|
import org.pf4j.spring.SpringPluginManager;
|
|
-import org.springframework.beans.factory.annotation.Value;
|
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
-import java.io.File;
|
|
|
|
-import java.io.IOException;
|
|
|
|
-import java.nio.file.*;
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
-import java.util.concurrent.Executors;
|
|
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
-import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
|
|
|
|
|
|
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_DELETE_FAILED_RUNNING;
|
|
|
|
+import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.PLUGIN_INFO_NOT_EXISTS;
|
|
|
|
|
|
/**
|
|
/**
|
|
* IoT 插件信息 Service 实现类
|
|
* IoT 插件信息 Service 实现类
|
|
@@ -41,18 +32,17 @@ public class PluginInfoServiceImpl implements PluginInfoService {
|
|
|
|
|
|
@Resource
|
|
@Resource
|
|
private PluginInfoMapper pluginInfoMapper;
|
|
private PluginInfoMapper pluginInfoMapper;
|
|
|
|
+
|
|
@Resource
|
|
@Resource
|
|
- private SpringPluginManager pluginManager;
|
|
|
|
|
|
+ private PluginInstanceService pluginInstanceService;
|
|
|
|
|
|
- @Value("${pf4j.pluginsDir}")
|
|
|
|
- private String pluginsDir;
|
|
|
|
|
|
+ @Resource
|
|
|
|
+ private SpringPluginManager pluginManager;
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
|
|
public Long createPluginInfo(PluginInfoSaveReqVO createReqVO) {
|
|
- // 插入
|
|
|
|
PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
|
|
PluginInfoDO pluginInfo = BeanUtils.toBean(createReqVO, PluginInfoDO.class);
|
|
pluginInfoMapper.insert(pluginInfo);
|
|
pluginInfoMapper.insert(pluginInfo);
|
|
- // 返回
|
|
|
|
return pluginInfo.getId();
|
|
return pluginInfo.getId();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -67,41 +57,21 @@ public class PluginInfoServiceImpl implements PluginInfoService {
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public void deletePluginInfo(Long id) {
|
|
public void deletePluginInfo(Long id) {
|
|
- // 校验存在
|
|
|
|
|
|
+ // 1.1 校验存在
|
|
PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
|
|
PluginInfoDO pluginInfoDO = validatePluginInfoExists(id);
|
|
-
|
|
|
|
- // 停止插件
|
|
|
|
|
|
+ // 1.2 停止插件
|
|
if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
|
|
if (IotPluginStatusEnum.RUNNING.getStatus().equals(pluginInfoDO.getStatus())) {
|
|
throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
|
|
throw exception(PLUGIN_INFO_DELETE_FAILED_RUNNING);
|
|
}
|
|
}
|
|
|
|
|
|
- // 卸载插件
|
|
|
|
- PluginWrapper plugin = pluginManager.getPlugin(pluginInfoDO.getPluginKey());
|
|
|
|
- if (plugin != null) {
|
|
|
|
- // 查询插件是否是启动状态
|
|
|
|
- if (plugin.getPluginState().equals(PluginState.STARTED)) {
|
|
|
|
- // 停止插件
|
|
|
|
- pluginManager.stopPlugin(plugin.getPluginId());
|
|
|
|
- }
|
|
|
|
- // 卸载插件
|
|
|
|
- pluginManager.unloadPlugin(plugin.getPluginId());
|
|
|
|
- }
|
|
|
|
|
|
+ // 2. 卸载插件
|
|
|
|
+ pluginInstanceService.stopAndUnloadPlugin(pluginInfoDO.getPluginKey());
|
|
|
|
|
|
- // 删除
|
|
|
|
- pluginInfoMapper.deleteById(id);
|
|
|
|
- // 删除插件文件
|
|
|
|
- Executors.newSingleThreadExecutor().submit(() -> {
|
|
|
|
- try {
|
|
|
|
- TimeUnit.SECONDS.sleep(1); // 等待 1 秒,避免插件未卸载完毕
|
|
|
|
- File file = new File(pluginsDir, pluginInfoDO.getFileName());
|
|
|
|
- if (file.exists() && !file.delete()) {
|
|
|
|
- log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName());
|
|
|
|
- }
|
|
|
|
- } catch (InterruptedException e) {
|
|
|
|
- log.error("[deletePluginInfo][删除插件文件({}) 失败]", pluginInfoDO.getFileName(), e);
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ // 3. 删除插件文件
|
|
|
|
+ pluginInstanceService.deletePluginFile(pluginInfoDO);
|
|
|
|
|
|
|
|
+ // 4. 删除插件信息
|
|
|
|
+ pluginInfoMapper.deleteById(id);
|
|
}
|
|
}
|
|
|
|
|
|
private PluginInfoDO validatePluginInfoExists(Long id) {
|
|
private PluginInfoDO validatePluginInfoExists(Long id) {
|
|
@@ -127,99 +97,37 @@ public class PluginInfoServiceImpl implements PluginInfoService {
|
|
// 1. 校验插件信息是否存在
|
|
// 1. 校验插件信息是否存在
|
|
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
|
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
|
|
|
|
|
- // 2. 获取插件标识
|
|
|
|
- String pluginKey = pluginInfoDo.getPluginKey();
|
|
|
|
-
|
|
|
|
- // 3. 停止并卸载旧的插件
|
|
|
|
- stopAndUnloadPlugin(pluginKey);
|
|
|
|
|
|
+ // 2. 停止并卸载旧的插件
|
|
|
|
+ pluginInstanceService.stopAndUnloadPlugin(pluginInfoDo.getPluginKey());
|
|
|
|
|
|
- // 4. 上传新的插件文件
|
|
|
|
- String pluginKeyNew = uploadAndLoadNewPlugin(file);
|
|
|
|
|
|
+ // 3 上传新的插件文件,更新插件启用状态文件
|
|
|
|
+ String pluginKeyNew = pluginInstanceService.uploadAndLoadNewPlugin(file);
|
|
|
|
|
|
- // 5. 更新插件启用状态文件
|
|
|
|
- updatePluginStatusFile(pluginKeyNew, false);
|
|
|
|
-
|
|
|
|
- // 6. 更新插件信息
|
|
|
|
|
|
+ // 4. 更新插件信息
|
|
updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
|
|
updatePluginInfo(pluginInfoDo, pluginKeyNew, file);
|
|
}
|
|
}
|
|
|
|
|
|
- // 停止并卸载旧的插件
|
|
|
|
- private void stopAndUnloadPlugin(String pluginKey) {
|
|
|
|
- PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
|
|
|
|
- if (plugin != null) {
|
|
|
|
- if (plugin.getPluginState().equals(PluginState.STARTED)) {
|
|
|
|
- pluginManager.stopPlugin(pluginKey); // 停止插件
|
|
|
|
- }
|
|
|
|
- pluginManager.unloadPlugin(pluginKey); // 卸载插件
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 上传并加载新的插件文件
|
|
|
|
- private String uploadAndLoadNewPlugin(MultipartFile file) {
|
|
|
|
- Path pluginsPath = Paths.get(pluginsDir);
|
|
|
|
- try {
|
|
|
|
- if (!Files.exists(pluginsPath)) {
|
|
|
|
- Files.createDirectories(pluginsPath); // 创建插件目录
|
|
|
|
- }
|
|
|
|
- String filename = file.getOriginalFilename();
|
|
|
|
- if (filename != null) {
|
|
|
|
- Path jarPath = pluginsPath.resolve(filename);
|
|
|
|
- Files.copy(file.getInputStream(), jarPath, StandardCopyOption.REPLACE_EXISTING); // 保存上传的 JAR 文件
|
|
|
|
- return pluginManager.loadPlugin(jarPath.toAbsolutePath()); // 加载插件
|
|
|
|
- } else {
|
|
|
|
- throw exception(PLUGIN_INSTALL_FAILED);
|
|
|
|
- }
|
|
|
|
- } catch (Exception e) {
|
|
|
|
- throw exception(PLUGIN_INSTALL_FAILED);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 更新插件状态文件
|
|
|
|
- private void updatePluginStatusFile(String pluginKeyNew, boolean isEnabled) {
|
|
|
|
- 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(PLUGIN_INSTALL_FAILED);
|
|
|
|
- }
|
|
|
|
- String pluginInfo = pluginKeyNew + "@" + pluginWrapper.getDescriptor().getVersion();
|
|
|
|
- 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(pluginInfo)) {
|
|
|
|
- targetLines.add(pluginInfo);
|
|
|
|
- Files.write(targetFilePath, targetLines, StandardOpenOption.CREATE,
|
|
|
|
- StandardOpenOption.TRUNCATE_EXISTING);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (oppositeLines.contains(pluginInfo)) {
|
|
|
|
- oppositeLines.remove(pluginInfo);
|
|
|
|
- Files.write(oppositeFilePath, oppositeLines, StandardOpenOption.CREATE,
|
|
|
|
- StandardOpenOption.TRUNCATE_EXISTING);
|
|
|
|
- }
|
|
|
|
- } catch (IOException e) {
|
|
|
|
- throw exception(PLUGIN_INSTALL_FAILED);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 更新插件信息
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 更新插件信息
|
|
|
|
+ *
|
|
|
|
+ * @param pluginInfoDo 插件信息
|
|
|
|
+ * @param pluginKeyNew 插件标识符
|
|
|
|
+ * @param file 文件
|
|
|
|
+ */
|
|
private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) {
|
|
private void updatePluginInfo(PluginInfoDO pluginInfoDo, String pluginKeyNew, MultipartFile file) {
|
|
- pluginInfoDo.setPluginKey(pluginKeyNew);
|
|
|
|
- pluginInfoDo.setStatus(IotPluginStatusEnum.STOPPED.getStatus());
|
|
|
|
- pluginInfoDo.setFileName(file.getOriginalFilename());
|
|
|
|
- pluginInfoDo.setScript("");
|
|
|
|
-
|
|
|
|
- PluginDescriptor pluginDescriptor = pluginManager.getPlugin(pluginKeyNew).getDescriptor();
|
|
|
|
- pluginInfoDo.setConfigSchema(pluginDescriptor.getPluginDescription());
|
|
|
|
- pluginInfoDo.setVersion(pluginDescriptor.getVersion());
|
|
|
|
- pluginInfoDo.setDescription(pluginDescriptor.getPluginDescription());
|
|
|
|
- pluginInfoMapper.updateById(pluginInfoDo);
|
|
|
|
|
|
+ // 创建新的插件信息对象并链式设置属性
|
|
|
|
+ PluginInfoDO updatedPluginInfo = new PluginInfoDO()
|
|
|
|
+ .setId(pluginInfoDo.getId())
|
|
|
|
+ .setPluginKey(pluginKeyNew)
|
|
|
|
+ .setStatus(IotPluginStatusEnum.STOPPED.getStatus())
|
|
|
|
+ .setFileName(file.getOriginalFilename())
|
|
|
|
+ .setScript("")
|
|
|
|
+ .setConfigSchema(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription())
|
|
|
|
+ .setVersion(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getVersion())
|
|
|
|
+ .setDescription(pluginManager.getPlugin(pluginKeyNew).getDescriptor().getPluginDescription());
|
|
|
|
+
|
|
|
|
+ // 执行更新
|
|
|
|
+ pluginInfoMapper.updateById(updatedPluginInfo);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -227,44 +135,24 @@ public class PluginInfoServiceImpl implements PluginInfoService {
|
|
// 1. 校验插件信息是否存在
|
|
// 1. 校验插件信息是否存在
|
|
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
|
PluginInfoDO pluginInfoDo = validatePluginInfoExists(id);
|
|
|
|
|
|
- // 2. 校验插件状态是否有效
|
|
|
|
- if (!IotPluginStatusEnum.contains(status)) {
|
|
|
|
- throw exception(PLUGIN_STATUS_INVALID);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 3. 获取插件标识和插件实例
|
|
|
|
- String pluginKey = pluginInfoDo.getPluginKey();
|
|
|
|
- PluginWrapper plugin = pluginManager.getPlugin(pluginKey);
|
|
|
|
-
|
|
|
|
- // 4. 根据状态更新插件
|
|
|
|
- if (plugin != null) {
|
|
|
|
- // 4.1 如果目标状态是运行且插件未启动,则启动插件
|
|
|
|
- if (status.equals(IotPluginStatusEnum.RUNNING.getStatus())
|
|
|
|
- && plugin.getPluginState() != PluginState.STARTED) {
|
|
|
|
- pluginManager.startPlugin(pluginKey);
|
|
|
|
- updatePluginStatusFile(pluginKey, true); // 更新插件状态文件为启用
|
|
|
|
- }
|
|
|
|
- // 4.2 如果目标状态是停止且插件已启动,则停止插件
|
|
|
|
- else if (status.equals(IotPluginStatusEnum.STOPPED.getStatus())
|
|
|
|
- && plugin.getPluginState() == PluginState.STARTED) {
|
|
|
|
- pluginManager.stopPlugin(pluginKey);
|
|
|
|
- updatePluginStatusFile(pluginKey, false); // 更新插件状态文件为禁用
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- // 5. 插件不存在且状态为停止,抛出异常
|
|
|
|
- if (IotPluginStatusEnum.STOPPED.getStatus().equals(pluginInfoDo.getStatus())) {
|
|
|
|
- throw exception(PLUGIN_STATUS_INVALID);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ // 2. 更新插件状态
|
|
|
|
+ pluginInstanceService.updatePluginStatus(pluginInfoDo, status);
|
|
|
|
|
|
- // 6. 更新数据库中的插件状态
|
|
|
|
- pluginInfoDo.setStatus(status);
|
|
|
|
- pluginInfoMapper.updateById(pluginInfoDo);
|
|
|
|
|
|
+ // 3. 更新数据库中的插件状态
|
|
|
|
+ PluginInfoDO updatedPluginInfo = new PluginInfoDO();
|
|
|
|
+ updatedPluginInfo.setId(id);
|
|
|
|
+ updatedPluginInfo.setStatus(status);
|
|
|
|
+ pluginInfoMapper.updateById(updatedPluginInfo);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
public List<PluginInfoDO> getPluginInfoList() {
|
|
public List<PluginInfoDO> getPluginInfoList() {
|
|
- return pluginInfoMapper.selectList(null);
|
|
|
|
|
|
+ return pluginInfoMapper.selectList();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public List<PluginInfoDO> getPluginInfoListByStatus(Integer status) {
|
|
|
|
+ return pluginInfoMapper.selectListByStatus(status);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|