Skip to content

Commit 291d8da

Browse files
authored
Restore context ClassLoader management during plugin lifecycle (#7746)
#### What type of PR is this? /kind improvement /area plugin /milestone 2.21.x #### What this PR does / why we need it: This PR restores context ClassLoader mangement during plugin lifecycle to prevent resource leak. Meanwhile, it also fixes Class initialization error when starting plugins. Superseds #7725 #### Does this PR introduce a user-facing change? ```release-note None ```
1 parent b822de2 commit 291d8da

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

application/src/main/java/run/halo/app/plugin/DefaultPluginApplicationContextFactory.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,6 @@ public ApplicationContext create(String pluginId) {
7070
var pluginWrapper = pluginManager.getPlugin(pluginId);
7171
var classLoader = pluginWrapper.getPluginClassLoader();
7272

73-
// Set the context ClassLoader to the plugin ClassLoader to ensure that
74-
// any class loading operations performed by the context (e.g., initializing
75-
// bean definitions, loading class resources during static initialization)
76-
// use the correct ClassLoader.
77-
Thread.currentThread().setContextClassLoader(classLoader);
7873

7974
/*
8075
* Manually creating a BeanFactory and setting the plugin's ClassLoader is necessary
@@ -189,7 +184,18 @@ public ApplicationContext create(String pluginId) {
189184
log.debug("Refreshing application context for plugin {}", pluginId);
190185
sw.start("Refresh");
191186

192-
context.refresh();
187+
// Set the context ClassLoader to the plugin ClassLoader to ensure that
188+
// any class loading operations performed by the context (e.g., initializing
189+
// bean definitions, loading class resources during static initialization)
190+
// use the correct ClassLoader.
191+
var previous = Thread.currentThread().getContextClassLoader();
192+
try {
193+
Thread.currentThread().setContextClassLoader(classLoader);
194+
context.refresh();
195+
} finally {
196+
// reset the class loader to previous one to prevent resource leak
197+
Thread.currentThread().setContextClassLoader(previous);
198+
}
193199
sw.stop();
194200
log.debug("Refreshed application context for plugin {}", pluginId);
195201
if (log.isDebugEnabled()) {

application/src/main/java/run/halo/app/plugin/SpringPlugin.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ public SpringPlugin(PluginApplicationContextFactory contextFactory,
2727
public void start() {
2828
log.info("Preparing starting plugin {}", pluginContext.getName());
2929
var pluginId = pluginContext.getName();
30+
var previous = Thread.currentThread().getContextClassLoader();
3031
try {
3132
// initialize context
3233
this.context = contextFactory.create(pluginId);
34+
Thread.currentThread().setContextClassLoader(this.context.getClassLoader());
3335
log.info("Application context {} for plugin {} is created", this.context, pluginId);
3436

3537
var pluginOpt = context.getBeanProvider(Plugin.class)
@@ -55,13 +57,17 @@ public void start() {
5557
this.stop();
5658
// propagate exception to invoker.
5759
throw t;
60+
} finally {
61+
Thread.currentThread().setContextClassLoader(previous);
5862
}
5963
}
6064

6165
@Override
6266
public void stop() {
67+
var previous = Thread.currentThread().getContextClassLoader();
6368
try {
6469
if (context != null) {
70+
Thread.currentThread().setContextClassLoader(context.getClassLoader());
6571
log.info("Before publishing plugin stopping event for plugin {}",
6672
pluginContext.getName());
6773
context.publishEvent(new SpringPluginStoppingEvent(this, this));
@@ -74,6 +80,7 @@ public void stop() {
7480
log.info("Stopped {} for plugin {}", this.delegate, pluginContext.getName());
7581
}
7682
} finally {
83+
Thread.currentThread().setContextClassLoader(previous);
7784
if (context instanceof ConfigurableApplicationContext configurableContext) {
7885
log.info("Closing plugin context for plugin {}", pluginContext.getName());
7986
configurableContext.close();

0 commit comments

Comments
 (0)