fix: failed to load plugin when add fixedPluginPath dynamically in development mode (#2941)

#### What type of PR is this?
/kind bug
/area core

#### What this PR does / why we need it:
修复插件开发模式下后续增加的 fixedPluginPath 项无法被加载的问题
- 目前启动时会加载 pluginRepository 的所有 path,fixedPluginPath 被 DefaultDevelopmentPluginRepository 管理,所以在遍历 fixedPluginPath 加载时可能已经被加载过,需要判断是否被加载过,但即使被加载过也不能跳过而要继续执行创建/更新 plugin.yaml 资源的逻辑
- 创建/更新 plugin.yaml 时需要使用重试机制防止因为乐观锁冲突导致 Halo 无法启动

see #2939 for more detail
#### Which issue(s) this PR fixes:

Fixes #2939

#### Special notes for your reviewer:
/cc @halo-dev/sig-halo 
#### Does this PR introduce a user-facing change?

```release-note
修复插件开发模式下后续增加的 fixedPluginPath 项无法被加载的问题
```
pull/2923/head
guqing 2022-12-14 15:49:23 +08:00 committed by GitHub
parent df0e6cff49
commit 07f5b0dbcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 24 additions and 17 deletions

View File

@ -1,14 +1,18 @@
package run.halo.app.plugin; package run.halo.app.plugin;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Duration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginWrapper; import org.pf4j.PluginWrapper;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.lang.NonNull; import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;
import run.halo.app.core.extension.Plugin; import run.halo.app.core.extension.Plugin;
import run.halo.app.extension.ExtensionClient; import run.halo.app.extension.ReactiveExtensionClient;
/** /**
* @author guqing * @author guqing
@ -22,10 +26,10 @@ public class PluginDevelopmentInitializer implements ApplicationListener<Applica
private final PluginProperties pluginProperties; private final PluginProperties pluginProperties;
private final ExtensionClient extensionClient; private final ReactiveExtensionClient extensionClient;
public PluginDevelopmentInitializer(HaloPluginManager haloPluginManager, public PluginDevelopmentInitializer(HaloPluginManager haloPluginManager,
PluginProperties pluginProperties, ExtensionClient extensionClient) { PluginProperties pluginProperties, ReactiveExtensionClient extensionClient) {
this.haloPluginManager = haloPluginManager; this.haloPluginManager = haloPluginManager;
this.pluginProperties = pluginProperties; this.pluginProperties = pluginProperties;
this.extensionClient = extensionClient; this.extensionClient = extensionClient;
@ -41,19 +45,18 @@ public class PluginDevelopmentInitializer implements ApplicationListener<Applica
private void createFixedPluginIfNecessary(HaloPluginManager pluginManager) { private void createFixedPluginIfNecessary(HaloPluginManager pluginManager) {
for (Path path : pluginProperties.getFixedPluginPath()) { for (Path path : pluginProperties.getFixedPluginPath()) {
// already loaded
if (idForPath(path) != null) { // Already loaded do not load again
continue; String pluginId = idForPath(path);
}
// for issue #2901 // for issue #2901
String pluginId; if (pluginId == null) {
try {
try { pluginId = pluginManager.loadPlugin(path);
pluginId = pluginManager.loadPlugin(path); } catch (Exception e) {
} catch (Exception e) { log.warn(e.getMessage(), e);
log.warn(e.getMessage(), e); continue;
continue; }
} }
PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId);
@ -62,10 +65,14 @@ public class PluginDevelopmentInitializer implements ApplicationListener<Applica
} }
Plugin plugin = new YamlPluginFinder().find(pluginWrapper.getPluginPath()); Plugin plugin = new YamlPluginFinder().find(pluginWrapper.getPluginPath());
extensionClient.fetch(Plugin.class, plugin.getMetadata().getName()) extensionClient.fetch(Plugin.class, plugin.getMetadata().getName())
.ifPresentOrElse(persistent -> { .flatMap(persistent -> {
plugin.getMetadata().setVersion(persistent.getMetadata().getVersion()); plugin.getMetadata().setVersion(persistent.getMetadata().getVersion());
extensionClient.update(plugin); return extensionClient.update(plugin);
}, () -> extensionClient.create(plugin)); })
.switchIfEmpty(Mono.defer(() -> extensionClient.create(plugin)))
.retryWhen(Retry.backoff(10, Duration.ofMillis(100))
.filter(t -> t instanceof OptimisticLockingFailureException))
.block();
} }
} }