|
@@ -14,7 +14,6 @@ import org.apache.lucene.codecs.Codec;
|
|
|
import org.apache.lucene.codecs.DocValuesFormat;
|
|
|
import org.apache.lucene.codecs.PostingsFormat;
|
|
|
import org.elasticsearch.ElasticsearchException;
|
|
|
-import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.action.admin.cluster.node.info.PluginsAndModules;
|
|
|
import org.elasticsearch.common.settings.Setting;
|
|
|
import org.elasticsearch.common.settings.Setting.Property;
|
|
@@ -23,6 +22,7 @@ import org.elasticsearch.common.util.set.Sets;
|
|
|
import org.elasticsearch.core.PathUtils;
|
|
|
import org.elasticsearch.core.SuppressForbidden;
|
|
|
import org.elasticsearch.core.Tuple;
|
|
|
+import org.elasticsearch.env.Environment;
|
|
|
import org.elasticsearch.jdk.JarHell;
|
|
|
import org.elasticsearch.node.ReportingService;
|
|
|
import org.elasticsearch.plugins.spi.SPIClassIterator;
|
|
@@ -105,77 +105,38 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
|
|
|
/**
|
|
|
* Constructs a new PluginService
|
|
|
- * @param settings The settings of the system
|
|
|
+ *
|
|
|
+ * @param settings The settings of the system
|
|
|
* @param modulesDirectory The directory modules exist in, or null if modules should not be loaded from the filesystem
|
|
|
* @param pluginsDirectory The directory plugins exist in, or null if plugins should not be loaded from the filesystem
|
|
|
- * @param classpathPlugins Plugins that exist in the classpath which should be loaded
|
|
|
*/
|
|
|
- public PluginsService(
|
|
|
- Settings settings,
|
|
|
- Path configPath,
|
|
|
- Path modulesDirectory,
|
|
|
- Path pluginsDirectory,
|
|
|
- Collection<Class<? extends Plugin>> classpathPlugins
|
|
|
- ) {
|
|
|
+ public PluginsService(Settings settings, Path configPath, Path modulesDirectory, Path pluginsDirectory) {
|
|
|
this.settings = settings;
|
|
|
this.configPath = configPath;
|
|
|
|
|
|
- List<LoadedPlugin> pluginsLoaded = new ArrayList<>();
|
|
|
- List<PluginDescriptor> pluginsList = new ArrayList<>();
|
|
|
- // we need to build a List of plugins for checking mandatory plugins
|
|
|
- final List<String> pluginsNames = new ArrayList<>();
|
|
|
-
|
|
|
- // first we load plugins that are on the classpath. this is for tests
|
|
|
- for (Class<? extends Plugin> pluginClass : classpathPlugins) {
|
|
|
- Plugin plugin = loadPlugin(pluginClass, settings, configPath);
|
|
|
- PluginDescriptor pluginDescriptor = new PluginDescriptor(
|
|
|
- pluginClass.getName(),
|
|
|
- "classpath plugin",
|
|
|
- "NA",
|
|
|
- Version.CURRENT,
|
|
|
- "1.8",
|
|
|
- pluginClass.getName(),
|
|
|
- null,
|
|
|
- Collections.emptyList(),
|
|
|
- false,
|
|
|
- PluginType.ISOLATED,
|
|
|
- "",
|
|
|
- false
|
|
|
- );
|
|
|
- if (logger.isTraceEnabled()) {
|
|
|
- logger.trace("plugin loaded from classpath [{}]", pluginDescriptor);
|
|
|
- }
|
|
|
- pluginsLoaded.add(new LoadedPlugin(pluginDescriptor, plugin));
|
|
|
- pluginsList.add(pluginDescriptor);
|
|
|
- pluginsNames.add(pluginDescriptor.getName());
|
|
|
- }
|
|
|
-
|
|
|
Set<PluginBundle> seenBundles = new LinkedHashSet<>();
|
|
|
- List<PluginDescriptor> modulesList = new ArrayList<>();
|
|
|
+
|
|
|
// load modules
|
|
|
+ List<PluginDescriptor> modulesList = new ArrayList<>();
|
|
|
if (modulesDirectory != null) {
|
|
|
try {
|
|
|
Set<PluginBundle> modules = PluginsUtils.getModuleBundles(modulesDirectory);
|
|
|
- for (PluginBundle bundle : modules) {
|
|
|
- modulesList.add(bundle.plugin);
|
|
|
- }
|
|
|
+ modules.stream().map(PluginBundle::pluginDescriptor).forEach(modulesList::add);
|
|
|
seenBundles.addAll(modules);
|
|
|
} catch (IOException ex) {
|
|
|
throw new IllegalStateException("Unable to initialize modules", ex);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // now, find all the ones that are in plugins/
|
|
|
+ // load plugins
|
|
|
+ List<PluginDescriptor> pluginsList = new ArrayList<>();
|
|
|
if (pluginsDirectory != null) {
|
|
|
try {
|
|
|
// TODO: remove this leniency, but tests bogusly rely on it
|
|
|
if (isAccessibleDirectory(pluginsDirectory, logger)) {
|
|
|
PluginsUtils.checkForFailedPluginRemovals(pluginsDirectory);
|
|
|
Set<PluginBundle> plugins = PluginsUtils.getPluginBundles(pluginsDirectory);
|
|
|
- for (final PluginBundle bundle : plugins) {
|
|
|
- pluginsList.add(bundle.plugin);
|
|
|
- pluginsNames.add(bundle.plugin.getName());
|
|
|
- }
|
|
|
+ plugins.stream().map(PluginBundle::pluginDescriptor).forEach(pluginsList::add);
|
|
|
seenBundles.addAll(plugins);
|
|
|
}
|
|
|
} catch (IOException ex) {
|
|
@@ -183,13 +144,13 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- Collection<LoadedPlugin> loaded = loadBundles(seenBundles);
|
|
|
- pluginsLoaded.addAll(loaded);
|
|
|
-
|
|
|
this.info = new PluginsAndModules(pluginsList, modulesList);
|
|
|
- this.plugins = Collections.unmodifiableList(pluginsLoaded);
|
|
|
+ this.plugins = loadBundles(seenBundles);
|
|
|
|
|
|
- checkMandatoryPlugins(pluginsNames, MANDATORY_SETTING.get(settings));
|
|
|
+ checkMandatoryPlugins(
|
|
|
+ pluginsList.stream().map(PluginDescriptor::getName).collect(Collectors.toSet()),
|
|
|
+ new HashSet<>(MANDATORY_SETTING.get(settings))
|
|
|
+ );
|
|
|
|
|
|
// we don't log jars in lib/ we really shouldn't log modules,
|
|
|
// but for now: just be transparent so we can debug any potential issues
|
|
@@ -198,12 +159,12 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
}
|
|
|
|
|
|
// package-private for testing
|
|
|
- static void checkMandatoryPlugins(List<String> existingPlugins, List<String> mandatoryPlugins) {
|
|
|
+ static void checkMandatoryPlugins(Set<String> existingPlugins, Set<String> mandatoryPlugins) {
|
|
|
if (mandatoryPlugins.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Set<String> missingPlugins = Sets.difference(new HashSet<>(mandatoryPlugins), new HashSet<>(existingPlugins));
|
|
|
+ Set<String> missingPlugins = Sets.difference(mandatoryPlugins, existingPlugins);
|
|
|
if (missingPlugins.isEmpty() == false) {
|
|
|
final String message = "missing mandatory plugins ["
|
|
|
+ String.join(", ", missingPlugins)
|
|
@@ -231,8 +192,8 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
* @return A stream of results
|
|
|
* @param <T> The generic type of the result
|
|
|
*/
|
|
|
- public <T> Stream<T> map(Function<Plugin, T> function) {
|
|
|
- return plugins.stream().map(LoadedPlugin::instance).map(function);
|
|
|
+ public final <T> Stream<T> map(Function<Plugin, T> function) {
|
|
|
+ return plugins().stream().map(LoadedPlugin::instance).map(function);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -241,24 +202,24 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
* @return A stream of results
|
|
|
* @param <T> The generic type of the collection
|
|
|
*/
|
|
|
- public <T> Stream<T> flatMap(Function<Plugin, Collection<T>> function) {
|
|
|
- return plugins.stream().map(LoadedPlugin::instance).flatMap(p -> function.apply(p).stream());
|
|
|
+ public final <T> Stream<T> flatMap(Function<Plugin, Collection<T>> function) {
|
|
|
+ return plugins().stream().map(LoadedPlugin::instance).flatMap(p -> function.apply(p).stream());
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Apply a consumer action to each plugin
|
|
|
* @param consumer An action that consumes a plugin
|
|
|
*/
|
|
|
- public void forEach(Consumer<Plugin> consumer) {
|
|
|
- plugins.stream().map(LoadedPlugin::instance).forEach(consumer);
|
|
|
+ public final void forEach(Consumer<Plugin> consumer) {
|
|
|
+ plugins().stream().map(LoadedPlugin::instance).forEach(consumer);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Sometimes we want the plugin name for error handling.
|
|
|
* @return A map of plugin names to plugin instances.
|
|
|
*/
|
|
|
- public Map<String, Plugin> pluginMap() {
|
|
|
- return plugins.stream().collect(Collectors.toMap(p -> p.descriptor().getName(), LoadedPlugin::instance));
|
|
|
+ public final Map<String, Plugin> pluginMap() {
|
|
|
+ return plugins().stream().collect(Collectors.toMap(p -> p.descriptor().getName(), LoadedPlugin::instance));
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -269,7 +230,11 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
return info;
|
|
|
}
|
|
|
|
|
|
- private Collection<LoadedPlugin> loadBundles(Set<PluginBundle> bundles) {
|
|
|
+ protected List<LoadedPlugin> plugins() {
|
|
|
+ return this.plugins;
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<LoadedPlugin> loadBundles(Set<PluginBundle> bundles) {
|
|
|
Map<String, LoadedPlugin> loaded = new HashMap<>();
|
|
|
Map<String, Set<URL>> transitiveUrls = new HashMap<>();
|
|
|
List<PluginBundle> sortedBundles = PluginsUtils.sortBundles(bundles);
|
|
@@ -282,7 +247,7 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
}
|
|
|
|
|
|
loadExtensions(loaded.values());
|
|
|
- return loaded.values();
|
|
|
+ return List.copyOf(loaded.values());
|
|
|
}
|
|
|
|
|
|
// package-private for test visibility
|
|
@@ -500,7 +465,8 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static Plugin loadPlugin(Class<? extends Plugin> pluginClass, Settings settings, Path configPath) {
|
|
|
+ // package-private for testing
|
|
|
+ static Plugin loadPlugin(Class<? extends Plugin> pluginClass, Settings settings, Path configPath) {
|
|
|
final Constructor<?>[] constructors = pluginClass.getConstructors();
|
|
|
if (constructors.length == 0) {
|
|
|
throw new IllegalStateException("no public constructor for [" + pluginClass.getName() + "]");
|
|
@@ -543,8 +509,18 @@ public class PluginsService implements ReportingService<PluginsAndModules> {
|
|
|
}
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
- public <T> List<T> filterPlugins(Class<T> type) {
|
|
|
- return plugins.stream().filter(x -> type.isAssignableFrom(x.instance().getClass())).map(p -> ((T) p.instance())).toList();
|
|
|
+ public final <T> List<T> filterPlugins(Class<T> type) {
|
|
|
+ return plugins().stream().filter(x -> type.isAssignableFrom(x.instance().getClass())).map(p -> ((T) p.instance())).toList();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Get a function that will take a {@link Settings} object and return a {@link PluginsService}.
|
|
|
+ * This function passes in an empty list of classpath plugins.
|
|
|
+ * @param environment The environment for the plugins service.
|
|
|
+ * @return A function for creating a plugins service.
|
|
|
+ */
|
|
|
+ public static Function<Settings, PluginsService> getPluginsServiceCtor(Environment environment) {
|
|
|
+ return settings -> new PluginsService(settings, environment.configFile(), environment.modulesFile(), environment.pluginsFile());
|
|
|
}
|
|
|
|
|
|
static final LayerAndLoader createPluginModuleLayer(PluginBundle bundle, ClassLoader parentLoader, List<ModuleLayer> parentLayers) {
|