Consolidate & enhance "environment config" handling.
The new code is more careful about how it obtains config info, and issues warnings when sources of info conflict. It does not abort in such circumstances as Getdown's prime directive is to do the best it can to get the app updated and launched. But the warnings will hopefully allow accidental conflicts to be detected and avoided before being deployed to customers. We also now allow bootstrap.properties to provide an appid; that matches what was possible via system properties and command line arguments. We also avoid "communicating" environment configuration through the Java system properties mechanism. We now read from sysprops but do not write back to them. Lastly, this includes some name tidying (like appdir -> appDir and getAppdir -> getAppDir).
This commit is contained in:
@@ -42,6 +42,14 @@ public class Log
|
||||
*/
|
||||
public void warning (Object message, Object... args) { doLog(2, message, args); }
|
||||
|
||||
/**
|
||||
* Logs an error message.
|
||||
*
|
||||
* @param message the message to be logged.
|
||||
* @param args a list of key/value pairs and an optional final Throwable.
|
||||
*/
|
||||
public void error (Object message, Object... args) { doLog(3, message, args); }
|
||||
|
||||
protected void doLog (int levIdx, Object message, Object[] args) {
|
||||
if (_impl.isLoggable(LEVELS[levIdx])) {
|
||||
Throwable err = null;
|
||||
|
||||
@@ -48,7 +48,7 @@ public class ClassPaths
|
||||
*/
|
||||
public static ClassPath buildCachedClassPath (Application app) throws IOException
|
||||
{
|
||||
File cacheDir = new File(app.getAppdir(), CACHE_DIR);
|
||||
File cacheDir = new File(app.getAppDir(), CACHE_DIR);
|
||||
// a negative value of code_cache_retention_days allows to clean up the cache forcefully
|
||||
if (app.getCodeCacheRetentionDays() <= 0) {
|
||||
runGarbageCollection(app, cacheDir);
|
||||
|
||||
@@ -233,45 +233,21 @@ public class Application
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an application instance with no signers.
|
||||
*
|
||||
* @see #Application(File, String, List, String[], String[])
|
||||
*/
|
||||
public Application (File appdir, String appid)
|
||||
{
|
||||
this(appdir, appid, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an application instance which records the location of the <code>getdown.txt</code>
|
||||
* configuration file from the supplied application directory.
|
||||
*
|
||||
* @param appid usually null but a string identifier if a secondary application is desired to
|
||||
* be launched. That application will use {@code appid.class} and {@code appid.apparg} to
|
||||
* configure itself but all other parameters will be the same as the primary application.
|
||||
* @param signers a list of possible signers of this application. Used to verify the digest.
|
||||
* @param jvmargs additional arguments to pass on to launched jvms.
|
||||
* @param appargs additional arguments to pass on to launched application; these will be added
|
||||
* after the args in the getdown.txt file.
|
||||
*/
|
||||
public Application (File appdir, String appid, List<Certificate> signers,
|
||||
String[] jvmargs, String[] appargs)
|
||||
{
|
||||
_appdir = appdir;
|
||||
_appid = appid;
|
||||
_signers = (signers == null) ? Collections.<Certificate>emptyList() : signers;
|
||||
_config = getLocalPath(appdir, CONFIG_FILE);
|
||||
_extraJvmArgs = (jvmargs == null) ? EMPTY_STRING_ARRAY : jvmargs;
|
||||
_extraAppArgs = (appargs == null) ? EMPTY_STRING_ARRAY : appargs;
|
||||
public Application (EnvConfig envc) {
|
||||
_envc = envc;
|
||||
_config = getLocalPath(envc.appDir, CONFIG_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured application directory.
|
||||
*/
|
||||
public File getAppdir()
|
||||
{
|
||||
return _appdir;
|
||||
public File getAppDir () {
|
||||
return _envc.appDir;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,7 +526,7 @@ public class Application
|
||||
}
|
||||
// otherwise, issue a warning that we found no getdown file
|
||||
else {
|
||||
log.info("Found no getdown.txt file", "appdir", _appdir);
|
||||
log.info("Found no getdown.txt file", "appdir", getAppDir());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warning("Failure reading config file", "file", config, e);
|
||||
@@ -559,8 +535,8 @@ public class Application
|
||||
// if we failed to read our config file, check for an appbase specified via a system
|
||||
// property; we can use that to bootstrap ourselves back into operation
|
||||
if (config == null) {
|
||||
String appbase = SysProps.appBase();
|
||||
log.info("Attempting to obtain 'appbase' from system property", "appbase", appbase);
|
||||
String appbase = _envc.appBase;
|
||||
log.info("Using 'appbase' from bootstrap config", "appbase", appbase);
|
||||
Map<String, Object> cdata = new HashMap<>();
|
||||
cdata.put("appbase", appbase);
|
||||
config = new Config(cdata);
|
||||
@@ -607,7 +583,7 @@ public class Application
|
||||
}
|
||||
}
|
||||
|
||||
String appPrefix = StringUtil.isBlank(_appid) ? "" : (_appid + ".");
|
||||
String appPrefix = _envc.appId == null ? "" : (_envc.appId + ".");
|
||||
|
||||
// determine our application class name (use app-specific class _if_ one is provided)
|
||||
_class = config.getString("class");
|
||||
@@ -708,9 +684,6 @@ public class Application
|
||||
addAll(jvmargs, _jvmargs);
|
||||
}
|
||||
|
||||
// Add the launch specific JVM arguments
|
||||
addAll(_extraJvmArgs, _jvmargs);
|
||||
|
||||
// get the set of optimum JVM arguments
|
||||
_optimumJvmArgs = config.getMultiValue("optimum_jvmarg");
|
||||
|
||||
@@ -719,7 +692,7 @@ public class Application
|
||||
addAll(appargs, _appargs);
|
||||
|
||||
// add the launch specific application arguments
|
||||
addAll(_extraAppArgs, _appargs);
|
||||
_appargs.addAll(_envc.appArgs);
|
||||
|
||||
// look for custom arguments
|
||||
fillAssignmentListFromPairs("extra.txt", _txtJvmArgs);
|
||||
@@ -783,7 +756,7 @@ public class Application
|
||||
*/
|
||||
public File getLocalPath (String path)
|
||||
{
|
||||
return getLocalPath(_appdir, path);
|
||||
return getLocalPath(getAppDir(), path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -806,7 +779,7 @@ public class Application
|
||||
// if we have an unpacked VM, check the 'release' file for its version
|
||||
Resource vmjar = getJavaVMResource();
|
||||
if (vmjar != null && vmjar.isMarkedValid()) {
|
||||
File vmdir = new File(_appdir, LaunchUtil.LOCAL_JAVA_DIR);
|
||||
File vmdir = new File(getAppDir(), LaunchUtil.LOCAL_JAVA_DIR);
|
||||
File relfile = new File(vmdir, "release");
|
||||
if (!relfile.exists()) {
|
||||
log.warning("Unpacked JVM missing 'release' file. Assuming valid version.");
|
||||
@@ -925,7 +898,7 @@ public class Application
|
||||
ArrayList<String> args = new ArrayList<>();
|
||||
|
||||
// reconstruct the path to the JVM
|
||||
args.add(LaunchUtil.getJVMPath(_appdir, _windebug || optimum));
|
||||
args.add(LaunchUtil.getJVMPath(getAppDir(), _windebug || optimum));
|
||||
|
||||
// check whether we're using -jar mode or -classpath mode
|
||||
boolean dashJarMode = MANIFEST_CLASS.equals(_class);
|
||||
@@ -999,7 +972,7 @@ public class Application
|
||||
String[] sargs = args.toArray(new String[args.size()]);
|
||||
log.info("Running " + StringUtil.join(sargs, "\n "));
|
||||
|
||||
return Runtime.getRuntime().exec(sargs, envp, _appdir);
|
||||
return Runtime.getRuntime().exec(sargs, envp, getAppDir());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1095,7 +1068,7 @@ public class Application
|
||||
/** Replaces the application directory and version in any argument. */
|
||||
protected String processArg (String arg)
|
||||
{
|
||||
arg = arg.replace("%APPDIR%", _appdir.getAbsolutePath());
|
||||
arg = arg.replace("%APPDIR%", getAppDir().getAbsolutePath());
|
||||
arg = arg.replace("%VERSION%", String.valueOf(_version));
|
||||
|
||||
// if this argument contains %ENV.FOO% replace those with the associated values looked up
|
||||
@@ -1135,7 +1108,7 @@ public class Application
|
||||
|
||||
// this will read in the contents of the digest file and validate itself
|
||||
try {
|
||||
_digest = new Digest(_appdir, _strictComments);
|
||||
_digest = new Digest(getAppDir(), _strictComments);
|
||||
} catch (IOException ioe) {
|
||||
log.info("Failed to load digest: " + ioe.getMessage() + ". Attempting recovery...");
|
||||
}
|
||||
@@ -1149,7 +1122,7 @@ public class Application
|
||||
try {
|
||||
status.updateStatus("m.checking");
|
||||
downloadDigestFiles();
|
||||
_digest = new Digest(_appdir, _strictComments);
|
||||
_digest = new Digest(getAppDir(), _strictComments);
|
||||
if (!olddig.equals(_digest.getMetaDigest())) {
|
||||
log.info("Unversioned digest changed. Revalidating...");
|
||||
status.updateStatus("m.validating");
|
||||
@@ -1167,7 +1140,7 @@ public class Application
|
||||
if (_digest == null) {
|
||||
status.updateStatus("m.updating_metadata");
|
||||
downloadDigestFiles();
|
||||
_digest = new Digest(_appdir, _strictComments);
|
||||
_digest = new Digest(getAppDir(), _strictComments);
|
||||
}
|
||||
|
||||
// now verify the contents of our main config file
|
||||
@@ -1178,7 +1151,7 @@ public class Application
|
||||
// caller because there's nothing we can do to automatically recover
|
||||
downloadConfigFile();
|
||||
downloadDigestFiles();
|
||||
_digest = new Digest(_appdir, _strictComments);
|
||||
_digest = new Digest(getAppDir(), _strictComments);
|
||||
// revalidate everything if we end up downloading new metadata
|
||||
clearValidationMarkers();
|
||||
// if the new copy validates, reinitialize ourselves; otherwise report baffling hoseage
|
||||
@@ -1509,8 +1482,8 @@ public class Application
|
||||
File target = downloadFile(path);
|
||||
|
||||
if (sigVersion > 0) {
|
||||
if (_signers.isEmpty()) {
|
||||
log.info("No signers, not verifying file", "path", path);
|
||||
if (_envc.certs.isEmpty()) {
|
||||
log.info("No signing certs, not verifying digest.txt", "path", path);
|
||||
|
||||
} else {
|
||||
File signatureFile = downloadFile(path + SIGNATURE_SUFFIX);
|
||||
@@ -1523,7 +1496,7 @@ public class Application
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int length, validated = 0;
|
||||
for (Certificate cert : _signers) {
|
||||
for (Certificate cert : _envc.certs) {
|
||||
try (FileInputStream dataInput = new FileInputStream(target)) {
|
||||
Signature sig = Signature.getInstance(Digest.sigAlgorithm(sigVersion));
|
||||
sig.initVerify(cert);
|
||||
@@ -1705,8 +1678,7 @@ public class Application
|
||||
return new File(appdir, path);
|
||||
}
|
||||
|
||||
protected File _appdir;
|
||||
protected String _appid;
|
||||
protected final EnvConfig _envc;
|
||||
protected File _config;
|
||||
protected Digest _digest;
|
||||
|
||||
@@ -1749,15 +1721,10 @@ public class Application
|
||||
protected List<String> _jvmargs = new ArrayList<>();
|
||||
protected List<String> _appargs = new ArrayList<>();
|
||||
|
||||
protected String[] _extraJvmArgs;
|
||||
protected String[] _extraAppArgs;
|
||||
|
||||
protected String[] _optimumJvmArgs;
|
||||
|
||||
protected List<String> _txtJvmArgs = new ArrayList<>();
|
||||
|
||||
protected List<Certificate> _signers;
|
||||
|
||||
/** If a warning has been issued about not being able to set modtimes. */
|
||||
protected boolean _warnedAboutSetLastModified;
|
||||
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
//
|
||||
// Getdown - application installer, patcher and launcher
|
||||
// Copyright (C) 2004-2016 Getdown authors
|
||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
||||
|
||||
package com.threerings.getdown.data;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.*;
|
||||
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
|
||||
/** Configuration that comes from our "environment" (command line args, sys props, etc.). */
|
||||
public final class EnvConfig {
|
||||
|
||||
/** Used to report problems or feedback by {@link #create}. */
|
||||
public static final class Note {
|
||||
public static enum Level { INFO, WARN, ERROR };
|
||||
public static Note info (String msg) { return new Note(Level.INFO, msg); }
|
||||
public static Note warn (String msg) { return new Note(Level.WARN, msg); }
|
||||
public static Note error (String msg) { return new Note(Level.ERROR, msg); }
|
||||
public final Level level;
|
||||
public final String message;
|
||||
public Note (Level level, String message) {
|
||||
this.level = level;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an environment config, obtaining information (in order) from the following sources:
|
||||
*
|
||||
* <ul>
|
||||
* <li> A {@code bootstrap.properties} file bundled with the jar. </li>
|
||||
* <li> System properties supplied to the JVM. </li>
|
||||
* <li> The supplied command line arguments ({@code argv}). </li>
|
||||
* </ul>
|
||||
*
|
||||
* If a later source supplies a configuration already provided by a prior source, a warning
|
||||
* message will be logged to indicate the conflict, and the prior source will be used.
|
||||
*
|
||||
* @param notes a list into which notes are added, to be logged after the logging system has
|
||||
* been initialized (which cannot happen until the appdir is known). If any {@code ERROR} notes
|
||||
* are included, the app should terminate after reporting them.
|
||||
* @return an env config instance, or {@code null} if no appdir could be located via any
|
||||
* configuration source.
|
||||
*/
|
||||
public static EnvConfig create (String[] argv, List<Note> notes) {
|
||||
String appDir = null, appDirProv = null;
|
||||
String appId = null, appIdProv = null;
|
||||
String appBase = null, appBaseProv = null;
|
||||
|
||||
// start with bootstrap.properties config, if avaialble
|
||||
try {
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("bootstrap");
|
||||
if (bundle.containsKey("appdir")) {
|
||||
appDir = bundle.getString("appdir");
|
||||
appDir = appDir.replace(USER_HOME_KEY, System.getProperty("user.home"));
|
||||
File appDirFile = new File(appDir);
|
||||
if (!appDirFile.exists()) {
|
||||
appDirFile.mkdirs();
|
||||
}
|
||||
appDirProv = "bootstrap.properties";
|
||||
}
|
||||
if (bundle.containsKey("appid")) {
|
||||
appId = bundle.getString("appid");
|
||||
appIdProv = "bootstrap.properties";
|
||||
}
|
||||
if (bundle.containsKey("appbase")) {
|
||||
appBase = bundle.getString("appbase");
|
||||
appBaseProv = "bootstrap.properties";
|
||||
}
|
||||
} catch (MissingResourceException e) {
|
||||
// bootstrap.properties is optional; no need for a warning
|
||||
}
|
||||
|
||||
// next seek config from system properties
|
||||
String spropsAppDir = SysProps.appDir();
|
||||
if (!StringUtil.isBlank(spropsAppDir)) {
|
||||
if (appDir == null) {
|
||||
appDir = spropsAppDir;
|
||||
appDirProv = "system property";
|
||||
} else {
|
||||
notes.add(Note.warn("Ignoring 'appdir' system property, have appdir via '" +
|
||||
appDirProv + "'"));
|
||||
}
|
||||
}
|
||||
String spropsAppId = SysProps.appId();
|
||||
if (!StringUtil.isBlank(spropsAppId)) {
|
||||
if (appId == null) {
|
||||
appId = spropsAppId;
|
||||
appIdProv = "system property";
|
||||
} else {
|
||||
notes.add(Note.warn("Ignoring 'appid' system property, have appid via '" +
|
||||
appIdProv + "'"));
|
||||
}
|
||||
}
|
||||
String spropsAppBase = SysProps.appBase();
|
||||
if (!StringUtil.isBlank(spropsAppBase)) {
|
||||
if (appBase == null) {
|
||||
appBase = spropsAppBase;
|
||||
appBaseProv = "system property";
|
||||
} else {
|
||||
notes.add(Note.warn("Ignoring 'appbase' system property, have appbase via '" +
|
||||
appBaseProv + "'"));
|
||||
}
|
||||
}
|
||||
|
||||
// finally obtain config from command line arguments
|
||||
String argvAppDir = argv.length > 0 ? argv[0] : null;
|
||||
if (!StringUtil.isBlank(argvAppDir)) {
|
||||
if (appDir == null) {
|
||||
appDir = argvAppDir;
|
||||
appDirProv = "command line";
|
||||
} else {
|
||||
notes.add(Note.warn("Ignoring 'appdir' command line arg, have appdir via '" +
|
||||
appDirProv + "'"));
|
||||
}
|
||||
}
|
||||
String argvAppId = argv.length > 1 ? argv[1] : null;
|
||||
if (!StringUtil.isBlank(spropsAppId)) {
|
||||
if (appId == null) {
|
||||
appId = argvAppId;
|
||||
appIdProv = "command line";
|
||||
} else {
|
||||
notes.add(Note.warn("Ignoring 'appid' command line arg, have appid via '" +
|
||||
appIdProv + "'"));
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that we were able to fine an app dir
|
||||
if (appDir == null) {
|
||||
return null; // caller will report problem to user
|
||||
}
|
||||
|
||||
// ensure that the appdir refers to a directory that exists
|
||||
File appDirFile = new File(appDir);
|
||||
if (!appDirFile.exists() || !appDirFile.isDirectory()) {
|
||||
notes.add(Note.error("Invalid appdir '" + appDir + "'."));
|
||||
return null;
|
||||
}
|
||||
|
||||
notes.add(Note.info("Using appdir from " + appDirProv + ": " + appDir));
|
||||
if (appId != null) notes.add(Note.info("Using appid from " + appIdProv + ": " + appId));
|
||||
if (appBase != null) notes.add(Note.info("Using appbase from " + appBaseProv + ": " +
|
||||
appBase));
|
||||
|
||||
// pass along anything after the first tow args as extra app args
|
||||
List<String> appArgs = argv.length > 2 ?
|
||||
Arrays.asList(argv).subList(2, argv.length-2) :
|
||||
Collections.<String>emptyList();
|
||||
|
||||
// load X.509 certificate if it exists
|
||||
File crtFile = new File(appDirFile, Digest.digestFile(Digest.VERSION) + ".crt");
|
||||
List<Certificate> certs = new ArrayList<>();
|
||||
if (crtFile.exists()) {
|
||||
try (FileInputStream fis = new FileInputStream(crtFile)) {
|
||||
X509Certificate certificate = (X509Certificate)
|
||||
CertificateFactory.getInstance("X.509").generateCertificate(fis);
|
||||
certs.add(certificate);
|
||||
} catch (Exception e) {
|
||||
notes.add(Note.error("Certificate error: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
return new EnvConfig(appDirFile, appId, appBase, certs, appArgs);
|
||||
}
|
||||
|
||||
/** The directory in which the application and metadata is stored. */
|
||||
public final File appDir;
|
||||
|
||||
/** Either {@code null} or an identifier for a secondary application that should be
|
||||
* launched. That app will use {@code appid.class} and {@code appid.apparg} to configure
|
||||
* itself but all other parameters will be the same as the primary app. */
|
||||
public final String appId;
|
||||
|
||||
/** Either {@code null} or fallback {@code appbase} to use if one cannot be read from a
|
||||
* {@code getdown.txt} file during startup. */
|
||||
public final String appBase;
|
||||
|
||||
/** Zero or more signing certificates used to verify the digest file. */
|
||||
public final List<Certificate> certs;
|
||||
|
||||
/** Additional arguments to pass on to launched application. These will be added after the
|
||||
* args in the getdown.txt file. */
|
||||
public final List<String> appArgs;
|
||||
|
||||
public EnvConfig (File appDir) {
|
||||
this(appDir, null, null, Collections.<Certificate>emptyList(),
|
||||
Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
private EnvConfig (File appDir, String appId, String appBase, List<Certificate> certs,
|
||||
List<String> appArgs) {
|
||||
this.appDir = appDir;
|
||||
this.appId = appId;
|
||||
this.appBase = appBase;
|
||||
this.certs = certs;
|
||||
this.appArgs = appArgs;
|
||||
}
|
||||
|
||||
private static final String USER_HOME_KEY = "${user.home}";
|
||||
}
|
||||
@@ -28,8 +28,8 @@ public class SysProps
|
||||
return System.getProperty("appid");
|
||||
}
|
||||
|
||||
/** Configures the appbase (in lieu of providing a skeleton getdown.txt, and as a last resort
|
||||
* fallback). Usage: {@code -Dappbase=URL}. */
|
||||
/** Configures the bootstrap appbase (used in lieu of providing a skeleton getdown.txt, and as
|
||||
* a last resort fallback). Usage: {@code -Dappbase=URL}. */
|
||||
public static String appBase () {
|
||||
return System.getProperty("appbase");
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.security.MessageDigest;
|
||||
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Digest;
|
||||
import com.threerings.getdown.data.EnvConfig;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import com.threerings.getdown.util.StreamUtil;
|
||||
@@ -58,13 +59,13 @@ public class Differ
|
||||
", overs=" + overs + "].");
|
||||
}
|
||||
|
||||
Application oapp = new Application(ovdir, null);
|
||||
Application oapp = new Application(new EnvConfig(ovdir));
|
||||
oapp.init(false);
|
||||
ArrayList<Resource> orsrcs = new ArrayList<>();
|
||||
orsrcs.addAll(oapp.getCodeResources());
|
||||
orsrcs.addAll(oapp.getResources());
|
||||
|
||||
Application napp = new Application(nvdir, null);
|
||||
Application napp = new Application(new EnvConfig(nvdir));
|
||||
napp.init(false);
|
||||
ArrayList<Resource> nrsrcs = new ArrayList<>();
|
||||
nrsrcs.addAll(napp.getCodeResources());
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Digest;
|
||||
import com.threerings.getdown.data.EnvConfig;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.util.Base64;
|
||||
|
||||
@@ -73,7 +74,7 @@ public class Digester
|
||||
System.out.println("Generating digest file '" + target + "'...");
|
||||
|
||||
// create our application and instruct it to parse its business
|
||||
Application app = new Application(appdir, null);
|
||||
Application app = new Application(new EnvConfig(appdir));
|
||||
app.init(false);
|
||||
|
||||
List<Resource> rsrcs = new ArrayList<>();
|
||||
|
||||
@@ -381,7 +381,7 @@ public class JarDiff implements JarDiffCodes
|
||||
|
||||
public String hasSameContent (JarFile2 file, JarEntry entry) throws IOException {
|
||||
String thisName = null;
|
||||
Long crcL = new Long(entry.getCrc());
|
||||
Long crcL = Long.valueOf(entry.getCrc());
|
||||
// check if this jar contains files with the passed in entry's crc
|
||||
if (_crcToEntryMap.containsKey(crcL)) {
|
||||
// get the Linked List with files with the crc
|
||||
@@ -416,7 +416,7 @@ public class JarDiff implements JarDiffCodes
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
long crc = entry.getCrc();
|
||||
Long crcL = new Long(crc);
|
||||
Long crcL = Long.valueOf(crc);
|
||||
if (_debug) {
|
||||
System.out.println("\t" + entry.getName() + " CRC " + crc);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class ClassPathsTest
|
||||
when(_firstJar.getFinalTarget()).thenReturn(_firstJarFile);
|
||||
when(_secondJar.getFinalTarget()).thenReturn(_secondJarFile);
|
||||
when(_application.getActiveCodeResources()).thenReturn(Arrays.asList(_firstJar, _secondJar));
|
||||
when(_application.getAppdir()).thenReturn(_appdir.getRoot());
|
||||
when(_application.getAppDir()).thenReturn(_appdir.getRoot());
|
||||
}
|
||||
|
||||
@Test public void shouldBuildDefaultClassPath () throws IOException
|
||||
|
||||
@@ -43,22 +43,11 @@ import ca.beq.util.win32.registry.RootKey;
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
|
||||
import com.threerings.getdown.data.Application.UpdateInterface.Step;
|
||||
import com.threerings.getdown.data.Application;
|
||||
import com.threerings.getdown.data.Build;
|
||||
import com.threerings.getdown.data.Resource;
|
||||
import com.threerings.getdown.data.SysProps;
|
||||
import com.threerings.getdown.data.*;
|
||||
import com.threerings.getdown.net.Downloader;
|
||||
import com.threerings.getdown.net.HTTPDownloader;
|
||||
import com.threerings.getdown.tools.Patcher;
|
||||
import com.threerings.getdown.util.Config;
|
||||
import com.threerings.getdown.util.ConnectionUtil;
|
||||
import com.threerings.getdown.util.FileUtil;
|
||||
import com.threerings.getdown.util.LaunchUtil;
|
||||
import com.threerings.getdown.util.MessageUtil;
|
||||
import com.threerings.getdown.util.ProgressAggregator;
|
||||
import com.threerings.getdown.util.ProgressObserver;
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
import com.threerings.getdown.util.VersionUtil;
|
||||
import com.threerings.getdown.util.*;
|
||||
|
||||
import static com.threerings.getdown.Log.log;
|
||||
|
||||
@@ -68,13 +57,7 @@ import static com.threerings.getdown.Log.log;
|
||||
public abstract class Getdown extends Thread
|
||||
implements Application.StatusDisplay, RotatingBackgrounds.ImageLoader
|
||||
{
|
||||
public Getdown (File appDir, String appId)
|
||||
{
|
||||
this(appDir, appId, null, null, null);
|
||||
}
|
||||
|
||||
public Getdown (File appDir, String appId, List<Certificate> signers,
|
||||
String[] jvmargs, String[] appargs)
|
||||
public Getdown (EnvConfig envc)
|
||||
{
|
||||
super("Getdown");
|
||||
try {
|
||||
@@ -94,7 +77,7 @@ public abstract class Getdown extends Thread
|
||||
} catch (Exception e) {
|
||||
// welcome to hell, where java can't cope with a classpath that contains jars that live
|
||||
// in a directory that contains a !, at least the same bug happens on all platforms
|
||||
String dir = appDir.toString();
|
||||
String dir = envc.appDir.toString();
|
||||
if (dir.equals(".")) {
|
||||
dir = System.getProperty("user.dir");
|
||||
}
|
||||
@@ -103,7 +86,7 @@ public abstract class Getdown extends Thread
|
||||
"contains the '!' character, this will trigger this error.";
|
||||
fail(errmsg);
|
||||
}
|
||||
_app = new Application(appDir, appId, signers, jvmargs, appargs);
|
||||
_app = new Application(envc);
|
||||
_startup = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
|
||||
@@ -14,24 +14,18 @@ import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.WindowConstants;
|
||||
|
||||
import com.samskivert.swing.util.SwingUtil;
|
||||
import com.threerings.getdown.data.Digest;
|
||||
import com.threerings.getdown.data.EnvConfig;
|
||||
import com.threerings.getdown.data.SysProps;
|
||||
import com.threerings.getdown.util.LaunchUtil;
|
||||
import com.threerings.getdown.util.StringUtil;
|
||||
@@ -42,11 +36,10 @@ import static com.threerings.getdown.Log.log;
|
||||
*/
|
||||
public class GetdownApp
|
||||
{
|
||||
|
||||
private static final String USER_HOME = "${user.home}";
|
||||
|
||||
public static void main (String[] argv)
|
||||
{
|
||||
/**
|
||||
* The main entry point of the Getdown launcher application.
|
||||
*/
|
||||
public static void main (String[] argv) {
|
||||
try {
|
||||
start(argv);
|
||||
} catch (Exception e) {
|
||||
@@ -61,58 +54,17 @@ public class GetdownApp
|
||||
* @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);
|
||||
|
||||
// check for app dir in a sysprop and then via argv
|
||||
String adarg = SysProps.appDir();
|
||||
if (StringUtil.isBlank(adarg)) {
|
||||
loadBootstrapResource();
|
||||
if (args.isEmpty() && SysProps.appDir() == null) {
|
||||
System.err.println("Usage: java -jar getdown.jar app_dir [app_id] [app args]");
|
||||
System.exit(-1);
|
||||
}
|
||||
if (!args.isEmpty()) {
|
||||
adarg = args.get(aidx++);
|
||||
} else {
|
||||
adarg = SysProps.appDir();
|
||||
}
|
||||
}
|
||||
|
||||
// check for an app identifier in a sysprop and then via argv
|
||||
String appId = SysProps.appId();
|
||||
if (StringUtil.isBlank(appId) && aidx < args.size()) {
|
||||
appId = args.get(aidx++);
|
||||
}
|
||||
|
||||
// pass along anything after that as app args
|
||||
String[] appArgs = (aidx >= args.size()) ? null :
|
||||
args.subList(aidx, args.size()).toArray(new String[0]);
|
||||
|
||||
// ensure a valid directory was supplied
|
||||
File appDir = new File(adarg);
|
||||
if (!appDir.exists() || !appDir.isDirectory()) {
|
||||
log.warning("Invalid app_dir '" + adarg + "'.");
|
||||
List<EnvConfig.Note> notes = new ArrayList<>();
|
||||
EnvConfig envc = EnvConfig.create(argv, notes);
|
||||
if (envc == null) {
|
||||
if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
|
||||
else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// load X.509 certificate if it exists
|
||||
File crtFile = new File(appDir, Digest.digestFile(Digest.VERSION) + ".crt");
|
||||
List<Certificate> crts = new ArrayList<>();
|
||||
if (crtFile.exists()) {
|
||||
try (FileInputStream fis = new FileInputStream(crtFile)) {
|
||||
X509Certificate certificate = (X509Certificate)
|
||||
CertificateFactory.getInstance("X.509").generateCertificate(fis);
|
||||
crts.add(certificate);
|
||||
} catch (Exception e) {
|
||||
log.warning("Certificate error: " + e.getMessage());
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
// pipe our output into a file in the application directory
|
||||
if (!SysProps.noLogRedir()) {
|
||||
File logFile = new File(appDir, "launcher.log");
|
||||
File logFile = new File(envc.appDir, "launcher.log");
|
||||
try {
|
||||
PrintStream logOut = new PrintStream(
|
||||
new BufferedOutputStream(new FileOutputStream(logFile)), true);
|
||||
@@ -123,6 +75,17 @@ public class GetdownApp
|
||||
}
|
||||
}
|
||||
|
||||
// report any notes from reading our env config, and abort if necessary
|
||||
boolean abort = false;
|
||||
for (EnvConfig.Note note : notes) {
|
||||
switch (note.level) {
|
||||
case INFO: log.info(note.message); break;
|
||||
case WARN: log.warning(note.message); break;
|
||||
case ERROR: log.error(note.message); abort = true; break;
|
||||
}
|
||||
}
|
||||
if (abort) System.exit(-1);
|
||||
|
||||
// record a few things for posterity
|
||||
log.info("------------------ VM Info ------------------");
|
||||
log.info("-- OS Name: " + System.getProperty("os.name"));
|
||||
@@ -135,7 +98,7 @@ public class GetdownApp
|
||||
log.info("-- Cur dir: " + System.getProperty("user.dir"));
|
||||
log.info("---------------------------------------------");
|
||||
|
||||
Getdown app = new Getdown(appDir, appId, crts, null, appArgs) {
|
||||
Getdown app = new Getdown(envc) {
|
||||
@Override
|
||||
protected Container createContainer () {
|
||||
// create our user interface, and display it
|
||||
@@ -270,25 +233,4 @@ public class GetdownApp
|
||||
app.start();
|
||||
return app;
|
||||
}
|
||||
|
||||
private static void loadBootstrapResource() {
|
||||
try {
|
||||
ResourceBundle bundle = ResourceBundle.getBundle("bootstrap");
|
||||
if (bundle.containsKey("appbase")) {
|
||||
System.setProperty("appbase", bundle.getString("appbase"));
|
||||
}
|
||||
if (bundle.containsKey("appdir")) {
|
||||
String appDir = bundle.getString("appdir");
|
||||
appDir = appDir.replace(USER_HOME, System.getProperty("user.home"));
|
||||
File appDirFile = new File(appDir);
|
||||
if (!appDirFile.exists()) {
|
||||
appDirFile.mkdirs();
|
||||
}
|
||||
System.setProperty("appdir", appDir);
|
||||
}
|
||||
log.info("bootstrap.properties found using:", "_appdir", System.getProperty("appdir"), "appbase", System.getProperty("appbase"));
|
||||
} catch (MissingResourceException e) {
|
||||
log.info("bootstrap.properties not found in resource bundle, starting without bootstrapping");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user