diff --git a/war/src/main/java/executable/Main.java b/war/src/main/java/executable/Main.java index aca06dad93..6f760dc793 100644 --- a/war/src/main/java/executable/Main.java +++ b/war/src/main/java/executable/Main.java @@ -45,10 +45,10 @@ import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; -import java.util.HashSet; import java.util.List; import java.util.MissingResourceException; -import java.util.Set; +import java.util.NavigableSet; +import java.util.TreeSet; import java.util.UUID; import java.util.jar.JarFile; import java.util.jar.Manifest; @@ -63,14 +63,8 @@ import java.util.logging.Logger; */ public class Main { - private static final int MINIMUM_JAVA_VERSION = 11; - private static final int MAXIMUM_JAVA_VERSION = 17; - private static final Set SUPPORTED_JAVA_VERSIONS = - new HashSet<>(Arrays.asList(MINIMUM_JAVA_VERSION, MAXIMUM_JAVA_VERSION)); - private static final int MINIMUM_JAVA_CLASS_VERSION = 55; - private static final int MAXIMUM_JAVA_CLASS_VERSION = 61; - private static final Set SUPPORTED_JAVA_CLASS_VERSIONS = - new HashSet<>(Arrays.asList(MINIMUM_JAVA_CLASS_VERSION, MAXIMUM_JAVA_CLASS_VERSION)); + private static final NavigableSet SUPPORTED_JAVA_VERSIONS = + new TreeSet<>(Arrays.asList(11, 17)); private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); @@ -99,56 +93,73 @@ public class Main { */ private static final String ENABLE_FUTURE_JAVA_CLI_SWITCH = "--enable-future-java"; - public static void main(String[] args) throws IllegalAccessException { - try { - String v = System.getProperty("java.class.version"); - if (v != null) { - String classVersionString = v.split("\\.")[0]; - try { - int javaVersion = Integer.parseInt(classVersionString); - verifyJavaVersion(javaVersion, isFutureJavaEnabled(args)); - } catch (NumberFormatException e) { - // err on the safe side and keep on going - LOGGER.log(Level.WARNING, "Failed to parse java.class.version: {0}. Will continue execution", v); - } + /*package*/ static void verifyJavaVersion(int releaseVersion, boolean enableFutureJava) { + if (SUPPORTED_JAVA_VERSIONS.contains(releaseVersion)) { + // Great! + } else if (releaseVersion >= SUPPORTED_JAVA_VERSIONS.first()) { + if (enableFutureJava) { + LOGGER.log( + Level.WARNING, + "Running with Java {0} from {1}, which is not fully supported." + + " Continuing because {2} is set." + + " Supported Java versions are: {3}." + + " See https://jenkins.io/redirect/java-support/ for more information.", + new Object[] { + releaseVersion, + System.getProperty("java.home"), + ENABLE_FUTURE_JAVA_CLI_SWITCH, + SUPPORTED_JAVA_VERSIONS, + }); + } else if (releaseVersion > SUPPORTED_JAVA_VERSIONS.last()) { + throw new UnsupportedClassVersionError( + String.format( + "Running with Java %d from %s, which is not yet fully supported.%n" + + "Run the command again with the %s flag to enable preview support for future Java versions.%n" + + "Supported Java versions are: %s", + releaseVersion, + System.getProperty("java.home"), + ENABLE_FUTURE_JAVA_CLI_SWITCH, + SUPPORTED_JAVA_VERSIONS)); + } else { + throw new UnsupportedClassVersionError( + String.format( + "Running with Java %d from %s, which is not fully supported.%n" + + "Run the command again with the %s flag to bypass this error.%n" + + "Supported Java versions are: %s", + releaseVersion, + System.getProperty("java.home"), + ENABLE_FUTURE_JAVA_CLI_SWITCH, + SUPPORTED_JAVA_VERSIONS)); } - - _main(args); - } catch (UnsupportedClassVersionError e) { - System.err.printf( - "Jenkins requires Java versions %s but you are running with Java %s from %s%n", - SUPPORTED_JAVA_VERSIONS, System.getProperty("java.specification.version"), System.getProperty("java.home")); - e.printStackTrace(); + } else { + throw new UnsupportedClassVersionError( + String.format( + "Running with Java %d from %s, which is older than the minimum required version (Java %d).%n" + + "Supported Java versions are: %s", + releaseVersion, + System.getProperty("java.home"), + SUPPORTED_JAVA_VERSIONS.first(), + SUPPORTED_JAVA_VERSIONS)); } } - /*package*/ static void verifyJavaVersion(int javaClassVersion, boolean enableFutureJava) { - final String displayVersion = String.format("%d.0", javaClassVersion); - if (SUPPORTED_JAVA_CLASS_VERSIONS.contains(javaClassVersion)) { - // Great! - } else if (javaClassVersion > MINIMUM_JAVA_CLASS_VERSION) { - if (enableFutureJava) { - LOGGER.log(Level.WARNING, - String.format("Running with Java class version %s which is not in the list of supported versions: %s. " + - "Argument %s is set, so will continue. " + - "See https://jenkins.io/redirect/java-support/", - javaClassVersion, SUPPORTED_JAVA_CLASS_VERSIONS, ENABLE_FUTURE_JAVA_CLI_SWITCH)); - } else { - Error error = new UnsupportedClassVersionError(displayVersion); - LOGGER.log(Level.SEVERE, String.format("Running with Java class version %s which is not in the list of supported versions: %s. " + - "Run with the " + ENABLE_FUTURE_JAVA_CLI_SWITCH + " flag to enable such behavior. " + - "See https://jenkins.io/redirect/java-support/", - javaClassVersion, SUPPORTED_JAVA_CLASS_VERSIONS), error); - throw error; + /** + * Get the release version of the current JVM. + * + * @return The release version of the current JVM; e.g., 8, 11, or 17. + * @throws NumberFormatException If the release version could not be parsed. + */ + private static int getReleaseVersion() { + String version = System.getProperty("java.specification.version"); + version = version.trim(); + if (version.startsWith("1.")) { + String[] split = version.split("\\."); + if (split.length != 2) { + throw new NumberFormatException("Invalid Java specification version: " + version); } - } else { - Error error = new UnsupportedClassVersionError(displayVersion); - LOGGER.log(Level.SEVERE, - String.format("Running with Java class version %s, which is older than the Minimum required version %s. " + - "See https://jenkins.io/redirect/java-support/", - javaClassVersion, MINIMUM_JAVA_CLASS_VERSION), error); - throw error; + version = split[1]; } + return Integer.parseInt(version); } /** @@ -173,7 +184,15 @@ public class Main { @SuppressFBWarnings( value = {"PATH_TRAVERSAL_IN", "THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"}, justification = "User provided values for running the program and intentional propagation of reflection errors") - private static void _main(String[] args) throws IllegalAccessException { + public static void main(String[] args) throws IllegalAccessException { + try { + verifyJavaVersion(getReleaseVersion(), isFutureJavaEnabled(args)); + } catch (UnsupportedClassVersionError e) { + System.err.println(e.getMessage()); + System.err.println("See https://jenkins.io/redirect/java-support/ for more information."); + System.exit(1); + } + //Allows to pass arguments through stdin to "hide" sensitive parameters like httpsKeyStorePassword //to achieve this use --paramsFromStdIn if (hasArgument("--paramsFromStdIn", args)) { @@ -295,7 +314,7 @@ public class Main { " (NOTE: this option does not change the directory where the plugin archives are stored)\n" + " --extractedFilesFolder = folder where extracted files are to be located. Default is the temp folder\n" + " --logfile = redirect log messages to this file\n" + - " " + ENABLE_FUTURE_JAVA_CLI_SWITCH + " = allows running with new Java versions which are not fully supported (class version " + MINIMUM_JAVA_CLASS_VERSION + " and above)\n" + + " " + ENABLE_FUTURE_JAVA_CLI_SWITCH + " = allows running with Java versions which are not fully supported\n" + "{OPTIONS}"); if (!DISABLE_CUSTOM_JSESSIONID_COOKIE_NAME) { diff --git a/war/src/test/java/executable/MainTest.java b/war/src/test/java/executable/MainTest.java index 1948f2f551..6010759546 100644 --- a/war/src/test/java/executable/MainTest.java +++ b/war/src/test/java/executable/MainTest.java @@ -1,84 +1,82 @@ package executable; import edu.umd.cs.findbugs.annotations.CheckForNull; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class MainTest { @Test - void shouldFailForOldJava() { - assertJavaCheckFails(52, false); - assertJavaCheckFails(52, true); + void unsupported() { + assertJavaCheckFails(8, false); + assertJavaCheckFails(8, true); } @Test - void shouldBeOkForJava11() { - assertJavaCheckPasses(55, false); - assertJavaCheckPasses(55, true); + void supported() { + assertJavaCheckPasses(11, false); + assertJavaCheckPasses(11, true); + assertJavaCheckPasses(17, false); + assertJavaCheckPasses(17, true); } @Test - void shouldFailForMidJavaVersionsIfNoFlag() { - assertJavaCheckFails(56, false); - assertJavaCheckPasses(56, true); - assertJavaCheckFails(57, false); - assertJavaCheckPasses(57, true); - assertJavaCheckFails(58, false); - assertJavaCheckPasses(58, true); - assertJavaCheckFails(59, false); - assertJavaCheckPasses(59, true); - assertJavaCheckFails(60, false); - assertJavaCheckPasses(60, true); + void future() { + assertJavaCheckFails(12, false); + assertJavaCheckFails(13, false); + assertJavaCheckFails(14, false); + assertJavaCheckFails(15, false); + assertJavaCheckFails(16, false); + assertJavaCheckFails(18, false); + assertJavaCheckFails(19, false); + assertJavaCheckPasses(12, true); + assertJavaCheckPasses(13, true); + assertJavaCheckPasses(14, true); + assertJavaCheckPasses(15, true); + assertJavaCheckPasses(16, true); + assertJavaCheckPasses(18, true); + assertJavaCheckPasses(19, true); } - @Test - void shouldBeOkForJava17() { - assertJavaCheckPasses(61, false); - assertJavaCheckPasses(61, true); + private static void assertJavaCheckFails(int releaseVersion, boolean enableFutureJava) { + assertJavaCheckFails(null, releaseVersion, enableFutureJava); } - @Test - void shouldFailForNewJavaVersionsIfNoFlag() { - assertJavaCheckFails(62, false); - assertJavaCheckPasses(62, true); - assertJavaCheckFails(63, false); - assertJavaCheckPasses(63, true); - } + private static void assertJavaCheckFails( + @CheckForNull String message, int releaseVersion, boolean enableFutureJava) { + if (message == null) { + message = String.format( + "Java version check should have failed for Java version %d and enableFutureJava=%b", + releaseVersion, enableFutureJava); + } - private static void assertJavaCheckFails(int classVersion, boolean enableFutureJava) { - assertJavaCheckFails(null, classVersion, enableFutureJava); - } - - private static void assertJavaCheckFails(@CheckForNull String message, int classVersion, boolean enableFutureJava) { - boolean failed = false; + // TODO use assertThrows once we drop support for Java 8 in this module + boolean failed; try { - Main.verifyJavaVersion(classVersion, enableFutureJava); - } catch (Error error) { + Main.verifyJavaVersion(releaseVersion, enableFutureJava); + failed = false; + } catch (UnsupportedClassVersionError error) { failed = true; - System.out.printf("Java class version check failed as it was expected for Java class version %s.0 and enableFutureJava=%s%n", - classVersion, enableFutureJava); - error.printStackTrace(System.out); } - if (!failed) { - Assertions.fail(message != null ? message : - String.format("Java version Check should have failed for Java class version %s.0 and enableFutureJava=%s", - classVersion, enableFutureJava)); + throw new AssertionError(message); } } - private static void assertJavaCheckPasses(int classVersion, boolean enableFutureJava) { - assertJavaCheckPasses(null, classVersion, enableFutureJava); + private static void assertJavaCheckPasses(int releaseVersion, boolean enableFutureJava) { + assertJavaCheckPasses(null, releaseVersion, enableFutureJava); } - private static void assertJavaCheckPasses(@CheckForNull String message, int classVersion, boolean enableFutureJava) { + private static void assertJavaCheckPasses( + @CheckForNull String message, int releaseVersion, boolean enableFutureJava) { + if (message == null) { + message = String.format( + "Java version check should have passed for Java version %d and enableFutureJava=%b", + releaseVersion, enableFutureJava); + } try { - Main.verifyJavaVersion(classVersion, enableFutureJava); - } catch (Error error) { - throw new AssertionError(message != null ? message : - String.format("Java version Check should have passed for Java class version %s.0 and enableFutureJava=%s", - classVersion, enableFutureJava), error); + Main.verifyJavaVersion(releaseVersion, enableFutureJava); + } catch (UnsupportedClassVersionError e) { + throw new AssertionError(message, e); } } }