From 2ed1fb187d3d40b6b534e680449107bb05f4b622 Mon Sep 17 00:00:00 2001 From: David Pilato Date: Sat, 16 Mar 2013 17:11:30 +0100 Subject: [PATCH] Add version to plugins Plugin developpers can now add a version number to their es-plugin.properties file: ```properties plugin=org.elasticsearch.test.integration.nodesinfo.TestPlugin version=0.0.7-SNAPSHOT ``` Also, for site plugins, it's recommended to add a `es-plugin.properties` file in root site directory with `description` and `version` properties: ```properties description=This is a description for a dummy test site plugin. version=0.0.7-BOND-SITE ``` When running Nodes Info API, you will get information on versions: ```sh $ curl 'http://localhost:9200/_nodes?plugin=true&pretty' ``` ```javascript { "ok" : true, "cluster_name" : "test-cluster-MacBook-Air-de-David.local", "nodes" : { "RHMsToxiRcCXwHiS6mEaFw" : { "name" : "node2", "transport_address" : "inet[/192.168.0.15:9301]", "hostname" : "MacBook-Air-de-David.local", "version" : "0.90.0.Beta2-SNAPSHOT", "http_address" : "inet[/192.168.0.15:9201]", "plugins" : [ { "name" : "dummy", "version" : "0.0.7-BOND-SITE", "description" : "This is a description for a dummy test site plugin.", "url" : "/_plugin/dummy/", "site" : true, "jvm" : false } ] }, "IKiUOo-LSCq1Km1GUhBwPg" : { "name" : "node3", "transport_address" : "inet[/192.168.0.15:9302]", "hostname" : "MacBook-Air-de-David.local", "version" : "0.90.0.Beta2-SNAPSHOT", "http_address" : "inet[/192.168.0.15:9202]", "plugins" : [ { "name" : "test-plugin", "version" : "0.0.7-SNAPSHOT", "description" : "test-plugin description", "site" : false, "jvm" : true } ] }, "H64dcSF2R_GNWh6XRCYZJA" : { "name" : "node1", "transport_address" : "inet[/192.168.0.15:9300]", "hostname" : "MacBook-Air-de-David.local", "version" : "0.90.0.Beta2-SNAPSHOT", "http_address" : "inet[/192.168.0.15:9200]", "plugins" : [ ] }, "mGEZcYl8Tye0Rm5AACBhPA" : { "name" : "node4", "transport_address" : "inet[/192.168.0.15:9303]", "hostname" : "MacBook-Air-de-David.local", "version" : "0.90.0.Beta2-SNAPSHOT", "http_address" : "inet[/192.168.0.15:9203]", "plugins" : [ { "name" : "test-plugin", "version" : "0.0.7-SNAPSHOT", "description" : "test-plugin description", "site" : false, "jvm" : true }, { "name" : "test-no-version-plugin", "version" : "NA", "description" : "test-no-version-plugin description", "site" : false, "jvm" : true }, { "name" : "dummy", "version" : "NA", "description" : "No description found for dummy.", "url" : "/_plugin/dummy/", "site" : true, "jvm" : false } ] } } } ``` Relative to #2668. Closes #2784. --- .../admin/cluster/node/info/PluginInfo.java | 59 +++- .../elasticsearch/plugins/PluginsHelper.java | 54 --- .../elasticsearch/plugins/PluginsService.java | 326 +++++++++++------- .../nodesinfo/SimpleNodesInfoTests.java | 52 ++- .../node2/dummy/_site/es-plugin.properties | 1 + 5 files changed, 284 insertions(+), 208 deletions(-) delete mode 100644 src/main/java/org/elasticsearch/plugins/PluginsHelper.java diff --git a/src/main/java/org/elasticsearch/action/admin/cluster/node/info/PluginInfo.java b/src/main/java/org/elasticsearch/action/admin/cluster/node/info/PluginInfo.java index 671c9e8a225f..b1b2fc2d572e 100644 --- a/src/main/java/org/elasticsearch/action/admin/cluster/node/info/PluginInfo.java +++ b/src/main/java/org/elasticsearch/action/admin/cluster/node/info/PluginInfo.java @@ -18,6 +18,8 @@ */ package org.elasticsearch.action.admin.cluster.node.info; +import org.elasticsearch.Version; +import org.elasticsearch.common.Strings; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; @@ -29,18 +31,23 @@ import java.io.Serializable; public class PluginInfo implements Streamable, Serializable, ToXContent { + public static final String DESCRIPTION_NOT_AVAILABLE = "No description found."; + public static final String VERSION_NOT_AVAILABLE = "NA"; + static final class Fields { static final XContentBuilderString NAME = new XContentBuilderString("name"); static final XContentBuilderString DESCRIPTION = new XContentBuilderString("description"); static final XContentBuilderString URL = new XContentBuilderString("url"); static final XContentBuilderString JVM = new XContentBuilderString("jvm"); static final XContentBuilderString SITE = new XContentBuilderString("site"); + static final XContentBuilderString VERSION = new XContentBuilderString("version"); } private String name; private String description; private boolean site; private boolean jvm; + private String version; public PluginInfo() { } @@ -52,12 +59,18 @@ public PluginInfo() { * @param description Its description * @param site true if it's a site plugin * @param jvm true if it's a jvm plugin + * @param version Version number is applicable (NA otherwise) */ - public PluginInfo(String name, String description, boolean site, boolean jvm) { + public PluginInfo(String name, String description, boolean site, boolean jvm, String version) { this.name = name; this.description = description; this.site = site; this.jvm = jvm; + if (Strings.hasText(version)) { + this.version = version; + } else { + this.version = VERSION_NOT_AVAILABLE; + } } /** @@ -91,7 +104,7 @@ public boolean isJvm() { /** * We compute the URL for sites: "/_plugin/" + name + "/" * - * @return + * @return relative URL for site plugin */ public String getUrl() { if (site) { @@ -101,6 +114,13 @@ public String getUrl() { } } + /** + * @return Version number for the plugin + */ + public String getVersion() { + return version; + } + public static PluginInfo readPluginInfo(StreamInput in) throws IOException { PluginInfo info = new PluginInfo(); info.readFrom(in); @@ -113,6 +133,11 @@ public void readFrom(StreamInput in) throws IOException { this.description = in.readString(); this.site = in.readBoolean(); this.jvm = in.readBoolean(); + if (in.getVersion().onOrAfter(Version.V_1_0_0_RC2)) { + this.version = in.readString(); + } else { + this.version = VERSION_NOT_AVAILABLE; + } } @Override @@ -121,12 +146,16 @@ public void writeTo(StreamOutput out) throws IOException { out.writeString(description); out.writeBoolean(site); out.writeBoolean(jvm); + if (out.getVersion().onOrAfter(Version.V_1_0_0_RC2)) { + out.writeString(version); + } } @Override public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); builder.field(Fields.NAME, name); + builder.field(Fields.VERSION, version); builder.field(Fields.DESCRIPTION, description); if (site) { builder.field(Fields.URL, getUrl()); @@ -140,16 +169,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - PluginInfo p = (PluginInfo) o; + PluginInfo that = (PluginInfo) o; - return name.equals(p.getName()); + if (!name.equals(that.name)) return false; + if (version != null ? !version.equals(that.version) : that.version != null) return false; + + return true; } @Override @@ -157,4 +185,15 @@ public int hashCode() { return name.hashCode(); } + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("PluginInfo{"); + sb.append("name='").append(name).append('\''); + sb.append(", description='").append(description).append('\''); + sb.append(", site=").append(site); + sb.append(", jvm=").append(jvm); + sb.append(", version='").append(version).append('\''); + sb.append('}'); + return sb.toString(); + } } diff --git a/src/main/java/org/elasticsearch/plugins/PluginsHelper.java b/src/main/java/org/elasticsearch/plugins/PluginsHelper.java deleted file mode 100644 index 1a19af8f0357..000000000000 --- a/src/main/java/org/elasticsearch/plugins/PluginsHelper.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to Elasticsearch under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.elasticsearch.plugins; - -import com.google.common.collect.Sets; -import org.elasticsearch.env.Environment; - -import java.io.File; -import java.util.Set; - -/** - * Helper class for plugins - */ -public class PluginsHelper { - - /** - * Build a list of existing site plugins for a given environment - * @param environment We look into Environment#pluginsFile() - * @return A Set of existing site plugins - */ - public static Set sitePlugins(Environment environment) { - File pluginsFile = environment.pluginsFile(); - Set sitePlugins = Sets.newHashSet(); - - if (!pluginsFile.exists() || !pluginsFile.isDirectory()) { - return sitePlugins; - } - - for (File pluginFile : pluginsFile.listFiles()) { - if (new File(pluginFile, "_site").exists()) { - sitePlugins.add(pluginFile.getName()); - } - } - return sitePlugins; - } - -} diff --git a/src/main/java/org/elasticsearch/plugins/PluginsService.java b/src/main/java/org/elasticsearch/plugins/PluginsService.java index 49663c994293..159ca154d41a 100644 --- a/src/main/java/org/elasticsearch/plugins/PluginsService.java +++ b/src/main/java/org/elasticsearch/plugins/PluginsService.java @@ -20,11 +20,13 @@ package org.elasticsearch.plugins; import com.google.common.collect.*; +import org.apache.lucene.util.IOUtils; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.action.admin.cluster.node.info.PluginInfo; import org.elasticsearch.action.admin.cluster.node.info.PluginsInfo; import org.elasticsearch.common.Strings; import org.elasticsearch.common.collect.MapBuilder; +import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.component.LifecycleComponent; import org.elasticsearch.common.inject.Module; @@ -42,8 +44,6 @@ import java.net.URL; import java.util.*; -import static com.google.common.collect.Maps.newHashMap; - /** * */ @@ -52,7 +52,10 @@ public class PluginsService extends AbstractComponent { private final Environment environment; - private final ImmutableMap plugins; + /** + * We keep around a list of jvm plugins + */ + private final ImmutableList> plugins; private final ImmutableMap> onModuleReferences; @@ -79,25 +82,47 @@ public PluginsService(Settings settings, Environment environment) { super(settings); this.environment = environment; - Map plugins = Maps.newHashMap(); + ImmutableList.Builder> tupleBuilder = ImmutableList.builder(); - //first we load all the default plugins from the settings + // first we load all the default plugins from the settings String[] defaultPluginsClasses = settings.getAsArray("plugin.types"); for (String pluginClass : defaultPluginsClasses) { Plugin plugin = loadPlugin(pluginClass, settings); - plugins.put(plugin.name(), plugin); + PluginInfo pluginInfo = new PluginInfo(plugin.name(), plugin.description(), hasSite(plugin.name()), true, PluginInfo.VERSION_NOT_AVAILABLE); + if (logger.isTraceEnabled()) { + logger.trace("plugin loaded from settings [{}]", pluginInfo); + } + tupleBuilder.add(new Tuple(pluginInfo, plugin)); } // now, find all the ones that are in the classpath loadPluginsIntoClassLoader(); - plugins.putAll(loadPluginsFromClasspath(settings)); - Set sitePlugins = PluginsHelper.sitePlugins(this.environment); + tupleBuilder.addAll(loadPluginsFromClasspath(settings)); + this.plugins = tupleBuilder.build(); + + // We need to build a List of jvm and site plugins for checking mandatory plugins + Map jvmPlugins = Maps.newHashMap(); + List sitePlugins = Lists.newArrayList(); + for (Tuple tuple : this.plugins) { + jvmPlugins.put(tuple.v2().name(), tuple.v2()); + if (tuple.v1().isSite()) { + sitePlugins.add(tuple.v1().getName()); + } + } + + // we load site plugins + ImmutableList> tuples = loadSitePlugins(); + for (Tuple tuple : tuples) { + sitePlugins.add(tuple.v1().getName()); + } + + // Checking expected plugins String[] mandatoryPlugins = settings.getAsArray("plugin.mandatory", null); if (mandatoryPlugins != null) { Set missingPlugins = Sets.newHashSet(); for (String mandatoryPlugin : mandatoryPlugins) { - if (!plugins.containsKey(mandatoryPlugin) && !sitePlugins.contains(mandatoryPlugin) && !missingPlugins.contains(mandatoryPlugin)) { + if (!jvmPlugins.containsKey(mandatoryPlugin) && !sitePlugins.contains(mandatoryPlugin) && !missingPlugins.contains(mandatoryPlugin)) { missingPlugins.add(mandatoryPlugin); } } @@ -106,12 +131,10 @@ public PluginsService(Settings settings, Environment environment) { } } - logger.info("loaded {}, sites {}", plugins.keySet(), sitePlugins); - - this.plugins = ImmutableMap.copyOf(plugins); + logger.info("loaded {}, sites {}", jvmPlugins.keySet(), sitePlugins); MapBuilder> onModuleReferences = MapBuilder.newMapBuilder(); - for (Plugin plugin : plugins.values()) { + for (Plugin plugin : jvmPlugins.values()) { List list = Lists.newArrayList(); for (Method method : plugin.getClass().getDeclaredMethods()) { if (!method.getName().equals("onModule")) { @@ -139,7 +162,7 @@ public PluginsService(Settings settings, Environment environment) { } - public ImmutableMap plugins() { + public ImmutableList> plugins() { return plugins; } @@ -150,17 +173,17 @@ public void processModules(Iterable modules) { } public void processModule(Module module) { - for (Plugin plugin : plugins().values()) { - plugin.processModule(module); + for (Tuple plugin : plugins()) { + plugin.v2().processModule(module); // see if there are onModule references - List references = onModuleReferences.get(plugin); + List references = onModuleReferences.get(plugin.v2()); if (references != null) { for (OnModuleReference reference : references) { if (reference.moduleClass.isAssignableFrom(module.getClass())) { try { - reference.onModuleMethod.invoke(plugin, module); + reference.onModuleMethod.invoke(plugin.v2(), module); } catch (Exception e) { - logger.warn("plugin {}, failed to invoke custom onModule method", e, plugin.name()); + logger.warn("plugin {}, failed to invoke custom onModule method", e, plugin.v2().name()); } } } @@ -171,80 +194,80 @@ public void processModule(Module module) { public Settings updatedSettings() { ImmutableSettings.Builder builder = ImmutableSettings.settingsBuilder() .put(this.settings); - for (Plugin plugin : plugins.values()) { - builder.put(plugin.additionalSettings()); + for (Tuple plugin : plugins) { + builder.put(plugin.v2().additionalSettings()); } return builder.build(); } public Collection> modules() { List> modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.modules()); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().modules()); } return modules; } public Collection modules(Settings settings) { List modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.modules(settings)); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().modules(settings)); } return modules; } public Collection> services() { List> services = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - services.addAll(plugin.services()); + for (Tuple plugin : plugins) { + services.addAll(plugin.v2().services()); } return services; } public Collection> indexModules() { List> modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.indexModules()); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().indexModules()); } return modules; } public Collection indexModules(Settings settings) { List modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.indexModules(settings)); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().indexModules(settings)); } return modules; } public Collection> indexServices() { List> services = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - services.addAll(plugin.indexServices()); + for (Tuple plugin : plugins) { + services.addAll(plugin.v2().indexServices()); } return services; } public Collection> shardModules() { List> modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.shardModules()); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().shardModules()); } return modules; } public Collection shardModules(Settings settings) { List modules = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - modules.addAll(plugin.shardModules(settings)); + for (Tuple plugin : plugins) { + modules.addAll(plugin.v2().shardModules(settings)); } return modules; } public Collection> shardServices() { List> services = Lists.newArrayList(); - for (Plugin plugin : plugins.values()) { - services.addAll(plugin.shardServices()); + for (Tuple plugin : plugins) { + services.addAll(plugin.v2().shardServices()); } return services; } @@ -260,79 +283,35 @@ synchronized public PluginsInfo info() { if (refreshInterval.millis() != 0) { if (cachedPluginsInfo != null && (refreshInterval.millis() < 0 || (System.currentTimeMillis() - lastRefresh) < refreshInterval.millis())) { - if (logger.isTraceEnabled()) logger.trace("using cache to retrieve plugins info"); + if (logger.isTraceEnabled()) { + logger.trace("using cache to retrieve plugins info"); + } return cachedPluginsInfo; } lastRefresh = System.currentTimeMillis(); } - if (logger.isTraceEnabled()) logger.trace("starting to fetch info on plugins"); - cachedPluginsInfo = new PluginsInfo(); - - // We create a map to have only unique values - Set plugins = new HashSet(); - - for (Plugin plugin : plugins().values()) { - // We should detect if the plugin has also an embedded _site structure - File siteFile = new File(new File(environment.pluginsFile(), plugin.name()), "_site"); - boolean isSite = siteFile.exists() && siteFile.isDirectory(); - if (logger.isTraceEnabled()) logger.trace("found a jvm plugin [{}], [{}]{}", - plugin.name(), plugin.description(), isSite ? ": with _site structure" : ""); - cachedPluginsInfo.add(new PluginInfo(plugin.name(), plugin.description(), isSite, true)); - plugins.add(plugin.name()); + if (logger.isTraceEnabled()) { + logger.trace("starting to fetch info on plugins"); } + cachedPluginsInfo = new PluginsInfo(); - File pluginsFile = environment.pluginsFile(); - if (!pluginsFile.exists()) { - return cachedPluginsInfo; - } - if (!pluginsFile.isDirectory()) { - return cachedPluginsInfo; + // We first add all JvmPlugins + for (Tuple plugin : this.plugins) { + if (logger.isTraceEnabled()) { + logger.trace("adding jvm plugin [{}]", plugin.v1()); + } + cachedPluginsInfo.add(plugin.v1()); } - File[] pluginsFiles = pluginsFile.listFiles(); - if (pluginsFiles != null) { - for (File plugin : pluginsFiles) { - // We skip already known jvm plugins - if (!plugins.contains(plugin.getName())) { - File sitePluginDir = new File(plugin, "_site"); - if (sitePluginDir.exists()) { - String name = plugin.getName(); - String description = "No description found for " + name + "."; - - // We check if es-plugin.properties exists in plugin/_site dir - File pluginPropFile = new File(sitePluginDir, ES_PLUGIN_PROPERTIES); - if (pluginPropFile.exists()) { - - Properties pluginProps = new Properties(); - InputStream is = null; - try { - is = new FileInputStream(pluginPropFile.getAbsolutePath()); - pluginProps.load(is); - description = pluginProps.getProperty("description"); - } catch (Exception e) { - logger.warn("failed to load plugin description from [" + - pluginPropFile.getAbsolutePath() + "]", e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - // ignore - } - } - } - } - - if (logger.isTraceEnabled()) logger.trace("found a site plugin [{}], [{}]", - name, description); - cachedPluginsInfo.add(new PluginInfo(name, description, true, false)); - } - } + // We reload site plugins (in case of some changes) + for (Tuple plugin : loadSitePlugins()) { + if (logger.isTraceEnabled()) { + logger.trace("adding site plugin [{}]", plugin.v1()); } + cachedPluginsInfo.add(plugin.v1()); } - return cachedPluginsInfo; } @@ -367,7 +346,9 @@ private void loadPluginsIntoClassLoader() { if (pluginsFile != null) { for (File pluginFile : pluginsFiles) { if (pluginFile.isDirectory()) { - logger.trace("--- adding plugin [" + pluginFile.getAbsolutePath() + "]"); + if (logger.isTraceEnabled()) { + logger.trace("--- adding plugin [" + pluginFile.getAbsolutePath() + "]"); + } try { // add the root addURL.invoke(classLoader, pluginFile.toURI().toURL()); @@ -388,7 +369,7 @@ private void loadPluginsIntoClassLoader() { } addURL.invoke(classLoader, libFile.toURI().toURL()); } - } catch (Exception e) { + } catch (Throwable e) { logger.warn("failed to add plugin [" + pluginFile + "]", e); } } @@ -398,48 +379,130 @@ private void loadPluginsIntoClassLoader() { } } - private Map loadPluginsFromClasspath(Settings settings) { - Map plugins = newHashMap(); - Enumeration pluginUrls = null; + private ImmutableList> loadPluginsFromClasspath(Settings settings) { + ImmutableList.Builder> plugins = ImmutableList.builder(); + + // Trying JVM plugins: looking for es-plugin.properties files try { - pluginUrls = settings.getClassLoader().getResources(ES_PLUGIN_PROPERTIES); + Enumeration pluginUrls = settings.getClassLoader().getResources(ES_PLUGIN_PROPERTIES); + while (pluginUrls.hasMoreElements()) { + URL pluginUrl = pluginUrls.nextElement(); + Properties pluginProps = new Properties(); + InputStream is = null; + try { + is = pluginUrl.openStream(); + pluginProps.load(is); + String pluginClassName = pluginProps.getProperty("plugin"); + String pluginVersion = pluginProps.getProperty("version", PluginInfo.VERSION_NOT_AVAILABLE); + Plugin plugin = loadPlugin(pluginClassName, settings); + + // Is it a site plugin as well? Does it have also an embedded _site structure + File siteFile = new File(new File(environment.pluginsFile(), plugin.name()), "_site"); + boolean isSite = siteFile.exists() && siteFile.isDirectory(); + if (logger.isTraceEnabled()) { + logger.trace("found a jvm plugin [{}], [{}]{}", + plugin.name(), plugin.description(), isSite ? ": with _site structure" : ""); + } + + PluginInfo pluginInfo = new PluginInfo(plugin.name(), plugin.description(), isSite, true, pluginVersion); + + plugins.add(new Tuple(pluginInfo, plugin)); + } catch (Throwable e) { + logger.warn("failed to load plugin from [" + pluginUrl + "]", e); + } finally { + IOUtils.closeWhileHandlingException(is); + } + } } catch (IOException e) { - logger.warn("failed to find plugins from classpath", e); - return ImmutableMap.of(); + logger.warn("failed to find jvm plugins from classpath", e); } - while (pluginUrls.hasMoreElements()) { - URL pluginUrl = pluginUrls.nextElement(); - Properties pluginProps = new Properties(); - InputStream is = null; - try { - is = pluginUrl.openStream(); - pluginProps.load(is); - String pluginClassName = pluginProps.getProperty("plugin"); - Plugin plugin = loadPlugin(pluginClassName, settings); - plugins.put(plugin.name(), plugin); - } catch (Exception e) { - logger.warn("failed to load plugin from [" + pluginUrl + "]", e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - // ignore + + return plugins.build(); + } + + private ImmutableList> loadSitePlugins() { + ImmutableList.Builder> sitePlugins = ImmutableList.builder(); + List loadedJvmPlugins = new ArrayList(); + + // Already known jvm plugins are ignored + for(Tuple tuple : plugins) { + if (tuple.v1().isSite()) { + loadedJvmPlugins.add(tuple.v1().getName()); + } + } + + // Let's try to find all _site plugins we did not already found + File pluginsFile = environment.pluginsFile(); + + if (!pluginsFile.exists() || !pluginsFile.isDirectory()) { + return sitePlugins.build(); + } + + for (File pluginFile : pluginsFile.listFiles()) { + if (!loadedJvmPlugins.contains(pluginFile.getName())) { + File sitePluginDir = new File(pluginFile, "_site"); + if (sitePluginDir.exists()) { + // We have a _site plugin. Let's try to get more information on it + String name = pluginFile.getName(); + String version = PluginInfo.VERSION_NOT_AVAILABLE; + String description = PluginInfo.DESCRIPTION_NOT_AVAILABLE; + + // We check if es-plugin.properties exists in plugin/_site dir + File pluginPropFile = new File(sitePluginDir, ES_PLUGIN_PROPERTIES); + if (pluginPropFile.exists()) { + + Properties pluginProps = new Properties(); + InputStream is = null; + try { + is = new FileInputStream(pluginPropFile.getAbsolutePath()); + pluginProps.load(is); + description = pluginProps.getProperty("description", PluginInfo.DESCRIPTION_NOT_AVAILABLE); + version = pluginProps.getProperty("version", PluginInfo.VERSION_NOT_AVAILABLE); + } catch (Exception e) { + // Can not load properties for this site plugin. Ignoring. + logger.debug("can not load {} file.", e, ES_PLUGIN_PROPERTIES); + } finally { + IOUtils.closeWhileHandlingException(is); + } + } + + if (logger.isTraceEnabled()) { + logger.trace("found a site plugin name [{}], version [{}], description [{}]", + name, version, description); } + sitePlugins.add(new Tuple(new PluginInfo(name, description, true, false, version), null)); } } } - return plugins; + + return sitePlugins.build(); + } + + /** + * @param name plugin name + * @return if this jvm plugin has also a _site structure + */ + private boolean hasSite(String name) { + // Let's try to find all _site plugins we did not already found + File pluginsFile = environment.pluginsFile(); + + if (!pluginsFile.exists() || !pluginsFile.isDirectory()) { + return false; + } + + File sitePluginDir = new File(pluginsFile, name + "/_site"); + return sitePluginDir.exists(); } private Plugin loadPlugin(String className, Settings settings) { try { Class pluginClass = (Class) settings.getClassLoader().loadClass(className); + Plugin plugin; try { - return pluginClass.getConstructor(Settings.class).newInstance(settings); + plugin = pluginClass.getConstructor(Settings.class).newInstance(settings); } catch (NoSuchMethodException e) { try { - return pluginClass.getConstructor().newInstance(); + plugin = pluginClass.getConstructor().newInstance(); } catch (NoSuchMethodException e1) { throw new ElasticsearchException("No constructor for [" + pluginClass + "]. A plugin class must " + "have either an empty default constructor or a single argument constructor accepting a " + @@ -447,9 +510,10 @@ private Plugin loadPlugin(String className, Settings settings) { } } - } catch (Exception e) { + return plugin; + + } catch (Throwable e) { throw new ElasticsearchException("Failed to load plugin class [" + className + "]", e); } - } } diff --git a/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java b/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java index a375c873b665..6104a7cf3eeb 100644 --- a/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java +++ b/src/test/java/org/elasticsearch/nodesinfo/SimpleNodesInfoTests.java @@ -60,7 +60,7 @@ public class SimpleNodesInfoTests extends ElasticsearchIntegrationTest { static final class Fields { static final String SITE_PLUGIN = "dummy"; static final String SITE_PLUGIN_DESCRIPTION = "This is a description for a dummy test site plugin."; - static final String SITE_PLUGIN_NO_DESCRIPTION = "No description found for dummy."; + static final String SITE_PLUGIN_VERSION = "0.0.7-BOND-SITE"; } @@ -129,32 +129,41 @@ public void testNodeInfoPlugin() throws URISyntaxException { ClusterHealthResponse clusterHealth = client().admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet(); logger.info("--> done cluster_health, status " + clusterHealth.getStatus()); - NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().execute().actionGet(); + NodesInfoResponse response = client().admin().cluster().prepareNodesInfo().clear().setPlugin(true).execute().actionGet(); logger.info("--> full json answer, status " + response.toString()); - assertNodeContainsPlugins(response, server1NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, - Collections.EMPTY_LIST, Collections.EMPTY_LIST); + assertNodeContainsPlugins(response, server1NodeId, + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST, // No JVM Plugin + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No Site Plugin - assertNodeContainsPlugins(response, server2NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, - Lists.newArrayList(Fields.SITE_PLUGIN), - Lists.newArrayList(Fields.SITE_PLUGIN_DESCRIPTION)); + assertNodeContainsPlugins(response, server2NodeId, + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST, // No JVM Plugin + Lists.newArrayList(Fields.SITE_PLUGIN), // Site Plugin + Lists.newArrayList(Fields.SITE_PLUGIN_DESCRIPTION), + Lists.newArrayList(Fields.SITE_PLUGIN_VERSION)); - assertNodeContainsPlugins(response, server3NodeId, Lists.newArrayList(TestPlugin.Fields.NAME), + assertNodeContainsPlugins(response, server3NodeId, + Lists.newArrayList(TestPlugin.Fields.NAME), // JVM Plugin Lists.newArrayList(TestPlugin.Fields.DESCRIPTION), - Collections.EMPTY_LIST, Collections.EMPTY_LIST); + Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE), + Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);// No site Plugin assertNodeContainsPlugins(response, server4NodeId, - Lists.newArrayList(TestNoVersionPlugin.Fields.NAME), + Lists.newArrayList(TestNoVersionPlugin.Fields.NAME), // JVM Plugin Lists.newArrayList(TestNoVersionPlugin.Fields.DESCRIPTION), - Lists.newArrayList(Fields.SITE_PLUGIN, TestNoVersionPlugin.Fields.NAME), - Lists.newArrayList(Fields.SITE_PLUGIN_NO_DESCRIPTION, TestNoVersionPlugin.Fields.DESCRIPTION)); + Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE), + Lists.newArrayList(Fields.SITE_PLUGIN, TestNoVersionPlugin.Fields.NAME),// Site Plugin + Lists.newArrayList(PluginInfo.DESCRIPTION_NOT_AVAILABLE), + Lists.newArrayList(PluginInfo.VERSION_NOT_AVAILABLE)); } private void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId, List expectedJvmPluginNames, List expectedJvmPluginDescriptions, + List expectedJvmVersions, List expectedSitePluginNames, - List expectedSitePluginDescriptions) { + List expectedSitePluginDescriptions, + List expectedSiteVersions) { assertThat(response.getNodesMap().get(nodeId), notNullValue()); @@ -171,6 +180,11 @@ private void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId assertThat(pluginDescriptions, hasItem(expectedJvmPluginDescription)); } + List jvmPluginVersions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(versionFunction).toList(); + for (String pluginVersion : expectedJvmVersions) { + assertThat(jvmPluginVersions, hasItem(pluginVersion)); + } + FluentIterable jvmUrls = FluentIterable.from(plugins.getInfos()) .filter(and(jvmPluginPredicate, Predicates.not(sitePluginPredicate))) .filter(isNull()) @@ -189,6 +203,12 @@ private void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId List sitePluginUrls = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(urlFunction).toList(); assertThat(sitePluginUrls, not(contains(nullValue()))); + + + List sitePluginVersions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(versionFunction).toList(); + for (String pluginVersion : expectedSiteVersions) { + assertThat(sitePluginVersions, hasItem(pluginVersion)); + } } private String startNodeWithPlugins(int nodeId, String ... pluginClassNames) throws URISyntaxException { @@ -242,4 +262,10 @@ public String apply(PluginInfo pluginInfo) { return pluginInfo.getUrl(); } }; + + private Function versionFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getVersion(); + } + }; } diff --git a/src/test/resources/org/elasticsearch/nodesinfo/node2/dummy/_site/es-plugin.properties b/src/test/resources/org/elasticsearch/nodesinfo/node2/dummy/_site/es-plugin.properties index 4e7537c83694..4487d7c8de90 100644 --- a/src/test/resources/org/elasticsearch/nodesinfo/node2/dummy/_site/es-plugin.properties +++ b/src/test/resources/org/elasticsearch/nodesinfo/node2/dummy/_site/es-plugin.properties @@ -17,4 +17,5 @@ # under the License. ################################################################ description=This is a description for a dummy test site plugin. +version=0.0.7-BOND-SITE