Further cleanups. We now report progress to the best of our ability modulo
some deplorable oversight on the part of JarDiffPatcher's author.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Application.java,v 1.11 2004/07/14 12:11:46 mdb Exp $
|
||||
// $Id: Application.java,v 1.12 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
@@ -32,6 +32,8 @@ import org.apache.commons.io.CopyUtils;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.util.ConfigUtil;
|
||||
import com.threerings.getdown.util.MetaProgressObserver;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
|
||||
/**
|
||||
* Parses and provide access to the information contained in the
|
||||
@@ -429,13 +431,13 @@ public class Application
|
||||
|
||||
// now verify the contents of our main config file
|
||||
Resource crsrc = getConfigResource();
|
||||
if (!_digest.validateResource(crsrc)) {
|
||||
if (!_digest.validateResource(crsrc, null)) {
|
||||
// attempt to redownload the file; again we pass errors up to
|
||||
// our caller because we have no recourse to recovery
|
||||
downloadControlFile(CONFIG_FILE);
|
||||
// if the new copy validates, reinitialize ourselves;
|
||||
// otherwise report baffling hoseage
|
||||
if (_digest.validateResource(crsrc)) {
|
||||
if (_digest.validateResource(crsrc, null)) {
|
||||
init();
|
||||
}
|
||||
}
|
||||
@@ -473,11 +475,46 @@ public class Application
|
||||
* to go, null will be returned and the application is considered
|
||||
* ready to run.
|
||||
*/
|
||||
public List verifyResources ()
|
||||
public List verifyResources (ProgressObserver obs)
|
||||
{
|
||||
ArrayList failures = new ArrayList();
|
||||
verifyResources(_codes.iterator(), failures);
|
||||
verifyResources(_resources.iterator(), failures);
|
||||
ArrayList rsrcs = new ArrayList(), failures = new ArrayList();
|
||||
rsrcs.addAll(_codes);
|
||||
rsrcs.addAll(_resources);
|
||||
|
||||
// total up the file size of the resources to validate
|
||||
long totalSize = 0L;
|
||||
for (Iterator iter = rsrcs.iterator(); iter.hasNext(); ) {
|
||||
Resource rsrc = (Resource)iter.next();
|
||||
totalSize += rsrc.getLocal().length();
|
||||
}
|
||||
|
||||
MetaProgressObserver mpobs = new MetaProgressObserver(obs, totalSize);
|
||||
for (Iterator iter = rsrcs.iterator(); iter.hasNext(); ) {
|
||||
Resource rsrc = (Resource)iter.next();
|
||||
mpobs.startElement(rsrc.getLocal().length());
|
||||
|
||||
if (rsrc.isMarkedValid()) {
|
||||
mpobs.progress(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (_digest.validateResource(rsrc, mpobs)) {
|
||||
// make a note that this file is kosher
|
||||
rsrc.markAsValid();
|
||||
continue;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.info("Failure validating resource [rsrc=" + rsrc +
|
||||
", error=" + e + "]. Requesting redownload...");
|
||||
|
||||
} finally {
|
||||
mpobs.progress(100);
|
||||
}
|
||||
failures.add(rsrc);
|
||||
}
|
||||
|
||||
return (failures.size() == 0) ? null : failures;
|
||||
}
|
||||
|
||||
@@ -500,30 +537,6 @@ public class Application
|
||||
StringUtil.replace(_appbase, "%VERSION%", "" + version));
|
||||
}
|
||||
|
||||
/** A helper function used by {@link #verifyResources()}. */
|
||||
protected void verifyResources (Iterator rsrcs, List failures)
|
||||
{
|
||||
while (rsrcs.hasNext()) {
|
||||
Resource rsrc = (Resource)rsrcs.next();
|
||||
if (rsrc.isMarkedValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if (_digest.validateResource(rsrc)) {
|
||||
// make a note that this file is kosher
|
||||
rsrc.markAsValid();
|
||||
continue;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.info("Failure validating resource [rsrc=" + rsrc +
|
||||
", error=" + e + "]. Requesting redownload...");
|
||||
}
|
||||
failures.add(rsrc);
|
||||
}
|
||||
}
|
||||
|
||||
/** Clears all validation marker files for the resources in the
|
||||
* supplied iterator. */
|
||||
protected void clearValidationMarkers (Iterator iter)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Digest.java,v 1.3 2004/07/06 09:46:35 mdb Exp $
|
||||
// $Id: Digest.java,v 1.4 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.util.ConfigUtil;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
|
||||
/**
|
||||
* Manages the <code>digest.txt</code> file and the computing and
|
||||
@@ -79,10 +80,10 @@ public class Digest
|
||||
* digest check or if an I/O error was encountered during the
|
||||
* validation process.
|
||||
*/
|
||||
public boolean validateResource (Resource resource)
|
||||
public boolean validateResource (Resource resource, ProgressObserver obs)
|
||||
{
|
||||
try {
|
||||
String cmd5 = resource.computeDigest(getMessageDigest());
|
||||
String cmd5 = resource.computeDigest(getMessageDigest(), obs);
|
||||
String emd5 = (String)_digests.get(resource.getPath());
|
||||
if (cmd5.equals(emd5)) {
|
||||
return true;
|
||||
@@ -112,7 +113,7 @@ public class Digest
|
||||
for (Iterator iter = resources.iterator(); iter.hasNext(); ) {
|
||||
Resource rsrc = (Resource)iter.next();
|
||||
String path = rsrc.getPath();
|
||||
String digest = rsrc.computeDigest(md);
|
||||
String digest = rsrc.computeDigest(md, null);
|
||||
note(data, path, digest);
|
||||
pout.println(path + " = " + digest);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Resource.java,v 1.9 2004/07/14 12:14:44 mdb Exp $
|
||||
// $Id: Resource.java,v 1.10 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.samskivert.util.SortableArrayList;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
|
||||
/**
|
||||
* Models a single file resource used by an {@link Application}.
|
||||
@@ -68,7 +69,7 @@ public class Resource
|
||||
* Computes the MD5 hash of this resource's underlying file.
|
||||
* <em>Note:</em> This is both CPU and I/O intensive.
|
||||
*/
|
||||
public String computeDigest (MessageDigest md)
|
||||
public String computeDigest (MessageDigest md, ProgressObserver obs)
|
||||
throws IOException
|
||||
{
|
||||
md.reset();
|
||||
@@ -85,11 +86,13 @@ public class Resource
|
||||
CollectionUtil.addAll(entries, jar.entries());
|
||||
entries.sort(ENTRY_COMP);
|
||||
|
||||
int eidx = 0;
|
||||
for (Iterator iter = entries.iterator(); iter.hasNext(); ) {
|
||||
JarEntry entry = (JarEntry)iter.next();
|
||||
|
||||
// skip metadata; we just want the goods
|
||||
if (entry.getName().startsWith("META-INF")) {
|
||||
updateProgress(obs, eidx, entries.size());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -103,6 +106,7 @@ public class Resource
|
||||
} finally {
|
||||
StreamUtil.close(in);
|
||||
}
|
||||
updateProgress(obs, eidx, entries.size());
|
||||
}
|
||||
|
||||
} finally {
|
||||
@@ -115,11 +119,14 @@ public class Resource
|
||||
}
|
||||
|
||||
} else {
|
||||
long totalSize = _local.length(), position = 0L;
|
||||
FileInputStream fin = null;
|
||||
try {
|
||||
fin = new FileInputStream(_local);
|
||||
while ((read = fin.read(buffer)) != -1) {
|
||||
md.update(buffer, 0, read);
|
||||
position += read;
|
||||
updateProgress(obs, position, totalSize);
|
||||
}
|
||||
} finally {
|
||||
StreamUtil.close(fin);
|
||||
@@ -208,6 +215,14 @@ public class Resource
|
||||
return _path;
|
||||
}
|
||||
|
||||
/** Helper function to simplify the process of reporting progress. */
|
||||
protected void updateProgress (ProgressObserver obs, long pos, long total)
|
||||
{
|
||||
if (obs != null) {
|
||||
obs.progress((int)(100 * pos / total));
|
||||
}
|
||||
}
|
||||
|
||||
protected String _path;
|
||||
protected URL _remote;
|
||||
protected File _local, _marker;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
//
|
||||
// $Id: Getdown.java,v 1.9 2004/07/13 17:45:40 mdb Exp $
|
||||
// $Id: Getdown.java,v 1.10 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.launcher;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@@ -29,15 +30,17 @@ import com.threerings.getdown.Log;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.tools.Patcher;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
|
||||
/**
|
||||
* Manages the main control for the Getdown application updater and
|
||||
* deployment system.
|
||||
*/
|
||||
public class Getdown
|
||||
public class Getdown extends Thread
|
||||
{
|
||||
public Getdown (File appDir)
|
||||
{
|
||||
super("Getdown");
|
||||
_app = new Application(appDir);
|
||||
_msgs = ResourceBundle.getBundle("com.threerings.getdown.messages");
|
||||
}
|
||||
@@ -57,6 +60,7 @@ public class Getdown
|
||||
for (int ii = 0; ii < MAX_LOOPS; ii++) {
|
||||
// make sure we have the desired version and that the
|
||||
// metadata files are valid...
|
||||
setStatus("m.validating", -1, -1L);
|
||||
if (_app.verifyMetadata()) {
|
||||
Log.info("Application requires update.");
|
||||
update();
|
||||
@@ -65,7 +69,7 @@ public class Getdown
|
||||
}
|
||||
|
||||
// now verify our resources...
|
||||
List failures = _app.verifyResources();
|
||||
List failures = _app.verifyResources(_progobs);
|
||||
if (failures == null) {
|
||||
Log.info("Resources verified.");
|
||||
launch();
|
||||
@@ -97,7 +101,7 @@ public class Getdown
|
||||
_app.clearValidationMarkers();
|
||||
|
||||
// attempt to download the patch file
|
||||
Resource patch = _app.getPatchResource();
|
||||
final Resource patch = _app.getPatchResource();
|
||||
if (patch != null) {
|
||||
// download the patch file...
|
||||
ArrayList list = new ArrayList();
|
||||
@@ -105,9 +109,10 @@ public class Getdown
|
||||
download(list);
|
||||
|
||||
// and apply it...
|
||||
setStatus("m.patching", -1, -1L);
|
||||
Patcher patcher = new Patcher();
|
||||
patcher.patch(patch.getLocal().getParentFile(), patch.getLocal(),
|
||||
null);
|
||||
patcher.patch(patch.getLocal().getParentFile(),
|
||||
patch.getLocal(), _progobs);
|
||||
}
|
||||
// if the patch resource is null, that means something was booched
|
||||
// in the application, so we skip the patching process but update
|
||||
@@ -133,12 +138,11 @@ public class Getdown
|
||||
// create a downloader to download our resources
|
||||
Downloader.Observer obs = new Downloader.Observer() {
|
||||
public void resolvingDownloads () {
|
||||
_status.setStatus(_msgs.getString("m.resolving"));
|
||||
setStatus("m.resolving", -1, -1L);
|
||||
}
|
||||
|
||||
public void downloadProgress (int percent, long remaining) {
|
||||
_status.setStatus(_msgs.getString("m.downloading"));
|
||||
_status.setProgress(percent, remaining);
|
||||
setStatus("m.downloading", percent, remaining);
|
||||
if (percent == 100) {
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
@@ -150,7 +154,7 @@ public class Getdown
|
||||
String msg = MessageFormat.format(
|
||||
_msgs.getString("m.failure"),
|
||||
new Object[] { e.getMessage() });
|
||||
_status.setStatus(msg);
|
||||
setStatus(msg, -1, -1L);
|
||||
Log.warning("Download failed [rsrc=" + rsrc + "].");
|
||||
Log.logStackTrace(e);
|
||||
synchronized (lock) {
|
||||
@@ -177,9 +181,7 @@ public class Getdown
|
||||
*/
|
||||
protected void launch ()
|
||||
{
|
||||
if (_status != null) {
|
||||
_status.setStatus(_msgs.getString("m.launching"));
|
||||
}
|
||||
setStatus("m.launching", 100, -1L);
|
||||
|
||||
try {
|
||||
Process proc = _app.createProcess();
|
||||
@@ -227,6 +229,23 @@ public class Getdown
|
||||
_frame.show();
|
||||
}
|
||||
|
||||
protected void setStatus (final String message, final int percent,
|
||||
final long remaining)
|
||||
{
|
||||
if (_status != null) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run () {
|
||||
if (message != null) {
|
||||
_status.setStatus(_msgs.getString(message));
|
||||
}
|
||||
if (percent >= 0) {
|
||||
_status.setProgress(percent, remaining);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
// maybe they specified the appdir in a system property
|
||||
@@ -261,12 +280,19 @@ public class Getdown
|
||||
|
||||
try {
|
||||
Getdown app = new Getdown(appDir);
|
||||
app.run();
|
||||
app.start();
|
||||
} catch (Exception e) {
|
||||
Log.logStackTrace(e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to pass progress on to our user interface. */
|
||||
protected ProgressObserver _progobs = new ProgressObserver() {
|
||||
public void progress (final int percent) {
|
||||
setStatus(null, percent, -1L);
|
||||
}
|
||||
};
|
||||
|
||||
protected Application _app;
|
||||
protected Application.UpdateInterface _ifc;
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#
|
||||
# $Id: messages.properties,v 1.1 2004/07/07 10:45:20 mdb Exp $
|
||||
# $Id: messages.properties,v 1.2 2004/07/14 13:44:49 mdb Exp $
|
||||
#
|
||||
# Getdown translation messages
|
||||
|
||||
m.resolving = Resolving downloads...
|
||||
m.downloading = Downloading data...
|
||||
m.failure = Download failed: {0}
|
||||
m.launching = Launching application...
|
||||
|
||||
m.validating = Validating...
|
||||
m.patching = Patching...
|
||||
m.launching = Launching...
|
||||
|
||||
m.complete = {0}% complete
|
||||
m.complete_remain = {0}% complete {1}s remaining
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// $Id: Patcher.java,v 1.1 2004/07/13 17:45:40 mdb Exp $
|
||||
// $Id: Patcher.java,v 1.2 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.tools;
|
||||
|
||||
@@ -12,24 +12,22 @@ import java.util.Enumeration;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import com.sun.javaws.cache.Patcher.PatchDelegate;
|
||||
import com.sun.javaws.jardiff.JarDiffPatcher;
|
||||
|
||||
import com.samskivert.io.StreamUtil;
|
||||
import org.apache.commons.io.CopyUtils;
|
||||
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
|
||||
/**
|
||||
* Applies a unified patch file to an application directory, providing
|
||||
* percentage completion feedback along the way.
|
||||
* percentage completion feedback along the way. <em>Note:</em> the
|
||||
* patcher is not thread safe. Create a separate patcher instance for each
|
||||
* patching action that is desired.
|
||||
*/
|
||||
public class Patcher
|
||||
{
|
||||
/** Used to communicate patching progress. */
|
||||
public static interface Observer
|
||||
{
|
||||
/** Informs the observer that we have completed the specified
|
||||
* percentage of the patching process. */
|
||||
public void progress (int percent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the specified patch file to the application living in the
|
||||
* specified application directory. The supplied observer, if
|
||||
@@ -40,14 +38,19 @@ public class Patcher
|
||||
* with the patcher so that the user interface is not blocked for the
|
||||
* duration of the patch.
|
||||
*/
|
||||
public void patch (File appdir, File patch, Observer obs)
|
||||
public void patch (File appdir, File patch, ProgressObserver obs)
|
||||
throws IOException
|
||||
{
|
||||
// save this information for later
|
||||
_obs = obs;
|
||||
_plength = patch.length();
|
||||
|
||||
JarFile file = new JarFile(patch);
|
||||
Enumeration entries = file.entries(); // old skool!
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = (ZipEntry)entries.nextElement();
|
||||
String path = entry.getName();
|
||||
long elength = entry.getCompressedSize();
|
||||
|
||||
// depending on the suffix, we do The Right Thing (tm)
|
||||
if (path.endsWith(Differ.CREATE)) {
|
||||
@@ -71,6 +74,9 @@ public class Patcher
|
||||
} else {
|
||||
System.err.println("Skipping bogus patch file entry: " + path);
|
||||
}
|
||||
|
||||
// note that we've completed this entry
|
||||
_complete += elength;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,11 +87,22 @@ public class Patcher
|
||||
|
||||
protected void createFile (JarFile file, ZipEntry entry, File target)
|
||||
{
|
||||
// create our copy buffer if necessary
|
||||
if (_buffer == null) {
|
||||
_buffer = new byte[COPY_BUFFER_SIZE];
|
||||
}
|
||||
|
||||
InputStream in = null;
|
||||
FileOutputStream fout = null;
|
||||
try {
|
||||
CopyUtils.copy(in = file.getInputStream(entry),
|
||||
fout = new FileOutputStream(target));
|
||||
in = file.getInputStream(entry);
|
||||
fout = new FileOutputStream(target);
|
||||
int total = 0, read;
|
||||
while ((read = in.read(_buffer)) != -1) {
|
||||
total += read;
|
||||
fout.write(_buffer, 0, read);
|
||||
updateProgress(total);
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
System.err.println("Error creating '" + target + "': " + ioe);
|
||||
@@ -122,10 +139,20 @@ public class Patcher
|
||||
return;
|
||||
}
|
||||
|
||||
// we'll need this to pass progress along to our observer
|
||||
final String which = entry.getName();
|
||||
final long elength = entry.getCompressedSize();
|
||||
PatchDelegate pd = new PatchDelegate() {
|
||||
public void patching (int percent) {
|
||||
// System.out.println(which + ": " + percent);
|
||||
updateProgress((int)(percent * elength / 100));
|
||||
}
|
||||
};
|
||||
|
||||
// now apply the patch to create the new target file
|
||||
patcher = new JarDiffPatcher();
|
||||
fout = new FileOutputStream(target);
|
||||
patcher.applyPatch(null, otarget.getPath(), patch.getPath(), fout);
|
||||
patcher.applyPatch(pd, otarget.getPath(), patch.getPath(), fout);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
if (patcher == null) {
|
||||
@@ -144,6 +171,13 @@ public class Patcher
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateProgress (int progress)
|
||||
{
|
||||
if (_obs != null) {
|
||||
_obs.progress((int)(100 * (_complete + progress) / _plength));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
if (args.length != 2) {
|
||||
@@ -159,4 +193,10 @@ public class Patcher
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
protected ProgressObserver _obs;
|
||||
protected long _complete, _plength;
|
||||
protected byte[] _buffer;
|
||||
|
||||
protected static final int COPY_BUFFER_SIZE = 4096;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id: MetaProgressObserver.java,v 1.1 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
/**
|
||||
* Accumulates the progress from a number of elements into a single
|
||||
* smoothly progressing progress.
|
||||
*/
|
||||
public class MetaProgressObserver implements ProgressObserver
|
||||
{
|
||||
public MetaProgressObserver (ProgressObserver target, long totalSize)
|
||||
{
|
||||
_target = target;
|
||||
_totalSize = totalSize;
|
||||
}
|
||||
|
||||
public void startElement (long elementSize)
|
||||
{
|
||||
_currentSize += elementSize;
|
||||
_elementSize = elementSize;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void progress (int percent)
|
||||
{
|
||||
if (_target != null && _elementSize > 0) {
|
||||
long position = _currentSize + (100 * percent / _elementSize);
|
||||
_target.progress((int)(100 * position / _totalSize));
|
||||
}
|
||||
}
|
||||
|
||||
protected ProgressObserver _target;
|
||||
protected long _totalSize, _currentSize, _elementSize;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// $Id: ProgressObserver.java,v 1.1 2004/07/14 13:44:49 mdb Exp $
|
||||
|
||||
package com.threerings.getdown.util;
|
||||
|
||||
/**
|
||||
* Used to communicate progress.
|
||||
*/
|
||||
public interface ProgressObserver
|
||||
{
|
||||
/**
|
||||
* Informs the observer that we have completed the specified
|
||||
* percentage of the process.
|
||||
*/
|
||||
public void progress (int percent);
|
||||
}
|
||||
Reference in New Issue
Block a user