From 985f2baaa102a1c3adb06361c72ce040c590e800 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Mon, 16 Feb 2015 10:45:08 -0800 Subject: [PATCH] Prepare for more flexible Java version parsing. This preserves existing functionality, but accomplishes it with a general purpose method that can parse any Java version string, given a regular expression that matches all the number parts (which have to be in most to least significant order). --- .../threerings/getdown/data/Application.java | 61 ++++++++----------- .../com/threerings/getdown/data/SysProps.java | 57 +++++++++++++++++ .../threerings/getdown/data/SysPropsTest.java | 21 +++++++ 3 files changed, 104 insertions(+), 35 deletions(-) create mode 100644 src/test/java/com/threerings/getdown/data/SysPropsTest.java diff --git a/src/main/java/com/threerings/getdown/data/Application.java b/src/main/java/com/threerings/getdown/data/Application.java index 63f8d87..f700301 100644 --- a/src/main/java/com/threerings/getdown/data/Application.java +++ b/src/main/java/com/threerings/getdown/data/Application.java @@ -562,15 +562,15 @@ public class Application // check to see if we require a particular JVM version and have a supplied JVM vstr = (String)cdata.get("java_version"); - if (vstr != null) _javaMinVersion = (int)parseLong(vstr, "m.invalid_java_version"); + if (vstr != null) _javaMinVersion = parseLong(vstr, "m.invalid_java_version"); // we support java_min_version as an alias of java_version; it better expresses the check // that's going on and better mirrors java_max_version vstr = (String)cdata.get("java_min_version"); - if (vstr != null) _javaMinVersion = (int)parseLong(vstr, "m.invalid_java_version"); + if (vstr != null) _javaMinVersion = parseLong(vstr, "m.invalid_java_version"); // check to see if we require a particular max JVM version and have a supplied JVM vstr = (String)cdata.get("java_max_version"); - if (vstr != null) _javaMaxVersion = (int)parseLong(vstr, "m.invalid_java_version"); + if (vstr != null) _javaMaxVersion = parseLong(vstr, "m.invalid_java_version"); // check to see if we require a particular JVM version and have a supplied JVM vstr = (String)cdata.get("java_exact_version_required"); @@ -782,46 +782,37 @@ public class Application public boolean haveValidJavaVersion () { // if we're doing no version checking, then yay! - if (_javaMinVersion == 0 && _javaMaxVersion == 0) { - return true; - } + if (_javaMinVersion == 0 && _javaMaxVersion == 0) return true; // if we have a fully unpacked VM assume it is the right version (TODO: don't) Resource vmjar = getJavaVMResource(); - if (vmjar != null && vmjar.isMarkedValid()) { - return true; - } + if (vmjar != null && vmjar.isMarkedValid()) return true; - // parse the version out of the java.version system property - String verstr = System.getProperty("java.version"); - Matcher m = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?.*").matcher(verstr); - if (!m.matches()) { + try { + // parse the version out of the java.version system property + long version = SysProps.parseJavaVersion( + "java.version", "(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?.*"); + + if (_javaExactVersionRequired) { + if (version == _javaMinVersion) return true; + else { + log.warning("An exact Java VM version is required.", "current", version, + "required", _javaMinVersion); + return false; + } + } + + boolean minVersionOK = (_javaMinVersion == 0) || (version >= _javaMinVersion); + boolean maxVersionOK = (_javaMaxVersion == 0) || (version <= _javaMaxVersion); + return minVersionOK && maxVersionOK; + + } catch (RuntimeException re) { // if we can't parse the java version we're in weird land and should probably just try // our luck with what we've got rather than try to download a new jvm log.warning("Unable to parse VM version, hoping for the best", - "version", verstr, "needed", _javaMinVersion); + "error", re, "needed", _javaMinVersion); return true; } - - int major = Integer.parseInt(m.group(1)); - int minor = Integer.parseInt(m.group(2)); - int revis = Integer.parseInt(m.group(3)); - int patch = m.group(4) == null ? 0 : Integer.parseInt(m.group(4).substring(1)); - int version = patch + 100 * (revis + 100 * (minor + 100 * major)); - - if (_javaExactVersionRequired) { - if (version == _javaMinVersion) { - return true; - } else { - log.warning("An exact Java VM version is required.", "current", version, - "required", _javaMinVersion); - return false; - } - } - - boolean minVersionOK = (_javaMinVersion == 0) || (version >= _javaMinVersion); - boolean maxVersionOK = (_javaMaxVersion == 0) || (version <= _javaMaxVersion); - return minVersionOK && maxVersionOK; } /** @@ -1729,7 +1720,7 @@ public class Application protected long _trackingStart; protected int _trackingId; - protected int _javaMinVersion, _javaMaxVersion; + protected long _javaMinVersion, _javaMaxVersion; protected boolean _javaExactVersionRequired; protected String _javaLocation; diff --git a/src/main/java/com/threerings/getdown/data/SysProps.java b/src/main/java/com/threerings/getdown/data/SysProps.java index 3ab2c32..3359b24 100644 --- a/src/main/java/com/threerings/getdown/data/SysProps.java +++ b/src/main/java/com/threerings/getdown/data/SysProps.java @@ -5,6 +5,9 @@ package com.threerings.getdown.data; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * This class encapsulates all system properties that are read and processed by Getdown. Don't * stick a call to {@code System.getProperty} randomly into the code, put it in here and give it an @@ -79,4 +82,58 @@ public class SysProps public static int connectTimeout () { return Integer.getInteger("connect_timeout", 0); } + + /** Parses a Java version system property using the supplied regular expression. The numbers + * extracted from the regexp will be placed in each consecutive hundreds position in the + * returned value. + * + *

