From 636b792051df7dc5452217f1bff6157ec13e6df1 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Tue, 5 Aug 2003 01:33:20 +0000 Subject: [PATCH] The great big resource manager revamp: the resource manager now supports the JNLP versioned resource protocol so that it can download jardiffs instead of whole new jar files when a resource bundle is updated. Unversioned http downloads (which we'll probably continue to use for the dynamically generated island bundles) are currently not working. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@2739 542714f4-19e9-0310-aa3c-eee0fc999fb1 --- .../threerings/resource/DownloadManager.java | 243 ++++-------------- .../com/threerings/resource/Downloader.java | 153 +++++++++++ .../threerings/resource/FileDownloader.java | 33 +++ .../threerings/resource/JNLPDownloader.java | 236 +++++++++++++++++ .../com/threerings/resource/ProgressInfo.java | 60 +++++ .../threerings/resource/ResourceManager.java | 67 ++++- 6 files changed, 593 insertions(+), 199 deletions(-) create mode 100644 src/java/com/threerings/resource/Downloader.java create mode 100644 src/java/com/threerings/resource/FileDownloader.java create mode 100644 src/java/com/threerings/resource/JNLPDownloader.java create mode 100644 src/java/com/threerings/resource/ProgressInfo.java diff --git a/src/java/com/threerings/resource/DownloadManager.java b/src/java/com/threerings/resource/DownloadManager.java index 8c5f94cda..d3a9b53de 100644 --- a/src/java/com/threerings/resource/DownloadManager.java +++ b/src/java/com/threerings/resource/DownloadManager.java @@ -1,5 +1,5 @@ // -// $Id: DownloadManager.java,v 1.7 2003/05/27 07:51:49 mdb Exp $ +// $Id: DownloadManager.java,v 1.8 2003/08/05 01:33:20 mdb Exp $ package com.threerings.resource; @@ -61,8 +61,13 @@ public class DownloadManager public void downloadProgress (int percent, long remaining); /** - * Called after the download has completed on the download manager - * thread, immediately after reporting download progress of 100%. + * Called on the download thread during the patching of jar files. + */ + public void patchingProgress (int percent); + + /** + * Called after the download and patching has completed on the + * download manager thread. */ public void postDownloadHook (); @@ -95,11 +100,11 @@ public class DownloadManager /** The destination file to which the file is to be written. */ public File destFile; - /** The last-modified timestamp of the source file. */ - public long lastModified; + /** The version of the file to be downloaded. */ + public String version; - /** The size in bytes of the source files. */ - public long fileSize; + /** The last-modified timestamp of the source. */ + public long lastModified; /** The last-modified timestamp of the destination file. */ public long destLastModified; @@ -108,13 +113,32 @@ public class DownloadManager public long destFileSize; /** - * Constructs a download descriptor to retrieve the given file - * from the given URL. + * Constructs a download descriptor to retrieve the specified + * version of the given file from the given URL. */ - public DownloadDescriptor (URL url, File file) + public DownloadDescriptor (URL url, File file, String version) { this.sourceURL = url; this.destFile = file; + this.version = version; + } + + /** + * Creates the appropriate type of downloader for this descriptor. + */ + public Downloader createDownloader () + throws IOException + { + String protocol = sourceURL.getProtocol(); + if (protocol.equals("file")) { + return new FileDownloader(); + } else if (protocol.equals("http")) { + return new JNLPDownloader(); + } else { + throw new IOException( + "Unknown source file protocol " + + "[protocol=" + protocol + ", desc=" + this + "]."); + } } /** Returns a string representation of this instance. */ @@ -218,51 +242,10 @@ public class DownloadManager DownloadDescriptor desc = (DownloadDescriptor)descriptors.get(ii); try { - // get the source file information - desc.fileSize = 0; - desc.lastModified = 0; - String protocol = desc.sourceURL.getProtocol(); - if (protocol.equals("file")) { - // read the file information directly from the file system - File tfile = new File(desc.sourceURL.getPath()); - desc.fileSize = tfile.length(); - desc.lastModified = tfile.lastModified(); - - } else if (protocol.equals("http")) { - // read the file information via an HTTP HEAD request - HttpURLConnection ucon = (HttpURLConnection) - desc.sourceURL.openConnection(); - ucon.setRequestMethod("HEAD"); - ucon.connect(); - desc.fileSize = ucon.getContentLength(); - desc.lastModified = ucon.getLastModified(); - - } else { - throw new IOException( - "Unknown source file protocol " + - "[protocol=" + protocol + ", desc=" + desc + "]."); - } - - // determine whether we actually need to fetch the file by - // checking to see whether we have a local copy at all, - // and if so, whether its file size or last-modified time - // differs from the source file - desc.destFileSize = desc.destFile.length(); - desc.destLastModified = desc.destFile.lastModified(); - if ((!desc.destFile.exists()) || - (desc.destFileSize != desc.fileSize) || - (desc.destLastModified < (desc.lastModified - DELTA)) || - (desc.destLastModified > (desc.lastModified + DELTA))) { - // increment the total file size to be fetched - pinfo.totalSize += desc.fileSize; - // add the file to the list of files to be fetched - fetch.add(desc); - Log.debug("File deemed stale " + - "[url=" + desc.sourceURL + "]."); - - } else { - Log.debug("File deemed up-to-date " + - "[url=" + desc.sourceURL + "]."); + Downloader loader = desc.createDownloader(); + loader.init(desc); + if (loader.checkUpdate(pinfo)) { + fetch.add(loader); } } catch (final IOException ioe) { @@ -273,15 +256,16 @@ public class DownloadManager } } + Log.info("Initiating download of " + pinfo.totalSize + " bytes."); // download all stale files size = fetch.size(); pinfo.start = System.currentTimeMillis(); for (int ii = 0; ii < size; ii++) { - DownloadDescriptor desc = (DownloadDescriptor)fetch.get(ii); + Downloader loader = (Downloader)fetch.get(ii); try { - processDownload(desc, obs, pinfo); + loader.processDownload(this, obs, pinfo, _buffer); } catch (IOException ioe) { - notifyFailed(obs, desc, ioe); + notifyFailed(obs, loader.getDescriptor(), ioe); if (fragile) { return; } @@ -290,9 +274,7 @@ public class DownloadManager // make sure to always let the observer know that we've wrapped up // by reporting 100% completion - if (!pinfo.complete) { - notifyProgress(obs, 100, 0L); - } + notifyProgress(obs, 100, 0L); } /** Helper function. */ @@ -347,71 +329,6 @@ public class DownloadManager } } - /** - * Processes a single download descriptor. - */ - protected void processDownload ( - DownloadDescriptor desc, DownloadObserver obs, ProgressInfo pinfo) - throws IOException - { - // download the resource bundle from the specified URL - URLConnection ucon = desc.sourceURL.openConnection(); - - // prepare to read the data from the URL into the cache file - Log.info("Downloading file [url=" + desc.sourceURL + "]."); - InputStream in = ucon.getInputStream(); - FileOutputStream out = new FileOutputStream(desc.destFile); - int read; - - // read in the file data - while ((read = in.read(_buffer)) != -1) { - // write it out to our local copy - out.write(_buffer, 0, read); - // report our progress to the download observer as a - // percentage of the total file data to be transferred - pinfo.currentSize += read; - int pctdone = pinfo.getPercentDone(); - pinfo.complete = (pctdone >= 100); - // if we've finished downloading everything, hold off on - // notifying the observer until we're done working with the - // file to ensure that any action the observer may take with - // respect to the downloaded files can be safely undertaken - if (!pinfo.complete) { - // update the transfer rate to reflect the bit of data we - // just transferred - long now = System.currentTimeMillis(); - pinfo.updateXferRate(now); - - // notify the progress observer if it's been sufficiently - // long since our last notification - if ((now - pinfo.lastUpdate) >= UPDATE_DELAY) { - pinfo.lastUpdate = now; - long remaining = pinfo.getXferTimeRemaining(); - notifyProgress(obs, pctdone, remaining); - } - } - } - - // close the streams - in.close(); - out.close(); - - // if we have a last modified time, we want to adjust our cache - // file accordingly - if (desc.lastModified != 0) { - if (!desc.destFile.setLastModified(desc.lastModified)) { - Log.warning("Failed to set last-modified date " + - "[file=" + desc.destFile + "]."); - } - } - - if (pinfo.complete) { - // let the observer know we're finished now that we've - // finished all of our work with the file - notifyProgress(obs, 100, 0L); - } - } - /** * A record describing a single download request. */ @@ -427,62 +344,6 @@ public class DownloadManager public boolean fragile; } - /** - * A record detailing the progress of a download request. - */ - protected class ProgressInfo - { - /** The total file size in bytes to be transferred. */ - public long totalSize; - - /** The file size in bytes transferred thus far. */ - public long currentSize; - - /** The time at which the file transfer began. */ - public long start; - - /** The current transfer rate in bytes per second. */ - public long bytesPerSecond; - - /** The time at which the last progress update was posted to the - * progress observer. */ - public long lastUpdate; - - /** Whether the download has completed and the progress observer - * notified. */ - public boolean complete; - - /** - * Returns the percent completion, based on data size transferred, - * of the file transfer. - */ - public int getPercentDone () - { - return (int)((currentSize / (float)totalSize) * 100f); - } - - /** - * Updates the bytes per second transfer rate for the download - * associated with this progress info record. - */ - public void updateXferRate (long now) - { - long secs = (now - start) / 1000L; - bytesPerSecond = (secs == 0) ? 0 : (currentSize / secs); - } - - /** - * Returns the estimated transfer time remaining for the download - * associated with this progress info record, or -1 if - * the transfer time cannot currently be estimated. - */ - public long getXferTimeRemaining () - { - return (bytesPerSecond == 0) ? -1 : - (totalSize - currentSize) / bytesPerSecond; - } - } - /** The downloading thread. */ protected Thread _dlthread; @@ -495,12 +356,14 @@ public class DownloadManager /** The data buffer size for reading file data. */ protected static final int BUFFER_SIZE = 2048; - /** The last-modified difference in milliseconds allowed between the - * source and destination file without considering the destination - * file to be out of date. */ - protected static final long DELTA = 5000L; - - /** The delay in milliseconds between notifying progress observers of - * file download progress. */ - protected static final long UPDATE_DELAY = 2500L; + /** Indicates whether or not we're using versioned resources. */ + protected static boolean VERSIONING = false; + static { + try { + VERSIONING = "true".equalsIgnoreCase( + System.getProperty("versioned_rsrcs")); + } catch (Throwable t) { + // no versioning, no problem + } + } } diff --git a/src/java/com/threerings/resource/Downloader.java b/src/java/com/threerings/resource/Downloader.java new file mode 100644 index 000000000..3040f17ec --- /dev/null +++ b/src/java/com/threerings/resource/Downloader.java @@ -0,0 +1,153 @@ +// +// $Id: Downloader.java,v 1.1 2003/08/05 01:33:20 mdb Exp $ + +package com.threerings.resource; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLConnection; + +import com.threerings.resource.DownloadManager.DownloadDescriptor; +import com.threerings.resource.DownloadManager.DownloadObserver; + +/** + * Does something extraordinary. + */ +public abstract class Downloader +{ + /** + * Provides this downloader with a reference to the download + * descriptor it needs to do its business. + */ + public void init (DownloadDescriptor ddesc) + { + _desc = ddesc; + _desc.destFileSize = _desc.destFile.length(); + _desc.destLastModified = _desc.destFile.lastModified(); + } + + /** + * Returns a reference to the descriptor we are downloading. + */ + public DownloadDescriptor getDescriptor () + { + return _desc; + } + + /** + * Requests that this downloader determine whether it needs to be + * updated and if so, it should add its total download requirement to + * {@link ProgressInfo#totalSize} the supplied {@link ProgressInfo} + * record. + * + * @return true if this downloader needs be updated, false if not. + */ + public abstract boolean checkUpdate (ProgressInfo info) + throws IOException; + + /** + * Compares the supplied size and last modified time with the size and + * last modified time of our local copy of the file. + */ + protected boolean compareWithLocal (long fileSize, long lastModified) + { + // check to see whether we have a local copy at all, and if so, + // whether its file size or last-modified time differs from the + // source file modulo a delta to account for inexactness on the + // part of the file-system (thanks Microsoft) + return ((!_desc.destFile.exists()) || + (_desc.destFileSize != fileSize) || + (_desc.destLastModified < (lastModified - DELTA)) || + (_desc.destLastModified > (lastModified + DELTA))); + } + + /** + * Processes a single download descriptor. + */ + public void processDownload (DownloadManager dmgr, DownloadObserver obs, + ProgressInfo pinfo, byte[] buffer) + throws IOException + { + // download the resource bundle from the specified URL + URLConnection ucon = _desc.sourceURL.openConnection(); + + // prepare to read the data from the URL into the cache file + Log.info("Downloading file [url=" + _desc.sourceURL + "]."); + downloadContent(dmgr, obs, pinfo, buffer, ucon, _desc.destFile); + + // if we have a last modified time, we want to adjust our cache + // file accordingly + if (_desc.lastModified != 0) { + if (!_desc.destFile.setLastModified(_desc.lastModified)) { + Log.warning("Failed to set last-modified date " + + "[file=" + _desc.destFile + "]."); + } + } + } + + /** + * Downloads the content from the supplied URL connection into the + * specified destination file, informing the supplied download manager + * and observer of our progress along the way. + */ + protected void downloadContent (DownloadManager dmgr, DownloadObserver obs, + ProgressInfo pinfo, byte[] buffer, + URLConnection ucon, File destFile) + throws IOException + { + InputStream in = ucon.getInputStream(); + FileOutputStream out = new FileOutputStream(destFile); + int read; + + // read in the file data + while ((read = in.read(buffer)) != -1) { + // write it out to our local copy + out.write(buffer, 0, read); + + // report our progress to the download observer as a + // percentage of the total file data to be transferred + pinfo.currentSize += read; + + // update our percent completion; if we're totally done, hold + // off on notifying the observer until the download manager + // finishes fiddling to ensure that any action the observer + // may take on the downloaded files can be safely undertaken + int pctdone = pinfo.getPercentDone(); + pinfo.complete = (pctdone >= 100); + if (pinfo.complete) { + continue; + } + + // update the transfer rate to reflect the bit of data we just + // transferred + long now = System.currentTimeMillis(); + pinfo.updateXferRate(now); + + // notify the progress observer if it's been sufficiently long + // since our last notification + if ((now - pinfo.lastUpdate) >= UPDATE_DELAY) { + pinfo.lastUpdate = now; + long remaining = pinfo.getXferTimeRemaining(); + dmgr.notifyProgress(obs, pctdone, remaining); + } + } + + // close the streams + in.close(); + out.close(); + } + + /** The descriptor that we're downloading. */ + protected DownloadDescriptor _desc; + + /** The last-modified difference in milliseconds allowed between the + * source and destination file without considering the destination + * file to be out of date. */ + protected static final long DELTA = 5000L; + + /** The delay in milliseconds between notifying progress observers of + * file download progress. */ + protected static final long UPDATE_DELAY = 2500L; +} diff --git a/src/java/com/threerings/resource/FileDownloader.java b/src/java/com/threerings/resource/FileDownloader.java new file mode 100644 index 000000000..08f782da7 --- /dev/null +++ b/src/java/com/threerings/resource/FileDownloader.java @@ -0,0 +1,33 @@ +// +// $Id: FileDownloader.java,v 1.1 2003/08/05 01:33:20 mdb Exp $ + +package com.threerings.resource; + +import java.io.File; +import java.io.IOException; + +/** + * "Downloads" a file from a location on the filesystem. + */ +public class FileDownloader extends Downloader +{ + // documentation inherited + public boolean checkUpdate (ProgressInfo info) + throws IOException + { + // read the file information directly from the file system + File tfile = new File(_desc.sourceURL.getPath()); + long fileSize = tfile.length(); + _desc.lastModified = tfile.lastModified(); + + if (compareWithLocal(fileSize, _desc.lastModified)) { + // increment the total file size to be fetched + info.totalSize += fileSize; + Log.debug("File deemed stale [url=" + _desc.sourceURL + "]."); + return true; + } else { + Log.debug("File deemed up-to-date [url=" + _desc.sourceURL + "]."); + return false; + } + } +} diff --git a/src/java/com/threerings/resource/JNLPDownloader.java b/src/java/com/threerings/resource/JNLPDownloader.java new file mode 100644 index 000000000..c8ff3ded9 --- /dev/null +++ b/src/java/com/threerings/resource/JNLPDownloader.java @@ -0,0 +1,236 @@ +// +// $Id: JNLPDownloader.java,v 1.1 2003/08/05 01:33:20 mdb Exp $ + +package com.threerings.resource; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; + +import com.sun.javaws.cache.Patcher; +import com.sun.javaws.jardiff.JarDiffPatcher; + +import com.samskivert.util.StringUtil; + +import com.threerings.resource.DownloadManager.DownloadDescriptor; +import com.threerings.resource.DownloadManager.DownloadObserver; + +/** + * Does something extraordinary. + */ +public class JNLPDownloader extends Downloader +{ + // documentation inherited + public void init (DownloadDescriptor ddesc) + { + super.init(ddesc); + + // determine which version we already have, if any + _vfile = new File(mungePath(_desc.destFile, ".vers")); + try { + BufferedReader vin = new BufferedReader(new FileReader(_vfile)); + _cvers = vin.readLine(); + + } catch (FileNotFoundException fnfe) { + // TEMP: assume version 0.22 which is the last version + // published prior to our moving to versioned diffs + if (_desc.destFile.exists()) { + // Log.info("Assuming v0.22 for " + _vfile + "."); + _cvers = "0.22"; + } + + } catch (IOException ioe) { + Log.warning("Error reading version file [path=" + _vfile + + ", error=" + ioe + "]."); + } + } + + // documentation inherited + public boolean checkUpdate (ProgressInfo info) + throws IOException + { + // if we're doing versioning and we have the appropriate version, + // no need to update + if (_desc.version != null && _desc.version.equals(_cvers)) { + return false; + } + + // read the file information via an HTTP HEAD request + HttpURLConnection ucon = (HttpURLConnection) + getResourceURL().openConnection(); + ucon.setRequestMethod("HEAD"); + ucon.connect(); + + // make sure we got a satisfactory response code + if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) { + String errmsg = "Unable to check up-to-date for " + + getResourceURL() + ": " + ucon.getResponseCode(); + throw new IOException(errmsg); + } + + // if we're versioning, we know we need an update + info.totalSize += ucon.getContentLength(); + Log.info(getResourceURL() + " requires " + ucon.getContentLength() + + " byte update."); + return true; + } + + /** + * Processes a single download descriptor. + */ + public void processDownload (DownloadManager dmgr, DownloadObserver obs, + ProgressInfo pinfo, byte[] buffer) + throws IOException + { + // download the resource bundle from the specified URL + URL rsrcURL = getResourceURL(); + HttpURLConnection ucon = (HttpURLConnection)rsrcURL.openConnection(); + ucon.connect(); + + // make sure we got a satisfactory response code + if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) { + String errmsg = "Unable to download update for " + + getResourceURL() + ": " + ucon.getResponseCode(); + throw new IOException(errmsg); + } + + // determine whether or not this is a patch + if (ucon.getContentType().equals(JARDIFF_TYPE)) { + Log.info("Downloading patch [url=" + rsrcURL + "]."); + _patchFile = new File(mungePath(_desc.destFile, ".diff")); + downloadContent(dmgr, obs, pinfo, buffer, ucon, _patchFile); + + // move the old jar out of the way + File oldDest = new File(_desc.destFile.getPath() + ".old"); + if (!_desc.destFile.renameTo(oldDest)) { + Log.warning("Unable to move " + _desc.destFile + " to " + + oldDest + ". Cleaning up and failing."); + // attempt to blow everything away before choking so that + // next time we'll download afresh + cleanUpAndFail(null); + } + + // now apply the patch + Log.info("Applying patch [old=" + oldDest + ", path=" + _patchFile + + ", new=" + _desc.destFile + "]."); + Patcher.PatchDelegate delegate = new Patcher.PatchDelegate() { + public void patching (int value) { + // System.out.println("Patching " + _desc.destFile + ": " + + // value); + } + }; + JarDiffPatcher patcher = new JarDiffPatcher(); + + try { + BufferedOutputStream out = new BufferedOutputStream( + new FileOutputStream(_desc.destFile)); + patcher.applyPatch(delegate, oldDest.getPath(), + _patchFile.getPath(), out); + out.close(); + + } catch (IOException ioe) { + Log.warning("Failure applying patch [rfile=" + _desc.destFile + + ", error=" + ioe + "]. Cleaning up and failing."); + oldDest.delete(); + cleanUpAndFail(ioe); + } + + } else { + Log.info("Downloading whole jar [url=" + rsrcURL + "]."); + downloadContent(dmgr, obs, pinfo, buffer, ucon, _desc.destFile); + } + + // now that we've patched, update our version file + PrintWriter pout = new PrintWriter( + new BufferedWriter(new FileWriter(_vfile))); + pout.println(_desc.version); + pout.close(); + // Log.info("Updated version to " + _desc.version + "."); + } + + /** + * Replaces .jar with the supplied new extention if the + * supplied file path ends in .jar. Otherwise the new + * extension is appended to the whole existing file path. + */ + protected String mungePath (File file, String newext) + { + String path = file.getPath(); + if (path.endsWith(".jar")) { + path = path.substring(0, path.length()-4); + } + return path + newext; + } + + /** + * Constructs the resource URL using the source URL supplied in the + * download descriptor and any version information that is + * appropriate. + */ + protected URL getResourceURL () + { + URL rsrcURL = _desc.sourceURL; + String vargs = _desc.sourceURL.getPath() + + "?version-id=" + _desc.version; + if (_cvers != null) { + vargs += "¤t-version-id=" + _cvers; + } + try { + rsrcURL = new URL(rsrcURL, vargs); + } catch (MalformedURLException mue) { + Log.warning("Error creating versioned resource URL " + + "[url=" + rsrcURL + ", vargs=" + vargs + "]."); + } + return rsrcURL; + } + + /** + * Attempts to wipe out everything relating to this resource so that + * the next time we attempt to run the client we can download it + * completely afresh. + */ + protected void cleanUpAndFail (IOException cause) + throws IOException + { + _desc.destFile.delete(); + if (_patchFile != null) { + _patchFile.delete(); + } + if (_vfile != null) { + _vfile.delete(); + } + + IOException failure = new IOException( + "Failed to patch " + _desc.destFile + "."); + if (cause != null) { + failure.initCause(cause); + } + throw failure; + } + + /** A file that contains the current resource version. */ + protected File _vfile; + + /** The file that contains our patch if we have one. */ + protected File _patchFile; + + /** The current version of the resource if we have one. */ + protected String _cvers; + + /** The mime-type of a jardiff patch file. */ + protected static final String JARDIFF_TYPE = + "application/x-java-archive-diff"; +} diff --git a/src/java/com/threerings/resource/ProgressInfo.java b/src/java/com/threerings/resource/ProgressInfo.java new file mode 100644 index 000000000..68c7a6b82 --- /dev/null +++ b/src/java/com/threerings/resource/ProgressInfo.java @@ -0,0 +1,60 @@ +// +// $Id: ProgressInfo.java,v 1.1 2003/08/05 01:33:20 mdb Exp $ + +package com.threerings.resource; + +/** + * Used to track download progress. + */ +public class ProgressInfo +{ + /** The total file size in bytes to be transferred. */ + public long totalSize; + + /** The file size in bytes transferred thus far. */ + public long currentSize; + + /** The time at which the file transfer began. */ + public long start; + + /** The current transfer rate in bytes per second. */ + public long bytesPerSecond; + + /** The time at which the last progress update was posted to the + * progress observer. */ + public long lastUpdate; + + /** Whether the download has completed and the progress observer + * notified. */ + public boolean complete; + + /** + * Returns the percent completion, based on data size transferred, + * of the file transfer. + */ + public int getPercentDone () + { + return (int)((currentSize / (float)totalSize) * 100f); + } + + /** + * Updates the bytes per second transfer rate for the download + * associated with this progress info record. + */ + public void updateXferRate (long now) + { + long secs = (now - start) / 1000L; + bytesPerSecond = (secs == 0) ? 0 : (currentSize / secs); + } + + /** + * Returns the estimated transfer time remaining for the download + * associated with this progress info record, or -1 if + * the transfer time cannot currently be estimated. + */ + public long getXferTimeRemaining () + { + return (bytesPerSecond == 0) ? -1 : + (totalSize - currentSize) / bytesPerSecond; + } +} diff --git a/src/java/com/threerings/resource/ResourceManager.java b/src/java/com/threerings/resource/ResourceManager.java index b77e7b28d..ddd01d563 100644 --- a/src/java/com/threerings/resource/ResourceManager.java +++ b/src/java/com/threerings/resource/ResourceManager.java @@ -1,5 +1,5 @@ // -// $Id: ResourceManager.java,v 1.31 2003/06/19 01:30:27 ray Exp $ +// $Id: ResourceManager.java,v 1.32 2003/08/05 01:33:20 mdb Exp $ package com.threerings.resource; @@ -208,6 +208,8 @@ public class ResourceManager * resource_url will be used, if available. * @param configPath the path (relative to the resource URL) of the * resource definition file. + * @param version the version of the resource bundles to be + * downloaded. * @param downloadObs the bundle download observer to notify of * download progress and success or failure, or null if * the caller doesn't care to be informed; note that in the latter @@ -215,8 +217,10 @@ public class ResourceManager * complete. */ public void initBundles (String resourceURL, String configPath, - BundleDownloadObserver downloadObs) + String version, BundleDownloadObserver downloadObs) { + _version = version; + // if the resource URL wasn't provided, we try to figure it out // for ourselves if (resourceURL == null) { @@ -246,6 +250,33 @@ public class ResourceManager ", error=" + mue + "]."); } + // now attempt to locate the dynamic resource URL + try { + String dynResURL = System.getProperty("dyn_rsrc_url"); + if (dynResURL != null) { + // make sure there's a slash at the end of the URL + if (!dynResURL.endsWith("/")) { + dynResURL += "/"; + } + try { + _drurl = new URL(dynResURL); + } catch (MalformedURLException mue) { + Log.warning("Invalid dynamic resource URL " + + "[url=" + dynResURL + ", error=" + mue + "]."); + } + } + } catch (SecurityException se) { + } + + // if no dynamic resource URL was specified, use the normal + // resource URL in its stead + if (_drurl == null) { + _drurl = _rurl; + } + + Log.debug("Resource manager ready [rurl=" + _rurl + + ", drurl=" + _drurl + "]."); + // load up our configuration Properties config = loadConfig(configPath); @@ -294,12 +325,12 @@ public class ResourceManager * Resolve the specified dynamic bundle and return it on the specified * result listener. */ - public void resolveDynamicBundle (String path, - final ResultListener listener) + public void resolveDynamicBundle (String path, String version, + final ResultListener listener) { URL burl; try { - burl = new URL(_rurl, path); + burl = new URL(_drurl, path); } catch (MalformedURLException mue) { listener.requestFailed(mue); return; @@ -313,10 +344,10 @@ public class ResourceManager return; } - // slap this on the list for retrieval or update by the - // download manager + // slap this on the list for retrieval or update by the download + // manager ArrayList list = new ArrayList(); - list.add(new DownloadDescriptor(burl, bundle.getSource())); + list.add(new DownloadDescriptor(burl, bundle.getSource(), version)); // TODO: There should only be one download manager for all dynamic // bundles, with each bundle waiting its turn to use it. @@ -331,6 +362,9 @@ public class ResourceManager public void downloadProgress (int percent, long remaining) { } + public void patchingProgress (int percent) { + } + public void postDownloadHook () { } @@ -385,6 +419,10 @@ public class ResourceManager // nothing for now } + public void patchingProgress (int percent) { + // nothing for now + } + public void postDownloadHook () { bundlesDownloaded(); } @@ -442,6 +480,11 @@ public class ResourceManager obs.downloadProgress(percent, remaining); } + public void patchingProgress (int percent) { + // TODO: + // obs.patchingProgress(percent)? + } + public void postDownloadHook () { bundlesDownloaded(); } @@ -596,7 +639,7 @@ public class ResourceManager // slap this on the list for retrieval or update by the // download manager - dlist.add(new DownloadDescriptor(burl, cfile)); + dlist.add(new DownloadDescriptor(burl, cfile, _version)); // finally, add the file that will be cached to the set as // a resource bundle @@ -783,9 +826,15 @@ public class ResourceManager /** The classloader we use for classpath-based resource loading. */ protected ClassLoader _loader; + /** The version of the resource bundles we'll be downloading. */ + protected String _version; + /** The url via which we download our bundles. */ protected URL _rurl; + /** The url via which we download our dynamically generated bundles. */ + protected URL _drurl; + /** The prefix we prepend to resource paths before attempting to load * them from the classpath. */ protected String _rootPath;