Introduce Java version checker

Today when users start Elasticsearch with their Java configuration
pointing to a pre-Java 8 install, they encounter a cryptic message:

    Exception in thread "main" java.lang.UnsupportedClassVersionError:
    org/elasticsearch/bootstrap/Elasticsearch : Unsupported major.minor
    version 52.0

They often think that they have Java 8 installed but if their JAVA_HOME
or other configuration is causing them to start with a pre-Java 8
install, this error message does not help them.

We introduce a Java version checker that runs on Java 6 as part of the
startup scripts. If the Java version is pre-Java 8, we can display a
helpful error message to the user informing them of the Java version
that the runtime was started with. Otherwise, Elasticsearch starts as it
does today.
This commit is contained in:
Jason Tedor 2017-02-15 18:13:54 -05:00
parent 60b823c756
commit c9cde11a5e
5 changed files with 150 additions and 1 deletions

View File

@ -143,6 +143,13 @@ if [ "x$JAVA_OPTS" != "x" ]; then
echo "Please pass JVM parameters via ES_JAVA_OPTS instead"
fi
"$JAVA" -cp "$ES_CLASSPATH" org.elasticsearch.tools.JavaVersionChecker
if [ $? -ne 0 ]; then
echo "Elasticsearch requires at least Java 8 but your Java version from $JAVA does not meet this requirement"
exit 1
fi
# full hostname passed through cut for portability on systems that do not support hostname -s
# export on separate line for shells that do not support combining definition and export
HOSTNAME=`hostname | cut -d. -f1`

View File

@ -22,4 +22,12 @@ ECHO additional elements via the plugin mechanism, or if code must really be 1>&
ECHO added to the main classpath, add jars to lib\, unsupported 1>&2
EXIT /B 1
)
%JAVA% -cp "%ES_CLASSPATH%" "org.elasticsearch.tools.JavaVersionChecker"
IF ERRORLEVEL 1 (
ECHO Elasticsearch requires at least Java 8 but your Java version from %JAVA% does not meet this requirement
EXIT /B 1
)
set ES_PARAMS=-Delasticsearch -Des.path.home="%ES_HOME%"

View File

@ -17,12 +17,25 @@
* under the License.
*/
import org.elasticsearch.gradle.precommit.PrecommitTasks
import org.gradle.api.JavaVersion
apply plugin: 'elasticsearch.build'
apply plugin: 'ru.vyarus.animalsniffer'
targetCompatibility = JavaVersion.VERSION_1_6
sourceCompatibility = JavaVersion.VERSION_1_6
targetCompatibility = JavaVersion.VERSION_1_6
dependencies {
signature "org.codehaus.mojo.signature:java16:1.0@signature"
}
forbiddenApisMain {
// java-version-checker does not depend on core so only JDK signatures should be checked
signaturesURLs = [PrecommitTasks.getResource('/forbidden/jdk-signatures.txt')]
}
test.enabled = false
loggerUsageCheck.enabled = false
jarHell.enabled=false

View File

@ -0,0 +1,88 @@
/*
* 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.tools;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Simple program that checks if the runtime Java version is at least 1.8.
*/
final class JavaVersionChecker {
private JavaVersionChecker() {
}
private static final List<Integer> JAVA_8 = Arrays.asList(1, 8);
/**
* The main entry point. The exit code is 0 if the Java version is at least 1.8, otherwise the exit code is 1.
*
* @param args the args to the program which are rejected if not empty
*/
public static void main(final String[] args) {
// no leniency!
if (args.length != 0) {
throw new IllegalArgumentException("expected zero arguments but was: " + Arrays.toString(args));
}
final String javaSpecificationVersion = System.getProperty("java.specification.version");
final List<Integer> current = parse(javaSpecificationVersion);
if (compare(current, JAVA_8) < 0) {
exit(1);
}
exit(0);
}
private static List<Integer> parse(final String value) {
if (!value.matches("^0*[0-9]+(\\.[0-9]+)*$")) {
throw new IllegalArgumentException(value);
}
final List<Integer> version = new ArrayList<Integer>();
final String[] components = value.split("\\.");
for (final String component : components) {
version.add(Integer.valueOf(component));
}
return version;
}
private static int compare(final List<Integer> left, final List<Integer> right) {
// lexicographically compare two lists, treating missing entries as zeros
final int len = Math.max(left.size(), right.size());
for (int i = 0; i < len; i++) {
final int l = (i < left.size()) ? left.get(i) : 0;
final int r = (i < right.size()) ? right.get(i) : 0;
if (l < r) {
return -1;
}
if (r < l) {
return 1;
}
}
return 0;
}
@SuppressForbidden(reason = "exit")
private static void exit(final int status) {
System.exit(status);
}
}

View File

@ -0,0 +1,33 @@
/*
* 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.tools;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation to suppress forbidden-apis errors inside a whole class, a method, or a field.
*/
@Retention(RetentionPolicy.CLASS)
@Target({ ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.TYPE })
@interface SuppressForbidden {
String reason();
}