From 15433b56cf5a3d50c9676bc4efc29e4500ca5a72 Mon Sep 17 00:00:00 2001 From: Halo Dev Bot <87291978+halo-dev-bot@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:20:14 +0800 Subject: [PATCH] [release-2.6] refactor: refresh the plugin wrapper when starting the plugin (#4078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an automated cherry-pick of #4023 /assign ruibaby ```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 ed186f24bd..f513514fec 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 @@ import lombok.extern.slf4j.Slf4j; import org.pf4j.Plugin; -import org.pf4j.PluginManager; import org.pf4j.PluginWrapper; /** @@ -15,12 +14,12 @@ @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 b8cedd3155..e4cb26bfa4 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 @@ void stopAction(String name) { 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 @@ void stateTransition(String name, Function stateAction, 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 805f7e99fa..c769ab8e29 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 Plugin create(PluginWrapper pluginWrapper) { "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 b748e04c79..7abcb1d5f4 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 boolean validatePluginVersion(PluginWrapper pluginWrapper) { 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 @@ private void removePluginComponentsCache(String pluginId) { } } + /** + *

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();