Set context class loader for plugin initialization (#63185)
Plugins are loaded in isolated child class loaders of the root class loader. However, some libraries depend on the context class loader being set. This commit sets the context class loader for the duration of calling each plugins constructor. relates #52320 Co-authored-by: Ryan Ernst <ryan@iernst.net>
This commit is contained in:
parent
f17ca18dfa
commit
dbcbdcc029
|
@ -53,6 +53,8 @@ import java.net.URLClassLoader;
|
||||||
import java.nio.file.DirectoryStream;
|
import java.nio.file.DirectoryStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -637,15 +639,31 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
||||||
// reload SPI with any new services from the plugin
|
// reload SPI with any new services from the plugin
|
||||||
reloadLuceneSPI(loader);
|
reloadLuceneSPI(loader);
|
||||||
|
|
||||||
Class<? extends Plugin> pluginClass = loadPluginClass(bundle.plugin.getClassname(), loader);
|
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||||
if (loader != pluginClass.getClassLoader()) {
|
try {
|
||||||
throw new IllegalStateException("Plugin [" + name + "] must reference a class loader local Plugin class ["
|
// Set context class loader to plugin's class loader so that plugins
|
||||||
+ bundle.plugin.getClassname()
|
// that have dependencies with their own SPI endpoints have a chance to load
|
||||||
+ "] (class loader [" + pluginClass.getClassLoader() + "])");
|
// and initialize them appropriately.
|
||||||
|
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||||
|
Thread.currentThread().setContextClassLoader(loader);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
Class<? extends Plugin> pluginClass = loadPluginClass(bundle.plugin.getClassname(), loader);
|
||||||
|
if (loader != pluginClass.getClassLoader()) {
|
||||||
|
throw new IllegalStateException("Plugin [" + name + "] must reference a class loader local Plugin class ["
|
||||||
|
+ bundle.plugin.getClassname()
|
||||||
|
+ "] (class loader [" + pluginClass.getClassLoader() + "])");
|
||||||
|
}
|
||||||
|
Plugin plugin = loadPlugin(pluginClass, settings, configPath);
|
||||||
|
loaded.put(name, plugin);
|
||||||
|
return plugin;
|
||||||
|
} finally {
|
||||||
|
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||||
|
Thread.currentThread().setContextClassLoader(cl);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Plugin plugin = loadPlugin(pluginClass, settings, configPath);
|
|
||||||
loaded.put(name, plugin);
|
|
||||||
return plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -668,7 +686,7 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
||||||
|
|
||||||
private Class<? extends Plugin> loadPluginClass(String className, ClassLoader loader) {
|
private Class<? extends Plugin> loadPluginClass(String className, ClassLoader loader) {
|
||||||
try {
|
try {
|
||||||
return loader.loadClass(className).asSubclass(Plugin.class);
|
return Class.forName(className, false, loader).asSubclass(Plugin.class);
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
throw new ElasticsearchException("Could not find plugin class [" + className + "]", e);
|
throw new ElasticsearchException("Could not find plugin class [" + className + "]", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,13 @@ grant codeBase "${codebase.elasticsearch-secure-sm}" {
|
||||||
permission java.security.AllPermission;
|
permission java.security.AllPermission;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//// Elasticsearch core:
|
||||||
|
//// These are only allowed inside the server jar, not in plugins
|
||||||
|
grant codeBase "${codebase.elasticsearch}" {
|
||||||
|
// needed for loading plugins which may expect the context class loader to be set
|
||||||
|
permission java.lang.RuntimePermission "setContextClassLoader";
|
||||||
|
};
|
||||||
|
|
||||||
//// Very special jar permissions:
|
//// Very special jar permissions:
|
||||||
//// These are dangerous permissions that we don't want to grant to everything.
|
//// These are dangerous permissions that we don't want to grant to everything.
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,8 @@ public class BootstrapForTesting {
|
||||||
|
|
||||||
// read test-framework permissions
|
// read test-framework permissions
|
||||||
Map<String, URL> codebases = Security.getCodebaseJarMap(JarHell.parseClassPath());
|
Map<String, URL> codebases = Security.getCodebaseJarMap(JarHell.parseClassPath());
|
||||||
|
// when testing server, the main elasticsearch code is not yet in a jar, so we need to manually add it
|
||||||
|
addClassCodebase(codebases,"elasticsearch", "org.elasticsearch.plugins.PluginsService");
|
||||||
if (System.getProperty("tests.gradle") == null) {
|
if (System.getProperty("tests.gradle") == null) {
|
||||||
// intellij and eclipse don't package our internal libs, so we need to set the codebases for them manually
|
// intellij and eclipse don't package our internal libs, so we need to set the codebases for them manually
|
||||||
addClassCodebase(codebases,"plugin-classloader", "org.elasticsearch.plugins.ExtendedPluginsClassLoader");
|
addClassCodebase(codebases,"plugin-classloader", "org.elasticsearch.plugins.ExtendedPluginsClassLoader");
|
||||||
|
|
Loading…
Reference in New Issue