From 0fd023b8f68ffead65b7fe9be63ea17150d5cda2 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Thu, 2 Feb 2023 10:44:11 +0800 Subject: [PATCH] fix: plugin disappeared after plugin with same name reinstalled (#3198) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind bug /area core /milestone 2.2.x #### What this PR does / why we need it: 修复重复安装插件时 JAR 文件被删除的问题 需要注意的场景: prod 模式下,假如安装一个 sitemap 插件,版本为 1.0.0 ,而此时 plugins 目录已经存在同名文件 sitemap-1.0.0.jar 文件但 sitemap 的 plugin.yaml 没有被持久化过,则能正常安装,旧的 JAR 文件被覆盖。 see #3159 for more details #### Which issue(s) this PR fixes: Fixes #3159 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note 修复重复安装插件时 JAR 文件被删除的问题 ``` --- .../extension/endpoint/PluginEndpoint.java | 28 ++++++++++--------- .../PluginInstallationException.java | 19 +++++++++++++ .../resources/config/i18n/messages.properties | 2 ++ .../config/i18n/messages_zh.properties | 2 ++ 4 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 src/main/java/run/halo/app/infra/exception/PluginInstallationException.java diff --git a/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java b/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java index 164792e6a..38e8a49c9 100644 --- a/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java +++ b/src/main/java/run/halo/app/core/extension/endpoint/PluginEndpoint.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.ArrayList; import java.util.Comparator; @@ -61,6 +62,7 @@ import run.halo.app.extension.ConfigMap; import run.halo.app.extension.ReactiveExtensionClient; import run.halo.app.extension.router.IListRequest.QueryListRequest; import run.halo.app.infra.SystemVersionSupplier; +import run.halo.app.infra.exception.PluginInstallationException; import run.halo.app.infra.exception.UnsatisfiedAttributeValueException; import run.halo.app.infra.utils.FileUtils; import run.halo.app.infra.utils.VersionUtils; @@ -455,25 +457,25 @@ public class PluginEndpoint implements CustomEndpoint { // Disable auto enable during installation plugin.getSpec().setEnabled(false); return client.fetch(Plugin.class, plugin.getMetadata().getName()) - .switchIfEmpty(Mono.defer(() -> client.create(plugin))) + .doOnNext(oldPlugin -> { + String pluginName = oldPlugin.getMetadata().getName(); + throw new PluginInstallationException( + "Plugin [" + pluginName + "] already installed", + "problemDetail.plugin.install.alreadyInstalled", + new Object[] {pluginName}); + }) + .then(client.create(plugin)) .publishOn(Schedulers.boundedElastic()) - .map(created -> { + .doOnNext(created -> { String fileName = created.generateFileName(); var pluginRoot = Paths.get(pluginProperties.getPluginsRoot()); createDirectoriesIfNotExists(pluginRoot); Path pluginFilePath = pluginRoot.resolve(fileName); - if (Files.exists(pluginFilePath)) { - throw new IllegalArgumentException( - "Plugin already installed : " + pluginFilePath); - } - FileUtils.copy(tempJarFilePath, pluginFilePath); - return created; + // move the plugin jar file to the plugin root + // replace the old plugin jar file if exists + FileUtils.copy(tempJarFilePath, pluginFilePath, + StandardCopyOption.REPLACE_EXISTING); }) - .onErrorResume( - error -> client.fetch(Plugin.class, plugin.getMetadata().getName()) - .flatMap(client::delete) - .then(Mono.error(error)) - ) .doFinally(signalType -> { try { Files.deleteIfExists(tempJarFilePath); diff --git a/src/main/java/run/halo/app/infra/exception/PluginInstallationException.java b/src/main/java/run/halo/app/infra/exception/PluginInstallationException.java new file mode 100644 index 000000000..e6100f5c1 --- /dev/null +++ b/src/main/java/run/halo/app/infra/exception/PluginInstallationException.java @@ -0,0 +1,19 @@ +package run.halo.app.infra.exception; + +import jakarta.validation.constraints.Null; +import org.springframework.lang.Nullable; +import org.springframework.web.server.ServerWebInputException; + +/** + * {@link ServerWebInputException} subclass that indicates plugin installation failure. + * + * @author guqing + * @since 2.0.0 + */ +public class PluginInstallationException extends ServerWebInputException { + + public PluginInstallationException(String reason, @Nullable String messageDetailCode, + @Null Object[] messageDetailArguments) { + super(reason, null, null, messageDetailCode, messageDetailArguments); + } +} diff --git a/src/main/resources/config/i18n/messages.properties b/src/main/resources/config/i18n/messages.properties index 89463b3d7..f0dd3280c 100644 --- a/src/main/resources/config/i18n/messages.properties +++ b/src/main/resources/config/i18n/messages.properties @@ -14,6 +14,7 @@ problemDetail.title.run.halo.app.infra.exception.AccessDeniedException=Access De problemDetail.title.reactor.core.Exceptions.RetryExhaustedException=Retry Exhausted problemDetail.title.run.halo.app.infra.exception.ThemeInstallationException=Theme Install Error problemDetail.title.run.halo.app.infra.exception.ThemeUpgradeException=Theme Upgrade Error +problemDetail.title.run.halo.app.infra.exception.PluginInstallationException=Plugin Install Error # Detail definitions problemDetail.org.springframework.web.server.UnsupportedMediaTypeStatusException=Content type {0} is not supported. Supported media types: {1}. @@ -38,3 +39,4 @@ problemDetail.theme.version.unsatisfied.requires=The theme requires a minimum sy problemDetail.directoryTraversal=Directory traversal detected. Base path is {0}, but real path is {1}. problemDetail.plugin.version.unsatisfied.requires=Plugin requires a minimum system version of {0}, but the current version is {1}. +problemDetail.plugin.install.alreadyInstalled=Plugin {0} already installed. \ No newline at end of file diff --git a/src/main/resources/config/i18n/messages_zh.properties b/src/main/resources/config/i18n/messages_zh.properties index 2d7c204e3..a35ca8f25 100644 --- a/src/main/resources/config/i18n/messages_zh.properties +++ b/src/main/resources/config/i18n/messages_zh.properties @@ -1,9 +1,11 @@ problemDetail.title.org.springframework.web.server.ServerWebInputException=请求参数有误 problemDetail.title.run.halo.app.infra.exception.UnsatisfiedAttributeValueException=请求参数属性值不满足要求 +problemDetail.title.run.halo.app.infra.exception.PluginInstallationException=插件安装失败 problemDetail.title.run.halo.app.infra.exception.AttachmentAlreadyExistsException=附件已存在 problemDetail.run.halo.app.infra.exception.AttachmentAlreadyExistsException=文件 {0} 已存在,建议更名后重试。 problemDetail.plugin.version.unsatisfied.requires=插件要求一个最小的系统版本为 {0}, 但当前版本为 {1}。 +problemDetail.plugin.install.alreadyInstalled=插件 {0} 已经被安装。 problemDetail.theme.version.unsatisfied.requires=主题要求一个最小的系统版本为 {0}, 但当前版本为 {1}。 \ No newline at end of file