Merge pull request #60 from runiter/master

Avoid corrupted resources when download is interrupted
This commit is contained in:
Michael Bayne
2016-11-04 16:54:26 -07:00
committed by GitHub
5 changed files with 73 additions and 6 deletions
@@ -35,6 +35,7 @@ public class Resource
_path = path; _path = path;
_remote = remote; _remote = remote;
_local = local; _local = local;
_local_new = new File(local.toString() + "_new");
String lpath = _local.getPath(); String lpath = _local.getPath();
_marker = new File(lpath + "v"); _marker = new File(lpath + "v");
@@ -66,6 +67,11 @@ public class Resource
return _local; return _local;
} }
public File getLocalNew ()
{
return _local_new;
}
/** /**
* Returns the location of the unpacked resource. * Returns the location of the unpacked resource.
*/ */
@@ -106,7 +112,16 @@ public class Resource
public String computeDigest (MessageDigest md, ProgressObserver obs) public String computeDigest (MessageDigest md, ProgressObserver obs)
throws IOException throws IOException
{ {
return computeDigest(_local, md, obs); File file;
if (_local.toString().toLowerCase().endsWith(Application.CONFIG_FILE))
file = _local;
else {
if (_local_new.exists())
file = _local_new;
else
file = _local;
}
return computeDigest(file, md, obs);
} }
/** /**
@@ -284,7 +299,7 @@ public class Resource
protected static boolean isJar (String path) protected static boolean isJar (String path)
{ {
return path.endsWith(".jar"); return path.endsWith(".jar") || path.endsWith(".jar_new");
} }
protected static boolean isPacked200Jar (String path) protected static boolean isPacked200Jar (String path)
@@ -294,7 +309,7 @@ public class Resource
protected String _path; protected String _path;
protected URL _remote; protected URL _remote;
protected File _local, _marker, _unpacked; protected File _local, _local_new, _marker, _unpacked;
protected boolean _unpack, _isJar, _isPacked200Jar; protected boolean _unpack, _isJar, _isPacked200Jar;
/** Used to sort the entries in a jar file. */ /** Used to sort the entries in a jar file. */
@@ -49,6 +49,10 @@ public class SysProps
return System.getProperty("silent") != null; return System.getProperty("silent") != null;
} }
public static boolean install () {
return System.getProperty("no_install") == null;
}
/** If true, Getdown installs the app without ever bringing up a UI and then launches it. /** If true, Getdown installs the app without ever bringing up a UI and then launches it.
* Usage: {@code -Dsilent=launch}. */ * Usage: {@code -Dsilent=launch}. */
public static boolean launchInSilent () { public static boolean launchInSilent () {
@@ -68,6 +68,7 @@ import com.threerings.getdown.net.HTTPDownloader;
import com.threerings.getdown.tools.Patcher; import com.threerings.getdown.tools.Patcher;
import com.threerings.getdown.util.ConfigUtil; import com.threerings.getdown.util.ConfigUtil;
import com.threerings.getdown.util.ConnectionUtil; import com.threerings.getdown.util.ConnectionUtil;
import com.threerings.getdown.util.FileUtil;
import com.threerings.getdown.util.LaunchUtil; import com.threerings.getdown.util.LaunchUtil;
import com.threerings.getdown.util.ProgressAggregator; import com.threerings.getdown.util.ProgressAggregator;
import com.threerings.getdown.util.ProgressObserver; import com.threerings.getdown.util.ProgressObserver;
@@ -100,6 +101,7 @@ public abstract class Getdown extends Thread
// If the silent property exists, install without bringing up any gui. If it equals // If the silent property exists, install without bringing up any gui. If it equals
// launch, start the application after installing. Otherwise, just install and exit. // launch, start the application after installing. Otherwise, just install and exit.
_silent = SysProps.silent(); _silent = SysProps.silent();
_install = SysProps.install();
if (_silent) { if (_silent) {
_launchInSilent = SysProps.launchInSilent(); _launchInSilent = SysProps.launchInSilent();
} }
@@ -409,6 +411,9 @@ public abstract class Getdown extends Thread
// we'll keep track of all the resources we unpack // we'll keep track of all the resources we unpack
Set<Resource> unpacked = new HashSet<Resource>(); Set<Resource> unpacked = new HashSet<Resource>();
toBeInstalledResouces = new ArrayList<Resource>();
readyToInstall = false;
//setStep(Step.START); //setStep(Step.START);
for (int ii = 0; ii < MAX_LOOPS; ii++) { for (int ii = 0; ii < MAX_LOOPS; ii++) {
@@ -442,6 +447,7 @@ public abstract class Getdown extends Thread
setStep(Step.VERIFY_RESOURCES); setStep(Step.VERIFY_RESOURCES);
setStatusAsync("m.validating", -1, -1L, false); setStatusAsync("m.validating", -1, -1L, false);
List<Resource> failures = _app.verifyResources(_progobs, alreadyValid, unpacked); List<Resource> failures = _app.verifyResources(_progobs, alreadyValid, unpacked);
addToBeInstalledResources(failures);
if (failures == null) { if (failures == null) {
log.info("Resources verified."); log.info("Resources verified.");
@@ -471,6 +477,10 @@ public abstract class Getdown extends Thread
} }
} }
readyToInstall = true;
if (_install)
install();
// Only launch if we aren't in silent mode. Some mystery program starting out // Only launch if we aren't in silent mode. Some mystery program starting out
// of the blue would be disconcerting. // of the blue would be disconcerting.
if (!_silent || _launchInSilent) { if (!_silent || _launchInSilent) {
@@ -531,7 +541,16 @@ public abstract class Getdown extends Thread
} }
} }
// documentation inherited from interface private void addToBeInstalledResources(List<Resource> resources) {
if (resources == null)
return;
else
for (Resource r : resources)
if (!toBeInstalledResouces.contains(r))
toBeInstalledResouces.add(r);
}
// documentation inherited from interface
public void updateStatus (String message) public void updateStatus (String message)
{ {
setStatusAsync(message, -1, -1L, true); setStatusAsync(message, -1, -1L, true);
@@ -754,7 +773,32 @@ public abstract class Getdown extends Thread
throw new MultipleGetdownRunning(); throw new MultipleGetdownRunning();
} }
} }
public static void install ()
throws IOException, InterruptedException
{
if (readyToInstall) {
log.info("Installing downloaded resources:");
for (Resource resource : toBeInstalledResouces) {
log.info(resource);
if (!FileUtil.renameTo(resource.getLocalNew(), resource.getLocal()))
throw new IOException("Failed to rename(" + resource.getLocalNew() + ", " + resource.getLocal() + ")");
if (Thread.interrupted()) {
throw new InterruptedException("m.applet_stopped");
}
}
toBeInstalledResouces.clear();
readyToInstall = false;
log.info("Install completed.");
} else {
log.info("Nothing to install.");
}
}
public static boolean isUpdateAvailable() {
return readyToInstall && !toBeInstalledResouces.isEmpty();
}
/** /**
* Called to launch the application if everything is determined to be ready to go. * Called to launch the application if everything is determined to be ready to go.
*/ */
@@ -1219,6 +1263,7 @@ public abstract class Getdown extends Thread
protected boolean _dead; protected boolean _dead;
protected boolean _silent; protected boolean _silent;
protected boolean _install;
protected boolean _launchInSilent; protected boolean _launchInSilent;
protected long _startup; protected long _startup;
@@ -1239,4 +1284,6 @@ public abstract class Getdown extends Thread
protected static final long PLAY_AGAIN_TIME = 3000L; protected static final long PLAY_AGAIN_TIME = 3000L;
protected static final String PROXY_REGISTRY = protected static final String PROXY_REGISTRY =
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
protected static List<Resource> toBeInstalledResouces;
protected static boolean readyToInstall;
} }
@@ -78,7 +78,7 @@ public class HTTPDownloader extends Downloader
long currentSize = 0L; long currentSize = 0L;
try { try {
in = conn.getInputStream(); in = conn.getInputStream();
out = new FileOutputStream(rsrc.getLocal()); out = new FileOutputStream(rsrc.getLocalNew());
int read; int read;
// TODO: look to see if we have a download info file // TODO: look to see if we have a download info file
@@ -60,6 +60,7 @@ public class FileUtil extends com.samskivert.util.FileUtil
fin = new FileInputStream(source); fin = new FileInputStream(source);
fout = new FileOutputStream(dest); fout = new FileOutputStream(dest);
StreamUtil.copy(fin, fout); StreamUtil.copy(fin, fout);
fin.close(); // needed otherwise cannot delete source
if (!source.delete()) { if (!source.delete()) {
log.warning("Failed to delete " + source + log.warning("Failed to delete " + source +
" after brute force copy to " + dest + "."); " after brute force copy to " + dest + ".");