diff --git a/src/java/com/threerings/getdown/data/Application.java b/src/java/com/threerings/getdown/data/Application.java index 4e9496f..2aac61a 100644 --- a/src/java/com/threerings/getdown/data/Application.java +++ b/src/java/com/threerings/getdown/data/Application.java @@ -1,5 +1,5 @@ // -// $Id: Application.java,v 1.8 2004/07/07 16:18:23 mdb Exp $ +// $Id: Application.java,v 1.9 2004/07/13 02:42:52 mdb Exp $ package com.threerings.getdown.data; @@ -83,7 +83,7 @@ public class Application try { return createResource(CONFIG_FILE); } catch (Exception e) { - throw new RuntimeException("Booched appbase '" + _appbase + "'!?"); + throw new RuntimeException("Booched appbase '" + _vappbase + "'!?"); } } @@ -105,6 +105,32 @@ public class Application return _resources; } + /** + * Returns a resource that can be used to download a patch file that + * will bring this application from its current version to the target + * version. + */ + public Resource getPatchResource () + { + if (_targetVersion <= _version) { + Log.warning("Requested patch resource for up-to-date or " + + "non-versioned appliation [cvers=" + _version + + ", tvers=" + _targetVersion + "]."); + return null; + } + + String pfile = "patch" + _version + ".dat"; + try { + URL remote = new URL(createVAppBase(_targetVersion), pfile); + return new Resource(pfile, remote, getLocalPath(pfile)); + } catch (Exception e) { + Log.warning("Failed to create patch resource path [pfile=" + pfile + + ", appbase=" + _appbase + ", tvers=" + _targetVersion + + ", error=" + e + "]."); + return null; + } + } + /** * Instructs the application to parse its getdown.txt * configuration and prepare itself for operation. The application @@ -140,19 +166,13 @@ public class Application // first determine our application base, this way if anything goes // wrong later in the process, our caller can use the appbase to // download a new configuration file - String appbase = (String)cdata.get("appbase"); - if (appbase == null) { + _appbase = (String)cdata.get("appbase"); + if (_appbase == null) { throw new IOException("m.missing_appbase"); } - try { - // make sure there's a trailing slash - if (!appbase.endsWith("/")) { - appbase = appbase + "/"; - } - _appbase = new URL(appbase); - } catch (Exception e) { - String err = MessageUtil.tcompose("m.invalid_appbase", appbase); - throw new NestableIOException(err, e); + // make sure there's a trailing slash + if (!_appbase.endsWith("/")) { + _appbase = _appbase + "/"; } // extract our version information @@ -167,16 +187,15 @@ public class Application } // if we are a versioned deployment, create a versioned appbase - if (_version < 0) { - _vappbase = _appbase; - } else { - try { - _vappbase = new URL( - StringUtil.replace(_appbase.toString(), "%VERSION%", vstr)); - } catch (MalformedURLException mue) { - String err = MessageUtil.tcompose("m.invalid_appbase", appbase); - throw new NestableIOException(err, mue); + try { + if (_version < 0) { + _vappbase = new URL(_appbase); + } else { + _vappbase = createVAppBase(_version); } + } catch (MalformedURLException mue) { + String err = MessageUtil.tcompose("m.invalid_appbase", _appbase); + throw new NestableIOException(err, mue); } // determine our application class name @@ -271,6 +290,30 @@ public class Application downloadControlFile(CONFIG_FILE); } + /** + * Downloads and replaces the getdown.txt and + * digest.txt files with those for the target version of + * our application. + */ + public void updateMetadata () + throws IOException + { + try { + // update our versioned application base with the target version + _vappbase = createVAppBase(_targetVersion); + } catch (MalformedURLException mue) { + String err = MessageUtil.tcompose("m.invalid_appbase", _appbase); + throw new NestableIOException(err, mue); + } + + // now re-download our control files; we download the digest first + // so that if it fails, our config file will still reference the + // old version and re-running the updater will start the whole + // process over again + downloadControlFile(Digest.DIGEST_FILE); + downloadControlFile(CONFIG_FILE); + } + /** * Invokes the process associated with this application definition. */ @@ -345,7 +388,7 @@ public class Application public boolean verifyMetadata () throws IOException { - Log.info("Verifying application: " + _appbase); + Log.info("Verifying application: " + _vappbase); Log.info("Version: " + _version); Log.info("Class: " + _class); // Log.info("Code: " + StringUtil.toString(_codes.iterator())); @@ -439,6 +482,25 @@ public class Application return (failures.size() == 0) ? null : failures; } + /** + * Clears all validation marker files. + */ + public void clearValidationMarkers () + { + clearValidationMarkers(_codes.iterator()); + clearValidationMarkers(_resources.iterator()); + } + + /** + * Creates a versioned application base URL for the specified version. + */ + protected URL createVAppBase (int version) + throws MalformedURLException + { + return new URL( + StringUtil.replace(_appbase, "%VERSION%", "" + version)); + } + /** A helper function used by {@link #verifyResources()}. */ protected void verifyResources (Iterator rsrcs, List failures) { @@ -463,13 +525,6 @@ public class Application } } - /** Clears all validation marker files. */ - protected void clearValidationMarkers () - { - clearValidationMarkers(_codes.iterator()); - clearValidationMarkers(_resources.iterator()); - } - /** Clears all validation marker files for the resources in the * supplied iterator. */ protected void clearValidationMarkers (Iterator iter) @@ -493,7 +548,7 @@ public class Application targetURL = getRemoteURL(path); } catch (Exception e) { Log.warning("Requested to download invalid control file " + - "[appbase=" + _appbase + ", path=" + path + + "[appbase=" + _vappbase + ", path=" + path + ", error=" + e + "]."); throw new NestableIOException("Invalid path '" + path + "'.", e); } @@ -567,7 +622,8 @@ public class Application protected int _version = -1; protected int _targetVersion = -1; - protected URL _appbase, _vappbase; + protected String _appbase; + protected URL _vappbase; protected String _class; protected ArrayList _codes = new ArrayList(); diff --git a/src/java/com/threerings/getdown/launcher/Getdown.java b/src/java/com/threerings/getdown/launcher/Getdown.java index 75a0088..eec17b9 100644 --- a/src/java/com/threerings/getdown/launcher/Getdown.java +++ b/src/java/com/threerings/getdown/launcher/Getdown.java @@ -1,5 +1,5 @@ // -// $Id: Getdown.java,v 1.6 2004/07/07 10:45:20 mdb Exp $ +// $Id: Getdown.java,v 1.7 2004/07/13 02:42:52 mdb Exp $ package com.threerings.getdown.launcher; @@ -16,6 +16,7 @@ import java.io.IOException; import java.io.PrintStream; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; @@ -43,6 +44,7 @@ public class Getdown public void run () { try { + // first parses our application deployment file try { _ifc = _app.init(); } catch (IOException ioe) { @@ -52,29 +54,35 @@ 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... if (_app.verifyMetadata()) { Log.info("Application requires update."); update(); - - } else { - Log.info("Metadata verified."); - List failures = _app.verifyResources(); - if (failures == null) { - Log.info("Resources verified."); - launch(); - return; - - } else { - Log.info(failures.size() + " rsrcs require update."); - download(failures); - // now we'll loop back and try it all again - } + // loop back again and reverify the metadata + continue; } + + // now verify our resources... + List failures = _app.verifyResources(); + if (failures == null) { + Log.info("Resources verified."); + launch(); + return; + } + + // redownload any that are corrupt or invalid... + Log.info(failures.size() + " rsrcs require update."); + download(failures); + // now we'll loop back and try it all again } + Log.warning("Pants! We couldn't get the job done."); + // TODO: throw exception } catch (Exception e) { Log.logStackTrace(e); + // TODO: display error to user } } @@ -82,7 +90,30 @@ public class Getdown * Called if the application is determined to be of an old version. */ protected void update () + throws IOException { + // first clear all validation markers + _app.clearValidationMarkers(); + + // attempt to download the patch file + Resource patch = _app.getPatchResource(); + if (patch != null) { + // download the patch file... + ArrayList list = new ArrayList(); + list.add(patch); + download(list); + + // and apply it... + // TODO + } + // if the patch resource is null, that means something was booched + // in the application, so we skip the patching process but update + // the metadata which will result in a "brute force" upgrade + + // finally update our metadata files... + _app.updateMetadata(); + // ...and reinitialize the application + _ifc = _app.init(); } /** @@ -196,16 +227,21 @@ public class Getdown public static void main (String[] args) { - // ensure the proper parameters are passed - if (args.length != 1) { - System.err.println("Usage: java -jar getdown.jar app_dir"); - System.exit(-1); + // maybe they specified the appdir in a system property + String adarg = System.getProperty("appdir"); + // if not, check for a command line argument + if (StringUtil.blank(adarg)) { + if (args.length != 1) { + System.err.println("Usage: java -jar getdown.jar app_dir"); + System.exit(-1); + } + adarg = args[0]; } // ensure a valid directory was supplied - File appDir = new File(args[0]); + File appDir = new File(adarg); if (!appDir.exists() || !appDir.isDirectory()) { - Log.warning("Invalid app_dir '" + args[0] + "'."); + Log.warning("Invalid app_dir '" + adarg + "'."); System.exit(-1); }