refactor: refresh the plugin wrapper when starting the plugin (#4023)

#### 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 对象缓存未清除的问题
```
pull/4081/head
guqing 2023-06-14 18:08:14 +08:00 committed by GitHub
parent 997a73d81b
commit 6d251a7f58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 8 deletions

View File

@ -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() {
}
}

View File

@ -289,8 +289,7 @@ public class PluginReconciler implements Reconciler<Request> {
void stateTransition(String name, Function<PluginState, Boolean> 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<Request> {
break;
}
// update current state
currentState = pluginWrapper.getPluginState();
currentState = getPluginWrapper(name).getPluginState();
} catch (Throwable e) {
persistenceFailureStatus(name, e);
throw e;

View File

@ -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;
}

View File

@ -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
}
}
/**
* <p>Refresh plugin wrapper by plugin name.</p>
*
* <p>It will be create a new plugin wrapper and replace old plugin wrapper to clean
* {@link PluginWrapper#getPlugin()} cache object.</p>
*
* @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();