Further progress on versioned application upgrades.

This commit is contained in:
Michael Bayne
2004-07-13 02:42:52 +00:00
parent 1384a79330
commit f376eadd7c
2 changed files with 145 additions and 53 deletions
@@ -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 <code>getdown.txt</code>
* 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);
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);
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 <code>getdown.txt</code> and
* <code>digest.txt</code> 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();
@@ -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();
// loop back again and reverify the metadata
continue;
}
} else {
Log.info("Metadata verified.");
// now verify our resources...
List failures = _app.verifyResources();
if (failures == null) {
Log.info("Resources verified.");
launch();
return;
}
} else {
// 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
// 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);
}