diff --git a/src/java/com/threerings/getdown/data/Application.java b/src/java/com/threerings/getdown/data/Application.java index 323f7ac..7bd3c44 100644 --- a/src/java/com/threerings/getdown/data/Application.java +++ b/src/java/com/threerings/getdown/data/Application.java @@ -1027,23 +1027,35 @@ public class Application protected void downloadConfigFile () throws IOException { - checkForAnotherGetdown(); + requireNoOtherGetdownRunning(); downloadControlFile(CONFIG_FILE, false); updateConfigModtime(); } /** - * Checks the modtime on CONFIG_FILE and if it's changed since the last time this was called, - * raise a MultipleGetdownRunning exception + * Checks the modtime on CONFIG_FILE and returns whether it has changed since the last time + * this method was called. */ - public void checkForAnotherGetdown () - throws MultipleGetdownRunning + public boolean checkForAnotherGetdown () { File config = getLocalPath(CONFIG_FILE); if (_lastConfigModtime != -1 && _lastConfigModtime < config.lastModified()) { - throw new MultipleGetdownRunning(); + return true; } _lastConfigModtime = config.lastModified(); + return false; + } + + /** + * Calls {@link #checkForAnotherGetdown} and throws a {@link MultipleGetdownRunning} if it + * detects that another Getdown instance is running. + */ + public void requireNoOtherGetdownRunning () + throws MultipleGetdownRunning + { + if (checkForAnotherGetdown()) { + throw new MultipleGetdownRunning(); + } } /** @@ -1054,8 +1066,9 @@ public class Application { File config = getLocalPath(CONFIG_FILE); _lastConfigModtime = System.currentTimeMillis(); - if(!config.setLastModified(_lastConfigModtime) && !_warnedAboutSetLastModified){ - Log.warning("Unable to set modtime on config file, will be unable to check for other instances of getdown running"); + if (!config.setLastModified(_lastConfigModtime) && !_warnedAboutSetLastModified) { + Log.warning("Unable to set modtime on config file, will be unable to check for " + + "other instances of getdown running."); _warnedAboutSetLastModified = true; } } @@ -1152,7 +1165,7 @@ public class Application // Check that another getdown hasn't started running since we started downloading this // file. The rename will obliterate the modtime we're tracking to keep multiple instances // from running. - checkForAnotherGetdown(); + requireNoOtherGetdownRunning(); // now move the temporary file over the original File original = getLocalPath(path); diff --git a/src/java/com/threerings/getdown/launcher/Getdown.java b/src/java/com/threerings/getdown/launcher/Getdown.java index 60058ea..2b5e58c 100644 --- a/src/java/com/threerings/getdown/launcher/Getdown.java +++ b/src/java/com/threerings/getdown/launcher/Getdown.java @@ -62,6 +62,9 @@ import com.samskivert.util.StringUtil; import com.threerings.getdown.Log; import com.threerings.getdown.data.Application; import com.threerings.getdown.data.Resource; +import com.threerings.getdown.net.Downloader; +import com.threerings.getdown.net.HTTPDownloader; +import com.threerings.getdown.net.TorrentDownloader; import com.threerings.getdown.tools.Patcher; import com.threerings.getdown.util.ConfigUtil; import com.threerings.getdown.util.LaunchUtil; @@ -359,9 +362,9 @@ public abstract class Getdown extends Thread // now force our UI to be recreated with the updated info createInterface(true); } - _app.checkForAnotherGetdown(); + _app.requireNoOtherGetdownRunning(); // Update the modtime here to stake a claim that we're going to getdown eventually - _app.updateConfigModtime(); + _app.updateConfigModtime(); if (_delay > 0) { try { Log.info("Waiting " + _delay + " minutes before beginning actual work"); @@ -413,7 +416,7 @@ public abstract class Getdown extends Thread // Only launch if we aren't in silent mode. Some mystery program starting out // of the blue would be disconcerting. if (!_silent || _launchInSilent) { - _app.checkForAnotherGetdown(); + _app.requireNoOtherGetdownRunning(); launch(); } return; @@ -628,34 +631,29 @@ public abstract class Getdown extends Thread updateStatus("m.resolving"); } - public void downloadProgress (int percent, long remaining) - throws IOException { - // Check for another getdown running at 0 and every 10% after that + public boolean downloadProgress (int percent, long remaining) { + // check for another getdown running at 0 and every 10% after that if (_lastCheck == -1 || percent >= _lastCheck + 10) { - _app.checkForAnotherGetdown(); + if (_app.checkForAnotherGetdown()) { + return false; + } _lastCheck = percent; } setStatus("m.downloading", percent, remaining, true); if (percent > 0) { reportTrackingEvent("progress", percent); } + return true; } - public void downloadFailed (Resource rsrc, Exception e) - throws IOException { - if (e instanceof MultipleGetdownRunning) { - throw (IOException)e; - } else { - updateStatus(MessageUtil.tcompose("m.failure", e.getMessage())); - Log.warning("Download failed [rsrc=" + rsrc + "]."); - Log.logStackTrace(e); - } + public void downloadFailed (Resource rsrc, Exception e) { + updateStatus(MessageUtil.tcompose("m.failure", e.getMessage())); + Log.warning("Download failed [rsrc=" + rsrc + "]."); + Log.logStackTrace(e); } - - /** - * The last percentage at which we checked for another getdown running, or -1 for not - * having checked at all. - */ + + /** The last percentage at which we checked for another getdown running, or -1 for not + * having checked at all. */ protected int _lastCheck = -1; }; @@ -680,7 +678,9 @@ public abstract class Getdown extends Thread } // start the download and wait for it to complete - dl.download(); + if (!dl.download()) { + throw new MultipleGetdownRunning(); + } } /** diff --git a/src/java/com/threerings/getdown/net/DownloadAbortedException.java b/src/java/com/threerings/getdown/net/DownloadAbortedException.java new file mode 100644 index 0000000..0764f2d --- /dev/null +++ b/src/java/com/threerings/getdown/net/DownloadAbortedException.java @@ -0,0 +1,30 @@ +// +// $Id$ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2006 Three Rings Design, Inc. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the: Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +package com.threerings.getdown.net; + +import java.io.IOException; + +/** + * Used to terminate the download process in its midst. + */ +public class DownloadAbortedException extends IOException +{ +} diff --git a/src/java/com/threerings/getdown/launcher/Downloader.java b/src/java/com/threerings/getdown/net/Downloader.java similarity index 68% rename from src/java/com/threerings/getdown/launcher/Downloader.java rename to src/java/com/threerings/getdown/net/Downloader.java index 2c084a7..300e021 100644 --- a/src/java/com/threerings/getdown/launcher/Downloader.java +++ b/src/java/com/threerings/getdown/net/Downloader.java @@ -18,69 +18,63 @@ // this program; if not, write to the: Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA -package com.threerings.getdown.launcher; +package com.threerings.getdown.net; import java.io.File; import java.io.IOException; + import java.util.List; import com.threerings.getdown.Log; import com.threerings.getdown.data.Resource; /** - * Handles the download of a collection of files, first issuing HTTP head - * requests to obtain size information and then downloading the files - * individually, reporting progress back via a callback interface. + * Handles the download of a collection of files, first issuing HTTP head requests to obtain size + * information and then downloading the files individually, reporting progress back via a callback + * interface. */ public abstract class Downloader extends Thread { /** - * An interface used to communicate status back to an external entity. - * Note: these methods are all called on the download thread, - * so implementors must take care to only execute thread-safe code or - * simply pass a message to the AWT thread, for example. + * An interface used to communicate status back to an external entity. Note: these + * methods are all called on the download thread, so implementors must take care to only + * execute thread-safe code or simply pass a message to the AWT thread, for example. */ public interface Observer { /** - * Called before the downloader begins the series of HTTP head - * requests to determine the size of the files it needs to - * download. + * Called before the downloader begins the series of HTTP head requests to determine the + * size of the files it needs to download. */ public void resolvingDownloads (); /** - * Called to inform the observer of ongoing progress toward - * completion of the overall downloading task. The caller is - * guaranteed to get at least one call reporting 100% completion. + * Called to inform the observer of ongoing progress toward completion of the overall + * downloading task. The caller is guaranteed to get at least one call reporting 100% + * completion. * - * @param percent the percent completion, in terms of total file - * size, of the downloads. - * @param remaining the estimated download time remaining in - * seconds, or -1 if the time can not yet be - * determined. - * @throws IOException if getdown shouldn't continue in its current state. + * @param percent the percent completion, in terms of total file size, of the downloads. + * @param remaining the estimated download time remaining in seconds, or -1 if + * the time can not yet be determined. + * + * @return true if the download should continue, false if it should be aborted. */ - public void downloadProgress (int percent, long remaining) throws IOException; + public boolean downloadProgress (int percent, long remaining); /** - * Called if a failure occurs while checking for an update or - * downloading a file. + * Called if a failure occurs while checking for an update or downloading a file. * - * @param rsrc the resource that was being downloaded when the - * error occurred, or null if the failure occurred - * while resolving downloads. + * @param rsrc the resource that was being downloaded when the error occurred, or + * null if the failure occurred while resolving downloads. * @param e the exception detailing the failure. - * @throws IOException if getdown shouldn't continue after this failure. */ - public void downloadFailed (Resource rsrc, Exception e) throws IOException; + public void downloadFailed (Resource rsrc, Exception e); } /** - * Creates a downloader that will download the supplied list of - * resources and communicate with the specified observer. The {@link - * #download} method must be called on the downloader to initiate the - * download process. + * Creates a downloader that will download the supplied list of resources and communicate with + * the specified observer. The {@link #download} method must be called on the downloader to + * initiate the download process. */ public Downloader (List resources, Observer obs) { @@ -95,20 +89,16 @@ public abstract class Downloader extends Thread @Override public void run () { - try { - download(); - } catch (IOException e) { - // This was either logged or reported to our observer in download, so we're good. - } + download(); } /** - * Start downloading the resources in this Downloader. - * - * @throws IOException if an unrecoverable error was discovered in dowloading. + * Start downloading the resources in this downloader. + * + * @return true if the download completed or failed for unexpected reasons (in which case the + * observer will have been notified), false if it was aborted by the observer. */ - public void download () - throws IOException + public boolean download () { Resource current = null; try { @@ -132,12 +122,17 @@ public abstract class Downloader extends Thread download(resource); } - // finally report our download completion if we did not - // already do so when downloading our final resource + // finally report our download completion if we did not already do so when downloading + // our final resource if (_obs != null && !_complete) { - _obs.downloadProgress(100, 0); + if (!_obs.downloadProgress(100, 0)) { + return false; + } } + } catch (DownloadAbortedException e) { + return false; + } catch (Exception e) { if (_obs != null) { _obs.downloadFailed(current, e); @@ -145,6 +140,7 @@ public abstract class Downloader extends Thread Log.logStackTrace(e); } } + return true; } /** @@ -163,8 +159,7 @@ public abstract class Downloader extends Thread protected abstract long checkSize (Resource rsrc) throws IOException; /** - * Downloads the specified resource from its remote location to its - * local location. + * Downloads the specified resource from its remote location to its local location. */ protected void download (Resource rsrc) throws IOException @@ -173,23 +168,20 @@ public abstract class Downloader extends Thread File parent = new File(rsrc.getLocal().getParent()); if (!parent.exists()) { if (!parent.mkdirs()) { - Log.warning("Failed to create target directory for " + - "resource '" + rsrc + "'. Download will " + - "certainly fail."); + Log.warning("Failed to create target directory for resource '" + rsrc + "'. " + + "Download will certainly fail."); } } doDownload(rsrc); } /** - * Periodically called by the protocol-specific downloaders - * to update their progress. - * @throws IOException + * Periodically called by the protocol-specific downloaders to update their progress. */ - protected void updateObserver () throws IOException + protected void updateObserver () + throws IOException { - // notify the observer if it's been sufficiently long - // since our last notification + // notify the observer if it's been sufficiently long since our last notification long now = System.currentTimeMillis(); if ((now - _lastUpdate) >= UPDATE_DELAY) { _lastUpdate = now; @@ -199,24 +191,24 @@ public abstract class Downloader extends Thread long bps = (secs == 0) ? 0 : (_currentSize / secs); // compute our percentage completion - int pctdone = (_totalSize == 0) ? 0 : - (int)((_currentSize * 100f) / _totalSize); + int pctdone = (_totalSize == 0) ? 0 : (int)((_currentSize * 100f) / _totalSize); // estimate our time remaining - long remaining = (bps <= 0 || _totalSize == 0) ? -1 : - (_totalSize - _currentSize) / bps; + long remaining = (bps <= 0 || _totalSize == 0) ? -1 : (_totalSize - _currentSize) / bps; // make sure we only report 100% exactly once if (pctdone < 100 || !_complete) { _complete = (pctdone == 100); - _obs.downloadProgress(pctdone, remaining); + if (!_obs.downloadProgress(pctdone, remaining)) { + throw new DownloadAbortedException(); + } } } } /** - * Accomplishes the copying of the resource from remote location to - * local location using protocol-specific code + * Accomplishes the copying of the resource from remote location to local location using + * protocol-specific code */ protected abstract void doDownload (Resource rsrc) throws IOException; @@ -241,15 +233,13 @@ public abstract class Downloader extends Thread /** The current transfer rate in bytes per second. */ protected long _bytesPerSecond; - /** The time at which the last progress update was posted to the - * progress observer. */ + /** The time at which the last progress update was posted to the progress observer. */ protected long _lastUpdate; - /** Whether the download has completed and the progress observer - * notified. */ + /** Whether the download has completed and the progress observer notified. */ protected boolean _complete; - /** The delay in milliseconds between notifying progress observers of - * file download progress. */ + /** The delay in milliseconds between notifying progress observers of file download + * progress. */ protected static final long UPDATE_DELAY = 500L; } diff --git a/src/java/com/threerings/getdown/launcher/HTTPDownloader.java b/src/java/com/threerings/getdown/net/HTTPDownloader.java similarity index 76% rename from src/java/com/threerings/getdown/launcher/HTTPDownloader.java rename to src/java/com/threerings/getdown/net/HTTPDownloader.java index e2d124c..0a645e8 100644 --- a/src/java/com/threerings/getdown/launcher/HTTPDownloader.java +++ b/src/java/com/threerings/getdown/net/HTTPDownloader.java @@ -1,4 +1,24 @@ -package com.threerings.getdown.launcher; +// +// $Id$ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2006 Three Rings Design, Inc. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the: Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +package com.threerings.getdown.net; import java.io.FileOutputStream; import java.io.IOException; @@ -59,8 +79,7 @@ public class HTTPDownloader extends Downloader throws IOException { // download the resource from the specified URL - HttpURLConnection ucon = (HttpURLConnection) - rsrc.getRemote().openConnection(); + HttpURLConnection ucon = (HttpURLConnection)rsrc.getRemote().openConnection(); ucon.connect(); // make sure we got a satisfactory response code diff --git a/src/java/com/threerings/getdown/launcher/TorrentDownloader.java b/src/java/com/threerings/getdown/net/TorrentDownloader.java similarity index 85% rename from src/java/com/threerings/getdown/launcher/TorrentDownloader.java rename to src/java/com/threerings/getdown/net/TorrentDownloader.java index 1a8cabf..912ea5a 100644 --- a/src/java/com/threerings/getdown/launcher/TorrentDownloader.java +++ b/src/java/com/threerings/getdown/net/TorrentDownloader.java @@ -1,4 +1,24 @@ -package com.threerings.getdown.launcher; +// +// $Id$ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2006 Three Rings Design, Inc. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the: Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +package com.threerings.getdown.net; import java.io.IOException; import java.util.HashMap;