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).
This commit is contained in:
Michael Bayne
2015-02-16 10:45:08 -08:00
parent 8e446d96e4
commit 985f2baaa1
3 changed files with 104 additions and 35 deletions
@@ -562,15 +562,15 @@ public class Application
// check to see if we require a particular JVM version and have a supplied JVM // check to see if we require a particular JVM version and have a supplied JVM
vstr = (String)cdata.get("java_version"); 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 // 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 // that's going on and better mirrors java_max_version
vstr = (String)cdata.get("java_min_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 // check to see if we require a particular max JVM version and have a supplied JVM
vstr = (String)cdata.get("java_max_version"); 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 // check to see if we require a particular JVM version and have a supplied JVM
vstr = (String)cdata.get("java_exact_version_required"); vstr = (String)cdata.get("java_exact_version_required");
@@ -782,46 +782,37 @@ public class Application
public boolean haveValidJavaVersion () public boolean haveValidJavaVersion ()
{ {
// if we're doing no version checking, then yay! // if we're doing no version checking, then yay!
if (_javaMinVersion == 0 && _javaMaxVersion == 0) { if (_javaMinVersion == 0 && _javaMaxVersion == 0) return true;
return true;
}
// if we have a fully unpacked VM assume it is the right version (TODO: don't) // if we have a fully unpacked VM assume it is the right version (TODO: don't)
Resource vmjar = getJavaVMResource(); Resource vmjar = getJavaVMResource();
if (vmjar != null && vmjar.isMarkedValid()) { if (vmjar != null && vmjar.isMarkedValid()) return true;
return true;
}
// parse the version out of the java.version system property try {
String verstr = System.getProperty("java.version"); // parse the version out of the java.version system property
Matcher m = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(_\\d+)?.*").matcher(verstr); long version = SysProps.parseJavaVersion(
if (!m.matches()) { "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 // 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 // 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", log.warning("Unable to parse VM version, hoping for the best",
"version", verstr, "needed", _javaMinVersion); "error", re, "needed", _javaMinVersion);
return true; 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 long _trackingStart;
protected int _trackingId; protected int _trackingId;
protected int _javaMinVersion, _javaMaxVersion; protected long _javaMinVersion, _javaMaxVersion;
protected boolean _javaExactVersionRequired; protected boolean _javaExactVersionRequired;
protected String _javaLocation; protected String _javaLocation;
@@ -5,6 +5,9 @@
package com.threerings.getdown.data; 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 * 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 * 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 () { public static int connectTimeout () {
return Integer.getInteger("connect_timeout", 0); 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.
*
* <p>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}.
*
* <p>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.
*
* <p>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}.
*
* <p>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;
}
} }
@@ -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);
}
}