For example, {@code java.version} takes the form {@code 1.8.0_31}, and with the regexp + * {@code (\d+)\.(\d+)\.(\d+)(_\d+)?} we would parse {@code 1, 8, 0, 31} and combine them into + * the final value {@code 1080031}. + * + *

Note that non-numeric characters matched by the regular expression will simply be + * ignored, and optional groups which do not match are treated as zero in the final version + * calculation. + * + *

One can instead parse {@code java.runtime.version} which takes the form {@code + * 1.8.0_31-b13}. Using regexp {@code (\d+)\.(\d+)\.(\d+)_(\d+)-b(\d+)} we would parse + * {@code 1, 8, 0, 31, 13} and combine them into a final value {@code 108003113}. + * + *

Other (or future) JVMs may provide different version properties which can be parsed as + * desired using this general scheme as long as the numbers appear from left to right in order + * of significance. + * + * @throws IllegalArgumentException if no system named {@code propName} exists, or if + * {@code propRegex} does not match the returned version string. + */ + public static long parseJavaVersion (String propName, String propRegex) { + String verstr = System.getProperty(propName); + if (verstr == null) throw new IllegalArgumentException( + "No system property '" + propName + "'."); + + Matcher m = Pattern.compile(propRegex).matcher(verstr); + if (!m.matches()) throw new IllegalArgumentException( + "Regexp '" + propRegex + "' does not match '" + verstr + "' (from " + propName + ")"); + + long vers = 0L; + for (int ii = 1; ii <= m.groupCount(); ii++) { + String valstr = m.group(ii); + int value = (valstr == null) ? 0 : parseInt(valstr); + vers *= 100; + vers += value; + } + return vers; + } + + private static int parseInt (String str) { + int value = 0; + for (int ii = 0, ll = str.length(); ii < ll; ii++) { + char c = str.charAt(ii); + if (c >= '0' && c <= '9') { + value *= 10; + value += (c - '0'); + } + } + return value; + } } diff --git a/src/test/java/com/threerings/getdown/data/SysPropsTest.java b/src/test/java/com/threerings/getdown/data/SysPropsTest.java new file mode 100644 index 0000000..06b964f --- /dev/null +++ b/src/test/java/com/threerings/getdown/data/SysPropsTest.java @@ -0,0 +1,21 @@ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2014 Three Rings Design, Inc. +// https://raw.github.com/threerings/getdown/master/LICENSE + +package com.threerings.getdown.data; + +import org.junit.*; +import static org.junit.Assert.*; + +public class SysPropsTest { + + @Test public void testParseJavaVersion () { + long vers = SysProps.parseJavaVersion("java.version", "(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?"); + assert(vers > 1060000); + + long runVers = SysProps.parseJavaVersion("java.runtime.version", + "(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?(-b\\d+)?"); + assert(runVers > 106000000); + } +}