Provide support for automatically downloading and installing a local Java

version from within Getdown if the user does not have the desired version for
the application.

This does not yet support upgrading a locally installed version of Java, right
now it assumes that if we have a locally installed version, it's good enough.
We can make that do the right thing too, later.
This commit is contained in:
Michael Bayne
2007-02-01 01:41:58 +00:00
parent 62ea52f83b
commit a2adc382a3
5 changed files with 180 additions and 48 deletions
@@ -49,6 +49,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.samskivert.io.StreamUtil;
import com.samskivert.text.MessageUtil;
@@ -277,6 +279,29 @@ public class Application
}
}
/**
* Returns a resource for a zip file containing a Java VM that can be downloaded to use in
* place of the installed VM (in the case where the VM that launched Getdown does not meet the
* application's version requirements) or null if no VM is available for this platform.
*/
public Resource getJavaVMResource ()
{
if (StringUtil.isBlank(_javaLocation)) {
return null;
}
String vmfile = LaunchUtil.LOCAL_JAVA_DIR + ".jar";
try {
URL remote = new URL(createVAppBase(_targetVersion), _javaLocation);
return new Resource(vmfile, remote, getLocalPath(vmfile), true);
} catch (Exception e) {
Log.warning("Failed to create VM resource [vmfile=" + vmfile + ", appbase=" + _appbase +
", tvers=" + _targetVersion + ", javaloc=" + _javaLocation +
", error=" + e + "].");
return null;
}
}
/**
* Returns a resource that can be used to download an archive containing all files belonging to
* the application.
@@ -493,7 +518,34 @@ public class Application
*/
public boolean haveValidJavaVersion ()
{
return true; // TODO
// if we're doing no version checking, then yay!
if (_javaVersion <= 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;
}
// 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()) {
// 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=" + _javaVersion + "].");
return true;
}
int one = Integer.parseInt(m.group(1)); // will there ever be a two?
int major = Integer.parseInt(m.group(2));
int minor = Integer.parseInt(m.group(3));
int patch = Integer.parseInt(m.group(4));
int version = patch + 100 * (minor + 100 * (major + 100 * one));
return version >= _javaVersion;
}
/**
@@ -548,7 +600,7 @@ public class Application
ArrayList<String> args = new ArrayList<String>();
// reconstruct the path to the JVM
args.add(LaunchUtil.getJVMPath(_windebug));
args.add(LaunchUtil.getJVMPath(_appdir, _windebug));
// add the classpath arguments
args.add("-classpath");
@@ -152,11 +152,9 @@ public class Resource
return false;
}
try {
return FileUtil.unpackJar(
new JarFile(_local), _local.getParentFile());
return FileUtil.unpackJar(new JarFile(_local), _local.getParentFile());
} catch (IOException ioe) {
Log.warning("Failed to create JarFile from '" +
_local + "': " + ioe);
Log.warning("Failed to create JarFile from '" + _local + "': " + ioe);
return false;
}
}
@@ -340,10 +340,17 @@ public abstract class Getdown extends Thread
createInterface(true);
}
// if we aren't running in a JVM that meets our version requirements, either complain
// or attempt to download and install the appropriate version
for (int ii = 0; ii < MAX_LOOPS; ii++) {
// if we aren't running in a JVM that meets our version requirements, either
// complain or attempt to download and install the appropriate version
if (!_app.haveValidJavaVersion()) {
// download and install the necessary version of java, then loop back again and
// reverify everything; if we can't download java; we'll throw an exception
Log.info("Attempting to update Java VM...");
updateJava();
continue;
}
// make sure we have the desired version and that the metadata files are valid...
setStatus("m.validating", -1, -1L, false);
if (_app.verifyMetadata(this)) {
@@ -397,6 +404,47 @@ public abstract class Getdown extends Thread
setStatus(message, -1, -1L, true);
}
/**
* Downloads and installs an Java VM bundled with the application. This is called if we are not
* running with the necessary Java version.
*/
protected void updateJava ()
throws IOException
{
Resource vmjar = _app.getJavaVMResource();
if (vmjar == null) {
throw new IOException("m.java_download_failed");
}
updateStatus("m.downloading_java");
ArrayList<Resource> list = new ArrayList<Resource>();
list.add(vmjar);
download(list);
updateStatus("m.unpacking_java");
if (!vmjar.unpack()) {
throw new IOException("m.java_unpack_failed");
}
vmjar.markAsValid();
// Sun, why dost thou spite me? Java doesn't know anything about file permissions (and by
// extension then, neither does Jar), so on Joonix we have to hackily make java_vm/bin/java
// executable by execing chmod; a pox on their children!
if (RunAnywhere.isLinux()) {
String vmbin = LaunchUtil.LOCAL_JAVA_DIR + File.separator + "bin" +
File.separator + "java";
String cmd = "chmod a+rx " + _app.getLocalPath(vmbin);
try {
Log.info("Please smack a Java engineer. Running: " + cmd);
Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
Log.warning("Failed to mark VM binary as executable [cmd=" + cmd +
", error=" + e + "].");
// we should do something like tell the user or something but fucking fuck
}
}
}
/**
* Called if the application is determined to be of an old version.
*/
@@ -480,8 +528,7 @@ public abstract class Getdown extends Thread
}
public void downloadFailed (Resource rsrc, Exception e) {
updateStatus(
MessageUtil.tcompose("m.failure", e.getMessage()));
updateStatus(MessageUtil.tcompose("m.failure", e.getMessage()));
Log.warning("Download failed [rsrc=" + rsrc + "].");
Log.logStackTrace(e);
synchronized (lock) {
@@ -643,22 +690,20 @@ public abstract class Getdown extends Thread
createInterface(false);
}
if (_status != null) {
EventQueue.invokeLater(new Runnable() {
public void run () {
if (_status == null) {
Log.info("Dropping status '" + message + "'.");
return;
}
if (message != null) {
_status.setStatus(message);
}
if (percent >= 0) {
_status.setProgress(percent, remaining);
}
EventQueue.invokeLater(new Runnable() {
public void run () {
if (_status == null) {
Log.info("Dropping status '" + message + "'.");
return;
}
});
}
if (message != null) {
_status.setStatus(message);
}
if (percent >= 0) {
_status.setProgress(percent, remaining);
}
}
});
}
/**
@@ -32,6 +32,9 @@ m.proxy_port = Proxy port
m.proxy_ok = OK
m.proxy_cancel = Cancel
m.downloading_java = Downloading Java Virtual Machine...
m.unpacking_java = Unpacking Java Virtual Machine...
m.resolving = Resolving downloads...
m.downloading = Downloading data...
m.failure = Download failed: {0}
@@ -49,6 +52,17 @@ m.updating_metadata = Downloading control files...
m.init_failed = Our configuration file is missing or corrupt. Attempting \
to download a new copy...
m.java_download_failed = We were unable to automatically download the \
necessary verson of Java for your computer.\n\n\
Please go to www.java.com and download the latest version of \
Java, then try running the application again.
m.java_unpack_failed = We were unable to unpack an updated version of \
Java. Please make sure you have at least 100 MB of free space on your \
harddrive and try running the application again.\n\n\
If that does not solve the problem, go to www.java.com and download and \
install the latest version of Java and try again.
m.unable_to_repair = We were unable to download the necessary files after \
five attempts. You can try running the application again, but if it \
fails you may need to uninstall and reinstall.
@@ -35,43 +35,40 @@ import com.threerings.getdown.Log;
*/
public class LaunchUtil
{
/** The directory into which a local VM installation should be unpacked. */
public static final String LOCAL_JAVA_DIR = "java_vm";
/**
* Reconstructs the path to the JVM used to launch this process.
*/
public static String getJVMPath ()
public static String getJVMPath (File appdir)
{
return getJVMPath(false);
return getJVMPath(appdir, false);
}
/**
* Reconstructs the path to the JVM used to launch this process.
*
* @param windebug if true we will use java.exe instead of javaw.exe on
* Windows.
* @param windebug if true we will use java.exe instead of javaw.exe on Windows.
*/
public static String getJVMPath (boolean windebug)
public static String getJVMPath (File appdir, boolean windebug)
{
String apbase = System.getProperty("java.home") +
File.separator + "bin" + File.separator;
String apath = apbase + "java";
if (new File(apath).exists()) {
return apath;
// first look in our application directory for an installed VM
String vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR).getPath(), windebug);
if (vmpath != null) {
return vmpath;
}
if (!windebug) {
apath = apbase + "javaw.exe";
if (new File(apath).exists()) {
return apath;
}
// then fall back to the VM in which we're already running
vmpath = checkJVMPath(System.getProperty("java.home"), windebug);
if (vmpath != null) {
return vmpath;
}
apath = apbase + "java.exe";
if (new File(apath).exists()) {
return apath;
}
Log.warning("Unable to find java! [jhome=" + apbase + "].");
return apbase + "java";
// then throw up our hands and hope for the best
Log.warning("Unable to find java [appdir=" + appdir +
", java.home=" + System.getProperty("java.home") + "]!");
return "java";
}
/**
@@ -142,4 +139,30 @@ public class LaunchUtil
return (osname.indexOf("windows 98") != -1 ||
osname.indexOf("windows me") != -1);
}
/**
* Checks whether a Java Virtual Machine can be located in the supplied path.
*/
protected static String checkJVMPath (String vmhome, boolean windebug)
{
String vmbase = vmhome + File.separator + "bin" + File.separator;
String vmpath = vmbase + "java";
if (new File(vmpath).exists()) {
return vmpath;
}
if (!windebug) {
vmpath = vmbase + "javaw.exe";
if (new File(vmpath).exists()) {
return vmpath;
}
}
vmpath = vmbase + "java.exe";
if (new File(vmpath).exists()) {
return vmpath;
}
return null;
}
}