From 6d251a7f581b8d13816f6b613a6def57f763378b Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:08:14 +0800 Subject: [PATCH] refactor: refresh the plugin wrapper when starting the plugin (#4023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /kind bug /area core /area plugin /milestone 2.6.x #### What this PR does / why we need it: 修复插件重启后 MainClass 对象缓存未清除的问题 how to test it? 下载此插件: [plugin-starter-1.0.0-SNAPSHOT.jar.zip](https://github.com/halo-dev/halo/files/11620847/plugin-starter-1.0.0-SNAPSHOT.jar.zip) 安装并启动插件,会看到类似如下日志: ``` 测试从 [/var/folders/1z/3hlt62691tx63dxx6y0mryw00000gn/T/halo-plugin3709893537121269748.txt] 文件读取内容 插件启动成功! ``` 修改日志中给出的文件的内容后 reload 插件会看到`插件启动成功!` 后会跟随最新的文件内容则表示 MainClass 是最新的状态没有缓存。 #### Which issue(s) this PR fixes: Fixes #4016 #### Does this PR introduce a user-facing change? ```release-note 修复插件重启后 MainClass 对象缓存未清除的问题 ``` --- .../java/run/halo/app/plugin/BasePlugin.java | 5 +-- .../reconciler/PluginReconciler.java | 5 +-- .../halo/app/plugin/BasePluginFactory.java | 2 +- .../halo/app/plugin/HaloPluginManager.java | 37 ++++++++++++++++++- 4 files changed, 41 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/run/halo/app/plugin/BasePlugin.java b/api/src/main/java/run/halo/app/plugin/BasePlugin.java index ed186f24b..f513514fe 100644 --- a/api/src/main/java/run/halo/app/plugin/BasePlugin.java +++ b/api/src/main/java/run/halo/app/plugin/BasePlugin.java @@ -2,7 +2,6 @@ package run.halo.app.plugin; import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; -import org.pf4j.PluginManager; import org.pf4j.PluginWrapper; /** @@ -15,12 +14,12 @@ import org.pf4j.PluginWrapper; @Slf4j public class BasePlugin extends Plugin { + @Deprecated public BasePlugin(PluginWrapper wrapper) { super(wrapper); log.info("Initialized plugin {}", wrapper.getPluginId()); } - private PluginManager getPluginManager() { - return getWrapper().getPluginManager(); + public BasePlugin() { } } diff --git a/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java b/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java index b8cedd315..e4cb26bfa 100644 --- a/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java +++ b/application/src/main/java/run/halo/app/core/extension/reconciler/PluginReconciler.java @@ -289,8 +289,7 @@ public class PluginReconciler implements Reconciler { void stateTransition(String name, Function stateAction, PluginState desiredState) { - PluginWrapper pluginWrapper = getPluginWrapper(name); - PluginState currentState = pluginWrapper.getPluginState(); + PluginState currentState = getPluginWrapper(name).getPluginState(); int maxRetries = PluginState.values().length; for (int i = 0; i < maxRetries && currentState != desiredState; i++) { try { @@ -303,7 +302,7 @@ public class PluginReconciler implements Reconciler { break; } // update current state - currentState = pluginWrapper.getPluginState(); + currentState = getPluginWrapper(name).getPluginState(); } catch (Throwable e) { persistenceFailureStatus(name, e); throw e; diff --git a/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java b/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java index 805f7e99f..c769ab8e2 100644 --- a/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java +++ b/application/src/main/java/run/halo/app/plugin/BasePluginFactory.java @@ -29,7 +29,7 @@ public class BasePluginFactory implements PluginFactory { "No bean named 'basePlugin' found in the context create default instance"); DefaultListableBeanFactory beanFactory = context.getDefaultListableBeanFactory(); - BasePlugin pluginInstance = new BasePlugin(pluginWrapper); + BasePlugin pluginInstance = new BasePlugin(); beanFactory.registerSingleton(Plugin.class.getName(), pluginInstance); return pluginInstance; } diff --git a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java index b748e04c7..7abcb1d5f 100644 --- a/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java +++ b/application/src/main/java/run/halo/app/plugin/HaloPluginManager.java @@ -229,7 +229,11 @@ public class HaloPluginManager extends DefaultPluginManager private PluginState doStartPlugin(String pluginId) { checkPluginId(pluginId); - PluginWrapper pluginWrapper = getPlugin(pluginId); + // refresh plugin to ensure cache object of PluginWrapper.plugin is up-to-date + // see gh-4016 to know why we need this + // TODO if has a better way to do this? + PluginWrapper pluginWrapper = refreshPluginWrapper(pluginId); + checkExtensionFinderReady(pluginWrapper); PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); @@ -423,6 +427,37 @@ public class HaloPluginManager extends DefaultPluginManager } } + /** + *

Refresh plugin wrapper by plugin name.

+ * + *

It will be create a new plugin wrapper and replace old plugin wrapper to clean + * {@link PluginWrapper#getPlugin()} cache object.

+ * + * @param pluginName plugin name + * @return refreshed plugin wrapper instance, plugin cache object will be null + * @throws IllegalArgumentException if plugin not found + */ + protected synchronized PluginWrapper refreshPluginWrapper(String pluginName) { + checkPluginId(pluginName); + // get old plugin wrapper + PluginWrapper pluginWrapper = getPlugin(pluginName); + // create new plugin wrapper to replace old plugin wrapper + PluginWrapper refreshed = copyPluginWrapper(pluginWrapper); + this.plugins.put(pluginName, refreshed); + return refreshed; + } + + @NonNull + PluginWrapper copyPluginWrapper(@NonNull PluginWrapper pluginWrapper) { + PluginWrapper refreshed = + createPluginWrapper(pluginWrapper.getDescriptor(), pluginWrapper.getPluginPath(), + pluginWrapper.getPluginClassLoader()); + refreshed.setPluginFactory(getPluginFactory()); + refreshed.setPluginState(pluginWrapper.getPluginState()); + refreshed.setFailedException(pluginWrapper.getFailedException()); + return refreshed; + } + @Override public void destroy() throws Exception { stopPlugins();