make java.version mandatory for jvm plugins

JarHell has a low level check, but its more of a best effort one,
only checking if X-Compile-Target-JDK is set in the manifest. This
is the case for all lucene- and elasticsearch- generated jars, but
lets just be explicit for plugins.
This commit is contained in:
Robert Muir 2015-07-23 10:58:52 -04:00
parent 73c0cd512a
commit 4b2bda4195
5 changed files with 86 additions and 26 deletions

View File

@ -136,21 +136,9 @@ public class JarHell {
/** inspect manifest for sure incompatibilities */ /** inspect manifest for sure incompatibilities */
static void checkManifest(Manifest manifest, Path jar) { static void checkManifest(Manifest manifest, Path jar) {
// give a nice error if jar requires a newer java version // give a nice error if jar requires a newer java version
String systemVersion = System.getProperty("java.specification.version");
String targetVersion = manifest.getMainAttributes().getValue("X-Compile-Target-JDK"); String targetVersion = manifest.getMainAttributes().getValue("X-Compile-Target-JDK");
if (targetVersion != null) { if (targetVersion != null) {
float current = Float.POSITIVE_INFINITY; checkJavaVersion(jar.toString(), targetVersion);
float target = Float.NEGATIVE_INFINITY;
try {
current = Float.parseFloat(systemVersion);
target = Float.parseFloat(targetVersion);
} catch (NumberFormatException e) {
// some spec changed, time for a more complex parser
}
if (current < target) {
throw new IllegalStateException(jar + " requires Java " + targetVersion
+ ", your system: " + systemVersion);
}
} }
// give a nice error if jar is compiled against different es version // give a nice error if jar is compiled against different es version
@ -162,6 +150,26 @@ public class JarHell {
} }
} }
/**
* Checks that the java specification version {@code targetVersion}
* required by {@code resource} is compatible with the current installation.
*/
public static void checkJavaVersion(String resource, String targetVersion) {
String systemVersion = System.getProperty("java.specification.version");
float current = Float.POSITIVE_INFINITY;
float target = Float.NEGATIVE_INFINITY;
try {
current = Float.parseFloat(systemVersion);
target = Float.parseFloat(targetVersion);
} catch (NumberFormatException e) {
// some spec changed, time for a more complex parser
}
if (current < target) {
throw new IllegalStateException(resource + " requires Java " + targetVersion
+ ", your system: " + systemVersion);
}
}
static void checkClass(Map<String,Path> clazzes, String clazz, Path jarpath) { static void checkClass(Map<String,Path> clazzes, String clazz, Path jarpath) {
Path previous = clazzes.put(clazz, jarpath); Path previous = clazzes.put(clazz, jarpath);
if (previous != null) { if (previous != null) {

View File

@ -19,6 +19,7 @@
package org.elasticsearch.plugins; package org.elasticsearch.plugins;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.bootstrap.JarHell;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable; import org.elasticsearch.common.io.stream.Streamable;
@ -110,6 +111,11 @@ public class PluginInfo implements Streamable, ToXContent {
if (esVersion.equals(Version.CURRENT) == false) { if (esVersion.equals(Version.CURRENT) == false) {
throw new IllegalArgumentException("Elasticsearch version [" + esVersionString + "] is too old for plugin [" + name + "]"); throw new IllegalArgumentException("Elasticsearch version [" + esVersionString + "] is too old for plugin [" + name + "]");
} }
String javaVersionString = props.getProperty("java.version");
if (javaVersionString == null) {
throw new IllegalArgumentException("Property [java.version] is missing for jvm plugin [" + name + "]");
}
JarHell.checkJavaVersion(name, javaVersionString);
isolated = Boolean.parseBoolean(props.getProperty("isolated", "true")); isolated = Boolean.parseBoolean(props.getProperty("isolated", "true"));
classname = props.getProperty("classname"); classname = props.getProperty("classname");
if (classname == null) { if (classname == null) {

View File

@ -57,6 +57,7 @@ public class PluginInfoTests extends ElasticsearchTestCase {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
PluginInfo info = PluginInfo.readFromProperties(pluginDir); PluginInfo info = PluginInfo.readFromProperties(pluginDir);
@ -119,6 +120,38 @@ public class PluginInfoTests extends ElasticsearchTestCase {
} }
} }
public void testReadFromPropertiesJavaVersionMissing() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"elasticsearch.version", Version.CURRENT.toString(),
"version", "1.0",
"jvm", "true");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected missing java version exception");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("[java.version] is missing"));
}
}
public void testReadFromPropertiesJavaVersionIncompatible() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir,
"description", "fake desc",
"elasticsearch.version", Version.CURRENT.toString(),
"java.version", "1000000.0",
"classname", "FakePlugin",
"version", "1.0",
"jvm", "true");
try {
PluginInfo.readFromProperties(pluginDir);
fail("expected incompatible java version exception");
} catch (IllegalStateException e) {
assertTrue(e.getMessage(), e.getMessage().contains("fake-plugin requires Java"));
}
}
public void testReadFromPropertiesBogusElasticsearchVersion() throws Exception { public void testReadFromPropertiesBogusElasticsearchVersion() throws Exception {
Path pluginDir = createTempDir().resolve("fake-plugin"); Path pluginDir = createTempDir().resolve("fake-plugin");
writeProperties(pluginDir, writeProperties(pluginDir,
@ -155,6 +188,7 @@ public class PluginInfoTests extends ElasticsearchTestCase {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true"); "jvm", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);
@ -170,7 +204,6 @@ public class PluginInfoTests extends ElasticsearchTestCase {
writeProperties(pluginDir, writeProperties(pluginDir,
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"site", "true"); "site", "true");
PluginInfo info = PluginInfo.readFromProperties(pluginDir); PluginInfo info = PluginInfo.readFromProperties(pluginDir);
assertTrue(info.isSite()); assertTrue(info.isSite());
@ -183,7 +216,6 @@ public class PluginInfoTests extends ElasticsearchTestCase {
writeProperties(pluginDir, writeProperties(pluginDir,
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(),
"site", "true"); "site", "true");
try { try {
PluginInfo.readFromProperties(pluginDir); PluginInfo.readFromProperties(pluginDir);

View File

@ -131,6 +131,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
@ -175,6 +176,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
@ -210,6 +212,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "2.0", "version", "2.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
@ -245,6 +248,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "3.0", "version", "3.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
@ -272,6 +276,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
@ -305,6 +310,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "1.0", "version", "1.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");
assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl)); assertStatusOk(String.format(Locale.ROOT, "install %s --url %s --verbose", pluginName, pluginUrl));
@ -412,6 +418,7 @@ public class PluginManagerTests extends ElasticsearchIntegrationTest {
"description", "fake desc", "description", "fake desc",
"version", "1.0.0", "version", "1.0.0",
"elasticsearch.version", Version.CURRENT.toString(), "elasticsearch.version", Version.CURRENT.toString(),
"java.version", System.getProperty("java.specification.version"),
"jvm", "true", "jvm", "true",
"classname", "FakePlugin"); "classname", "FakePlugin");

View File

@ -1,15 +1,24 @@
# elasticsearch plugin descriptor file # Elasticsearch plugin descriptor file
# This file must be at the root of the plugin.
# A plugin can be 'jvm', 'site', or both
# #
# example: # example jvm plugin:
# jvm=true # jvm=true
# classname=foo.bar.BazPlugin # classname=foo.bar.BazPlugin
# isolated=true
# site=false
# description=My cool plugin # description=My cool plugin
# version=2.0 # version=2.0
# elasticsearch.version=2.0 # elasticsearch.version=2.0
# java.version=1.7
# #
# A plugin can be 'jvm', 'site', or both # example site plugin:
# site=true
# description=My cool plugin
# version=1.0
#
# 'description': simple summary of the plugin
description=${project.description}
# 'version': plugin's version
version=${project.version}
# #
# 'jvm': true if the 'classname' class should be loaded # 'jvm': true if the 'classname' class should be loaded
# from jar files in the root directory of the plugin # from jar files in the root directory of the plugin
@ -20,13 +29,11 @@ classname=${elasticsearch.plugin.classname}
# passing false is deprecated, and only intended to support plugins # passing false is deprecated, and only intended to support plugins
# that have hard dependencies against each other # that have hard dependencies against each other
isolated=${elasticsearch.plugin.isolated} isolated=${elasticsearch.plugin.isolated}
# 'java.version' version of java the code is built against
java.version=${maven.compiler.target}
# 'elasticsearch.version' version of elasticsearch compiled against
elasticsearch.version=${elasticsearch.version}
# #
# 'site': true if the contents of _site should be served # 'site': true if the contents of _site should be served
site=${elasticsearch.plugin.site} site=${elasticsearch.plugin.site}
# #
# 'description': simple summary of the plugin
description=${project.description}
# 'version': plugin's version
version=${project.version}
# 'elasticsearch.version' version of elasticsearch compiled against
elasticsearch.version=${elasticsearch.version}