Tidy up new resources stuff a bit.

The one major change here is that Getdown.isUpdateAvailable/install()
are no longer static.

GetdownApp has a start() method that you can call if you want to get the
Getdown reference back. You can call isUpdateAvailable() on that. It was
super weird that you would call GetdownApp.main() and then just call
some static methods after that.
This commit is contained in:
Michael Bayne
2016-11-04 17:45:51 -07:00
parent 345f64d62d
commit 7c651fbd73
5 changed files with 189 additions and 166 deletions
@@ -35,7 +35,7 @@ public class Resource
_path = path;
_remote = remote;
_local = local;
_local_new = new File(local.toString() + "_new");
_localNew = new File(local.toString() + "_new");
String lpath = _local.getPath();
_marker = new File(lpath + "v");
@@ -67,9 +67,12 @@ public class Resource
return _local;
}
/**
* Returns the location of the to-be-installed new version of this resource.
*/
public File getLocalNew ()
{
return _local_new;
return _localNew;
}
/**
@@ -109,18 +112,14 @@ 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, ProgressObserver obs)
throws IOException
public String computeDigest (MessageDigest md, ProgressObserver obs) throws IOException
{
File file;
if (_local.toString().toLowerCase().endsWith(Application.CONFIG_FILE))
file = _local;
else {
if (_local_new.exists())
file = _local_new;
else
file = _local;
}
File file;
if (_local.toString().toLowerCase().endsWith(Application.CONFIG_FILE)) {
file = _local;
} else {
file = _localNew.exists() ? _localNew : _local;
}
return computeDigest(file, md, obs);
}
@@ -309,7 +308,7 @@ public class Resource
protected String _path;
protected URL _remote;
protected File _local, _local_new, _marker, _unpacked;
protected File _local, _localNew, _marker, _unpacked;
protected boolean _unpack, _isJar, _isPacked200Jar;
/** Used to sort the entries in a jar file. */
@@ -49,8 +49,11 @@ public class SysProps
return System.getProperty("silent") != null;
}
public static boolean install () {
return System.getProperty("no_install") == null;
/** If true, Getdown does not automatically install updates after downloading them. It waits
* for the application to call {@link Getdown#install}.
* Usage: {@code -Dno_install}. */
public static boolean noInstall () {
return System.getProperty("no_install") != null;
}
/** If true, Getdown installs the app without ever bringing up a UI and then launches it.
@@ -101,11 +101,11 @@ public abstract class Getdown extends Thread
// 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.
_silent = SysProps.silent();
_install = SysProps.install();
if (_silent) {
_launchInSilent = SysProps.launchInSilent();
}
_delay = SysProps.startDelay();
_noInstall = SysProps.noInstall();
} catch (SecurityException se) {
// don't freak out, just assume non-silent and no delay; we're probably already
// recovering from a security failure
@@ -128,6 +128,39 @@ public abstract class Getdown extends Thread
_startup = System.currentTimeMillis();
}
/**
* Returns true if there are pending new resources, waiting to be installed.
*/
public boolean isUpdateAvailable ()
{
return _readyToInstall && !_toBeInstalledResouces.isEmpty();
}
/**
* Installs the currently pending new resources.
*/
public void install () throws IOException, InterruptedException
{
if (_readyToInstall) {
log.info("Installing downloaded resources:");
for (Resource resource : _toBeInstalledResouces) {
File source = resource.getLocalNew(), dest = resource.getLocal();
log.info("- " + source);
if (!FileUtil.renameTo(source, dest)) {
throw new IOException("Failed to rename " + source + " to " + dest);
}
if (Thread.interrupted()) {
throw new InterruptedException("m.applet_stopped");
}
}
_toBeInstalledResouces.clear();
_readyToInstall = false;
log.info("Install completed.");
} else {
log.info("Nothing to install.");
}
}
/**
* This is used by the applet which always needs a user interface and wants to load it as soon
* as possible.
@@ -411,9 +444,9 @@ public abstract class Getdown extends Thread
// we'll keep track of all the resources we unpack
Set<Resource> unpacked = new HashSet<Resource>();
toBeInstalledResouces = new ArrayList<Resource>();
readyToInstall = false;
_toBeInstalledResouces = new ArrayList<Resource>();
_readyToInstall = false;
//setStep(Step.START);
for (int ii = 0; ii < MAX_LOOPS; ii++) {
@@ -447,7 +480,6 @@ public abstract class Getdown extends Thread
setStep(Step.VERIFY_RESOURCES);
setStatusAsync("m.validating", -1, -1L, false);
List<Resource> failures = _app.verifyResources(_progobs, alreadyValid, unpacked);
addToBeInstalledResources(failures);
if (failures == null) {
log.info("Resources verified.");
@@ -477,9 +509,11 @@ public abstract class Getdown extends Thread
}
}
readyToInstall = true;
if (_install)
install();
// assuming we're not doing anything funny, install the update
_readyToInstall = true;
if (!_noInstall) {
install();
}
// Only launch if we aren't in silent mode. Some mystery program starting out
// of the blue would be disconcerting.
@@ -496,6 +530,13 @@ public abstract class Getdown extends Thread
return;
}
// we have failures, those will be redownloaded so we note them as to-be-installed
for (Resource r : failures) {
if (!_toBeInstalledResouces.contains(r)) {
_toBeInstalledResouces.add(r);
}
}
try {
// if any of our resources have already been marked valid this is not a first
// time install and we don't want to enable tracking
@@ -509,6 +550,7 @@ public abstract class Getdown extends Thread
download(failures);
reportTrackingEvent("app_complete", -1);
} finally {
_enableTracking = false;
}
@@ -541,16 +583,7 @@ public abstract class Getdown extends Thread
}
}
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
// documentation inherited from interface
public void updateStatus (String message)
{
setStatusAsync(message, -1, -1L, true);
@@ -773,32 +806,7 @@ public abstract class Getdown extends Thread
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.
*/
@@ -1263,10 +1271,13 @@ public abstract class Getdown extends Thread
protected boolean _dead;
protected boolean _silent;
protected boolean _install;
protected boolean _noInstall;
protected boolean _launchInSilent;
protected long _startup;
protected List<Resource> _toBeInstalledResouces;
protected boolean _readyToInstall;
protected boolean _enableTracking = true;
protected int _reportedProgress = 0;
@@ -1284,6 +1295,4 @@ public abstract class Getdown extends Thread
protected static final long PLAY_AGAIN_TIME = 3000L;
protected static final String PROXY_REGISTRY =
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
protected static List<Resource> toBeInstalledResouces;
protected static boolean readyToInstall;
}
@@ -36,6 +36,20 @@ public class GetdownApp
{
public static void main (String[] argv)
{
try {
start(argv);
} catch (Exception e) {
log.warning("main() failed.", e);
}
}
/**
* Runs Getdown as an application, using the arguments supplie as {@code argv}.
* @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
* called on it.
* @throws Exception if anything goes wrong starting Getdown.
*/
public static Getdown start (String[] argv) throws Exception {
int aidx = 0;
List<String> args = Arrays.asList(argv);
@@ -91,118 +105,114 @@ public class GetdownApp
log.info("-- Cur dir: " + System.getProperty("user.dir"));
log.info("---------------------------------------------");
try {
Getdown app = new Getdown(appDir, appId, null, null, appArgs) {
@Override
protected Container createContainer () {
// create our user interface, and display it
String title = StringUtil.isBlank(_ifc.name) ? "" : _ifc.name;
if (_frame == null) {
_frame = new JFrame(title);
_frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing (WindowEvent evt) {
handleWindowClose();
}
});
_frame.setUndecorated(_ifc.hideDecorations);
_frame.setResizable(false);
} else {
_frame.setTitle(title);
_frame.getContentPane().removeAll();
}
if (_ifc.iconImages != null) {
ArrayList<Image> icons = new ArrayList<Image>();
for (String path : _ifc.iconImages) {
Image img = loadImage(path);
if (img == null) {
log.warning("Error loading icon image", "path", path);
} else {
icons.add(img);
}
Getdown app = new Getdown(appDir, appId, null, null, appArgs) {
@Override
protected Container createContainer () {
// create our user interface, and display it
String title = StringUtil.isBlank(_ifc.name) ? "" : _ifc.name;
if (_frame == null) {
_frame = new JFrame(title);
_frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing (WindowEvent evt) {
handleWindowClose();
}
if (icons.isEmpty()) {
log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
});
_frame.setUndecorated(_ifc.hideDecorations);
_frame.setResizable(false);
} else {
_frame.setTitle(title);
_frame.getContentPane().removeAll();
}
if (_ifc.iconImages != null) {
ArrayList<Image> icons = new ArrayList<Image>();
for (String path : _ifc.iconImages) {
Image img = loadImage(path);
if (img == null) {
log.warning("Error loading icon image", "path", path);
} else {
SwingUtil.setFrameIcons(_frame, icons);
icons.add(img);
}
}
_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
return _frame.getContentPane();
}
@Override
protected void showContainer () {
if (_frame != null) {
_frame.pack();
SwingUtil.centerWindow(_frame);
_frame.setVisible(true);
}
}
@Override
protected void disposeContainer () {
if (_frame != null) {
_frame.dispose();
_frame = null;
}
}
@Override
protected void showDocument (String url) {
String[] cmdarray;
if (RunAnywhere.isWindows()) {
String osName = System.getProperty("os.name");
if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
cmdarray = new String[] {
"command.com", "/c", "start", "\"" + url + "\"" };
} else {
cmdarray = new String[] {
"cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
}
} else if (RunAnywhere.isMacOS()) {
cmdarray = new String[] { "open", url };
} else { // Linux, Solaris, etc.
cmdarray = new String[] { "firefox", url };
}
try {
Runtime.getRuntime().exec(cmdarray);
} catch (Exception e) {
log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
}
}
@Override
protected void exit (int exitCode) {
// if we're running the app in the same JVM, don't call System.exit, but do
// make double sure that the download window is closed.
if (invokeDirect()) {
disposeContainer();
if (icons.isEmpty()) {
log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
} else {
System.exit(exitCode);
SwingUtil.setFrameIcons(_frame, icons);
}
}
@Override
protected void fail (String message) {
// if the frame was set to be undecorated, make window decoration available
// to allow the user to close the window
if (_frame != null && _frame.isUndecorated()) {
_frame.dispose();
_frame.setUndecorated(false);
showContainer();
}
super.fail(message);
_frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
return _frame.getContentPane();
}
@Override
protected void showContainer () {
if (_frame != null) {
_frame.pack();
SwingUtil.centerWindow(_frame);
_frame.setVisible(true);
}
}
protected JFrame _frame;
};
app.start();
@Override
protected void disposeContainer () {
if (_frame != null) {
_frame.dispose();
_frame = null;
}
}
} catch (Exception e) {
log.warning("main() failed.", e);
}
@Override
protected void showDocument (String url) {
String[] cmdarray;
if (RunAnywhere.isWindows()) {
String osName = System.getProperty("os.name");
if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
cmdarray = new String[] {
"command.com", "/c", "start", "\"" + url + "\"" };
} else {
cmdarray = new String[] {
"cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
}
} else if (RunAnywhere.isMacOS()) {
cmdarray = new String[] { "open", url };
} else { // Linux, Solaris, etc.
cmdarray = new String[] { "firefox", url };
}
try {
Runtime.getRuntime().exec(cmdarray);
} catch (Exception e) {
log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
}
}
@Override
protected void exit (int exitCode) {
// if we're running the app in the same JVM, don't call System.exit, but do
// make double sure that the download window is closed.
if (invokeDirect()) {
disposeContainer();
} else {
System.exit(exitCode);
}
}
@Override
protected void fail (String message) {
// if the frame was set to be undecorated, make window decoration available
// to allow the user to close the window
if (_frame != null && _frame.isUndecorated()) {
_frame.dispose();
_frame.setUndecorated(false);
showContainer();
}
super.fail(message);
}
protected JFrame _frame;
};
app.start();
return app;
}
}
@@ -60,7 +60,9 @@ public class FileUtil extends com.samskivert.util.FileUtil
fin = new FileInputStream(source);
fout = new FileOutputStream(dest);
StreamUtil.copy(fin, fout);
fin.close(); // needed otherwise cannot delete source
// close the input stream now so we can delete 'source'
fin.close();
fin = null;
if (!source.delete()) {
log.warning("Failed to delete " + source +
" after brute force copy to " + dest + ".");