diff --git a/getdown/src/getdown/CHANGELOG.md b/getdown/src/getdown/CHANGELOG.md index 098651eb1..ec7a923e1 100644 --- a/getdown/src/getdown/CHANGELOG.md +++ b/getdown/src/getdown/CHANGELOG.md @@ -1,16 +1,73 @@ # Getdown Releases -## 1.8.3 - Unreleased +## 1.8.7 - Unreleased + +* Reinstated env var support in `appbase` property. + +* Fixed issue with `myIpAddress()` in PAC proxy support. + +## 1.8.6 - June 4, 2019 + +* Fixed issues with PAC proxy support: added `myIpAddress()`, fixed `dnsResolve()`, fixed crash + when detecting PAC proxy. + +* Reverted env var support in `appbase` property. It's causing problems that need to be + investigated. + +## 1.8.5 - May 29, 2019 + +* Fixed issues with proxy information not getting properly passed through to app. + Via [#216](//github.com/threerings/getdown/pull/216). + +* `appbase` and `latest` properties in `getdown.txt` now process env var subtitutions. + +* Added support for [Proxy Auto-config](https://en.wikipedia.org/wiki/Proxy_auto-config) via PAC + files. + +* Proxy handling can now recover from credentials going out of date. It will detect the error and + ask for updated credentials. + +* Added `try_no_proxy` system property. This instructs Getdown to always first try to run without a + proxy, regardless of whether it has been configured to use a proxy in the past. And if it can run + without a proxy, it does so for that session, but retains the proxy config for future sessions in + which the proxy may again be needed. + +* Added `revalidate_policy` config to control when Getdown revalidates resources (by hashing them + and comparing that hash to the values in `digest.txt`). The default, `after_update`, only + validates resources after the app is updated. A new mode, `always`, validates resources prior to + every application launch. + +## 1.8.4 - May 14, 2019 + +* Added `verify_timeout` config to allow customization of the default (60 second) timeout during + the resource verification process. Apparently in some pathological situations, this is needed. + Woe betide the users who have to stare at an unmoving progress bar for more than 60 seconds. + Via [#198](//github.com/threerings/getdown/pull/198) + and [901682d](//github.com/threerings/getdown/commit/901682d). + +* Added `java_local_dir` config to allow custom location for Java if `java_location` is specified. + Via [#206](//github.com/threerings/getdown/pull/206). + +* `messages_XX.properties` files are now all maintained in UTF-8 encoding and then converted to + escaped ISO-8859-1 during the build process. + +* Resources and unpacked resources now support `.zip` files as well as `.jar` files. + Via [#210](//github.com/threerings/getdown/pull/210). + +* Fixed issue when path to JVM contained spaces. Via [#214](//github.com/threerings/getdown/pull/214). + +## 1.8.3 - Apr 10, 2019 * Added support for `nresource` resources which must be jar files that contain native libraries. Prior to launching the application, these resources will be unpacked and their contents added to the `java.library.path` system property. * When the app is updated to require a new version of the JVM, that JVM will be downloaded and used - immediately during that app invocation (instead of one invocation later). Via PR#169. + immediately during that app invocation (instead of one invocation later). + Via [#169](//github.com/threerings/getdown/pull/169). -* When a custom JVM is installed, old JVM files will be deleted prior to unpacking the new JVM. Via - PR#170. +* When a custom JVM is installed, old JVM files will be deleted prior to unpacking the new JVM. + Via [#170](//github.com/threerings/getdown/pull/170). * Number of concurrent downloads now defaults to num-cores minus one. Though downloads are I/O bound rather than CPU bound, this still turns out to be a decent default. @@ -23,6 +80,14 @@ credentials supplied by the user. Otherwise they will be requested every time Getdown runs, which is not a viable user experience. +* The Getdown window can be now closed by pressing the `ESC` key. + Via [#191](//github.com/threerings/getdown/pull/191). + +* If no `appdir` is specified via the command line or system property, the current working + directory will be used as the `appdir`. Via [8d59367](//github.com/threerings/getdown/commit/8d59367) + +* A basic Russian translation has been added. Thanks [@sergiorussia](//github.com/sergiorussia)! + ## 1.8.2 - Nov 27, 2018 * Fixed a data corruption bug introduced at last minute into 1.8.1 release. Oops. diff --git a/getdown/src/getdown/ant/.project-MOVED b/getdown/src/getdown/ant/.project-MOVED deleted file mode 100644 index 097cb89db..000000000 --- a/getdown/src/getdown/ant/.project-MOVED +++ /dev/null @@ -1,23 +0,0 @@ - - - getdown-ant - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index e9441bb12..000000000 --- a/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding/=UTF-8 diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 54e56721d..000000000 --- a/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1c..000000000 --- a/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/getdown/src/getdown/ant/pom.xml b/getdown/src/getdown/ant/pom.xml index f8231aa2e..a72d95d87 100644 --- a/getdown/src/getdown/ant/pom.xml +++ b/getdown/src/getdown/ant/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-SNAPSHOT + 1.8.7-SNAPSHOT getdown-ant diff --git a/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java b/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java index 48cc8d426..76212ae89 100644 --- a/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java +++ b/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java @@ -7,16 +7,13 @@ package com.threerings.getdown.tools; import java.io.File; import java.io.IOException; - import java.security.GeneralSecurityException; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; -import com.threerings.getdown.data.Digest; - /** - * An ant task used to create a digest.txt for a Getdown + * An ant task used to create a {@code digest.txt} for a Getdown * application deployment. */ public class DigesterTask extends Task diff --git a/getdown/src/getdown/core/.project-MOVED b/getdown/src/getdown/core/.project-MOVED deleted file mode 100644 index 177252f5f..000000000 --- a/getdown/src/getdown/core/.project-MOVED +++ /dev/null @@ -1,23 +0,0 @@ - - - getdown-core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 0a9bbb889..000000000 --- a/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/it/java=UTF-8 -encoding//src/main/java=UTF-8 -encoding//src/test/java=UTF-8 -encoding//src/test/resources=UTF-8 -encoding/=UTF-8 diff --git a/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 54e56721d..000000000 --- a/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1c..000000000 --- a/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/getdown/src/getdown/core/pom.xml b/getdown/src/getdown/core/pom.xml index efec8b6ce..5e0f852ac 100644 --- a/getdown/src/getdown/core/pom.xml +++ b/getdown/src/getdown/core/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-SNAPSHOT + 1.8.7-SNAPSHOT getdown-core @@ -34,7 +34,7 @@ Wildcards can be used (*.mycompany.com) and multiple values can be separated by commas (app1.foo.com,app2.bar.com,app3.baz.com). --> - jalview.org,*.jalview.org + diff --git a/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java b/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java index 52b4b5ee3..d2ddaf272 100644 --- a/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java +++ b/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java @@ -5,16 +5,16 @@ package com.threerings.getdown.tests; -import java.io.File; import java.nio.charset.StandardCharsets; -import java.nio.file.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.List; -import org.junit.*; -import static org.junit.Assert.*; - import com.threerings.getdown.tools.Digester; +import org.junit.Test; +import static org.junit.Assert.assertEquals; public class DigesterIT { @@ -32,23 +32,23 @@ public class DigesterIT { Files.delete(digest2); assertEquals(Arrays.asList( - "getdown.txt = 779c74fb4b251e18faf6e240a0667964", + "getdown.txt = 9c9b2494929c99d44ae51034d59e1a1b", "testapp.jar = 404dafa55e78b25ec0e3a936357b1883", "funny%test dir/some=file.txt = d8e8fca2dc0f896fd7cb4cb0031ba249", "crazyhashfile#txt = f29d23fd5ab1781bd8d0760b3a516f16", "foo.jar = 46ca4cc9079d9d019bb30cd21ebbc1ec", "script.sh = f66e8ea25598e67e99c47d9b0b2a2cdf", - "digest.txt = f5561d85e4d80cc85883963897e58ff6" + "digest.txt = 11f9ba349cf9edacac4d72a3158447e5" ), digestLines); assertEquals(Arrays.asList( - "getdown.txt = 4f0c657895c3c3a35fa55bf5951c64fa9b0694f8fc685af3f1d8635c639e066b", + "getdown.txt = 1efecfae2a189002a6658f17d162b1922c7bde978944949276dc038a0df2461f", "testapp.jar = c9cb1906afbf48f8654b416c3f831046bd3752a76137e5bf0a9af2f790bf48e0", "funny%test dir/some=file.txt = f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2", "crazyhashfile#txt = 6816889f922de38f145db215a28ad7c5e1badf7354b5cdab225a27486789fa3b", "foo.jar = ea188b872e0496debcbe00aaadccccb12a8aa9b025bb62c130cd3d9b8540b062", "script.sh = cca1c5c7628d9bf7533f655a9cfa6573d64afb8375f81960d1d832dc5135c988", - "digest2.txt = 70b442c9f56660561921da3368e1a206f05c379182fab3062750b7ddcf303407" + "digest2.txt = 41eacdabda8909bdbbf61e4f980867f4003c16a12f6770e6fc619b6af100e05b" ), digest2Lines); } } diff --git a/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt b/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt index 3e0e5381a..ab0e47383 100644 --- a/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt +++ b/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt @@ -13,6 +13,18 @@ apparg = %APPDIR% # test the %env% mechanism jvmarg = -Dusername=\%ENV.USER% +# test various java_*** configs, they are not interesting for digester +java_local_dir = jre +java_max_version = 1089999 +java_min_version = [windows] 1080111 +java_min_version = [!windows] 1080192 +java_exact_version_required = [linux] true +java_location = [linux-amd64] /files/java/java_linux_64.zip +java_location = [linux-i386] /files/java/java_linux_32.zip +java_location = [mac] /files/java/java_mac_64.zip +java_location = [windows-amd64] /files/java/java_windows_64.zip +java_location = [windows-x86] /files/java/java_windows_32.zip + strict_comments = true resource = funny%test dir/some=file.txt resource = crazyhashfile#txt diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java index 13b99564a..da98c9031 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java @@ -15,7 +15,7 @@ import java.util.logging.*; /** * A placeholder class that contains a reference to the log object used by the Getdown code. */ -public class Log +public final class Log { public static class Shim { /** @@ -69,6 +69,13 @@ public class Log /** We dispatch our log messages through this logging shim. */ public static final Shim log = new Shim(); + /** + * Formats a message with key/value pairs. The pairs will be appended to the message as a + * comma separated list of {@code key=value} in square brackets. + * @param message the main log message. + * @param args the key/value pairs. Any trailing key with no value will be ignored. + * @return the formatted message, i.e. {@code Some log message [key=value, key=value]}. + */ public static String format (Object message, Object... args) { if (args.length < 2) return String.valueOf(message); StringBuilder buf = new StringBuilder(String.valueOf(message)); @@ -76,13 +83,13 @@ public class Log buf.append(' '); } buf.append('['); - for (int ii = 0; ii < args.length; ii += 2) { + for (int ii = 0, ll = args.length/2; ii < ll; ii += 1) { if (ii > 0) { buf.append(',').append(' '); } - buf.append(args[ii]).append('='); + buf.append(args[2*ii]).append('='); try { - buf.append(args[ii+1]); + buf.append(args[2*ii+1]); } catch (Throwable t) { buf.append(""); } @@ -136,6 +143,5 @@ public class Log protected FieldPosition _fpos = new FieldPosition(SimpleDateFormat.DATE_FIELD); } - protected static final String DATE_FORMAT = "{0,date} {0,time}"; protected static final Level[] LEVELS = {Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE}; } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java index 67ea64575..7e01e87c5 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java @@ -6,6 +6,8 @@ package com.threerings.getdown.cache; import java.io.File; + +import com.threerings.getdown.data.Resource; import com.threerings.getdown.util.FileUtil; /** @@ -55,9 +57,9 @@ public class GarbageCollector if (subdirs != null) { for (File dir : subdirs) { if (dir.isDirectory()) { - // Get all the native jars in the directory (there should only be one) + // Get all the native jars or zips in the directory (there should only be one) for (File file : dir.listFiles()) { - if (!file.getName().endsWith(".jar")) { + if (!Resource.isJar(file) && !Resource.isZip(file)) { continue; } File cachedFile = getCachedFile(file); @@ -94,6 +96,6 @@ public class GarbageCollector private static File getCachedFile (File file) { return !isLastAccessedFile(file) ? file : new File( - file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf("."))); + file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf('.'))); } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java index 0210e9a86..41f0c5f6d 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java @@ -69,7 +69,7 @@ public class ResourceCache private String getFileSuffix (File fileToCache) { String fileName = fileToCache.getName(); - int index = fileName.lastIndexOf("."); + int index = fileName.lastIndexOf('.'); return index > -1 ? fileName.substring(index) : ""; } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java index 0de5c8ac8..a93122553 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java @@ -22,24 +22,17 @@ import java.util.*; import java.util.concurrent.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.zip.GZIPInputStream; -import com.sun.management.OperatingSystemMXBean; -import java.lang.management.ManagementFactory; - -import jalview.bin.MemorySetting; +import com.threerings.getdown.net.Connector; import com.threerings.getdown.util.*; // avoid ambiguity with java.util.Base64 which we can't use as it's 1.8+ import com.threerings.getdown.util.Base64; -import com.threerings.getdown.data.EnvConfig; -import com.threerings.getdown.data.EnvConfig.Note; - import static com.threerings.getdown.Log.log; import static java.nio.charset.StandardCharsets.UTF_8; /** - * Parses and provide access to the information contained in the getdown.txt + * Parses and provide access to the information contained in the {@code getdown.txt} * configuration file. */ public class Application @@ -218,10 +211,10 @@ public class Application * Used by {@link #verifyMetadata} to communicate status in circumstances where it needs to * take network actions. */ - public static interface StatusDisplay + public interface StatusDisplay { /** Requests that the specified status message be displayed. */ - public void updateStatus (String message); + void updateStatus (String message); } /** @@ -239,19 +232,56 @@ public class Application } } - /** The proxy that should be used to do HTTP downloads. This must be configured prior to using - * the application instance. Yes this is a public mutable field, no I'm not going to create a + /** + * Reads the {@code getdown.txt} config file into a {@code Config} object and returns it. + */ + public static Config readConfig (EnvConfig envc, boolean checkPlatform) throws IOException { + Config config = null; + File cfgfile = new File(envc.appDir, CONFIG_FILE); + Config.ParseOpts opts = Config.createOpts(checkPlatform); + try { + // if we have a configuration file, read the data from it + if (cfgfile.exists()) { + config = Config.parseConfig(cfgfile, opts); + } + // otherwise, try reading data from our backup config file; thanks to funny windows + // bullshit, we have to do this backup file fiddling in case we got screwed while + // updating getdown.txt during normal operation + else if ((cfgfile = new File(envc.appDir, Application.CONFIG_FILE + "_old")).exists()) { + config = Config.parseConfig(cfgfile, opts); + } + // otherwise, issue a warning that we found no getdown file + else { + log.info("Found no getdown.txt file", "appdir", envc.appDir); + } + } catch (Exception e) { + log.warning("Failure reading config file", "file", config, e); + } + + // 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) { + log.info("Using 'appbase' from bootstrap config", "appbase", envc.appBase); + Map cdata = new HashMap<>(); + cdata.put("appbase", envc.appBase); + config = new Config(cdata); + } + + return config; + } + + /** A helper that is used to do HTTP downloads. This must be configured prior to using the + * application instance. Yes this is a public mutable field, no I'm not going to create a * getter and setter just to pretend like that's not the case. */ - public Proxy proxy = Proxy.NO_PROXY; + public Connector conn = Connector.DEFAULT; /** - * Creates an application instance which records the location of the getdown.txt + * Creates an application instance which records the location of the {@code getdown.txt} * configuration file from the supplied application directory. * */ public Application (EnvConfig envc) { _envc = envc; - _config = getLocalPath(envc.appDir, CONFIG_FILE); } /** @@ -375,8 +405,7 @@ public class Application */ public List getActiveCodeResources () { - ArrayList codes = new ArrayList<>(); - codes.addAll(getCodeResources()); + List codes = new ArrayList<>(getCodeResources()); for (AuxGroup aux : getAuxGroups()) { if (isAuxGroupActive(aux.name)) { codes.addAll(aux.codes); @@ -404,8 +433,7 @@ public class Application */ public List getActiveResources () { - ArrayList rsrcs = new ArrayList<>(); - rsrcs.addAll(getResources()); + List rsrcs = new ArrayList<>(getResources()); for (AuxGroup aux : getAuxGroups()) { if (isAuxGroupActive(aux.name)) { rsrcs.addAll(aux.rsrcs); @@ -442,7 +470,15 @@ public class Application } /** - * Returns a resource for a zip file containing a Java VM that can be downloaded to use in + * @return directory into which a local VM installation should be unpacked. + */ + public File getJavaLocalDir () + { + return _javaLocalDir; + } + + /** + * @return a resource for a zip file containing a Java VM that can be downloaded to use in * place of the installed VM (in the case where the VM that launched Getdown does not meet the * application's version requirements) or null if no VM is available for this platform. */ @@ -452,21 +488,16 @@ public class Application return null; } - String extension = (_javaLocation.endsWith(".tgz"))?".tgz":".jar"; - String vmfile = LaunchUtil.LOCAL_JAVA_DIR + extension; - log.info("vmfile is '"+vmfile+"'"); - System.out.println("vmfile is '"+vmfile+"'"); + // take extension from java location + String vmfileExt = _javaLocation.substring(_javaLocation.lastIndexOf('.')); + String vmfile = _javaLocalDir.getName() + vmfileExt; try { URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation)); - log.info("Attempting to fetch jvm at "+remote.toString()); - System.out.println("Attempting to fetch jvm at "+remote.toString()); return new Resource(vmfile, remote, getLocalPath(vmfile), EnumSet.of(Resource.Attr.UNPACK, Resource.Attr.CLEAN)); } catch (Exception e) { log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase, "tvers", _targetVersion, "javaloc", _javaLocation, "error", e); - System.out.println("Failed to create VM resource: vmfile="+vmfile+", appbase="+_appbase+ - ", tvers="+_targetVersion+", javaloc="+_javaLocation+", error="+e); return null; } } @@ -547,88 +578,52 @@ public class Application * @exception IOException thrown if there is an error reading the file or an error encountered * during its parsing. */ - public Config init (boolean checkPlatform) - throws IOException + public Config init (boolean checkPlatform) throws IOException { - Config config = null; - File cfgfile = _config; - Config.ParseOpts opts = Config.createOpts(checkPlatform); - try { - // if we have a configuration file, read the data from it - if (cfgfile.exists()) { - config = Config.parseConfig(_config, opts); - } - // otherwise, try reading data from our backup config file; thanks to funny windows - // bullshit, we have to do this backup file fiddling in case we got screwed while - // updating getdown.txt during normal operation - else if ((cfgfile = getLocalPath(CONFIG_FILE + "_old")).exists()) { - config = Config.parseConfig(cfgfile, opts); - } - // otherwise, issue a warning that we found no getdown file - else { - log.info("Found no getdown.txt file", "appdir", getAppDir()); - } - } catch (Exception e) { - log.warning("Failure reading config file", "file", _config, e); - } - - // see if there's an override config from locator file - Config locatorConfig = createLocatorConfig(opts); - - // merge the locator file config into config (or replace config with) - if (locatorConfig != null) { - if (config == null || locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_replace")) { - config = locatorConfig; - } else { - config.mergeConfig(locatorConfig, locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_merge")); - } - } + Config config = readConfig(_envc, checkPlatform); + initBase(config); + initJava(config); + initTracking(config); + initResources(config); + initArgs(config); + return config; + } - // 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 = _envc.appBase; - log.info("Using 'appbase' from bootstrap config", "appbase", appbase); - Map cdata = new HashMap<>(); - cdata.put("appbase", appbase); - config = new Config(cdata); - } + /** + * Reads the basic config info from {@code config} into this instance. This includes things + * like the appbase and version. + */ + public void initBase (Config config) throws IOException { + // first extract our version information + _version = config.getLong("version", -1L); - // first determine our application base, this way if anything goes wrong later in the + // 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 _appbase = config.getString("appbase"); - - // see if locatorConfig override - if (locatorConfig != null && !StringUtil.isBlank(locatorConfig.getString("appbase"))) { - _appbase = locatorConfig.getString("appbase"); - } - if (_appbase == null) { throw new RuntimeException("m.missing_appbase"); } - // check if we're overriding the domain in the appbase - _appbase = SysProps.overrideAppbase(_appbase); + // check if we're overriding the domain in the appbase, and sub envvars + _appbase = resolveEnvVars(SysProps.overrideAppbase(_appbase)); // make sure there's a trailing slash if (!_appbase.endsWith("/")) { - _appbase = _appbase + "/"; + _appbase += "/"; } - // extract our version information - _version = config.getLong("version", -1L); - // if we are a versioned deployment, create a versioned appbase try { _vappbase = createVAppBase(_version); } catch (MalformedURLException mue) { String err = MessageUtil.tcompose("m.invalid_appbase", _appbase); - throw (IOException) new IOException(err).initCause(mue); + throw new IOException(err, mue); } // check for a latest config URL String latest = config.getString("latest"); if (latest != null) { + latest = processArg(latest); if (latest.startsWith(_appbase)) { latest = _appbase + latest.substring(_appbase.length()); } else { @@ -641,20 +636,25 @@ public class Application } } - String appPrefix = _envc.appId == null ? "" : (_envc.appId + "."); - - // determine our application class name (use app-specific class _if_ one is provided) - _class = config.getString("class"); - if (appPrefix.length() > 0) { - _class = config.getString(appPrefix + "class", _class); - } - if (_class == null) { - throw new IOException("m.missing_class"); - } - - // determine whether we want strict comments + // read some miscellaneous configurations _strictComments = config.getBoolean("strict_comments"); + _allowOffline = config.getBoolean("allow_offline"); + _revalidatePolicy = config.getEnum( + "revalidate_policy", RevalidatePolicy.class, RevalidatePolicy.AFTER_UPDATE); + int tpSize = SysProps.threadPoolSize(); + _maxConcDownloads = Math.max(1, config.getInt("max_concurrent_downloads", tpSize)); + _verifyTimeout = config.getInt("verify_timeout", 60); + + // whether to cache code resources and launch from cache + _useCodeCache = config.getBoolean("use_code_cache"); + _codeCacheRetentionDays = config.getInt("code_cache_retention_days", 7); + } + /** + * Reads the JVM requirements from {@code config} into this instance. This includes things like + * the min and max java version, location of a locally installed JRE, etc. + */ + public void initJava (Config config) { // check to see if we're using a custom java.version property and regex _javaVersionProp = config.getString("java_version_prop", _javaVersionProp); _javaVersionRegex = config.getString("java_version_regex", _javaVersionRegex); @@ -669,14 +669,16 @@ public class Application // check to see if we require a particular JVM version and have a supplied JVM _javaExactVersionRequired = config.getBoolean("java_exact_version_required"); - // this is a little weird, but when we're run from the digester, we see a String[] which - // contains java locations for all platforms which we can't grok, but the digester doesn't - // need to know about that; when we're run in a real application there will be only one! - Object javaloc = config.getRaw("java_location"); - if (javaloc instanceof String) { - _javaLocation = (String)javaloc; - } + _javaLocation = config.getString("java_location"); + + // used only in conjunction with java_location + _javaLocalDir = getLocalPath(config.getString("java_local_dir", LaunchUtil.LOCAL_JAVA_DIR)); + } + /** + * Reads the install tracking info from {@code config} into this instance. + */ + public void initTracking (Config config) { // determine whether we have any tracking configuration _trackingURL = config.getString("tracking_url"); @@ -701,14 +703,16 @@ public class Application // Some app may need to generate google analytics code _trackingGAHash = config.getString("tracking_ga_hash"); + } + /** + * Reads the app resource info from {@code config} into this instance. + */ + public void initResources (Config config) throws IOException { // clear our arrays as we may be reinitializing _codes.clear(); _resources.clear(); _auxgroups.clear(); - _jvmargs.clear(); - _appargs.clear(); - _txtJvmArgs.clear(); // parse our code resources if (config.getMultiValue("code") == null && @@ -727,10 +731,10 @@ public class Application // parse our auxiliary resource groups for (String auxgroup : config.getList("auxgroups")) { - ArrayList codes = new ArrayList<>(); + List codes = new ArrayList<>(); parseResources(config, auxgroup + ".code", Resource.NORMAL, codes); parseResources(config, auxgroup + ".ucode", Resource.UNPACK, codes); - ArrayList rsrcs = new ArrayList<>(); + List rsrcs = new ArrayList<>(); parseResources(config, auxgroup + ".resource", Resource.NORMAL, rsrcs); parseResources(config, auxgroup + ".xresource", Resource.EXEC, rsrcs); parseResources(config, auxgroup + ".uresource", Resource.UNPACK, rsrcs); @@ -738,87 +742,48 @@ public class Application parseResources(config, auxgroup + ".nresource", Resource.NATIVE, rsrcs); _auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs)); } + } - // transfer our JVM arguments (we include both "global" args and app_id-prefixed args) - String[] jvmargs = config.getMultiValue("jvmarg"); - addAll(jvmargs, _jvmargs); + /** + * Reads the command line arg info from {@code config} into this instance. + */ + public void initArgs (Config config) throws IOException { + _jvmargs.clear(); + _appargs.clear(); + _txtJvmArgs.clear(); + + String appPrefix = _envc.appId == null ? "" : (_envc.appId + "."); + + // determine our application class name (use app-specific class _if_ one is provided) + _class = config.getString("class"); if (appPrefix.length() > 0) { - jvmargs = config.getMultiValue(appPrefix + "jvmarg"); - addAll(jvmargs, _jvmargs); + _class = config.getString(appPrefix + "class", _class); + } + if (_class == null) { + throw new IOException("m.missing_class"); } - // see if a percentage of physical memory option exists - int jvmmempc = config.getInt("jvmmempc", -1); - // app_id prefixed setting overrides + // transfer our JVM arguments (we include both "global" args and app_id-prefixed args) + addAll(config.getMultiValue("jvmarg"), _jvmargs); if (appPrefix.length() > 0) { - jvmmempc = config.getInt(appPrefix + "jvmmempc", jvmmempc); - } - if (0 <= jvmmempc && jvmmempc <= 100) { - - long maxMemLong = -1; - - try - { - maxMemLong = MemorySetting.memPercent(jvmmempc); - } catch (Exception e) - { - e.printStackTrace(); - } catch (Throwable t) - { - t.printStackTrace(); - } - - if (maxMemLong > 0) - { - - String[] maxMemHeapArg = new String[]{"-Xmx"+Long.toString(maxMemLong)}; - // remove other max heap size arg - ARG: for (int i = 0; i < _jvmargs.size(); i++) { - if (_jvmargs.get(i) instanceof java.lang.String && _jvmargs.get(i).startsWith("-Xmx")) { - _jvmargs.remove(i); - } - } - addAll(maxMemHeapArg, _jvmargs); - - } - - } else if (jvmmempc != -1) { - System.out.println("'jvmmempc' value must be in range 0 to 100 (read as '"+Integer.toString(jvmmempc)+"')"); + addAll(config.getMultiValue(appPrefix + "jvmarg"), _jvmargs); } // get the set of optimum JVM arguments _optimumJvmArgs = config.getMultiValue("optimum_jvmarg"); // transfer our application arguments - String[] appargs = config.getMultiValue(appPrefix + "apparg"); - addAll(appargs, _appargs); + addAll(config.getMultiValue(appPrefix + "apparg"), _appargs); // add the launch specific application arguments _appargs.addAll(_envc.appArgs); - + // look for custom arguments fillAssignmentListFromPairs("extra.txt", _txtJvmArgs); - // determine whether we want to allow offline operation (defaults to false) - _allowOffline = config.getBoolean("allow_offline"); - - // look for a debug.txt file which causes us to run in java.exe on Windows so that we can - // obtain a thread dump of the running JVM - _windebug = getLocalPath("debug.txt").exists(); - - // whether to cache code resources and launch from cache - _useCodeCache = config.getBoolean("use_code_cache"); - _codeCacheRetentionDays = config.getInt("code_cache_retention_days", 7); - - // maximum simultaneous downloads - _maxConcDownloads = Math.max(1, config.getInt("max_concurrent_downloads", - SysProps.threadPoolSize())); - // extract some info used to configure our child process on macOS _dockName = config.getString("ui.name"); _dockIconPath = config.getString("ui.mac_dock_icon", "../desktop.icns"); - - return config; } /** @@ -847,8 +812,7 @@ public class Application * Returns a URL from which the specified path can be fetched. Our application base URL is * properly versioned and combined with the supplied path. */ - public URL getRemoteURL (String path) - throws MalformedURLException + public URL getRemoteURL (String path) throws MalformedURLException { return new URL(_vappbase, encodePath(path)); } @@ -858,7 +822,7 @@ public class Application */ public File getLocalPath (String path) { - return getLocalPath(getAppDir(), path); + return new File(getAppDir(), path); } /** @@ -881,8 +845,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(getAppDir(), LaunchUtil.LOCAL_JAVA_DIR); - File relfile = new File(vmdir, "release"); + File relfile = new File(_javaLocalDir, "release"); if (!relfile.exists()) { log.warning("Unpacked JVM missing 'release' file. Assuming valid version."); return true; @@ -939,7 +902,7 @@ public class Application } /** - * Attempts to redownload the getdown.txt file based on information parsed from a + * Attempts to redownload the {@code getdown.txt} file based on information parsed from a * previous call to {@link #init}. */ public void attemptRecovery (StatusDisplay status) @@ -950,7 +913,7 @@ public class Application } /** - * Downloads and replaces the getdown.txt and digest.txt files with + * Downloads and replaces the {@code getdown.txt} and {@code digest.txt} files with * those for the target version of our application. */ public void updateMetadata () @@ -961,7 +924,7 @@ public class Application _vappbase = createVAppBase(_targetVersion); } catch (MalformedURLException mue) { String err = MessageUtil.tcompose("m.invalid_appbase", _appbase); - throw (IOException) new IOException(err).initCause(mue); + throw new IOException(err, mue); } try { @@ -1000,7 +963,7 @@ public class Application ArrayList args = new ArrayList<>(); // reconstruct the path to the JVM - args.add(LaunchUtil.getJVMPath(getAppDir(), _windebug || optimum)); + args.add(LaunchUtil.getJVMBinaryPath(_javaLocalDir, SysProps.debug() || optimum)); // check whether we're using -jar mode or -classpath mode boolean dashJarMode = MANIFEST_CLASS.equals(_class); @@ -1018,14 +981,8 @@ public class Application args.add("-Xdock:name=" + _dockName); } - // pass along our proxy settings - String proxyHost; - if ((proxyHost = System.getProperty("http.proxyHost")) != null) { - args.add("-Dhttp.proxyHost=" + proxyHost); - args.add("-Dhttp.proxyPort=" + System.getProperty("http.proxyPort")); - args.add("-Dhttps.proxyHost=" + proxyHost); - args.add("-Dhttps.proxyPort=" + System.getProperty("http.proxyPort")); - } + // forward our proxy settings + conn.addProxyArgs(args); // add the marker indicating the app is running in getdown args.add("-D" + Properties.GETDOWN + "=true"); @@ -1071,32 +1028,11 @@ public class Application args.add(_class); } - // almost finally check the startup file arguments - for (File f : _startupFiles) { - _appargs.add(f.getAbsolutePath()); - break; // Only add one file to open - } - - // check if one arg with recognised extension - if ( _appargs.size() == 1 && _appargs.get(0) != null ) { - String filename = _appargs.get(0); - String ext = null; - int j = filename.lastIndexOf('.'); - if (j > -1) { - ext = filename.substring(j+1); - } - if (LOCATOR_FILE_EXTENSION.equals(ext.toLowerCase())) { - // this file extension should have been dealt with in Getdown class - } else { - _appargs.add(0, "-open"); - } - } - // finally add the application arguments for (String string : _appargs) { args.add(processArg(string)); } - + String[] envp = createEnvironment(); String[] sargs = args.toArray(new String[args.size()]); log.info("Running " + StringUtil.join(sargs, "\n ")); @@ -1156,7 +1092,7 @@ public class Application for (String jvmarg : _jvmargs) { if (jvmarg.startsWith("-D")) { jvmarg = processArg(jvmarg.substring(2)); - int eqidx = jvmarg.indexOf("="); + int eqidx = jvmarg.indexOf('='); if (eqidx == -1) { log.warning("Bogus system property: '" + jvmarg + "'?"); } else { @@ -1194,32 +1130,36 @@ public class Application } } - /** Replaces the application directory and version in any argument. */ + /** Replaces the application directory, version and env vars in any argument. */ protected String processArg (String arg) { arg = arg.replace("%APPDIR%", getAppDir().getAbsolutePath()); arg = arg.replace("%VERSION%", String.valueOf(_version)); + arg = resolveEnvVars(arg); + return arg; + } - // if this argument contains %ENV.FOO% replace those with the associated values looked up - // from the environment - if (arg.contains(ENV_VAR_PREFIX)) { + /** Resolves env var substitutions in {@code text}. */ + protected String resolveEnvVars (String text) { + // if the text contains %ENV.FOO% replace it with FOO looked up in the environment + if (text.contains(ENV_VAR_PREFIX)) { StringBuffer sb = new StringBuffer(); - Matcher matcher = ENV_VAR_PATTERN.matcher(arg); + Matcher matcher = ENV_VAR_PATTERN.matcher(text); while (matcher.find()) { String varName = matcher.group(1), varValue = System.getenv(varName); String repValue = varValue == null ? "MISSING:"+varName : varValue; matcher.appendReplacement(sb, Matcher.quoteReplacement(repValue)); } matcher.appendTail(sb); - arg = sb.toString(); + return sb.toString(); + } else { + return text; } - - return arg; } /** - * Loads the digest.txt file and verifies the contents of both that file and the - * getdown.text file. Then it loads the version.txt and decides + * Loads the {@code digest.txt} file and verifies the contents of both that file and the + * {@code getdown.text} file. Then it loads the {@code version.txt} and decides * whether or not the application needs to be updated or whether we can proceed to verification * and execution. * @@ -1306,11 +1246,11 @@ public class Application } if (_latest != null) { - try (InputStream in = ConnectionUtil.open(proxy, _latest, 0, 0).getInputStream(); - InputStreamReader reader = new InputStreamReader(in, UTF_8); - BufferedReader bin = new BufferedReader(reader)) { - for (String[] pair : Config.parsePairs(bin, Config.createOpts(false))) { - if (pair[0].equals("version")) { + try { + List vdata = Config.parsePairs( + new StringReader(conn.fetch(_latest)), Config.createOpts(false)); + for (String[] pair : vdata) { + if ("version".equals(pair[0])) { _targetVersion = Math.max(Long.parseLong(pair[1]), _targetVersion); if (fileVersion != -1 && _targetVersion > fileVersion) { // replace the file with the newest version @@ -1404,7 +1344,10 @@ public class Application while (completed[0] < rsrcs.size()) { // we should be getting progress completion updates WAY more often than one every // minute, so if things freeze up for 60 seconds, abandon ship - Runnable action = actions.poll(60, TimeUnit.SECONDS); + Runnable action = actions.poll(_verifyTimeout, TimeUnit.SECONDS); + if (action == null) { + throw new IllegalStateException("m.verify_timeout"); + } action.run(); } @@ -1415,14 +1358,14 @@ public class Application unpacked.addAll(unpackedAsync); long complete = System.currentTimeMillis(); - log.info("Verified resources", "count", rsrcs.size(), "size", (totalSize/1024) + "k", - "duration", (complete-start) + "ms"); + log.info("Verified resources", "count", rsrcs.size(), "alreadyValid", alreadyValid[0], + "size", (totalSize/1024) + "k", "duration", (complete-start) + "ms"); } private void verifyResource (Resource rsrc, ProgressObserver obs, int[] alreadyValid, Set unpacked, Set toInstall, Set toDownload) { - if (rsrc.isMarkedValid()) { + if (_revalidatePolicy != RevalidatePolicy.ALWAYS && rsrc.isMarkedValid()) { if (alreadyValid != null) { alreadyValid[0]++; } @@ -1513,7 +1456,7 @@ public class Application protected URL createVAppBase (long version) throws MalformedURLException { - String url = version < 0 ? _appbase : _appbase.replace("%VERSION%", "" + version); + String url = version < 0 ? _appbase : _appbase.replace("%VERSION%", String.valueOf(version)); return HostWhitelist.verify(new URL(url)); } @@ -1530,8 +1473,7 @@ public class Application /** * Downloads a new copy of CONFIG_FILE. */ - protected void downloadConfigFile () - throws IOException + protected void downloadConfigFile () throws IOException { downloadControlFile(CONFIG_FILE, 0); } @@ -1673,8 +1615,7 @@ public class Application * Download a path to a temporary file, returning a {@link File} instance with the path * contents. */ - protected File downloadFile (String path) - throws IOException + protected File downloadFile (String path) throws IOException { File target = getLocalPath(path + "_new"); @@ -1684,30 +1625,11 @@ public class Application } catch (Exception e) { log.warning("Requested to download invalid control file", "appbase", _vappbase, "path", path, "error", e); - throw (IOException) new IOException("Invalid path '" + path + "'.").initCause(e); + throw new IOException("Invalid path '" + path + "'.", e); } log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'."); - - // stream the URL into our temporary file - URLConnection uconn = ConnectionUtil.open(proxy, targetURL, 0, 0); - // we have to tell Java not to use caches here, otherwise it will cache any request for - // same URL for the lifetime of this JVM (based on the URL string, not the URL object); - // if the getdown.txt file, for example, changes in the meanwhile, we would never hear - // about it; turning off caches is not a performance concern, because when Getdown asks - // to download a file, it expects it to come over the wire, not from a cache - uconn.setUseCaches(false); - uconn.setRequestProperty("Accept-Encoding", "gzip"); - try (InputStream fin = uconn.getInputStream()) { - String encoding = uconn.getContentEncoding(); - boolean gzip = "gzip".equalsIgnoreCase(encoding); - try (InputStream fin2 = (gzip ? new GZIPInputStream(fin) : fin)) { - try (FileOutputStream fout = new FileOutputStream(target)) { - StreamUtil.copy(fin2, fout); - } - } - } - + conn.download(targetURL, target); // stream the URL into our temporary file return target; } @@ -1721,9 +1643,7 @@ public class Application /** Helper function to add all values in {@code values} (if non-null) to {@code target}. */ protected static void addAll (String[] values, List target) { if (values != null) { - for (String value : values) { - target.add(value); - } + Collections.addAll(target, values); } } @@ -1805,96 +1725,7 @@ public class Application } } - protected File getLocalPath (File appdir, String path) - { - return new File(appdir, path); - } - - public static void setStartupFilesFromParameterString(String p) { - // multiple files *might* be passed in as space separated quoted filenames - String q = "\""; - if (!StringUtil.isBlank(p)) { - String[] filenames; - // split quoted params or treat as single string array - if (p.startsWith(q) && p.endsWith(q)) { - // this fails if, e.g. - // p=q("stupidfilename\" " "otherfilename") - // let's hope no-one ever ends a filename with '" ' - filenames = p.substring(q.length(),p.length()-q.length()).split(q+" "+q); - } else { - // single unquoted filename - filenames = new String[]{p}; - } - - // check for locator file. Only allow one locator file to be double clicked (if multiple files opened, ignore locator files) - String locatorFilename = filenames.length >= 1 ? filenames[0] : null; - if ( - !StringUtil.isBlank(locatorFilename) - && locatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION) - ) { - setLocatorFile(locatorFilename); - // remove the locator filename from the filenames array - String[] otherFilenames = new String[filenames.length - 1]; - System.arraycopy(filenames, 1, otherFilenames, 0, otherFilenames.length); - filenames = otherFilenames; - } - - for (int i = 0; i < filenames.length; i++) { - String filename = filenames[i]; - // skip any other locator files in a multiple file list - if (! filename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)) { - addStartupFile(filename); - } - } - } - } - - public static void setLocatorFile(String filename) { - _locatorFile = new File(filename); - } - - public static void addStartupFile(String filename) { - _startupFiles.add(new File(filename)); - } - - private Config createLocatorConfig(Config.ParseOpts opts) { - if (_locatorFile == null) { - return null; - } - - Config locatorConfig = null; - - try { - Config tmpConfig = null; - if (_locatorFile.exists()) { - tmpConfig = Config.parseConfig(_locatorFile, opts); - } else { - log.warning("Given locator file does not exist", "file", _locatorFile); - } - - // appbase is sanitised in HostWhitelist - Map tmpData = new HashMap<>(); - for (Map.Entry entry : tmpConfig.getData().entrySet()) { - String key = entry.getKey(); - Object value = entry.getValue(); - String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key; - if (Config.allowedReplaceKeys.contains(mkey) || Config.allowedMergeKeys.contains(mkey)) { - tmpData.put(key, value); - } - } - locatorConfig = new Config(tmpData); - - } catch (Exception e) { - log.warning("Failure reading locator file", "file", _locatorFile, e); - } - - log.info("Returning locatorConfig", locatorConfig); - - return locatorConfig; - } - protected final EnvConfig _envc; - protected File _config; protected Digest _digest; protected long _version = -1; @@ -1906,7 +1737,6 @@ public class Application protected String _dockName; protected String _dockIconPath; protected boolean _strictComments; - protected boolean _windebug; protected boolean _allowOffline; protected int _maxConcDownloads; @@ -1924,10 +1754,14 @@ public class Application protected long _javaMinVersion, _javaMaxVersion; protected boolean _javaExactVersionRequired; protected String _javaLocation; + protected File _javaLocalDir; protected List _codes = new ArrayList<>(); protected List _resources = new ArrayList<>(); + protected int _verifyTimeout = 60; + + protected RevalidatePolicy _revalidatePolicy = RevalidatePolicy.AFTER_UPDATE; protected boolean _useCodeCache; protected int _codeCacheRetentionDays; @@ -1941,9 +1775,6 @@ public class Application protected List _txtJvmArgs = new ArrayList<>(); - /** If a warning has been issued about not being able to set modtimes. */ - protected boolean _warnedAboutSetLastModified; - /** Locks gettingdown.lock in the app dir. Held the entire time updating is going on.*/ protected FileLock _lock; @@ -1956,8 +1787,6 @@ public class Application protected static final String ENV_VAR_PREFIX = "%ENV."; protected static final Pattern ENV_VAR_PATTERN = Pattern.compile("%ENV\\.(.*?)%"); - - protected static File _locatorFile; - protected static List _startupFiles = new ArrayList<>(); - public static final String LOCATOR_FILE_EXTENSION = "jvl"; + + protected static enum RevalidatePolicy { ALWAYS, AFTER_UPDATE } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java index bc8d14052..e310a52a2 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java @@ -21,7 +21,7 @@ import static com.threerings.getdown.Log.log; import static java.nio.charset.StandardCharsets.UTF_8; /** - * Manages the digest.txt file and the computing and processing of digests for an + * Manages the {@code digest.txt} file and the computing and processing of digests for an * application. */ public class Digest @@ -72,8 +72,7 @@ public class Digest digests.put(rsrc, rsrc.computeDigest(fversion, md, null)); completed.add(rsrc); } catch (Throwable t) { - completed.add(new IOException("Error computing digest for: " + rsrc). - initCause(t)); + completed.add(new IOException("Error computing digest for: " + rsrc, t)); } } }); @@ -88,7 +87,7 @@ public class Digest if (done instanceof IOException) { throw (IOException)done; } else if (done instanceof Resource) { - pending.remove((Resource)done); + pending.remove(done); } else { throw new AssertionError("What is this? " + done); } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java index 57b8d8493..a14b02c63 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java @@ -7,22 +7,19 @@ package com.threerings.getdown.data; import java.io.File; import java.io.FileInputStream; -import java.net.MalformedURLException; -import java.net.URL; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.*; import com.threerings.getdown.util.StringUtil; -import com.threerings.getdown.data.Application; /** 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 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); } @@ -141,23 +138,11 @@ public final class EnvConfig { appIdProv + "'")); } } - - int skipArgs = 2; - // Look for locator file, pass to Application and remove from appArgs - String argvLocatorFilename = argv.length > 2 ? argv[2] : null; - if ( - !StringUtil.isBlank(argvLocatorFilename) - && argvLocatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION) - ) { - notes.add(Note.info("locatorFilename in args: '"+argv[2]+"'")); - Application.setLocatorFile(argvLocatorFilename); - - skipArgs++; - } - // ensure that we were able to find an app dir + // if no appdir was provided, default to the current working directory if (appDir == null) { - return null; // caller will report problem to user + appDir = System.getProperty("user.dir"); + appDirProv = "default (cwd)"; } notes.add(Note.info("Using appdir from " + appDirProv + ": " + appDir)); @@ -187,9 +172,9 @@ public final class EnvConfig { return null; } - // pass along anything after the first two (or three) args as extra app args - List appArgs = argv.length > skipArgs ? - Arrays.asList(argv).subList(skipArgs, argv.length) : + // pass along anything after the first two args as extra app args + List appArgs = argv.length > 2 ? + Arrays.asList(argv).subList(2, argv.length) : Collections.emptyList(); // load X.509 certificate if it exists diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java index b0a1dc920..57e9275be 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java @@ -10,7 +10,7 @@ import java.io.IOException; import java.util.LinkedHashSet; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.jar.JarFile; +import java.util.zip.ZipFile; import com.threerings.getdown.cache.GarbageCollector; import com.threerings.getdown.cache.ResourceCache; @@ -112,7 +112,7 @@ public class PathBuilder if (!unpackedIndicator.exists()) { try { - FileUtil.unpackJar(new JarFile(cachedFile), cachedParent, false); + FileUtil.unpackJar(new ZipFile(cachedFile), cachedParent, false); unpackedIndicator.createNewFile(); } catch (IOException ioe) { log.warning("Failed to unpack native jar", diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java index adc2d4f21..d1ccba3ba 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java @@ -7,25 +7,14 @@ package com.threerings.getdown.data; import java.io.*; import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Paths; import java.security.MessageDigest; -import java.util.Collections; -import java.util.Comparator; -import java.util.EnumSet; -import java.util.List; -import java.util.Locale; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; - -import org.apache.commons.compress.archivers.ArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; -import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; +import java.util.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import com.threerings.getdown.util.FileUtil; import com.threerings.getdown.util.ProgressObserver; import com.threerings.getdown.util.StringUtil; - import static com.threerings.getdown.Log.log; /** @@ -34,7 +23,7 @@ import static com.threerings.getdown.Log.log; public class Resource implements Comparable { /** Defines special attributes for resources. */ - public static enum Attr { + public enum Attr { /** Indicates that the resource should be unpacked. */ UNPACK, /** If present, when unpacking a resource, any directories created by the newly @@ -46,7 +35,7 @@ public class Resource implements Comparable PRELOAD, /** Indicates that the resource is a jar containing native libs. */ NATIVE - }; + } public static final EnumSet NORMAL = EnumSet.noneOf(Attr.class); public static final EnumSet UNPACK = EnumSet.of(Attr.UNPACK); @@ -66,29 +55,30 @@ public class Resource implements Comparable byte[] buffer = new byte[DIGEST_BUFFER_SIZE]; int read; - boolean isJar = isJar(target.getPath()); - boolean isPacked200Jar = isPacked200Jar(target.getPath()); + boolean isZip = isJar(target) || isZip(target); // jar is a zip too + boolean isPacked200Jar = isPacked200Jar(target); // if this is a jar, we need to compute the digest in a "timestamp and file order" agnostic // manner to properly correlate jardiff patched jars with their unpatched originals - if (isJar || isPacked200Jar){ + if (isPacked200Jar || isZip){ File tmpJarFile = null; - JarFile jar = null; + ZipFile zip = null; try { - // if this is a compressed jar file, uncompress it to compute the jar file digest + // if this is a compressed zip file, uncompress it to compute the zip file digest if (isPacked200Jar){ tmpJarFile = new File(target.getPath() + ".tmp"); + tmpJarFile.deleteOnExit(); FileUtil.unpackPacked200Jar(target, tmpJarFile); - jar = new JarFile(tmpJarFile); + zip = new ZipFile(tmpJarFile); } else{ - jar = new JarFile(target); + zip = new ZipFile(target); } - List entries = Collections.list(jar.entries()); + List entries = Collections.list(zip.entries()); Collections.sort(entries, ENTRY_COMP); int eidx = 0; - for (JarEntry entry : entries) { + for (ZipEntry entry : entries) { // old versions of the digest code skipped metadata if (version < 2) { if (entry.getName().startsWith("META-INF")) { @@ -97,7 +87,7 @@ public class Resource implements Comparable } } - try (InputStream in = jar.getInputStream(entry)) { + try (InputStream in = zip.getInputStream(entry)) { while ((read = in.read(buffer)) != -1) { md.update(buffer, 0, read); } @@ -107,11 +97,11 @@ public class Resource implements Comparable } } finally { - if (jar != null) { + if (zip != null) { try { - jar.close(); + zip.close(); } catch (IOException ioe) { - log.warning("Error closing jar", "path", target, "jar", jar, "error", ioe); + log.warning("Error closing", "path", target, "zip", zip, "error", ioe); } } if (tmpJarFile != null) { @@ -132,6 +122,34 @@ public class Resource implements Comparable return StringUtil.hexlate(md.digest()); } + /** + * Returns whether {@code file} is a {@code zip} file. + */ + public static boolean isZip (File file) + { + String path = file.getName(); + return path.endsWith(".zip") || path.endsWith(".zip_new"); + } + + /** + * Returns whether {@code file} is a {@code jar} file. + */ + public static boolean isJar (File file) + { + String path = file.getName(); + return path.endsWith(".jar") || path.endsWith(".jar_new"); + } + + /** + * Returns whether {@code file} is a {@code jar.pack} file. + */ + public static boolean isPacked200Jar (File file) + { + String path = file.getName(); + return path.endsWith(".jar.pack") || path.endsWith(".jar.pack_new") || + path.endsWith(".jar.pack.gz") || path.endsWith(".jar.pack.gz_new"); + } + /** * Creates a resource with the supplied remote URL and local path. */ @@ -141,17 +159,13 @@ public class Resource implements Comparable _remote = remote; _local = local; _localNew = new File(local.toString() + "_new"); - String lpath = _local.getPath(); - _marker = new File(lpath + "v"); + _marker = new File(_local.getPath() + "v"); _attrs = attrs; - _isTgz = isTgz(lpath); - _isJar = isJar(lpath); - _isPacked200Jar = isPacked200Jar(lpath); + _isZip = isJar(local) || isZip(local); + _isPacked200Jar = isPacked200Jar(local); boolean unpack = attrs.contains(Attr.UNPACK); - if (unpack && _isJar) { - _unpacked = _local.getParentFile(); - } else if(unpack && _isTgz) { + if (unpack && _isZip) { _unpacked = _local.getParentFile(); } else if(unpack && _isPacked200Jar) { String dotJar = ".jar", lname = _local.getName(); @@ -307,20 +321,13 @@ public class Resource implements Comparable public void unpack () throws IOException { // sanity check - if (!_isJar && !_isPacked200Jar && !_isTgz) { - throw new IOException("Requested to unpack non-jar/tgz file '" + _local + "'."); + if (!_isZip && !_isPacked200Jar) { + throw new IOException("Requested to unpack non-jar file '" + _local + "'."); } - if (_isJar) { - try (JarFile jar = new JarFile(_local)) { + if (_isZip) { + try (ZipFile jar = new ZipFile(_local)) { FileUtil.unpackJar(jar, _unpacked, _attrs.contains(Attr.CLEAN)); } - } else if (_isTgz) { - try (InputStream fi = Files.newInputStream(_local.toPath()); - InputStream bi = new BufferedInputStream(fi); - InputStream gzi = new GzipCompressorInputStream(bi); - TarArchiveInputStream tgz = new TarArchiveInputStream(gzi)) { - FileUtil.unpackTgz(tgz, _unpacked, _attrs.contains(Attr.CLEAN)); - } } else { FileUtil.unpackPacked200Jar(_local, _unpacked); } @@ -382,31 +389,15 @@ public class Resource implements Comparable } } - protected static boolean isJar (String path) - { - return path.endsWith(".jar") || path.endsWith(".jar_new"); - } - - protected static boolean isTgz (String path) - { - return path.endsWith(".tgz") || path.endsWith(".tgz_new"); - } - - protected static boolean isPacked200Jar (String path) - { - return path.endsWith(".jar.pack") || path.endsWith(".jar.pack_new") || - path.endsWith(".jar.pack.gz")|| path.endsWith(".jar.pack.gz_new"); - } - protected String _path; protected URL _remote; protected File _local, _localNew, _marker, _unpacked; protected EnumSet _attrs; - protected boolean _isJar, _isPacked200Jar, _isTgz; + protected boolean _isZip, _isPacked200Jar; /** Used to sort the entries in a jar file. */ - protected static final Comparator ENTRY_COMP = new Comparator() { - @Override public int compare (JarEntry e1, JarEntry e2) { + protected static final Comparator ENTRY_COMP = new Comparator() { + @Override public int compare (ZipEntry e1, ZipEntry e2) { return e1.getName().compareTo(e2.getName()); } }; diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java index 0d96ecb71..b36d40021 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java @@ -16,7 +16,7 @@ import com.threerings.getdown.util.VersionUtil; * accessor so that it's easy to see all of the secret system property arguments that Getdown makes * use of. */ -public class SysProps +public final class SysProps { /** Configures the appdir (in lieu of passing it in argv). Usage: {@code -Dappdir=foo}. */ public static String appDir () { @@ -40,6 +40,14 @@ public class SysProps return System.getProperty("no_log_redir") != null; } + /** Used to debug Getdown's launching of an app. When set, it disables redirection of stdout + * and stderr into a log file, and on Windows it uses {@code java.exe} to launch the app so + * that its console output can be observed. + * Usage: {@code -Ddebug}. */ + public static boolean debug () { + return System.getProperty("debug") != null; + } + /** Overrides the domain on {@code appbase}. Usage: {@code -Dappbase_domain=foo}. */ public static String appbaseDomain () { return System.getProperty("appbase_domain"); @@ -101,6 +109,16 @@ public class SysProps return Boolean.getBoolean("direct"); } + /** If true, Getdown will always try to connect without proxy settings even it a proxy is set + * in {@code proxy.txt}. If direct access is possible it will not clear {@code proxy.txt}, it + * will preserve the settings. This is to support cases where a user uses a workstation in two + * different networks, one with proxy the other one without. They should not be asked for + * proxy settings again each time they switch back to the proxy network. + * Usage: {@code -Dtry_no_proxy}. */ + public static boolean tryNoProxyFirst () { + return Boolean.getBoolean("try_no_proxy"); + } + /** Specifies the connection timeout (in seconds) to use when downloading control files from * the server. This is chiefly useful when you are running in versionless mode and want Getdown * to more quickly timeout its startup update check if the server with which it is diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java index 6033e2f6e..2298d6099 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java @@ -6,7 +6,13 @@ package com.threerings.getdown.net; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URLConnection; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; import java.util.Collection; import java.util.HashMap; @@ -26,8 +32,13 @@ import static com.threerings.getdown.Log.log; * implementors must take care to only execute thread-safe code or simply pass a message to the AWT * thread, for example. */ -public abstract class Downloader +public class Downloader { + public Downloader (Connector conn) + { + _conn = conn; + } + /** * Start the downloading process. * @param resources the resources to download. @@ -129,10 +140,31 @@ public abstract class Downloader */ protected void downloadFailed (Resource rsrc, Exception cause) {} + /** + * Called when a to-be-downloaded resource returns a 404 not found. + */ + protected void resourceMissing (Resource rsrc) {} + /** * Performs the protocol-specific portion of checking download size. */ - protected abstract long checkSize (Resource rsrc) throws IOException; + protected long checkSize (Resource rsrc) throws IOException { + URLConnection conn = _conn.open(rsrc.getRemote(), 0, 0); + try { + // if we're accessing our data via HTTP, we only need a HEAD request + if (conn instanceof HttpURLConnection) { + ((HttpURLConnection)conn).setRequestMethod("HEAD"); + } + // if we get a satisfactory response code, return a size; ignore errors as we'll report + // those when we actually attempt to download the resource + int code = _conn.checkConnectStatus(conn); + return code == HttpURLConnection.HTTP_OK ? conn.getContentLength() : 0; + + } finally { + // let it be known that we're done with this connection + conn.getInputStream().close(); + } + } /** * Periodically called by the protocol-specific downloaders to update their progress. This @@ -203,13 +235,68 @@ public abstract class Downloader * protocol-specific code. This method should periodically check whether {@code _state} is set * to aborted and abort any in-progress download if so. */ - protected abstract void download (Resource rsrc) throws IOException; + protected void download (Resource rsrc) throws IOException { + URLConnection conn = _conn.open(rsrc.getRemote(), 0, 0); + // make sure we got a satisfactory response code + int code = _conn.checkConnectStatus(conn); + if (code == HttpURLConnection.HTTP_NOT_FOUND) { + resourceMissing(rsrc); + } else if (code != HttpURLConnection.HTTP_OK) { + throw new IOException( + "Resource returned HTTP error " + rsrc.getRemote() + " [code=" + code + "]"); + } + + // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a + // system property + if (true) { + // download the resource from the specified URL + long actualSize = conn.getContentLength(); + log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize); + long currentSize = 0L; + byte[] buffer = new byte[4*4096]; + try (InputStream in = conn.getInputStream(); + FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) { + + // TODO: look to see if we have a download info file + // containing info on potentially partially downloaded data; + // if so, use a "Range: bytes=HAVE-" header. + + // read in the file data + int read; + while ((read = in.read(buffer)) != -1) { + // abort the download if the downloader is aborted + if (_state == State.ABORTED) { + break; + } + // write it out to our local copy + out.write(buffer, 0, read); + // note that we've downloaded some data + currentSize += read; + reportProgress(rsrc, currentSize, actualSize); + } + } + + } else { + log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown"); + File localNew = rsrc.getLocalNew(); + try (ReadableByteChannel rbc = Channels.newChannel(conn.getInputStream()); + FileOutputStream fos = new FileOutputStream(localNew)) { + // TODO: more work is needed here, transferFrom can fail to transfer the entire + // file, in which case it's not clear what we're supposed to do.. call it again? + // will it repeatedly fail? + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + reportProgress(rsrc, localNew.length(), localNew.length()); + } + } + } + + protected final Connector _conn; /** The reported sizes of our resources. */ - protected Map _sizes = new HashMap<>(); + protected final Map _sizes = new HashMap<>(); /** The bytes downloaded for each resource. */ - protected Map _downloaded = new HashMap<>(); + protected final Map _downloaded = new HashMap<>(); /** The time at which the file transfer began. */ protected long _start; diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java deleted file mode 100644 index a7a3287a9..000000000 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java +++ /dev/null @@ -1,115 +0,0 @@ -// -// Getdown - application installer, patcher and launcher -// Copyright (C) 2004-2018 Getdown authors -// https://github.com/threerings/getdown/blob/master/LICENSE - -package com.threerings.getdown.net; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; - -import com.threerings.getdown.data.Resource; -import com.threerings.getdown.util.ConnectionUtil; - -import static com.threerings.getdown.Log.log; - -/** - * Implements downloading files over HTTP - */ -public class HTTPDownloader extends Downloader -{ - public HTTPDownloader (Proxy proxy) - { - _proxy = proxy; - } - - @Override protected long checkSize (Resource rsrc) throws IOException - { - URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0); - try { - // if we're accessing our data via HTTP, we only need a HEAD request - if (conn instanceof HttpURLConnection) { - HttpURLConnection hcon = (HttpURLConnection)conn; - hcon.setRequestMethod("HEAD"); - hcon.connect(); - // make sure we got a satisfactory response code - if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) { - throw new IOException("Unable to check up-to-date for " + - rsrc.getRemote() + ": " + hcon.getResponseCode()); - } - } - return conn.getContentLength(); - - } finally { - // let it be known that we're done with this connection - conn.getInputStream().close(); - } - } - - @Override protected void download (Resource rsrc) throws IOException - { - // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a - // system property - if (true) { - // download the resource from the specified URL - URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0); - conn.connect(); - - // make sure we got a satisfactory response code - if (conn instanceof HttpURLConnection) { - HttpURLConnection hcon = (HttpURLConnection)conn; - if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) { - throw new IOException("Unable to download resource " + rsrc.getRemote() + ": " + - hcon.getResponseCode()); - } - } - long actualSize = conn.getContentLength(); - log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize); - long currentSize = 0L; - byte[] buffer = new byte[4*4096]; - try (InputStream in = conn.getInputStream(); - FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) { - - // TODO: look to see if we have a download info file - // containing info on potentially partially downloaded data; - // if so, use a "Range: bytes=HAVE-" header. - - // read in the file data - int read; - while ((read = in.read(buffer)) != -1) { - // abort the download if the downloader is aborted - if (_state == State.ABORTED) { - break; - } - // write it out to our local copy - out.write(buffer, 0, read); - // note that we've downloaded some data - currentSize += read; - reportProgress(rsrc, currentSize, actualSize); - } - } - - } else { - log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown"); - File localNew = rsrc.getLocalNew(); - try (ReadableByteChannel rbc = Channels.newChannel(rsrc.getRemote().openStream()); - FileOutputStream fos = new FileOutputStream(localNew)) { - // TODO: more work is needed here, transferFrom can fail to transfer the entire - // file, in which case it's not clear what we're supposed to do.. call it again? - // will it repeatedly fail? - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - reportProgress(rsrc, localNew.length(), localNew.length()); - } - } - } - - protected final Proxy _proxy; -} diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java index 22446ec0a..8c6d84160 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java @@ -11,7 +11,7 @@ package com.threerings.getdown.spi; public interface ProxyAuth { /** Credentials for a proxy server. */ - public static class Credentials { + class Credentials { public final String username; public final String password; public Credentials (String username, String password) { @@ -23,10 +23,10 @@ public interface ProxyAuth /** * Loads the credentials for the app installed in {@code appDir}. */ - public Credentials loadCredentials (String appDir); + Credentials loadCredentials (String appDir); /** * Encrypts and saves the credentials for the app installed in {@code appDir}. */ - public void saveCredentials (String appDir, String username, String password); + void saveCredentials (String appDir, String username, String password); } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java index c2e740b6e..4f8e50ebd 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java @@ -11,15 +11,14 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; - +import java.io.OutputStream; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.JarOutputStream; +import java.util.List; import java.util.zip.ZipEntry; - -import java.security.MessageDigest; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; import com.threerings.getdown.data.Application; import com.threerings.getdown.data.Digest; @@ -39,8 +38,8 @@ public class Differ /** * Creates a single patch file that contains the differences between * the two specified application directories. The patch file will be - * created in the nvdir directory with name - * patchV.dat where V is the old application version. + * created in the {@code nvdir} directory with name + * {@code patchV.dat} where V is the old application version. */ public void createDiff (File nvdir, File ovdir, boolean verbose) throws IOException @@ -61,13 +60,13 @@ public class Differ Application oapp = new Application(new EnvConfig(ovdir)); oapp.init(false); - ArrayList orsrcs = new ArrayList<>(); + List orsrcs = new ArrayList<>(); orsrcs.addAll(oapp.getCodeResources()); orsrcs.addAll(oapp.getResources()); Application napp = new Application(new EnvConfig(nvdir)); napp.init(false); - ArrayList nrsrcs = new ArrayList<>(); + List nrsrcs = new ArrayList<>(); nrsrcs.addAll(napp.getCodeResources()); nrsrcs.addAll(napp.getResources()); @@ -91,15 +90,15 @@ public class Differ } } - protected void createPatch (File patch, ArrayList orsrcs, - ArrayList nrsrcs, boolean verbose) + protected void createPatch (File patch, List orsrcs, + List nrsrcs, boolean verbose) throws IOException { int version = Digest.VERSION; MessageDigest md = Digest.getMessageDigest(version); try (FileOutputStream fos = new FileOutputStream(patch); BufferedOutputStream buffered = new BufferedOutputStream(fos); - JarOutputStream jout = new JarOutputStream(buffered)) { + ZipOutputStream jout = new ZipOutputStream(buffered)) { // for each file in the new application, it either already exists // in the old application, or it is new @@ -172,13 +171,13 @@ public class Differ throws IOException { File temp = File.createTempFile("differ", "jar"); - try (JarFile jar = new JarFile(target); + try (ZipFile jar = new ZipFile(target); FileOutputStream tempFos = new FileOutputStream(temp); BufferedOutputStream tempBos = new BufferedOutputStream(tempFos); - JarOutputStream jout = new JarOutputStream(tempBos)) { + ZipOutputStream jout = new ZipOutputStream(tempBos)) { byte[] buffer = new byte[4096]; - for (Enumeration< JarEntry > iter = jar.entries(); iter.hasMoreElements();) { - JarEntry entry = iter.nextElement(); + for (Enumeration iter = jar.entries(); iter.hasMoreElements();) { + ZipEntry entry = iter.nextElement(); entry.setCompressedSize(-1); jout.putNextEntry(entry); try (InputStream in = jar.getInputStream(entry)) { @@ -193,8 +192,7 @@ public class Differ return temp; } - protected void jarDiff (File ofile, File nfile, JarOutputStream jout) - throws IOException + protected void jarDiff (File ofile, File nfile, ZipOutputStream jout) throws IOException { JarDiff.createPatch(ofile.getPath(), nfile.getPath(), jout, false); } @@ -209,7 +207,7 @@ public class Differ Differ differ = new Differ(); boolean verbose = false; int aidx = 0; - if (args[0].equals("-verbose")) { + if ("-verbose".equals(args[0])) { verbose = true; aidx++; } @@ -222,11 +220,10 @@ public class Differ } } - protected static void pipe (File file, JarOutputStream jout) - throws IOException + protected static void pipe (File file, OutputStream out) throws IOException { try (FileInputStream fin = new FileInputStream(file)) { - StreamUtil.copy(fin, jout); + StreamUtil.copy(fin, out); } } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java index b04a6539b..ae61b4333 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java @@ -23,6 +23,7 @@ import com.threerings.getdown.data.Digest; import com.threerings.getdown.data.EnvConfig; import com.threerings.getdown.data.Resource; import com.threerings.getdown.util.Base64; +import com.threerings.getdown.util.Config; import static java.nio.charset.StandardCharsets.UTF_8; @@ -74,8 +75,11 @@ public class Digester System.out.println("Generating digest file '" + target + "'..."); // create our application and instruct it to parse its business - Application app = new Application(new EnvConfig(appdir)); - app.init(false); + EnvConfig envc = new EnvConfig(appdir); + Application app = new Application(envc); + Config config = Application.readConfig(envc, false); + app.initBase(config); + app.initResources(config); List rsrcs = new ArrayList<>(); rsrcs.add(app.getConfigResource()); @@ -86,6 +90,9 @@ public class Digester rsrcs.addAll(ag.rsrcs); } + // reinit app just to verify that getdown.txt has valid format + app.init(true); + // now generate the digest file Digest.createDigest(version, rsrcs, target); } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java index 1cea0eacd..f0db8ac03 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java @@ -41,15 +41,24 @@ package com.threerings.getdown.tools; -import java.io.*; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; import java.util.*; -import java.util.jar.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; import static java.nio.charset.StandardCharsets.UTF_8; /** - * JarDiff is able to create a jar file containing the delta between two jar files (old and new). - * The delta jar file can then be applied to the old jar file to reconstruct the new jar file. + * JarDiff is able to create a zip file containing the delta between two jar or zip files (old + * and new). The delta file can then be applied to the old archive file to reconstruct the new + * archive file. * *

Refer to the JNLP spec for details on how this is done. * @@ -58,39 +67,37 @@ import static java.nio.charset.StandardCharsets.UTF_8; public class JarDiff implements JarDiffCodes { private static final int DEFAULT_READ_SIZE = 2048; - private static byte[] newBytes = new byte[DEFAULT_READ_SIZE]; - private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE]; + private static final byte[] newBytes = new byte[DEFAULT_READ_SIZE]; + private static final byte[] oldBytes = new byte[DEFAULT_READ_SIZE]; // The JARDiff.java is the stand-alone jardiff.jar tool. Thus, we do not depend on Globals.java // and other stuff here. Instead, we use an explicit _debug flag. private static boolean _debug; /** - * Creates a patch from the two passed in files, writing the result to os. + * Creates a patch from the two passed in files, writing the result to {@code os}. */ public static void createPatch (String oldPath, String newPath, OutputStream os, boolean minimal) throws IOException { - try (JarFile2 oldJar = new JarFile2(oldPath); - JarFile2 newJar = new JarFile2(newPath)) { + try (ZipFile2 oldArchive = new ZipFile2(oldPath); + ZipFile2 newArchive = new ZipFile2(newPath)) { - HashMap moved = new HashMap<>(); - HashSet implicit = new HashSet<>(); - HashSet moveSrc = new HashSet<>(); - HashSet newEntries = new HashSet<>(); + Map moved = new HashMap<>(); + Set implicit = new HashSet<>(); + Set moveSrc = new HashSet<>(); + Set newEntries = new HashSet<>(); // FIRST PASS - // Go through the entries in new jar and - // determine which files are candidates for implicit moves - // ( files that has the same filename and same content in old.jar - // and new.jar ) - // and for files that cannot be implicitly moved, we will either - // find out whether it is moved or new (modified) - for (JarEntry newEntry : newJar) { + // Go through the entries in new archive and determine which files are candidates for + // implicit moves (files that have the same filename and same content in old and new) + // and for files that cannot be implicitly moved, we will either find out whether it is + // moved or new (modified) + for (ZipEntry newEntry : newArchive) { String newname = newEntry.getName(); // Return best match of contents, will return a name match if possible - String oldname = oldJar.getBestMatch(newJar, newEntry); + String oldname = oldArchive.getBestMatch(newArchive, newEntry); if (oldname == null) { // New or modified entry if (_debug) { @@ -101,7 +108,7 @@ public class JarDiff implements JarDiffCodes // Content already exist - need to do a move // Should do implicit move? Yes, if names are the same, and - // no move command already exist from oldJar + // no move command already exist from oldArchive if (oldname.equals(newname) && !moveSrc.contains(oldname)) { if (_debug) { System.out.println(newname + " added to implicit set!"); @@ -117,12 +124,9 @@ public class JarDiff implements JarDiffCodes // JarDiffPatcher also. if (!minimal && (implicit.contains(oldname) || moveSrc.contains(oldname) )) { - // generate non-minimal jardiff // for backward compatibility - if (_debug) { - System.out.println("NEW: "+ newname); } newEntries.add(newname); @@ -153,8 +157,8 @@ public class JarDiff implements JarDiffCodes // SECOND PASS: = - - // - - ArrayList deleted = new ArrayList<>(); - for (JarEntry oldEntry : oldJar) { + List deleted = new ArrayList<>(); + for (ZipEntry oldEntry : oldArchive) { String oldName = oldEntry.getName(); if (!implicit.contains(oldName) && !moveSrc.contains(oldName) && !newEntries.contains(oldName)) { @@ -180,7 +184,7 @@ public class JarDiff implements JarDiffCodes } } - JarOutputStream jos = new JarOutputStream(os); + ZipOutputStream jos = new ZipOutputStream(os); // Write out all the MOVEs and REMOVEs createIndex(jos, deleted, moved); @@ -190,7 +194,7 @@ public class JarDiff implements JarDiffCodes if (_debug) { System.out.println("New File: " + newName); } - writeEntry(jos, newJar.getEntryByName(newName), newJar); + writeEntry(jos, newArchive.getEntryByName(newName), newArchive); } jos.finish(); @@ -199,11 +203,11 @@ public class JarDiff implements JarDiffCodes } /** - * Writes the index file out to jos. - * oldEntries gives the names of the files that were removed, - * movedMap maps from the new name to the old name. + * Writes the index file out to {@code jos}. + * {@code oldEntries} gives the names of the files that were removed, + * {@code movedMap} maps from the new name to the old name. */ - private static void createIndex (JarOutputStream jos, List oldEntries, + private static void createIndex (ZipOutputStream jos, List oldEntries, Map movedMap) throws IOException { @@ -220,17 +224,17 @@ public class JarDiff implements JarDiffCodes } // And those that have moved - for (String newName : movedMap.keySet()) { - String oldName = movedMap.get(newName); + for (Map.Entry entry : movedMap.entrySet()) { + String oldName = entry.getValue(); writer.write(MOVE_COMMAND); writer.write(" "); writeEscapedString(writer, oldName); writer.write(" "); - writeEscapedString(writer, newName); + writeEscapedString(writer, entry.getKey()); writer.write("\r\n"); } - jos.putNextEntry(new JarEntry(INDEX_NAME)); + jos.putNextEntry(new ZipEntry(INDEX_NAME)); byte[] bytes = writer.toString().getBytes(UTF_8); jos.write(bytes, 0, bytes.length); } @@ -264,10 +268,10 @@ public class JarDiff implements JarDiffCodes return writer; } - private static void writeEntry (JarOutputStream jos, JarEntry entry, JarFile2 file) + private static void writeEntry (ZipOutputStream jos, ZipEntry entry, ZipFile2 file) throws IOException { - try (InputStream data = file.getJarFile().getInputStream(entry)) { + try (InputStream data = file.getArchive().getInputStream(entry)) { jos.putNextEntry(entry); int size = data.read(newBytes); while (size != -1) { @@ -278,31 +282,31 @@ public class JarDiff implements JarDiffCodes } /** - * JarFile2 wraps a JarFile providing some convenience methods. + * ZipFile2 wraps a ZipFile providing some convenience methods. */ - private static class JarFile2 implements Iterable, Closeable + private static class ZipFile2 implements Iterable, Closeable { - private JarFile _jar; - private List _entries; - private HashMap _nameToEntryMap; - private HashMap> _crcToEntryMap; + private final ZipFile _archive; + private List _entries; + private HashMap _nameToEntryMap; + private HashMap> _crcToEntryMap; - public JarFile2 (String path) throws IOException { - _jar = new JarFile(new File(path)); + public ZipFile2 (String path) throws IOException { + _archive = new ZipFile(new File(path)); index(); } - public JarFile getJarFile () { - return _jar; + public ZipFile getArchive () { + return _archive; } - // from interface Iterable + // from interface Iterable @Override - public Iterator iterator () { + public Iterator iterator () { return _entries.iterator(); } - public JarEntry getEntryByName (String name) { + public ZipEntry getEntryByName (String name) { return _nameToEntryMap.get(name); } @@ -350,7 +354,7 @@ public class JarDiff implements JarDiffCodes return retVal; } - public String getBestMatch (JarFile2 file, JarEntry entry) throws IOException { + public String getBestMatch (ZipFile2 file, ZipEntry entry) throws IOException { // check for same name and same content, return name if found if (contains(file, entry)) { return (entry.getName()); @@ -360,11 +364,10 @@ public class JarDiff implements JarDiffCodes return (hasSameContent(file,entry)); } - public boolean contains (JarFile2 f, JarEntry e) throws IOException { - - JarEntry thisEntry = getEntryByName(e.getName()); + public boolean contains (ZipFile2 f, ZipEntry e) throws IOException { + ZipEntry thisEntry = getEntryByName(e.getName()); - // Look up name in 'this' Jar2File - if not exist return false + // Look up name in 'this' ZipFile2 - if not exist return false if (thisEntry == null) return false; @@ -373,26 +376,26 @@ public class JarDiff implements JarDiffCodes return false; // Check contents - if no match - return false - try (InputStream oldIS = getJarFile().getInputStream(thisEntry); - InputStream newIS = f.getJarFile().getInputStream(e)) { + try (InputStream oldIS = getArchive().getInputStream(thisEntry); + InputStream newIS = f.getArchive().getInputStream(e)) { return !differs(oldIS, newIS); } } - public String hasSameContent (JarFile2 file, JarEntry entry) throws IOException { + public String hasSameContent (ZipFile2 file, ZipEntry entry) throws IOException { String thisName = null; - Long crcL = Long.valueOf(entry.getCrc()); - // check if this jar contains files with the passed in entry's crc + Long crcL = entry.getCrc(); + // check if this archive contains files with the passed in entry's crc if (_crcToEntryMap.containsKey(crcL)) { // get the Linked List with files with the crc - LinkedList ll = _crcToEntryMap.get(crcL); + LinkedList ll = _crcToEntryMap.get(crcL); // go through the list and check for content match - ListIterator li = ll.listIterator(0); + ListIterator li = ll.listIterator(0); while (li.hasNext()) { - JarEntry thisEntry = li.next(); + ZipEntry thisEntry = li.next(); // check for content match - try (InputStream oldIS = getJarFile().getInputStream(thisEntry); - InputStream newIS = file.getJarFile().getInputStream(entry)) { + try (InputStream oldIS = getArchive().getInputStream(thisEntry); + InputStream newIS = file.getArchive().getInputStream(entry)) { if (!differs(oldIS, newIS)) { thisName = thisEntry.getName(); return thisName; @@ -404,19 +407,19 @@ public class JarDiff implements JarDiffCodes } private void index () throws IOException { - Enumeration entries = _jar.entries(); + Enumeration entries = _archive.entries(); _nameToEntryMap = new HashMap<>(); _crcToEntryMap = new HashMap<>(); _entries = new ArrayList<>(); if (_debug) { - System.out.println("indexing: " + _jar.getName()); + System.out.println("indexing: " + _archive.getName()); } if (entries != null) { while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); + ZipEntry entry = entries.nextElement(); long crc = entry.getCrc(); - Long crcL = Long.valueOf(crc); + Long crcL = crc; if (_debug) { System.out.println("\t" + entry.getName() + " CRC " + crc); } @@ -427,13 +430,13 @@ public class JarDiff implements JarDiffCodes // generate the CRC to entries map if (_crcToEntryMap.containsKey(crcL)) { // key exist, add the entry to the correcponding linked list - LinkedList ll = _crcToEntryMap.get(crcL); + LinkedList ll = _crcToEntryMap.get(crcL); ll.add(entry); _crcToEntryMap.put(crcL, ll); } else { // create a new entry in the hashmap for the new key - LinkedList ll = new LinkedList(); + LinkedList ll = new LinkedList<>(); ll.add(entry); _crcToEntryMap.put(crcL, ll); } @@ -443,7 +446,7 @@ public class JarDiff implements JarDiffCodes @Override public void close() throws IOException { - _jar.close(); + _archive.close(); } } } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java index b5a0a1763..e55034bca 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java @@ -16,27 +16,25 @@ import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; - -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.jar.JarOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import java.util.zip.ZipOutputStream; import com.threerings.getdown.util.ProgressObserver; - import static java.nio.charset.StandardCharsets.UTF_8; /** - * Applies a jardiff patch to a jar file. + * Applies a jardiff patch to a jar/zip file. */ public class JarDiffPatcher implements JarDiffCodes { /** - * Patches the specified jar file using the supplied patch file and writing - * the new jar file to the supplied target. + * Patches the specified jar file using the supplied patch file and writing the new jar file to + * the supplied target. * * @param jarPath the path to the original jar file. * @param diffPath the path to the jardiff patch file. @@ -49,11 +47,9 @@ public class JarDiffPatcher implements JarDiffCodes throws IOException { File oldFile = new File(jarPath), diffFile = new File(diffPath); - - try (JarFile oldJar = new JarFile(oldFile); - JarFile jarDiff = new JarFile(diffFile); - JarOutputStream jos = new JarOutputStream(new FileOutputStream(target))) { - + try (ZipFile oldJar = new ZipFile(oldFile); + ZipFile jarDiff = new ZipFile(diffFile); + ZipOutputStream jos = makeOutputStream(oldFile, target)) { Set ignoreSet = new HashSet<>(); Map renameMap = new HashMap<>(); determineNameMapping(jarDiff, ignoreSet, renameMap); @@ -63,7 +59,7 @@ public class JarDiffPatcher implements JarDiffCodes // Files to implicit move Set oldjarNames = new HashSet<>(); - Enumeration oldEntries = oldJar.entries(); + Enumeration oldEntries = oldJar.entries(); if (oldEntries != null) { while (oldEntries.hasMoreElements()) { oldjarNames.add(oldEntries.nextElement().getName()); @@ -83,10 +79,10 @@ public class JarDiffPatcher implements JarDiffCodes size -= ignoreSet.size(); // Add content from JARDiff - Enumeration entries = jarDiff.entries(); + Enumeration entries = jarDiff.entries(); if (entries != null) { while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); + ZipEntry entry = entries.nextElement(); if (!INDEX_NAME.equals(entry.getName())) { updateObserver(observer, currentEntry, size); currentEntry++; @@ -114,15 +110,15 @@ public class JarDiffPatcher implements JarDiffCodes // Apply move command String oldName = renameMap.get(newName); - // Get source JarEntry - JarEntry oldEntry = oldJar.getJarEntry(oldName); + // Get source ZipEntry + ZipEntry oldEntry = oldJar.getEntry(oldName); if (oldEntry == null) { String moveCmd = MOVE_COMMAND + oldName + " " + newName; throw new IOException("error.badmove: " + moveCmd); } - // Create dest JarEntry - JarEntry newEntry = new JarEntry(newName); + // Create dest ZipEntry + ZipEntry newEntry = new ZipEntry(newName); newEntry.setTime(oldEntry.getTime()); newEntry.setSize(oldEntry.getSize()); newEntry.setCompressedSize(oldEntry.getCompressedSize()); @@ -149,19 +145,15 @@ public class JarDiffPatcher implements JarDiffCodes } // implicit move - Iterator iEntries = oldjarNames.iterator(); - if (iEntries != null) { - while (iEntries.hasNext()) { - String name = iEntries.next(); - JarEntry entry = oldJar.getJarEntry(name); - if (entry == null) { - // names originally retrieved from the JAR, so this should never happen - throw new AssertionError("JAR entry not found: " + name); - } - updateObserver(observer, currentEntry, size); - currentEntry++; - writeEntry(jos, entry, oldJar); + for (String name : oldjarNames) { + ZipEntry entry = oldJar.getEntry(name); + if (entry == null) { + // names originally retrieved from the archive, so this should never happen + throw new AssertionError("Archive entry not found: " + name); } + updateObserver(observer, currentEntry, size); + currentEntry++; + writeEntry(jos, entry, oldJar); } updateObserver(observer, currentEntry, size); } @@ -175,7 +167,7 @@ public class JarDiffPatcher implements JarDiffCodes } protected void determineNameMapping ( - JarFile jarDiff, Set ignoreSet, Map renameMap) + ZipFile jarDiff, Set ignoreSet, Map renameMap) throws IOException { InputStream is = jarDiff.getInputStream(jarDiff.getEntry(INDEX_NAME)); @@ -223,7 +215,7 @@ public class JarDiffPatcher implements JarDiffCodes { int index = 0; int length = path.length(); - ArrayList sub = new ArrayList<>(); + List sub = new ArrayList<>(); while (index < length) { while (index < length && Character.isWhitespace @@ -231,9 +223,8 @@ public class JarDiffPatcher implements JarDiffCodes index++; } if (index < length) { - int start = index; - int last = start; - String subString = null; + int last = index; + StringBuilder subString = null; while (index < length) { char aChar = path.charAt(index); @@ -241,9 +232,9 @@ public class JarDiffPatcher implements JarDiffCodes path.charAt(index + 1) == ' ') { if (subString == null) { - subString = path.substring(last, index); + subString = new StringBuilder(path.substring(last, index)); } else { - subString += path.substring(last, index); + subString.append(path, last, index); } last = ++index; } else if (Character.isWhitespace(aChar)) { @@ -253,18 +244,20 @@ public class JarDiffPatcher implements JarDiffCodes } if (last != index) { if (subString == null) { - subString = path.substring(last, index); + subString = new StringBuilder(path.substring(last, index)); } else { - subString += path.substring(last, index); + subString.append(path, last, index); } } - sub.add(subString); + if (subString != null) { + sub.add(subString.toString()); + } } } return sub; } - protected void writeEntry (JarOutputStream jos, JarEntry entry, JarFile file) + protected void writeEntry (ZipOutputStream jos, ZipEntry entry, ZipFile file) throws IOException { try (InputStream data = file.getInputStream(entry)) { @@ -272,10 +265,10 @@ public class JarDiffPatcher implements JarDiffCodes } } - protected void writeEntry (JarOutputStream jos, JarEntry entry, InputStream data) + protected void writeEntry (ZipOutputStream jos, ZipEntry entry, InputStream data) throws IOException { - jos.putNextEntry(new JarEntry(entry.getName())); + jos.putNextEntry(new ZipEntry(entry.getName())); // Read the entry int size = data.read(newBytes); @@ -285,6 +278,15 @@ public class JarDiffPatcher implements JarDiffCodes } } + protected static ZipOutputStream makeOutputStream (File source, File target) + throws IOException + { + FileOutputStream out = new FileOutputStream(target); + if (source.getName().endsWith(".jar")) return new JarOutputStream(out); + else if (source.getName().endsWith(".zip")) return new ZipOutputStream(out); + else throw new AssertionError("Unsupported source file '" + source + "'. Not a .jar or .zip?"); + } + protected static final int DEFAULT_READ_SIZE = 2048; protected static byte[] newBytes = new byte[DEFAULT_READ_SIZE]; diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java index 4ead59bb3..5c8baaaab 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java @@ -9,16 +9,13 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; - import java.util.Enumeration; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import com.threerings.getdown.util.FileUtil; import com.threerings.getdown.util.ProgressObserver; import com.threerings.getdown.util.StreamUtil; - import static com.threerings.getdown.Log.log; /** @@ -55,10 +52,10 @@ public class Patcher _obs = obs; _plength = patch.length(); - try (JarFile file = new JarFile(patch)) { - Enumeration entries = file.entries(); // old skool! + try (ZipFile file = new ZipFile(patch)) { + Enumeration entries = file.entries(); while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); + ZipEntry entry = entries.nextElement(); String path = entry.getName(); long elength = entry.getCompressedSize(); @@ -96,7 +93,7 @@ public class Patcher return path.substring(0, path.length() - suffix.length()); } - protected void createFile (JarFile file, ZipEntry entry, File target) + protected void createFile (ZipFile file, ZipEntry entry, File target) { // create our copy buffer if necessary if (_buffer == null) { @@ -124,8 +121,7 @@ public class Patcher } } - protected void patchFile (JarFile file, ZipEntry entry, - File appdir, String path) + protected void patchFile (ZipFile file, ZipEntry entry, File appdir, String path) { File target = new File(appdir, path); File patch = new File(appdir, entry.getName()); diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java index 2a5db79ec..233f1e739 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java @@ -24,7 +24,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII; * href="http://www.ietf.org/rfc/rfc2045.txt">2045 and 3548. */ -public class Base64 { +public final class Base64 { /** * Default values for encoder/decoder flags. */ @@ -68,7 +68,7 @@ public class Base64 { // shared code // -------------------------------------------------------- - /* package */ static abstract class Coder { + /* package */ abstract static class Coder { public byte[] output; public int op; @@ -178,7 +178,7 @@ public class Base64 { * Lookup table for turning bytes into their position in the * Base64 alphabet. */ - private static final int DECODE[] = { + private static final int[] DECODE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, @@ -201,7 +201,7 @@ public class Base64 { * Decode lookup table for the "web safe" variant (RFC 3548 * sec. 4) where - and _ replace + and /. */ - private static final int DECODE_WEBSAFE[] = { + private static final int[] DECODE_WEBSAFE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, @@ -236,7 +236,7 @@ public class Base64 { private int state; // state number (0 to 6) private int value; - final private int[] alphabet; + private final int[] alphabet; public Decoder(int flags, byte[] output) { this.output = output; @@ -541,7 +541,7 @@ public class Base64 { * Lookup table for turning Base64 alphabet positions (6 bits) * into output bytes. */ - private static final byte ENCODE[] = { + private static final byte[] ENCODE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', @@ -552,21 +552,21 @@ public class Base64 { * Lookup table for turning Base64 alphabet positions (6 bits) * into output bytes. */ - private static final byte ENCODE_WEBSAFE[] = { + private static final byte[] ENCODE_WEBSAFE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', }; - final private byte[] tail; + private final byte[] tail; /* package */ int tailLen; private int count; - final public boolean do_padding; - final public boolean do_newline; - final public boolean do_cr; - final private byte[] alphabet; + public final boolean do_padding; + public final boolean do_newline; + public final boolean do_cr; + private final byte[] alphabet; public Encoder(int flags, byte[] output) { this.output = output; @@ -618,7 +618,7 @@ public class Base64 { ((input[p++] & 0xff) << 8) | (input[p++] & 0xff); tailLen = 0; - }; + } break; case 2: diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java index 047cead76..416f77e58 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java @@ -8,11 +8,11 @@ package com.threerings.getdown.util; /** * Utilities for handling ARGB colors. */ -public class Color +public final class Color { - public final static int CLEAR = 0x00000000; - public final static int WHITE = 0xFFFFFFFF; - public final static int BLACK = 0xFF000000; + public static final int CLEAR = 0x00000000; + public static final int WHITE = 0xFFFFFFFF; + public static final int BLACK = 0xFF000000; public static float brightness (int argb) { // TODO: we're ignoring alpha here... diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java index 6ad2b4fd9..75357e882 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java @@ -14,7 +14,6 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -58,33 +57,17 @@ public class Config } /** - * Parses a configuration file containing key/value pairs. The file must be in the UTF-8 - * encoding. - * + * Parses configuration text containing key/value pairs. * @param opts options that influence the parsing. See {@link #createOpts}. - * - * @return a list of String[] instances containing the key/value pairs in the + * @return a list of {@code String[]} instances containing the key/value pairs in the * order they were parsed from the file. */ - public static List parsePairs (File source, ParseOpts opts) - throws IOException - { - // annoyingly FileReader does not allow encoding to be specified (uses platform default) - try (FileInputStream fis = new FileInputStream(source); - InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) { - return parsePairs(input, opts); - } - } - - /** - * See {@link #parsePairs(File,ParseOpts)}. - */ public static List parsePairs (Reader source, ParseOpts opts) throws IOException { List pairs = new ArrayList<>(); for (String line : FileUtil.readLines(source)) { // nix comments - int cidx = line.indexOf("#"); + int cidx = line.indexOf('#'); if (opts.strictComments ? cidx == 0 : cidx != -1) { line = line.substring(0, cidx); } @@ -98,7 +81,7 @@ public class Config // parse our key/value pair String[] pair = new String[2]; // if we're biasing toward key, put all the extra = in the key rather than the value - int eidx = opts.biasToKey ? line.lastIndexOf("=") : line.indexOf("="); + int eidx = opts.biasToKey ? line.lastIndexOf('=') : line.indexOf('='); if (eidx != -1) { pair[0] = line.substring(0, eidx).trim(); pair[1] = line.substring(eidx+1).trim(); @@ -109,7 +92,7 @@ public class Config // if the pair has an os qualifier, we need to process it if (pair[1].startsWith("[")) { - int qidx = pair[1].indexOf("]"); + int qidx = pair[1].indexOf(']'); if (qidx == -1) { log.warning("Bogus platform specifier", "key", pair[0], "value", pair[1]); continue; // omit the pair entirely @@ -132,6 +115,21 @@ public class Config return pairs; } + /** + * Parses configuration file containing key/value pairs. + * @param source the file containing the config text. Must be in the UTF-8 encoding. + * @param opts options that influence the parsing. See {@link #createOpts}. + */ + public static List parsePairs (File source, ParseOpts opts) + throws IOException + { + // annoyingly FileReader does not allow encoding to be specified (uses platform default) + try (FileInputStream fis = new FileInputStream(source); + InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) { + return parsePairs(input, opts); + } + } + /** * Takes a comma-separated String of four integers and returns a rectangle using those ints as * the its x, y, width, and height. @@ -167,13 +165,10 @@ public class Config } /** - * Parses a configuration file containing key/value pairs. The file must be in the UTF-8 - * encoding. - * - * @return a map from keys to values, where a value will be an array of strings if more than - * one key/value pair in the config file was associated with the same key. + * Parses the data for a config instance from the supplied {@code source} reader. + * @return a map that can be used to create a {@link #Config}. */ - public static Config parseConfig (File source, ParseOpts opts) + public static Map parseData (Reader source, ParseOpts opts) throws IOException { Map data = new HashMap<>(); @@ -196,15 +191,34 @@ public class Config } } - // special magic for the getdown.txt config: if the parsed data contains 'strict_comments = - // true' then we reparse the file with strict comments (i.e. # is only assumed to start a - // comment in column 0) - if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) { - opts.strictComments = true; - return parseConfig(source, opts); - } + return data; + } + + /** + * Parses a configuration file containing key/value pairs. The file must be in the UTF-8 + * encoding. + * + * @return a map from keys to values, where a value will be an array of strings if more than + * one key/value pair in the config file was associated with the same key. + */ + public static Config parseConfig (File source, ParseOpts opts) + throws IOException + { + // annoyingly FileReader does not allow encoding to be specified (uses platform default) + try (FileInputStream fis = new FileInputStream(source); + InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) { + Map data = parseData(input, opts); + + // special magic for the getdown.txt config: if the parsed data contains + // 'strict_comments = true' then we reparse the file with strict comments (i.e. # is + // only assumed to start a comment in column 0) + if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) { + opts.strictComments = true; + return parseConfig(source, opts); + } - return new Config(data); + return new Config(data); + } } public Config (Map data) { @@ -248,6 +262,20 @@ public class Config return Boolean.parseBoolean(getString(name)); } + /** + * Returns the specified config value as an enum value. The string value of the config is + * converted to all upper case and then turned into an enum via {@link Enum#valueOf}. + */ + public > T getEnum (String name, Class eclass, T defval) { + String value = getString(name, defval.toString()); + try { + return Enum.valueOf(eclass, value.toUpperCase()); + } catch (Exception e) { + log.warning("Invalid value for '" + name + "' config: '" + value + "'."); + return defval; + } + } + /** * Massages a single string into an array and leaves existing array values as is. Simplifies * access to parameters that are expected to be arrays. @@ -255,9 +283,6 @@ public class Config public String[] getMultiValue (String name) { Object value = _data.get(name); - if (value == null) { - return new String[] {}; - } if (value instanceof String) { return new String[] { (String)value }; } else { @@ -355,7 +380,7 @@ public class Config protected static boolean checkQualifiers (String quals, String osname, String osarch) { if (quals.startsWith("!")) { - if (quals.indexOf(",") != -1) { // sanity check + if (quals.contains(",")) { // sanity check log.warning("Multiple qualifiers cannot be used when one of the qualifiers " + "is negative", "quals", quals); return false; @@ -375,103 +400,8 @@ public class Config { String[] bits = qual.trim().toLowerCase(Locale.ROOT).split("-"); String os = bits[0], arch = (bits.length > 1) ? bits[1] : ""; - return (osname.indexOf(os) != -1) && (osarch.indexOf(arch) != -1); - } - - public void mergeConfig(Config newValues, boolean merge) { - - for (Map.Entry entry : newValues.getData().entrySet()) { - - String key = entry.getKey(); - Object nvalue = entry.getValue(); - - String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key; - if (merge && allowedMergeKeys.contains(mkey)) { - - // merge multi values - - Object value = _data.get(key); - - if (value == null) { - _data.put(key, nvalue); - } else if (value instanceof String) { - if (nvalue instanceof String) { - - // value is String, nvalue is String - _data.put(key, new String[] { (String)value, (String)nvalue }); - - } else if (nvalue instanceof String[]) { - - // value is String, nvalue is String[] - String[] nvalues = (String[])nvalue; - String[] newvalues = new String[nvalues.length+1]; - newvalues[0] = (String)value; - System.arraycopy(nvalues, 0, newvalues, 1, nvalues.length); - _data.put(key, newvalues); - - } - } else if (value instanceof String[]) { - if (nvalue instanceof String) { - - // value is String[], nvalue is String - String[] values = (String[])value; - String[] newvalues = new String[values.length+1]; - System.arraycopy(values, 0, newvalues, 0, values.length); - newvalues[values.length] = (String)nvalue; - _data.put(key, newvalues); - - } else if (nvalue instanceof String[]) { - - // value is String[], nvalue is String[] - String[] values = (String[])value; - String[] nvalues = (String[])nvalue; - String[] newvalues = new String[values.length + nvalues.length]; - System.arraycopy(values, 0, newvalues, 0, values.length); - System.arraycopy(nvalues, 0, newvalues, values.length, newvalues.length); - _data.put(key, newvalues); - - } - } - - } else if (allowedReplaceKeys.contains(mkey)){ - - // replace value - _data.put(key, nvalue); - - } else { - log.warning("Not merging key '"+key+"' into config"); - } - - } - - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - for (Map.Entry entry : getData().entrySet()) { - String key = entry.getKey(); - Object val = entry.getValue(); - sb.append(key); - sb.append("="); - if (val instanceof String) { - sb.append((String)val); - } else if (val instanceof String[]) { - sb.append(Arrays.toString((String[])val)); - } else { - sb.append("Value not String or String[]"); - } - sb.append("\n"); - } - return sb.toString(); - } - - public Map getData() { - return _data; + return (osname.contains(os)) && (osarch.contains(arch)); } private final Map _data; - - public static final List allowedReplaceKeys = Arrays.asList("appbase","apparg","jvmarg"); // these are the ones we might use - public static final List allowedMergeKeys = Arrays.asList("apparg","jvmarg"); // these are the ones we might use - //private final List allowedMergeKeys = Arrays.asList("apparg","jvmarg","resource","code","java_location"); // (not exhaustive list here) } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java deleted file mode 100644 index 21b056932..000000000 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java +++ /dev/null @@ -1,73 +0,0 @@ -// -// Getdown - application installer, patcher and launcher -// Copyright (C) 2004-2018 Getdown authors -// https://github.com/threerings/getdown/blob/master/LICENSE - -package com.threerings.getdown.util; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.Proxy; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLDecoder; - -import com.threerings.getdown.data.SysProps; - -import static java.nio.charset.StandardCharsets.UTF_8; - -public class ConnectionUtil -{ - /** - * Opens a connection to a URL, setting the authentication header if user info is present. - * @param proxy the proxy via which to perform HTTP connections. - * @param url the URL to which to open a connection. - * @param connectTimeout if {@code > 0} then a timeout, in seconds, to use when opening the - * connection. If {@code 0} is supplied, the connection timeout specified via system properties - * will be used instead. - * @param readTimeout if {@code > 0} then a timeout, in seconds, to use while reading data from - * the connection. If {@code 0} is supplied, the read timeout specified via system properties - * will be used instead. - */ - public static URLConnection open (Proxy proxy, URL url, int connectTimeout, int readTimeout) - throws IOException - { - URLConnection conn = url.openConnection(proxy); - - // configure a connect timeout, if requested - int ctimeout = connectTimeout > 0 ? connectTimeout : SysProps.connectTimeout(); - if (ctimeout > 0) { - conn.setConnectTimeout(ctimeout * 1000); - } - - // configure a read timeout, if requested - int rtimeout = readTimeout > 0 ? readTimeout : SysProps.readTimeout(); - if (rtimeout > 0) { - conn.setReadTimeout(rtimeout * 1000); - } - - // If URL has a username:password@ before hostname, use HTTP basic auth - String userInfo = url.getUserInfo(); - if (userInfo != null) { - // Remove any percent-encoding in the username/password - userInfo = URLDecoder.decode(userInfo, "UTF-8"); - // Now base64 encode the auth info and make it a single line - String encoded = Base64.encodeToString(userInfo.getBytes(UTF_8), Base64.DEFAULT). - replaceAll("\\n","").replaceAll("\\r", ""); - conn.setRequestProperty("Authorization", "Basic " + encoded); - } - - return conn; - } - - /** - * Opens a connection to a http or https URL, setting the authentication header if user info is - * present. Throws a class cast exception if the connection returned is not the right type. See - * {@link #open} for parameter documentation. - */ - public static HttpURLConnection openHttp ( - Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException - { - return (HttpURLConnection)open(proxy, url, connectTimeout, readTimeout); - } -} diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java index 67d033086..d7de78bbd 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java @@ -6,25 +6,17 @@ package com.threerings.getdown.util; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.*; import java.util.jar.*; -import java.util.zip.GZIPInputStream; +import java.util.zip.*; -import org.apache.commons.compress.archivers.ArchiveEntry; -import org.apache.commons.compress.archivers.ArchiveInputStream; -import org.apache.commons.compress.archivers.tar.TarArchiveEntry; -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; - -import com.threerings.getdown.util.StreamUtil; import com.threerings.getdown.Log; import static com.threerings.getdown.Log.log; /** * File related utilities. */ -public class FileUtil +public final class FileUtil { /** * Gets the specified source file to the specified destination file by hook or crook. Windows @@ -122,13 +114,13 @@ public class FileUtil * @param cleanExistingDirs if true, all files in all directories contained in {@code jar} will * be deleted prior to unpacking the jar. */ - public static void unpackJar (JarFile jar, File target, boolean cleanExistingDirs) + public static void unpackJar (ZipFile jar, File target, boolean cleanExistingDirs) throws IOException { if (cleanExistingDirs) { - Enumeration entries = jar.entries(); + Enumeration entries = jar.entries(); while (entries.hasMoreElements()) { - JarEntry entry = (JarEntry)entries.nextElement(); + ZipEntry entry = entries.nextElement(); if (entry.isDirectory()) { File efile = new File(target, entry.getName()); if (efile.exists()) { @@ -141,9 +133,9 @@ public class FileUtil } } - Enumeration entries = jar.entries(); + Enumeration entries = jar.entries(); while (entries.hasMoreElements()) { - JarEntry entry = (JarEntry)entries.nextElement(); + ZipEntry entry = entries.nextElement(); File efile = new File(target, entry.getName()); // if we're unpacking a normal jar file, it will have special path @@ -164,7 +156,7 @@ public class FileUtil } try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile)); - InputStream jin = jar.getInputStream(entry)) { + InputStream jin = jar.getInputStream(entry)) { StreamUtil.copy(jin, fout); } catch (Exception e) { throw new IOException( @@ -174,82 +166,13 @@ public class FileUtil } /** - * Unpacks the specified tgz file into the specified target directory. - * @param cleanExistingDirs if true, all files in all directories contained in {@code tgz} will - * be deleted prior to unpacking the tgz. - */ - public static void unpackTgz (TarArchiveInputStream tgz, File target, boolean cleanExistingDirs) - throws IOException - { - TarArchiveEntry entry; - while ((entry = tgz.getNextTarEntry()) != null) { - // sanitize the entry name - String entryName = entry.getName(); - if (entryName.startsWith(File.separator)) - { - entryName = entryName.substring(File.separator.length()); - } - File efile = new File(target, entryName); - - // if we're unpacking a normal tgz file, it will have special path - // entries that allow us to create our directories first - if (entry.isDirectory()) { - - if (cleanExistingDirs) { - if (efile.exists()) { - for (File f : efile.listFiles()) { - if (!f.isDirectory()) - f.delete(); - } - } - } - - if (!efile.exists() && !efile.mkdir()) { - log.warning("Failed to create tgz entry path", "tgz", tgz, "entry", entry); - } - continue; - } - - // but some do not, so we want to ensure that our directories exist - // prior to getting down and funky - File parent = new File(efile.getParent()); - if (!parent.exists() && !parent.mkdirs()) { - log.warning("Failed to create tgz entry parent", "tgz", tgz, "parent", parent); - continue; - } - - if (entry.isLink()) - { - System.out.println("Creating hard link "+efile.getName()+" -> "+entry.getLinkName()); - Files.createLink(efile.toPath(), Paths.get(entry.getLinkName())); - continue; - } - - if (entry.isSymbolicLink()) - { - System.out.println("Creating symbolic link "+efile.getName()+" -> "+entry.getLinkName()); - Files.createSymbolicLink(efile.toPath(), Paths.get(entry.getLinkName())); - continue; - } - - try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile)); - InputStream tin = tgz;) { - StreamUtil.copy(tin, fout); - } catch (Exception e) { - throw new IOException( - Log.format("Failure unpacking", "tgz", tgz, "entry", efile), e); - } - } - } - - /** - * Unpacks a pack200 packed jar file from {@code packedJar} into {@code target}. If {@code - * packedJar} has a {@code .gz} extension, it will be gunzipped first. + * Unpacks a pack200 packed jar file from {@code packedJar} into {@code target}. + * If {@code packedJar} has a {@code .gz} extension, it will be gunzipped first. */ public static void unpackPacked200Jar (File packedJar, File target) throws IOException { try (InputStream packJarIn = new FileInputStream(packedJar); - JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(target))) { + JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(target))) { boolean gz = (packedJar.getName().endsWith(".gz") || packedJar.getName().endsWith(".gz_new")); try (InputStream packJarIn2 = (gz ? new GZIPInputStream(packJarIn) : packJarIn)) { diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java index f2f7ef39f..c05992abd 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java @@ -25,8 +25,6 @@ public final class HostWhitelist */ public static URL verify (URL url) throws MalformedURLException { - - return verify(Build.hostWhitelist(), url); } @@ -43,12 +41,6 @@ public final class HostWhitelist } String urlHost = url.getHost(); - String protocol = url.getProtocol(); - - if (ALLOW_LOCATOR_FILE_PROTOCOL && protocol.equals("file") && urlHost.equals("")) { - return url; - } - for (String host : hosts) { String regex = host.replace(".", "\\.").replace("*", ".*"); if (urlHost.matches(regex)) { @@ -59,5 +51,4 @@ public final class HostWhitelist throw new MalformedURLException( "The host for the specified URL (" + url + ") is not in the host whitelist: " + hosts); } - private static boolean ALLOW_LOCATOR_FILE_PROTOCOL = true; } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java index f2cd5736e..cc51794ef 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java @@ -17,21 +17,23 @@ import static com.threerings.getdown.Log.log; * Useful routines for launching Java applications from within other Java * applications. */ -public class LaunchUtil +public final class LaunchUtil { - /** The directory into which a local VM installation should be unpacked. */ - public static final String LOCAL_JAVA_DIR = "jre"; + /** The default directory into which a local VM installation should be unpacked. */ + public static final String LOCAL_JAVA_DIR = "java_vm"; /** - * Writes a version.txt file into the specified application directory and + * Writes a {@code version.txt} file into the specified application directory and * attempts to relaunch Getdown in that directory which will cause it to upgrade to the newly * specified version and relaunch the application. * * @param appdir the directory in which the application is installed. * @param getdownJarName the name of the getdown jar file in the application directory. This is - * probably getdown-pro.jar or getdown-retro-pro.jar if you are using + * probably {@code getdown-pro.jar} or {@code getdown-retro-pro.jar} if you are using * the results of the standard build. * @param newVersion the new version to which Getdown will update when it is executed. + * @param javaLocalDir the name of the directory (inside {@code appdir}) that contains a + * locally installed JRE. Defaults to {@link #LOCAL_JAVA_DIR} if null is passed. * * @return true if the relaunch succeeded, false if we were unable to relaunch due to being on * Windows 9x where we cannot launch subprocesses without waiting around for them to exit, @@ -39,13 +41,13 @@ public class LaunchUtil * after making this call as it will be upgraded and restarted. If false is returned, the * application should tell the user that they must restart the application manually. * - * @exception IOException thrown if we were unable to create the version.txt file + * @exception IOException thrown if we were unable to create the {@code version.txt} file * in the supplied application directory. If the version.txt file cannot be created, restarting * Getdown will not cause the application to be upgraded, so the application will have to * resort to telling the user that it is in a bad way. */ public static boolean updateVersionAndRelaunch ( - File appdir, String getdownJarName, String newVersion) + File appdir, String getdownJarName, String newVersion, String javaLocalDir) throws IOException { // create the file that instructs Getdown to upgrade @@ -61,9 +63,9 @@ public class LaunchUtil } // do the deed - String[] args = new String[] { - getJVMPath(appdir), "-jar", pro.toString(), appdir.getPath() - }; + String javaDir = StringUtil.isBlank(javaLocalDir) ? LOCAL_JAVA_DIR : javaLocalDir; + String javaBin = getJVMBinaryPath(new File(appdir, javaDir), false); + String[] args = { javaBin, "-jar", pro.toString(), appdir.getPath() }; log.info("Running " + StringUtil.join(args, "\n ")); try { Runtime.getRuntime().exec(args, null); @@ -75,25 +77,15 @@ public class LaunchUtil } /** - * Reconstructs the path to the JVM used to launch this process. - */ - public static String getJVMPath (File appdir) - { - return getJVMPath(appdir, false); - } - - /** - * Reconstructs the path to the JVM used to launch this process. - * + * Resolves a path to a JVM binary. + * @param javaLocalDir JRE location within appdir. * @param windebug if true we will use java.exe instead of javaw.exe on Windows. + * @return the path to the JVM binary used to launch this process. */ - public static String getJVMPath (File appdir, boolean windebug) + public static String getJVMBinaryPath (File javaLocalDir, boolean windebug) { // first look in our application directory for an installed VM - String vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR).getAbsolutePath(), windebug); - if (vmpath == null && isMacOS()) { - vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR + "/Contents/Home").getAbsolutePath(), windebug); - } + String vmpath = checkJVMPath(javaLocalDir.getAbsolutePath(), windebug); // then fall back to the VM in which we're already running if (vmpath == null) { @@ -102,7 +94,7 @@ public class LaunchUtil // then throw up our hands and hope for the best if (vmpath == null) { - log.warning("Unable to find java [appdir=" + appdir + + log.warning("Unable to find java [local=" + javaLocalDir + ", java.home=" + System.getProperty("java.home") + "]!"); vmpath = "java"; } @@ -153,7 +145,7 @@ public class LaunchUtil if (newgd.renameTo(curgd)) { FileUtil.deleteHarder(oldgd); // yay! try { - // copy the moved file back to getdown-dop-new.jar so that we don't end up + // copy the moved file back to newgd so that we don't end up // downloading another copy next time FileUtil.copy(curgd, newgd); } catch (IOException e) { @@ -185,23 +177,23 @@ public class LaunchUtil public static boolean mustMonitorChildren () { String osname = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); - return (osname.indexOf("windows 98") != -1 || osname.indexOf("windows me") != -1); + return (osname.contains("windows 98") || osname.contains("windows me")); } /** * Returns true if we're running in a JVM that identifies its operating system as Windows. */ - public static final boolean isWindows () { return _isWindows; } + public static boolean isWindows () { return _isWindows; } /** * Returns true if we're running in a JVM that identifies its operating system as MacOS. */ - public static final boolean isMacOS () { return _isMacOS; } + public static boolean isMacOS () { return _isMacOS; } /** * Returns true if we're running in a JVM that identifies its operating system as Linux. */ - public static final boolean isLinux () { return _isLinux; } + public static boolean isLinux () { return _isLinux; } /** * Checks whether a Java Virtual Machine can be located in the supplied path. @@ -240,10 +232,9 @@ public class LaunchUtil try { String osname = System.getProperty("os.name"); osname = (osname == null) ? "" : osname; - _isWindows = (osname.indexOf("Windows") != -1); - _isMacOS = (osname.indexOf("Mac OS") != -1 || - osname.indexOf("MacOS") != -1); - _isLinux = (osname.indexOf("Linux") != -1); + _isWindows = (osname.contains("Windows")); + _isMacOS = (osname.contains("Mac OS") || osname.contains("MacOS")); + _isLinux = (osname.contains("Linux")); } catch (Exception e) { // can't grab system properties; we'll just pretend we're not on any of these OSes } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java index 28dbdcff5..f2ee9a265 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java @@ -5,7 +5,7 @@ package com.threerings.getdown.util; -public class MessageUtil { +public final class MessageUtil { /** * Returns whether or not the provided string is tainted. See {@link #taint}. Null strings @@ -101,11 +101,11 @@ public class MessageUtil { } /** - * Used to escape single quotes so that they are not interpreted by MessageFormat. + * Used to escape single quotes so that they are not interpreted by {@code MessageFormat}. * As we assume all single quotes are to be escaped, we cannot use the characters * { and } in our translation strings, but this is a small price to * pay to have to differentiate between messages that will and won't eventually be parsed by a - * MessageFormat instance. + * {@code MessageFormat} instance. */ public static String escape (String message) { diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java index ad4c560a4..ac3e0a3de 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java @@ -14,5 +14,5 @@ public interface ProgressObserver * Informs the observer that we have completed the specified * percentage of the process. */ - public void progress (int percent); + void progress (int percent); } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java index 373cfff00..5cb1a405b 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java @@ -11,11 +11,10 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.Writer; -import java.nio.charset.Charset; import static com.threerings.getdown.Log.log; -public class StreamUtil { +public final class StreamUtil { /** * Convenient close for a stream. Use in a finally clause and love life. */ diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java index 03d3c9ccd..86277c881 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java @@ -7,14 +7,14 @@ package com.threerings.getdown.util; import java.util.StringTokenizer; -public class StringUtil { +public final class StringUtil { /** * @return true if the specified string could be a valid URL (contains no illegal characters) */ public static boolean couldBeValidUrl (String url) { - return url.matches("[A-Za-z0-9\\-\\._~:/\\?#\\[\\]@!$&'\\(\\)\\*\\+,;=%]+"); + return url.matches("[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=%]+"); } /** @@ -84,7 +84,7 @@ public class StringUtil { source = source.replace(",,", "%COMMA%"); // count up the number of tokens - while ((tpos = source.indexOf(",", tpos+1)) != -1) { + while ((tpos = source.indexOf(',', tpos+1)) != -1) { tcount++; } @@ -92,7 +92,7 @@ public class StringUtil { tpos = -1; tcount = 0; // do the split - while ((tpos = source.indexOf(",", tpos+1)) != -1) { + while ((tpos = source.indexOf(',', tpos+1)) != -1) { tokens[tcount] = source.substring(tstart, tpos); tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ","); if (intern) { @@ -119,7 +119,7 @@ public class StringUtil { /** * Generates a string from the supplied bytes that is the HEX encoded representation of those - * bytes. Returns the empty string for a null or empty byte array. + * bytes. Returns the empty string for a {@code null} or empty byte array. * * @param bytes the bytes for which we want a string representation. * @param count the number of bytes to stop at (which will be coerced into being {@code <=} the @@ -185,7 +185,7 @@ public class StringUtil { } /** - * Helper function for the various join methods. + * Helper function for the various {@code join} methods. */ protected static String join (Object[] values, String separator, boolean escape) { @@ -201,6 +201,6 @@ public class StringUtil { return buf.toString(); } - /** Used by {@link #hexlate} and {@link #unhexlate}. */ + /** Used by {@link #hexlate}. */ protected static final String XLATE = "0123456789abcdef"; } diff --git a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java index 49e4e6e0e..b2f289415 100644 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java @@ -22,7 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; /** * Version related utilities. */ -public class VersionUtil +public final class VersionUtil { /** * Reads a version number from a file. diff --git a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java b/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java deleted file mode 100644 index 8af09da6d..000000000 --- a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java +++ /dev/null @@ -1,51 +0,0 @@ -package jalview.bin; - -import java.lang.management.ManagementFactory; -import java.lang.management.OperatingSystemMXBean; - -public class MemorySetting -{ - public static final long leaveFreeMinMemory = 536870912; // 0.5 GB - - public static final long applicationMinMemory = 536870912; // 0.5 GB - - protected static long getPhysicalMemory() - { - final OperatingSystemMXBean o = ManagementFactory - .getOperatingSystemMXBean(); - - try - { - if (o instanceof com.sun.management.OperatingSystemMXBean) - { - final com.sun.management.OperatingSystemMXBean osb = (com.sun.management.OperatingSystemMXBean) o; - return osb.getTotalPhysicalMemorySize(); - } - } catch (NoClassDefFoundError e) - { - // com.sun.management.OperatingSystemMXBean doesn't exist in this JVM - System.err.println("No com.sun.management.OperatingSystemMXBean"); - } - - // We didn't get a com.sun.management.OperatingSystemMXBean. - return -1; - } - - public static long memPercent(int percent) - { - long memPercent = -1; - - long physicalMem = getPhysicalMemory(); - if (physicalMem > applicationMinMemory) - { - // try and set at least applicationMinMemory and thereafter ensure - // leaveFreeMinMemory is left for the OS - memPercent = Math.max(applicationMinMemory, - physicalMem - Math.max(physicalMem * (100 - percent) / 100, - leaveFreeMinMemory)); - } - - return memPercent; - } - -} diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java index d5a393745..c7fcf7271 100644 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java @@ -7,23 +7,36 @@ package com.threerings.getdown.cache; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.TimeUnit; import org.junit.*; import org.junit.rules.TemporaryFolder; - +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; /** * Validates that cache garbage is collected and deleted correctly. */ +@RunWith(Parameterized.class) public class GarbageCollectorTest { + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][] {{ ".jar" }, { ".zip" }}); + } + + @Parameterized.Parameter + public String extension; + @Before public void setupFiles () throws IOException { - _cachedFile = _folder.newFile("abc123.jar"); - _lastAccessedFile = _folder.newFile("abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX); + _cachedFile = _folder.newFile("abc123" + extension); + _lastAccessedFile = _folder.newFile( + "abc123" + extension + ResourceCache.LAST_ACCESSED_FILE_SUFFIX); } @Test public void shouldDeleteCacheEntryIfRetentionPeriodIsReached () diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java index 860c72a37..6acffda32 100644 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java @@ -7,26 +7,38 @@ package com.threerings.getdown.cache; import java.io.File; import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; import java.util.concurrent.TimeUnit; import org.junit.*; import org.junit.rules.TemporaryFolder; - +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import static org.junit.Assert.*; /** * Asserts the correct functionality of the {@link ResourceCache}. */ +@RunWith(Parameterized.class) public class ResourceCacheTest { + @Parameterized.Parameters(name = "{0}") + public static Collection data() { + return Arrays.asList(new Object[][] {{ ".jar" }, { ".zip" }}); + } + + @Parameterized.Parameter + public String extension; + @Before public void setupCache () throws IOException { - _fileToCache = _folder.newFile("filetocache.jar"); + _fileToCache = _folder.newFile("filetocache" + extension); _cache = new ResourceCache(_folder.newFolder(".cache")); } @Test public void shouldCacheFile () throws IOException { - assertEquals("abc123.jar", cacheFile().getName()); + assertEquals("abc123" + extension, cacheFile().getName()); } private File cacheFile() throws IOException @@ -36,7 +48,7 @@ public class ResourceCacheTest @Test public void shouldTrackFileUsage () throws IOException { - String name = "abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX; + String name = "abc123" + extension + ResourceCache.LAST_ACCESSED_FILE_SUFFIX; File lastAccessedFile = new File(cacheFile().getParentFile(), name); assertTrue(lastAccessedFile.exists()); } diff --git a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java index 61786518c..04a73d38b 100644 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java @@ -19,13 +19,13 @@ public class EnvConfigTest { static String TESTID = "testid"; static String TESTBASE = "https://test.com/test"; - private void debugNotes(List notes) { + private void debugNotes (List notes) { for (EnvConfig.Note note : notes) { System.out.println(note.message); } } - private void checkNoNotes (List notes) { + static void checkNoNotes (List notes) { StringBuilder msg = new StringBuilder(); for (EnvConfig.Note note : notes) { if (note.level != EnvConfig.Note.Level.INFO) { diff --git a/getdown/src/getdown/launcher/.project-MOVED b/getdown/src/getdown/launcher/.project-MOVED deleted file mode 100644 index d77a6e8db..000000000 --- a/getdown/src/getdown/launcher/.project-MOVED +++ /dev/null @@ -1,23 +0,0 @@ - - - getdown-launcher - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index abdea9ac0..000000000 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,4 +0,0 @@ -eclipse.preferences.version=1 -encoding//src/main/java=UTF-8 -encoding//src/main/resources=UTF-8 -encoding/=UTF-8 diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 54e56721d..000000000 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,6 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1c..000000000 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/getdown/src/getdown/launcher/pom.xml b/getdown/src/getdown/launcher/pom.xml index e77061a3b..f55234977 100644 --- a/getdown/src/getdown/launcher/pom.xml +++ b/getdown/src/getdown/launcher/pom.xml @@ -4,7 +4,7 @@ com.threerings.getdown getdown - 1.8.3-SNAPSHOT + 1.8.7-SNAPSHOT getdown-launcher @@ -24,11 +24,13 @@ com.threerings.getdown getdown-core ${project.version} + true com.samskivert samskivert 1.2 + true jregistrykey @@ -36,22 +38,47 @@ 1.0 true + + junit + junit + 4.12 + test + - + org.apache.maven.plugins maven-jar-plugin @@ -130,40 +157,6 @@ - - - org.apache.maven.plugins - maven-shade-plugin - 3.2.1 - - - - - - package - - shade - - - - - - - - - @@ -202,6 +195,7 @@ ${java.home}/jmods/java.base.jmod ${java.home}/jmods/java.desktop.jmod ${java.home}/jmods/java.logging.jmod + ${java.home}/jmods/java.scripting.jmod ${java.home}/jmods/jdk.jsobject.jmod diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java index dc1e54e46..4bd9f90b0 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java @@ -72,7 +72,7 @@ public final class AbortPanel extends JFrame public void actionPerformed (ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("ok")) { + if ("ok".equals(cmd)) { System.exit(0); } else { setVisible(false); diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java index 5750fce66..1cc6922c8 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -9,51 +9,73 @@ import java.awt.BorderLayout; import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; -import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URL; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLayeredPane; -import javax.swing.JPanel; import com.samskivert.swing.util.SwingUtil; -import com.threerings.getdown.data.*; +import com.threerings.getdown.data.Application; import com.threerings.getdown.data.Application.UpdateInterface.Step; +import com.threerings.getdown.data.Build; +import com.threerings.getdown.data.EnvConfig; +import com.threerings.getdown.data.Resource; +import com.threerings.getdown.data.SysProps; import com.threerings.getdown.net.Downloader; -import com.threerings.getdown.net.HTTPDownloader; import com.threerings.getdown.tools.Patcher; -import com.threerings.getdown.util.*; - +import com.threerings.getdown.util.Config; +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 static com.threerings.getdown.Log.log; /** * Manages the main control for the Getdown application updater and deployment system. */ -public abstract class Getdown extends Thread +public abstract class Getdown implements Application.StatusDisplay, RotatingBackgrounds.ImageLoader { + /** + * Starts a thread to run Getdown and ultimately (hopefully) launch the target app. + */ + public static void run (final Getdown getdown) { + new Thread("Getdown") { + @Override public void run () { + getdown.run(); + } + }.start(); + } + public Getdown (EnvConfig envc) { - super("Getdown"); try { // 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. @@ -80,7 +102,7 @@ public abstract class Getdown extends Thread // 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 = envc.appDir.toString(); - if (dir.equals(".")) { + if (".".equals(dir)) { dir = System.getProperty("user.dir"); } String errmsg = "The directory in which this application is installed:\n" + dir + @@ -107,7 +129,7 @@ public abstract class Getdown extends Thread { if (SysProps.noInstall()) { log.info("Skipping install due to 'no_install' sysprop."); - } else if (_readyToInstall) { + } else if (isUpdateAvailable()) { log.info("Installing " + _toInstallResources.size() + " downloaded resources:"); for (Resource resource : _toInstallResources) { resource.install(true); @@ -120,8 +142,28 @@ public abstract class Getdown extends Thread } } - @Override - public void run () + /** + * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher. + */ + public void configProxy (String host, String port, String username, String password) + { + log.info("User configured proxy", "host", host, "port", port); + ProxyUtil.configProxy(_app, host, port, username, password); + + // clear out our UI + disposeContainer(); + _container = null; + + // fire up a new thread + run(this); + } + + /** + * The main entry point of Getdown: does some sanity checks and preparation, then delegates the + * actual getting down to {@link #getdown}. This is not called directly, but rather via the + * static {@code run} method as Getdown does its main work on a separate thread. + */ + protected void run () { // if we have no messages, just bail because we're hosed; the error message will be // displayed to the user already @@ -135,95 +177,48 @@ public abstract class Getdown extends Thread File instdir = _app.getLocalPath(""); if (!instdir.canWrite()) { String path = instdir.getPath(); - if (path.equals(".")) { + if (".".equals(path)) { path = System.getProperty("user.dir"); } fail(MessageUtil.tcompose("m.readonly_error", path)); return; } - try { - _dead = false; - // if we fail to detect a proxy, but we're allowed to run offline, then go ahead and - // run the app anyway because we're prepared to cope with not being able to update - if (detectProxy() || _app.allowOffline()) { - getdown(); - } else if (_silent) { - log.warning("Need a proxy, but we don't want to bother anyone. Exiting."); - } else { - // create a panel they can use to configure the proxy settings - _container = createContainer(); - // allow them to close the window to abort the proxy configuration - _dead = true; - configureContainer(); - ProxyPanel panel = new ProxyPanel(this, _msgs); - // set up any existing configured proxy - String[] hostPort = ProxyUtil.loadProxy(_app); - panel.setProxy(hostPort[0], hostPort[1]); - _container.add(panel, BorderLayout.CENTER); - showContainer(); - } - - } catch (Exception e) { - log.warning("run() failed.", e); - String msg = e.getMessage(); - if (msg == null) { - msg = MessageUtil.compose("m.unknown_error", _ifc.installError); - } else if (!msg.startsWith("m.")) { - // try to do something sensible based on the type of error - if (e instanceof FileNotFoundException) { - msg = MessageUtil.compose( - "m.missing_resource", MessageUtil.taint(msg), _ifc.installError); - } else { - msg = MessageUtil.compose( - "m.init_error", MessageUtil.taint(msg), _ifc.installError); - } - } - fail(msg); - } - } - - /** - * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher. - */ - public void configProxy (String host, String port, String username, String password) - { - log.info("User configured proxy", "host", host, "port", port); - - if (!StringUtil.isBlank(host)) { - ProxyUtil.configProxy(_app, host, port, username, password); - } - - // clear out our UI - disposeContainer(); - _container = null; - - // fire up a new thread - new Thread(this).start(); + _dead = false; + // if we fail to detect a proxy, but we're allowed to run offline, then go ahead and + // run the app anyway because we're prepared to cope with not being able to update + if (detectProxy() || _app.allowOffline()) getdown(); + else requestProxyInfo(false); } protected boolean detectProxy () { - if (ProxyUtil.autoDetectProxy(_app)) { - return true; - } - - // otherwise see if we actually need a proxy; first we have to initialize our application - // to get some sort of interface configuration and the appbase URL + // first we have to initialize our application to get the appbase URL, etc. log.info("Checking whether we need to use a proxy..."); try { readConfig(true); } catch (IOException ioe) { // no worries } + + boolean tryNoProxy = SysProps.tryNoProxyFirst(); + if (!tryNoProxy && ProxyUtil.autoDetectProxy(_app)) { + return true; + } + + // see if we actually need a proxy updateStatus("m.detecting_proxy"); - if (!ProxyUtil.canLoadWithoutProxy(_app.getConfigResource().getRemote())) { - return false; + URL configURL = _app.getConfigResource().getRemote(); + if (!ProxyUtil.canLoadWithoutProxy(configURL, tryNoProxy ? 2 : 5)) { + // if we didn't auto-detect proxy first thing, do auto-detect now + return tryNoProxy ? ProxyUtil.autoDetectProxy(_app) : false; } - // we got through, so we appear not to require a proxy; make a blank proxy config so that - // we don't go through this whole detection process again next time log.info("No proxy appears to be needed."); - ProxyUtil.saveProxy(_app, null, null); + if (!tryNoProxy) { + // we got through, so we appear not to require a proxy; make a blank proxy config so + // that we don't go through this whole detection process again next time + ProxyUtil.saveProxy(_app, null, null); + } return true; } @@ -233,6 +228,25 @@ public abstract class Getdown extends Thread _ifc = new Application.UpdateInterface(config); } + protected void requestProxyInfo (boolean reinitAuth) { + if (_silent) { + log.warning("Need a proxy, but we don't want to bother anyone. Exiting."); + return; + } + + // create a panel they can use to configure the proxy settings + _container = createContainer(); + // allow them to close the window to abort the proxy configuration + _dead = true; + configureContainer(); + ProxyPanel panel = new ProxyPanel(this, _msgs, reinitAuth); + // set up any existing configured proxy + String[] hostPort = ProxyUtil.loadProxy(_app); + panel.setProxy(hostPort[0], hostPort[1]); + _container.add(panel, BorderLayout.CENTER); + showContainer(); + } + /** * Downloads and installs (without verifying) any resources that are marked with a * {@code PRELOAD} attribute. @@ -247,13 +261,15 @@ public abstract class Getdown extends Thread } } - try { - download(predownloads); - for (Resource rsrc : predownloads) { - rsrc.install(false); // install but don't validate yet + if (!predownloads.isEmpty()) { + try { + download(predownloads); + for (Resource rsrc : predownloads) { + rsrc.install(false); // install but don't validate yet + } + } catch (IOException ioe) { + log.warning("Failed to predownload resources. Continuing...", ioe); } - } catch (IOException ioe) { - log.warning("Failed to predownload resources. Continuing...", ioe); } } @@ -263,7 +279,7 @@ public abstract class Getdown extends Thread protected void getdown () { try { - // first parses our application deployment file + // first parse our application deployment file try { readConfig(true); } catch (IOException ioe) { @@ -278,7 +294,7 @@ public abstract class Getdown extends Thread throw new MultipleGetdownRunning(); } - // Update the config modtime so a sleeping getdown will notice the change. + // update the config modtime so a sleeping getdown will notice the change File config = _app.getLocalPath(Application.CONFIG_FILE); if (!config.setLastModified(System.currentTimeMillis())) { log.warning("Unable to set modtime on config file, will be unable to check for " + @@ -290,7 +306,7 @@ public abstract class Getdown extends Thread // Store the config modtime before waiting the delay amount of time long lastConfigModtime = config.lastModified(); log.info("Waiting " + _delay + " minutes before beginning actual work."); - Thread.sleep(_delay * 60 * 1000); + TimeUnit.MINUTES.sleep(_delay); if (lastConfigModtime < config.lastModified()) { log.warning("getdown.txt was modified while getdown was waiting."); throw new MultipleGetdownRunning(); @@ -339,11 +355,7 @@ public abstract class Getdown extends Thread if (toDownload.size() > 0) { // we have resources to download, also note them as to-be-installed - for (Resource r : toDownload) { - if (!_toInstallResources.contains(r)) { - _toInstallResources.add(r); - } - } + _toInstallResources.addAll(toDownload); try { // if any of our resources have already been marked valid this is not a @@ -427,24 +439,20 @@ public abstract class Getdown extends Thread throw new IOException("m.unable_to_repair"); } catch (Exception e) { - log.warning("getdown() failed.", e); - String msg = e.getMessage(); - if (msg == null) { - msg = MessageUtil.compose("m.unknown_error", _ifc.installError); - } else if (!msg.startsWith("m.")) { - // try to do something sensible based on the type of error - if (e instanceof FileNotFoundException) { - msg = MessageUtil.compose( - "m.missing_resource", MessageUtil.taint(msg), _ifc.installError); - } else { - msg = MessageUtil.compose( - "m.init_error", MessageUtil.taint(msg), _ifc.installError); - } + // if we failed due to proxy errors, ask for proxy info + switch (_app.conn.state) { + case NEED_PROXY: + requestProxyInfo(false); + break; + case NEED_PROXY_AUTH: + requestProxyInfo(true); + break; + default: + log.warning("getdown() failed.", e); + fail(e); + _app.releaseLock(); + break; } - // Since we're dead, clear off the 'time remaining' label along with displaying the - // error message - fail(msg); - _app.releaseLock(); } } @@ -499,6 +507,18 @@ public abstract class Getdown extends Thread throw new IOException("m.java_download_failed"); } + // on Windows, if the local JVM is in use, we will not be able to replace it with an + // updated JVM; we detect this by attempting to rename the java.dll to its same name, which + // will fail on Windows for in use files; hackery! + File javaLocalDir = _app.getJavaLocalDir(); + File javaDll = new File(javaLocalDir, "bin" + File.separator + "java.dll"); + if (javaDll.exists()) { + if (!javaDll.renameTo(javaDll)) { + log.info("Cannot update local Java VM as it is in use."); + return; + } + } + reportTrackingEvent("jvm_start", -1); updateStatus("m.downloading_java"); @@ -507,21 +527,24 @@ public abstract class Getdown extends Thread download(list); reportTrackingEvent("jvm_unpack", -1); - updateStatus("m.unpacking_java"); - vmjar.install(true); + try { + vmjar.install(true); + } catch (IOException ioe) { + throw new IOException("m.java_unpack_failed", ioe); + } // these only run on non-Windows platforms, so we use Unix file separators - String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/"; - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "bin/java")); - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/jspawnhelper")); - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/amd64/jspawnhelper")); + FileUtil.makeExecutable(new File(javaLocalDir, "bin/java")); + FileUtil.makeExecutable(new File(javaLocalDir, "lib/jspawnhelper")); + FileUtil.makeExecutable(new File(javaLocalDir, "lib/amd64/jspawnhelper")); // lastly regenerate the .jsa dump file that helps Java to start up faster - String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath("")); + String vmpath = LaunchUtil.getJVMBinaryPath(javaLocalDir, false); + String[] command = { vmpath, "-Xshare:dump" }; try { log.info("Regenerating classes.jsa for " + vmpath + "..."); - Runtime.getRuntime().exec(vmpath + " -Xshare:dump"); + Runtime.getRuntime().exec(command); } catch (Exception e) { log.warning("Failed to regenerate .jsa dump file", "error", e); } @@ -578,6 +601,8 @@ public abstract class Getdown extends Thread int ii = 0; for (Resource prsrc : list) { ProgressObserver pobs = pragg.startElement(ii++); try { + // if this patch file failed to download, skip it + if (!prsrc.getLocalNew().exists()) continue; // install the patch file (renaming them from _new) prsrc.install(false); // now apply the patch @@ -613,7 +638,7 @@ public abstract class Getdown extends Thread // create our user interface createInterfaceAsync(false); - Downloader dl = new HTTPDownloader(_app.proxy) { + Downloader dl = new Downloader(_app.conn) { @Override protected void resolvingDownloads () { updateStatus("m.resolving"); } @@ -640,6 +665,10 @@ public abstract class Getdown extends Thread log.warning("Download failed", "rsrc", rsrc, e); } + @Override protected void resourceMissing (Resource rsrc) { + log.warning("Resource missing (got 404)", "rsrc", rsrc); + } + /** The last percentage at which we checked for another getdown running, or -1 for not * having checked at all. */ protected int _lastCheck = -1; @@ -725,7 +754,7 @@ public abstract class Getdown extends Thread long minshow = _ifc.minShowSeconds * 1000L; if (_container != null && uptime < minshow) { try { - Thread.sleep(minshow - uptime); + TimeUnit.MILLISECONDS.sleep(minshow - uptime); } catch (Exception e) { } } @@ -750,10 +779,9 @@ public abstract class Getdown extends Thread if (_silent || (_container != null && !reinit)) { return; } -/* + EventQueue.invokeLater(new Runnable() { public void run () { -*/ if (_container == null || reinit) { if (_container == null) { _container = createContainer(); @@ -762,42 +790,6 @@ public abstract class Getdown extends Thread } configureContainer(); _layers = new JLayeredPane(); - - - - // added in the instant display of a splashscreen - try { - readConfig(false); - Graphics g = _container.getGraphics(); - String imageFile = _ifc.backgroundImage; - BufferedImage bgImage = loadImage(_ifc.backgroundImage); - int bwidth = bgImage.getWidth(); - int bheight = bgImage.getHeight(); - - instantSplashPane = new JPanel() { - @Override - protected void paintComponent(Graphics g) - { - super.paintComponent(g); - // attempt to draw a background image... - if (bgImage != null) { - g.drawImage(bgImage, 0, 0, this); - } - } - }; - - instantSplashPane.setSize(bwidth,bheight); - instantSplashPane.setPreferredSize(new Dimension(bwidth,bheight)); - - _layers.add(instantSplashPane, Integer.valueOf(0)); - - _container.setPreferredSize(new Dimension(bwidth,bheight)); - } catch (Exception e) { - log.warning("Failed to set instant background image", "bg", _ifc.backgroundImage); - } - - - _container.add(_layers, BorderLayout.CENTER); _patchNotes = new JButton(new AbstractAction(_msgs.getString("m.patch_notes")) { @Override public void actionPerformed (ActionEvent event) { @@ -807,14 +799,12 @@ public abstract class Getdown extends Thread _patchNotes.setFont(StatusPanel.FONT); _layers.add(_patchNotes); _status = new StatusPanel(_msgs); - _layers.add(_status, Integer.valueOf(10)); + _layers.add(_status); initInterface(); } showContainer(); -/* } }); -*/ } /** @@ -845,12 +835,13 @@ public abstract class Getdown extends Thread protected RotatingBackgrounds getBackground () { - if (_ifc.rotatingBackgrounds != null && _ifc.rotatingBackgrounds.size() > 0) { + if (_ifc.rotatingBackgrounds != null) { if (_ifc.backgroundImage != null) { log.warning("ui.background_image and ui.rotating_background were both specified. " + - "The background image is being used."); + "The rotating images are being used."); } - return new RotatingBackgrounds(_ifc.rotatingBackgrounds, _ifc.errorBackground, Getdown.this); + return new RotatingBackgrounds(_ifc.rotatingBackgrounds, _ifc.errorBackground, + Getdown.this); } else if (_ifc.backgroundImage != null) { return new RotatingBackgrounds(loadImage(_ifc.backgroundImage)); } else { @@ -879,8 +870,23 @@ public abstract class Getdown extends Thread } } + private void fail (Exception e) { + String msg = e.getMessage(); + if (msg == null) { + msg = MessageUtil.compose("m.unknown_error", _ifc.installError); + } else if (!msg.startsWith("m.")) { + // try to do something sensible based on the type of error + msg = MessageUtil.taint(msg); + msg = e instanceof FileNotFoundException ? + MessageUtil.compose("m.missing_resource", msg, _ifc.installError) : + MessageUtil.compose("m.init_error", msg, _ifc.installError); + } + // since we're dead, clear off the 'time remaining' label along with displaying the error + fail(msg); + } + /** - * Update the status to indicate getdown has failed for the reason in message. + * Update the status to indicate getdown has failed for the reason in {@code message}. */ protected void fail (String message) { @@ -961,14 +967,14 @@ public abstract class Getdown extends Thread do { URL url = _app.getTrackingProgressURL(++_reportedProgress); if (url != null) { - new ProgressReporter(url).start(); + reportProgress(url); } } while (_reportedProgress <= progress); } else { URL url = _app.getTrackingURL(event); if (url != null) { - new ProgressReporter(url).start(); + reportProgress(url); } } } @@ -1031,44 +1037,40 @@ public abstract class Getdown extends Thread } /** Used to fetch a progress report URL. */ - protected class ProgressReporter extends Thread - { - public ProgressReporter (URL url) { - setDaemon(true); - _url = url; - } - - @Override - public void run () { - try { - HttpURLConnection ucon = ConnectionUtil.openHttp(_app.proxy, _url, 0, 0); - - // if we have a tracking cookie configured, configure the request with it - if (_app.getTrackingCookieName() != null && - _app.getTrackingCookieProperty() != null) { - String val = System.getProperty(_app.getTrackingCookieProperty()); - if (val != null) { - ucon.setRequestProperty("Cookie", _app.getTrackingCookieName() + "=" + val); + protected void reportProgress (final URL url) { + Thread reporter = new Thread("Progress reporter") { + public void run () { + try { + HttpURLConnection ucon = _app.conn.openHttp(url, 0, 0); + + // if we have a tracking cookie configured, configure the request with it + if (_app.getTrackingCookieName() != null && + _app.getTrackingCookieProperty() != null) { + String val = System.getProperty(_app.getTrackingCookieProperty()); + if (val != null) { + ucon.setRequestProperty( + "Cookie", _app.getTrackingCookieName() + "=" + val); + } } - } - // now request our tracking URL and ensure that we get a non-error response - ucon.connect(); - try { - if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) { - log.warning("Failed to report tracking event", - "url", _url, "rcode", ucon.getResponseCode()); + // now request our tracking URL and ensure that we get a non-error response + ucon.connect(); + try { + if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) { + log.warning("Failed to report tracking event", + "url", url, "rcode", ucon.getResponseCode()); + } + } finally { + ucon.disconnect(); } - } finally { - ucon.disconnect(); - } - } catch (IOException ioe) { - log.warning("Failed to report tracking event", "url", _url, "error", ioe); + } catch (IOException ioe) { + log.warning("Failed to report tracking event", "url", url, "error", ioe); + } } - } - - protected URL _url; + }; + reporter.setDaemon(true); + reporter.start(); } /** Used to pass progress on to our user interface. */ @@ -1084,7 +1086,6 @@ public abstract class Getdown extends Thread protected ResourceBundle _msgs; protected Container _container; protected JLayeredPane _layers; - protected JPanel instantSplashPane; protected StatusPanel _status; protected JButton _patchNotes; protected AbortPanel _abort; @@ -1112,5 +1113,4 @@ public abstract class Getdown extends Thread protected static final int MAX_LOOPS = 5; protected static final long FALLBACK_CHECK_TIME = 1000L; - } diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java index 2d1908904..3859f6a09 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java @@ -28,258 +28,225 @@ import javax.swing.KeyStroke; import javax.swing.WindowConstants; import com.samskivert.swing.util.SwingUtil; -import com.threerings.getdown.data.Application; import com.threerings.getdown.data.EnvConfig; import com.threerings.getdown.data.SysProps; import com.threerings.getdown.util.LaunchUtil; import com.threerings.getdown.util.StringUtil; import static com.threerings.getdown.Log.log; -import jalview.bin.StartupNotificationListener; /** * The main application entry point for Getdown. */ public class GetdownApp { - public static String startupFilesParameterString = ""; - /** - * The main entry point of the Getdown launcher application. - */ - 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 { - List 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); - } - - // pipe our output into a file in the application directory - if (!SysProps.noLogRedir()) { - File logFile = new File(envc.appDir, "launcher.log"); - try { - PrintStream logOut = new PrintStream( - new BufferedOutputStream(new FileOutputStream(logFile)), true); - System.setOut(logOut); - System.setErr(logOut); - } catch (IOException ioe) { - log.warning("Unable to redirect output to '" + logFile + "': " + ioe); - } - } - - // 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; - } + /** + * The main entry point of the Getdown launcher application. + */ + public static void main (String[] argv) { + try { + start(argv); + } catch (Exception e) { + log.warning("main() failed.", e); + } } - if (abort) System.exit(-1); - try - { - jalview.bin.StartupNotificationListener.setListener(); - } catch (Exception e) - { - e.printStackTrace(); - } catch (NoClassDefFoundError e) - { - log.warning("Starting without install4j classes"); - } catch (Throwable t) - { - t.printStackTrace(); - } - - // record a few things for posterity - log.info("------------------ VM Info ------------------"); - log.info("-- OS Name: " + System.getProperty("os.name")); - log.info("-- OS Arch: " + System.getProperty("os.arch")); - log.info("-- OS Vers: " + System.getProperty("os.version")); - log.info("-- Java Vers: " + System.getProperty("java.version")); - log.info("-- Java Home: " + System.getProperty("java.home")); - log.info("-- User Name: " + System.getProperty("user.name")); - log.info("-- User Home: " + System.getProperty("user.home")); - log.info("-- Cur dir: " + System.getProperty("user.dir")); - log.info("-- startupFilesParameterString: " + startupFilesParameterString); - log.info("---------------------------------------------"); + /** + * Runs Getdown as an application, using the arguments supplie as {@code argv}. + * @return the {@code Getdown} instance that is running. {@link Getdown#run} will have been + * called on it. + * @throws Exception if anything goes wrong starting Getdown. + */ + public static Getdown start (String[] argv) throws Exception { + List 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); + } - Getdown app = new Getdown(envc) { - @Override - protected Container createContainer () { - // create our user interface, and display it - if (_frame == null) { - _frame = new JFrame(""); - _frame.addWindowListener(new WindowAdapter() { - @Override - public void windowClosing (WindowEvent evt) { - handleWindowClose(); - } - }); - // handle close on ESC - String cancelId = "Cancel"; // $NON-NLS-1$ - _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId); - _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() { - public void actionPerformed (ActionEvent e) { - handleWindowClose(); + // pipe our output into a file in the application directory + if (!SysProps.noLogRedir() && !SysProps.debug()) { + File logFile = new File(envc.appDir, "launcher.log"); + try { + PrintStream logOut = new PrintStream( + new BufferedOutputStream(new FileOutputStream(logFile)), true); + System.setOut(logOut); + System.setErr(logOut); + } catch (IOException ioe) { + log.warning("Unable to redirect output to '" + logFile + "': " + ioe); } - }); - // this cannot be called in configureContainer as it is only allowed before the - // frame has been displayed for the first time - _frame.setUndecorated(_ifc.hideDecorations); - _frame.setResizable(false); - } else { - _frame.getContentPane().removeAll(); } - _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); - return _frame.getContentPane(); - } - - @Override - protected void configureContainer () { - if (_frame == null) return; - - _frame.setTitle(_ifc.name); - try { - _frame.setBackground(new Color(_ifc.background, true)); - } catch (Exception e) { - log.warning("Failed to set background", "bg", _ifc.background, e); + // 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")); + log.info("-- OS Arch: " + System.getProperty("os.arch")); + log.info("-- OS Vers: " + System.getProperty("os.version")); + log.info("-- Java Vers: " + System.getProperty("java.version")); + log.info("-- Java Home: " + System.getProperty("java.home")); + log.info("-- User Name: " + System.getProperty("user.name")); + log.info("-- User Home: " + System.getProperty("user.home")); + log.info("-- Cur dir: " + System.getProperty("user.dir")); + log.info("---------------------------------------------"); + + Getdown getdown = new Getdown(envc) { + @Override + protected Container createContainer () { + // create our user interface, and display it + if (_frame == null) { + _frame = new JFrame(""); + _frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing (WindowEvent evt) { + handleWindowClose(); + } + }); + // handle close on ESC + String cancelId = "Cancel"; // $NON-NLS-1$ + _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put( + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId); + _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() { + public void actionPerformed (ActionEvent e) { + handleWindowClose(); + } + }); + // this cannot be called in configureContainer as it is only allowed before the + // frame has been displayed for the first time + _frame.setUndecorated(_ifc.hideDecorations); + _frame.setResizable(false); + } else { + _frame.getContentPane().removeAll(); + } + _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + return _frame.getContentPane(); + } - if (_ifc.iconImages != null && _ifc.iconImages.size() > 0) { - ArrayList icons = new ArrayList<>(); - for (String path : _ifc.iconImages) { - Image img = loadImage(path); - if (img == null) { - log.warning("Error loading icon image", "path", path); - } else { - icons.add(img); + @Override + protected void configureContainer () { + if (_frame == null) return; + + _frame.setTitle(_ifc.name); + + try { + _frame.setBackground(new Color(_ifc.background, true)); + } catch (Exception e) { + log.warning("Failed to set background", "bg", _ifc.background, e); + } + + if (_ifc.iconImages != null) { + List icons = new ArrayList<>(); + for (String path : _ifc.iconImages) { + Image img = loadImage(path); + if (img == null) { + log.warning("Error loading icon image", "path", path); + } else { + icons.add(img); + } + } + if (icons.isEmpty()) { + log.warning("Failed to load any icons", "iconImages", _ifc.iconImages); + } else { + _frame.setIconImages(icons); + } + } } - } - if (icons.isEmpty()) { - log.warning("Failed to load any icons", "iconImages", _ifc.iconImages); - } else { - _frame.setIconImages(icons); - } - } - } - @Override - protected void showContainer () { - if (_frame != null) { - _frame.pack(); - SwingUtil.centerWindow(_frame); - _frame.setVisible(true); - } - } + @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 disposeContainer () { + if (_frame != null) { + _frame.dispose(); + _frame = null; + } + } - @Override - protected void showDocument (String url) { - if (!StringUtil.couldBeValidUrl(url)) { - // command injection would be possible if we allowed e.g. spaces and double quotes - log.warning("Invalid document URL.", "url", url); - return; - } - String[] cmdarray; - if (LaunchUtil.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 (LaunchUtil.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 showDocument (String url) { + if (!StringUtil.couldBeValidUrl(url)) { + // command injection would be possible if we allowed e.g. spaces and double quotes + log.warning("Invalid document URL.", "url", url); + return; + } + String[] cmdarray; + if (LaunchUtil.isWindows()) { + String osName = System.getProperty("os.name", ""); + if (osName.contains("9") || osName.contains("Me")) { + cmdarray = new String[] { + "command.com", "/c", "start", "\"" + url + "\"" }; + } else { + cmdarray = new String[] { + "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" }; + } + } else if (LaunchUtil.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 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) { - super.fail(message); - // super.fail causes the UI to be created (if needed) on the next UI tick, so we - // want to wait until that happens before we attempt to redecorate the window - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - // 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(); - Color bg = _frame.getBackground(); - if (bg != null && bg.getAlpha() < 255) { - // decorated windows do not allow alpha backgrounds - _frame.setBackground( - new Color(bg.getRed(), bg.getGreen(), bg.getBlue())); - } - _frame.setUndecorated(false); - showContainer(); + @Override + protected void fail (String message) { + super.fail(message); + // super.fail causes the UI to be created (if needed) on the next UI tick, so we + // want to wait until that happens before we attempt to redecorate the window + EventQueue.invokeLater(new Runnable() { + @Override public void run () { + // 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(); + Color bg = _frame.getBackground(); + if (bg != null && bg.getAlpha() < 255) { + // decorated windows do not allow alpha backgrounds + _frame.setBackground( + new Color(bg.getRed(), bg.getGreen(), bg.getBlue())); + } + _frame.setUndecorated(false); + showContainer(); + } + } + }); } - } - }); - } - protected JFrame _frame; - }; - - String startupFile = getStartupFilesParameterString(); - if (!StringUtil.isBlank(startupFile)) { - Application.setStartupFilesFromParameterString(startupFile); + protected JFrame _frame; + }; + Getdown.run(getdown); + return getdown; } - - app.start(); - return app; - } - - public static void setStartupFilesParameterString(String parameters) { - startupFilesParameterString = parameters; - } - - public static String getStartupFilesParameterString() { - return startupFilesParameterString; - } } diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java index 217827364..5f18896c4 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java @@ -35,14 +35,16 @@ import static com.threerings.getdown.Log.log; */ public final class ProxyPanel extends JPanel implements ActionListener { - public ProxyPanel (Getdown getdown, ResourceBundle msgs) + public ProxyPanel (Getdown getdown, ResourceBundle msgs, boolean updateAuth) { _getdown = getdown; _msgs = msgs; + _updateAuth = updateAuth; setLayout(new VGroupLayout()); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - add(new SaneLabelField(get("m.configure_proxy"))); + String title = get(updateAuth ? "m.update_proxy_auth" : "m.configure_proxy"); + add(new SaneLabelField(title)); add(new Spacer(5, 5)); JPanel row = new JPanel(new GridLayout()); @@ -61,19 +63,20 @@ public final class ProxyPanel extends JPanel implements ActionListener row.add(new SaneLabelField(get("m.proxy_auth_required")), BorderLayout.WEST); _useAuth = new JCheckBox(); row.add(_useAuth); + _useAuth.setSelected(updateAuth); add(row); row = new JPanel(new GridLayout()); row.add(new SaneLabelField(get("m.proxy_username")), BorderLayout.WEST); _username = new SaneTextField(); - _username.setEnabled(false); + _username.setEnabled(updateAuth); row.add(_username); add(row); row = new JPanel(new GridLayout()); row.add(new SaneLabelField(get("m.proxy_password")), BorderLayout.WEST); _password = new SanePasswordField(); - _password.setEnabled(false); + _password.setEnabled(updateAuth); row.add(_password); add(row); @@ -112,7 +115,13 @@ public final class ProxyPanel extends JPanel implements ActionListener public void addNotify () { super.addNotify(); - _host.requestFocusInWindow(); + if (_updateAuth) { + // we are asking the user to update the credentials for an existing proxy + // configuration, so focus that instead of the proxy host config + _username.requestFocusInWindow(); + } else { + _host.requestFocusInWindow(); + } } // documentation inherited @@ -131,7 +140,7 @@ public final class ProxyPanel extends JPanel implements ActionListener public void actionPerformed (ActionEvent e) { String cmd = e.getActionCommand(); - if (cmd.equals("ok")) { + if ("ok".equals(cmd)) { String user = null, pass = null; if (_useAuth.isSelected()) { user = _username.getText(); @@ -184,8 +193,9 @@ public final class ProxyPanel extends JPanel implements ActionListener return dim; } - protected Getdown _getdown; - protected ResourceBundle _msgs; + protected final Getdown _getdown; + protected final ResourceBundle _msgs; + protected final boolean _updateAuth; protected JTextField _host; protected JTextField _port; diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java index a36b5fa67..8962d35b9 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java @@ -8,31 +8,41 @@ package com.threerings.getdown.launcher; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintStream; +import java.io.Reader; import java.net.Authenticator; import java.net.HttpURLConnection; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.PasswordAuthentication; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; +import java.net.UnknownHostException; import java.util.Iterator; import java.util.ServiceLoader; +import javax.script.Bindings; +import javax.script.Invocable; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + import ca.beq.util.win32.registry.RegistryKey; import ca.beq.util.win32.registry.RegistryValue; import ca.beq.util.win32.registry.RootKey; import com.threerings.getdown.data.Application; +import com.threerings.getdown.net.Connector; import com.threerings.getdown.spi.ProxyAuth; import com.threerings.getdown.util.Config; -import com.threerings.getdown.util.ConnectionUtil; import com.threerings.getdown.util.LaunchUtil; import com.threerings.getdown.util.StringUtil; import static com.threerings.getdown.Log.log; -public class ProxyUtil { +public final class ProxyUtil { public static boolean autoDetectProxy (Application app) { @@ -57,25 +67,40 @@ public class ProxyUtil { RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY); for (Iterator iter = r.values(); iter.hasNext(); ) { RegistryValue value = (RegistryValue)iter.next(); - if (value.getName().equals("ProxyEnable")) { - enabled = value.getStringValue().equals("1"); + if ("ProxyEnable".equals(value.getName())) { + enabled = "1".equals(value.getStringValue()); } if (value.getName().equals("ProxyServer")) { - String strval = value.getStringValue(); - int cidx = strval.indexOf(":"); - if (cidx != -1) { - rport = strval.substring(cidx+1); - strval = strval.substring(0, cidx); + String[] hostPort = splitHostPort(value.getStringValue()); + rhost = hostPort[0]; + rport = hostPort[1]; + } + if (value.getName().equals("AutoConfigURL")) { + String acurl = value.getStringValue(); + Reader acjs = new InputStreamReader(new URL(acurl).openStream()); + // technically we should be returning all this info and trying each proxy + // in succession, but that's complexity we'll leave for another day + URL configURL = app.getConfigResource().getRemote(); + for (String proxy : findPACProxiesForURL(acjs, configURL)) { + if (proxy.startsWith("PROXY ")) { + String[] hostPort = splitHostPort(proxy.substring(6)); + rhost = hostPort[0]; + rport = hostPort[1]; + // TODO: is this valid? Does AutoConfigURL imply proxy enabled? + enabled = true; + break; + } } - rhost = strval; } } + if (enabled) { host = rhost; port = rport; } else { log.info("Detected no proxy settings in the registry."); } + } catch (Throwable t) { log.info("Failed to find proxy settings in Windows registry", "error", t); } @@ -97,32 +122,31 @@ public class ProxyUtil { return true; } - public static boolean canLoadWithoutProxy (URL rurl) + public static boolean canLoadWithoutProxy (URL rurl, int timeoutSeconds) { - log.info("Testing whether proxy is needed, via: " + rurl); + log.info("Attempting to fetch without proxy: " + rurl); try { - // try to make a HEAD request for this URL (use short connect and read timeouts) - URLConnection conn = ConnectionUtil.open(Proxy.NO_PROXY, rurl, 5, 5); - if (conn instanceof HttpURLConnection) { - HttpURLConnection hcon = (HttpURLConnection)conn; - try { - hcon.setRequestMethod("HEAD"); - hcon.connect(); - // make sure we got a satisfactory response code - int rcode = hcon.getResponseCode(); - if (rcode == HttpURLConnection.HTTP_PROXY_AUTH || - rcode == HttpURLConnection.HTTP_FORBIDDEN) { - log.warning("Got an 'HTTP credentials needed' response", "code", rcode); - } else { - return true; - } - } finally { - hcon.disconnect(); - } - } else { - // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy + URLConnection conn = Connector.DEFAULT.open(rurl, timeoutSeconds, timeoutSeconds); + // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy + if (!(conn instanceof HttpURLConnection)) { return true; } + // otherwise, try to make a HEAD request for this URL + HttpURLConnection hcon = (HttpURLConnection)conn; + try { + hcon.setRequestMethod("HEAD"); + hcon.connect(); + // make sure we got a satisfactory response code + int rcode = hcon.getResponseCode(); + if (rcode == HttpURLConnection.HTTP_PROXY_AUTH || + rcode == HttpURLConnection.HTTP_FORBIDDEN) { + log.warning("Got an 'HTTP credentials needed' response", "code", rcode); + } else { + return true; + } + } finally { + hcon.disconnect(); + } } catch (IOException ioe) { log.info("Failed to HEAD " + rurl + ": " + ioe); log.info("We probably need a proxy, but auto-detection failed."); @@ -190,9 +214,14 @@ public class ProxyUtil { } boolean haveCreds = !StringUtil.isBlank(username) && !StringUtil.isBlank(password); - int pport = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port); - log.info("Using proxy", "host", host, "port", pport, "haveCreds", haveCreds); - app.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pport)); + if (StringUtil.isBlank(host)) { + log.info("Using no proxy"); + app.conn = new Connector(Proxy.NO_PROXY); + } else { + int pp = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port); + log.info("Using proxy", "host", host, "port", pp, "haveCreds", haveCreds); + app.conn = new Connector(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pp))); + } if (haveCreds) { final String fuser = username; @@ -205,6 +234,61 @@ public class ProxyUtil { } } + public static class Resolver { + public String dnsResolve (String host) { + try { + return InetAddress.getByName(host).getHostAddress(); + } catch (UnknownHostException uhe) { + return null; + } + } + public String myIpAddress () { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException uhe) { + return null; + } + } + } + + public static String[] findPACProxiesForURL (Reader pac, URL url) { + ScriptEngineManager manager = new ScriptEngineManager(); + ScriptEngine engine = manager.getEngineByName("javascript"); + Bindings globals = engine.createBindings(); + globals.put("resolver", new Resolver()); + engine.setBindings(globals, ScriptContext.GLOBAL_SCOPE); + try { + URL utils = ProxyUtil.class.getResource("PacUtils.js"); + if (utils == null) { + log.error("Unable to load PacUtils.js"); + return new String[0]; + } + engine.eval(new InputStreamReader(utils.openStream())); + Object res = engine.eval(pac); + if (engine instanceof Invocable) { + Object[] args = new Object[] { url.toString(), url.getHost() }; + res = ((Invocable) engine).invokeFunction("FindProxyForURL", args); + } + String[] proxies = res.toString().split(";"); + for (int ii = 0; ii < proxies.length; ii += 1) { + proxies[ii] = proxies[ii].trim(); + } + return proxies; + } catch (Exception e) { + log.warning("Failed to resolve PAC proxy", e); + } + return new String[0]; + } + + private static String[] splitHostPort (String hostPort) { + int cidx = hostPort.indexOf(":"); + if (cidx == -1) { + return new String[] { hostPort, null}; + } else { + return new String[] { hostPort.substring(0, cidx), hostPort.substring(cidx+1) }; + } + } + protected static final String PROXY_REGISTRY = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; } diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java index d3aa2bd25..d64e5f02d 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java @@ -14,7 +14,7 @@ public final class RotatingBackgrounds { public interface ImageLoader { /** Loads and returns the image with the supplied path. */ - public Image loadImage (String path); + Image loadImage (String path); } /** @@ -35,7 +35,7 @@ public final class RotatingBackgrounds } /** - * Create a sequence of images to be rotated through from backgrounds. + * Create a sequence of images to be rotated through from {@code backgrounds}. * * Each String in backgrounds should be the path to the image, a semicolon, and the minimum * amount of time to display the image in seconds. Each image will be active for an equal diff --git a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java index 99f44ca51..197dc9170 100644 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java @@ -26,12 +26,9 @@ import com.samskivert.swing.Label; import com.samskivert.swing.LabelStyleConstants; import com.samskivert.swing.util.SwingUtil; import com.samskivert.util.Throttle; - import com.threerings.getdown.data.Application.UpdateInterface; import com.threerings.getdown.util.MessageUtil; import com.threerings.getdown.util.Rectangle; -import com.threerings.getdown.util.StringUtil; - import static com.threerings.getdown.Log.log; /** @@ -344,7 +341,7 @@ public final class StatusPanel extends JComponent { String msg = get(key); if (msg != null) return MessageFormat.format(MessageUtil.escape(msg), (Object[])args); - return key + String.valueOf(Arrays.asList(args)); + return key + Arrays.asList(args); } /** Used by {@link #setStatus}, and {@link #setProgress}. */ diff --git a/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java b/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java deleted file mode 100644 index 5c6c7c393..000000000 --- a/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package jalview.bin; - -import com.threerings.getdown.launcher.GetdownApp; -import static com.threerings.getdown.Log.log; - -public class StartupNotificationListener { - - public static void setListener() { - - - try { - com.install4j.api.launcher.StartupNotification.registerStartupListener( - new com.install4j.api.launcher.StartupNotification.Listener() { - @Override - public void startupPerformed(String parameters) { - log.info("StartupNotification.Listener.startupPerformed: '"+parameters+"'"); - GetdownApp.setStartupFilesParameterString(parameters); - } - } - ); - } catch (Exception e) { - e.printStackTrace(); - } catch (NoClassDefFoundError t) { - log.warning("Starting without install4j classes"); - } - - } - -} diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties index 19b2999e1..7a33ca0e4 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties @@ -12,11 +12,14 @@ m.abort_cancel = Continue installation m.detecting_proxy = Trying to auto-detect proxy settings m.configure_proxy = We were unable to connect to the application server to download data. \ -

Please make sure that no virus scanner or firewall is blocking network communicaton with \ +

Please make sure that no virus scanner or firewall is blocking network communication with \ the server. \

Your computer may access the Internet through a proxy and we were unable to automatically \ detect your proxy settings. If you know your proxy settings, you can enter them below. +m.update_proxy_auth = The stored proxy user/password is wrong or obsolete. \ +

Please provide an updated user/password combination. + m.proxy_extra = If you are sure that you don't use a proxy then \ perhaps there is a temporary Internet outage that is preventing us from \ communicating with the servers. In this case, you can cancel and try \ @@ -41,10 +44,7 @@ m.checking = Checking for update m.validating = Validating m.patching = Patching m.launching = Launching - m.patch_notes = Patch Notes -m.play_again = Play Again - m.complete = {0}% complete m.remain = {0} remaining @@ -99,7 +99,7 @@ m.default_install_error = the support section of the website m.another_getdown_running = Multiple instances of this application's \ installer are running. This one will stop and let another complete. -m.applet_stopped = Getdown's applet was told to stop working. +m.verify_timeout = Verifying resources took too long. # application/digest errors m.missing_appbase = The configuration file is missing the 'appbase'. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties index 8e3683594..db35593b2 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties @@ -1,13 +1,9 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown German translation messages m.abort_title = Installation abbrechen? -m.abort_confirm = Bist du sicher, dass du die Installation abbrechen \ -m\u00f6chtest? \ - Du kannst sp\u00e4ter fortfahren, indem du die Anwendung erneut \ -ausf\u00fchrst. +m.abort_confirm = Bist du sicher, dass du die Installation abbrechen möchtest? \ + Du kannst später fortfahren, indem du die Anwendung erneut ausführst. m.abort_ok = Beenden m.abort_cancel = Installation fortsetzen @@ -17,9 +13,12 @@ m.configure_proxy = Es konnte keine Verbindung zum Applikations-Server auf

Bitte kontrollieren Sie die Proxyeinstellungen und stellen Sie sicher, dass keine lokal oder \ im Netzwerk betriebene Sicherheitsanwendung (Virenscanner, Firewall, etc.) die Kommunikation \ mit dem Server blockiert.
\ - Wenn kein Proxy verwendet werden soll, l\u00f6schen Sie bitte alle Eintr\u00e4ge in den unten \ + Wenn kein Proxy verwendet werden soll, löschen Sie bitte alle Einträge in den unten \ stehenden Feldern und klicken sie auf OK. +m.update_proxy_auth = Gespeicherte Proxy User/Passwort Kombination ungültig oder obsolet. \ +

Bitte geben Sie die korrekte User/Passwort Kombination ein. + m.proxy_extra = Sollten Sie keine Proxyeinstellungen gesetzt haben wenden Sie sich bitte \ an Ihren Administrator. @@ -46,71 +45,68 @@ m.launching = Starte m.patch_notes = Patchnotes m.complete = {0}% abgeschlossen -m.remain = {0} \u00fcbrig +m.remain = {0} übrig m.updating_metadata = Lade Steuerungsdateien herunter -m.init_failed = Unsere Konfigurationsdatei fehlt oder ist besch\u00e4digt. \ -Versuche, eine neue Kopie herunterzuladen... +m.init_failed = Unsere Konfigurationsdatei fehlt oder ist beschädigt. \ + Versuche, eine neue Kopie herunterzuladen... -m.java_download_failed = Wir konnten die notwendige Javaversion f\u00fcr deinen \ -Computer nicht automatisch herunterladen. \n\n \ -Bitte auf www.java.com die aktuelle Javaversion herunterladen und dann die \ -Anwendung erneut starten. +m.java_download_failed = Wir konnten die notwendige Javaversion für deinen \ + Computer nicht automatisch herunterladen. \n\n \ + Bitte auf www.java.com die aktuelle Javaversion herunterladen und dann die \ + Anwendung erneut starten. m.java_unpack_failed = Wir konnten die aktualisierte Javaversion nicht \ -entpacken. Bitte stelle sicher, dass wenigstens 100MB Platz auf der \ -Festplatte frei sind und versuche dann die Anwendung erneut zu \ -starten.\n\n\ \ -Falls das das Problem nicht beseitigt, bitte auf www.java.com die aktuelle \ -Javaversion herunterladen und installieren und dann erneut versuchen. + entpacken. Bitte stelle sicher, dass wenigstens 100MB Platz auf der \ + Festplatte frei sind und versuche dann die Anwendung erneut zu \ + starten.\n\n\ \ + Falls das das Problem nicht beseitigt, bitte auf www.java.com die aktuelle \ + Javaversion herunterladen und installieren und dann erneut versuchen. m.unable_to_repair = Wir konnten die notwendigen Dateien nach 5 Versuchen \ -nicht herunterladen. Du kannst versuchen, die Anwendung erneut zu starten, \ -aber wenn dies erneut fehlschl\u00e4gt, musst du die Anwendung deinstallieren \ -und erneut installieren. + nicht herunterladen. Du kannst versuchen, die Anwendung erneut zu starten, \ + aber wenn dies erneut fehlschlägt, musst du die Anwendung deinstallieren \ + und erneut installieren. m.unknown_error = Die Anwendung konnte wegen eines unbekannten Fehlers \ -nicht gestartet werden. Bitte auf \n{0} weiterlesen. + nicht gestartet werden. Bitte auf \n{0} weiterlesen. m.init_error = Die Anwendung konnte wegen folgendem Fehler nicht gestartet \ -werden:\n{0}\n\n Bitte auf \n{1} weiterlesen, um zu erfahren, wie bei \ -solchen Problemen vorzugehen ist. + werden:\n{0}\n\n Bitte auf \n{1} weiterlesen, um zu erfahren, wie bei \ + solchen Problemen vorzugehen ist. -m.readonly_error = Das Verzeichnis, in dem die Anwendung installiert ist: \ - \n{0}\nist nicht schreibberechtigt. Bitte in ein Verzeichnis mit \ -Schreibzugriff installieren. +m.readonly_error = Das Verzeichnis, in dem die Anwendung installiert ist:\n{0}\n \ + ist nicht schreibberechtigt. Bitte in ein Verzeichnis mit Schreibzugriff installieren. m.missing_resource = Die Anwendung konnte nicht gestartet werden, da die \ -folgende Quelle nicht gefunden wurde:\n{0}\n\n\ Bitte auf \n{1} \ -weiterlesen, um zu erfahren, wie bei solchen Problemen vorzugehen ist. + folgende Quelle nicht gefunden wurde:\n{0}\n\n\ Bitte auf \n{1} \ + weiterlesen, um zu erfahren, wie bei solchen Problemen vorzugehen ist. m.insufficient_permissions_error = Du hast die digitale Signatur dieser \ -Anwendung nicht akzeptiert. Falls du diese Anwendung benutzen willst, \ -musst du ihre digitale Signatur akzeptieren. \n\Um das zu tun, musst du \ -deinen Browser beenden, neu starten und erneut die Anwendung von dieser \ -Webseite aus starten. Wenn die Sicherheitsabfrage erscheint, bitte die \ -digitale Signatur akzeptieren, um der Anwendung die n\u00f6tigen Rechte zu \ -geben, die sie braucht, um zu laufen. + Anwendung nicht akzeptiert. Falls du diese Anwendung benutzen willst, \ + musst du ihre digitale Signatur akzeptieren. \n\Um das zu tun, musst du \ + deinen Browser beenden, neu starten und erneut die Anwendung von dieser \ + Webseite aus starten. Wenn die Sicherheitsabfrage erscheint, bitte die \ + digitale Signatur akzeptieren, um der Anwendung die nötigen Rechte zu \ + geben, die sie braucht, um zu laufen. m.corrupt_digest_signature_error = Wir konnten die digitale Signatur \ -dieser Anwendung nicht \u00fcberpr\u00fcfen.\nBitte \u00fcberpr\u00fcfe, ob du die Anwendung \ -von der richtigen Webseite aus startest. + dieser Anwendung nicht überprüfen.\nBitte überprüfe, ob du die Anwendung \ + von der richtigen Webseite aus startest. m.default_install_error = der Support-Webseite -m.another_getdown_running = Diese Installationsanwendung l\u00e4uft in mehreren \ -Instanzen. Diese Instanz wird sich beenden und eine andere Instanz den \ -Vorgang erledigen lassen. - -m.applet_stopped = Die Anwendung wurde beendet. +m.another_getdown_running = Diese Installationsanwendung läuft in mehreren \ + Instanzen. Diese Instanz wird sich beenden und eine andere Instanz den \ + Vorgang erledigen lassen. +m.verify_timeout = Timeout beim Verifizieren der Resourcen. # application/digest errors m.missing_appbase = In der Konfigurationsdatei fehlt die 'appbase'. m.invalid_version = In der Konfigurationsdatei steht die falsche Version. m.invalid_appbase = In der Konfigurationsdatei steht die falsche 'appbase'. m.missing_class = In der Konfigurationsdatei fehlt die Anwendungsklasse. -m.missing_code = Die Konfigurationsdatei enth\u00e4lt keine Codequellen. -m.invalid_digest_file = Die Hashwertedatei ist ung\u00fcltig. - +m.missing_code = Die Konfigurationsdatei enthält keine Codequellen. +m.invalid_digest_file = Die Hashwertedatei ist ungültig. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties index 609b02524..46cd64ac9 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties @@ -1,36 +1,34 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown Spanish translation messages -m.abort_title = \u00bfCancelar la instalaci\u00f3n? -m.abort_confirm = \u00bfEst\u00e1s seguro de querer cancelar la instalaci\u00f3n? \ - Puedes continuarla despu\u00e9s si corres de nuevo la aplicaci\u00f3n. +m.abort_title = ¿Cancelar la instalación? +m.abort_confirm = ¿Estás seguro de querer cancelar la instalación? \ + Puedes continuarla después si corres de nuevo la aplicación. m.abort_ok = Cancelar -m.abort_cancel = Continuar la instalaci\u00f3n +m.abort_cancel = Continuar la instalación -m.detecting_proxy = Detectando autom\u00e1ticamente la configuraci\u00f3n proxy +m.detecting_proxy = Detectando automáticamente la configuración proxy m.configure_proxy = No ha sido posible conectar con nuestros servidores para \ descargar los datos del juego. \

  • Si el cortafuegos de Windows o Norton Internet Security tiene instrucciones \ - de bloquear javaw.exe no podemos descargar el juego. Necesitar\u00e1s \ + de bloquear javaw.exe no podemos descargar el juego. Necesitarás \ permitir que javaw.exe tenga acceso al Internet. Puedes intentar \ correr el juego de nuevo, pero es posible que debas dar permisos a javaw.exe en la \ - configuraci\u00f3n de tu cortafuegos ( Inicio -> Panel de control -> Firewall de Windows ).
\ + configuración de tu cortafuegos ( Inicio -> Panel de control -> Firewall de Windows ). \

Es posible que tu computadora tenga acceso al Internet por medio de un proxy por lo que \ - no ha sido posible detectar autom\u00e1ticamente tu configuraci\u00f3n. Si conoces tu \ - configuraci\u00f3n proxy, puedes anotarla abajo. + no ha sido posible detectar automáticamente tu configuración. Si conoces tu \ + configuración proxy, puedes anotarla abajo. -m.proxy_extra = Si est\u00e1s seguro de que no tienes un proxy entonces \ - tal vez exista un falla temporal en el Internet que est\u00e1 evitando que podamos \ +m.proxy_extra = Si estás seguro de que no tienes un proxy entonces \ + tal vez exista un falla temporal en el Internet que está evitando que podamos \ comunicarnos con los servidores. En este caso, puedes cancelar e intentar \ - instalarla de nuevo m\u00e1s tarde. + instalarla de nuevo más tarde. m.proxy_host = IP proxy m.proxy_port = Puerto proxy m.proxy_username = Nombre de usuario -m.proxy_password = Contrase\u00f1a +m.proxy_password = Contraseña m.proxy_auth_required = Autenticacion requerida m.proxy_ok = OK m.proxy_cancel = Cancelar @@ -54,62 +52,59 @@ m.remain = {0} restante m.updating_metadata = Descargando los archivos de control -m.init_failed = Un archivo de configuraci\u00f3n est\u00e1 faltante o est\u00e1 corrupto. Intentando \ +m.init_failed = Un archivo de configuración está faltante o está corrupto. Intentando \ descargar una nueva copia... -m.java_download_failed = No ha sido posible descargar autom\u00e1ticamente la \ - versi\u00f3n de Java necesaria para tu computadora.\n\n\ - Por favor ve a www.java.com y descarga la \u00faltima versi\u00f3n de \ - Java, despu\u00e9s intenta correr de nuevo la aplicaci\u00f3n. +m.java_download_failed = No ha sido posible descargar automáticamente la \ + versión de Java necesaria para tu computadora.\n\n\ + Por favor ve a www.java.com y descarga la última versión de \ + Java, después intenta correr de nuevo la aplicación. -m.java_unpack_failed = No ha sido posible desempacar una versi\u00f3n actualizada de \ - Java. Por favor aseg\u00farate de tener al menos 100 MB de espacio libre en tu \ - disco duro e intenta correr de nuevo la aplicaci\u00f3n.\n\n\ +m.java_unpack_failed = No ha sido posible desempacar una versión actualizada de \ + Java. Por favor asegúrate de tener al menos 100 MB de espacio libre en tu \ + disco duro e intenta correr de nuevo la aplicación.\n\n\ Si eso no soluciona el problema, ve a www.java.com y descarga e \ - instala la \u00faltima versi\u00f3n de Java e intenta de nuevo. + instala la última versión de Java e intenta de nuevo. -m.unable_to_repair = No ha sido posible descargar los archivos necesarios despu\u00e9s de \ - cinco intentos. Puedes intentar correr de nuevo la aplicaci\u00f3n, pero si falla \ - de nuevo podr\u00edas necesitar desinstalar y reinstalar. +m.unable_to_repair = No ha sido posible descargar los archivos necesarios después de \ + cinco intentos. Puedes intentar correr de nuevo la aplicación, pero si falla \ + de nuevo podrías necesitar desinstalar y reinstalar. -m.unknown_error = La aplicaci\u00f3n no ha podido iniciar debido a un extra\u00f1o \ - error del que no se pudo recobrar. Por favor visita\n{0} para ver informaci\u00f3n acerca \ +m.unknown_error = La aplicación no ha podido iniciar debido a un extraño \ + error del que no se pudo recobrar. Por favor visita\n{0} para ver información acerca \ de como recuperarla. -m.init_error = La aplicaci\u00f3n no ha podido iniciar debido al siguiente \ +m.init_error = La aplicación no ha podido iniciar debido al siguiente \ error:\n{0}\n\nPor favor visita\n{1} para \ - ver informaci\u00f3n acerca de como manejar ese tipo de problemas. + ver información acerca de como manejar ese tipo de problemas. -m.readonly_error = El directorio en el que esta aplicaci\u00f3n est\u00e1 instalada: \ - \n{0}\nes solo lectura. Por favor instala la aplicaci\u00f3n en un directorio en el cual \ +m.readonly_error = El directorio en el que esta aplicación está instalada: \ + \n{0}\nes solo lectura. Por favor instala la aplicación en un directorio en el cual \ tengas acceso de escritura. -m.missing_resource = La aplicaci\u00f3n no ha podido iniciar debido a un recurso \ - faltante:\n{0}\n\nPor favor visita\n{1} para informaci\u00f3n acerca de como solucionar \ +m.missing_resource = La aplicación no ha podido iniciar debido a un recurso \ + faltante:\n{0}\n\nPor favor visita\n{1} para información acerca de como solucionar \ estos problemas. m.insufficient_permissions_error = No aceptaste la firma digital de \ - esta aplicaci\u00f3n. Si quieres correr la aplicaci\u00f3n, necesitas aceptar \ + esta aplicación. Si quieres correr la aplicación, necesitas aceptar \ su firma digital.\n\nPara hacerlo, necesitas cerrar tu navegador, \ - reiniciarlo, y regresar a esta p\u00e1gina web para reiniciar la aplicaci\u00f3n. Cuando se muestre \ - el di\u00e1logo de seguridad, haz clic en el bot\u00f3n para aceptar la firmar digital \ - y otorgar a esta aplicaci\u00f3n los privilegios que necesita para correr. + reiniciarlo, y regresar a esta página web para reiniciar la aplicación. Cuando se muestre \ + el diálogo de seguridad, haz clic en el botón para aceptar la firmar digital \ + y otorgar a esta aplicación los privilegios que necesita para correr. m.corrupt_digest_signature_error = No pudimos verificar la firma digital \ - de la aplicaci\u00f3n.\nPor favor revisa que est\u00e9s lanzando la aplicaci\u00f3n desde\nel \ + de la aplicación.\nPor favor revisa que estés lanzando la aplicación desde\nel \ sitio web correcto. -m.default_install_error = la secci\u00f3n de asistencia de este sitio web - -m.another_getdown_running = Est\u00e1n corriendo m\u00faltiples instancias de \ - este instalador. Este se detendr\u00e1 para permitir que otra contin\u00fae. +m.default_install_error = la sección de asistencia de este sitio web -m.applet_stopped = Se le dijo al applet de Getdown que dejara de trabajar. +m.another_getdown_running = Están corriendo múltiples instancias de \ + este instalador. Este se detendrá para permitir que otra continúe. # application/digest errors -m.missing_appbase = Al archivo de configuraci\u00f3n le falta el 'appbase'. -m.invalid_version = El archivo de configuraci\u00f3n especifica una versi\u00f3n no v\u00e1lida. -m.invalid_appbase = El archivo de configuraci\u00f3n especifica un 'appbase' no v\u00e1lido. -m.missing_class = Al archivo de configuraci\u00f3n le falta la clase de aplicaci\u00f3n. -m.missing_code = El archivo de configuraci\u00f3n especifica que no hay recursos de c\u00f3digo. -m.invalid_digest_file = El archivo digest no es v\u00e1lido. - +m.missing_appbase = Al archivo de configuración le falta el 'appbase'. +m.invalid_version = El archivo de configuración especifica una versión no válida. +m.invalid_appbase = El archivo de configuración especifica un 'appbase' no válido. +m.missing_class = Al archivo de configuración le falta la clase de aplicación. +m.missing_code = El archivo de configuración especifica que no hay recursos de código. +m.invalid_digest_file = El archivo digest no es válido. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties index 3666204e2..5eb8ec9cd 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties @@ -1,29 +1,27 @@ # -# $Id: messages.properties 485 2012-03-08 22:05:30Z ray.j.greenwell $ -# -# Getdown translation messages +# Getdown French translation messages m.abort_title = Annuler l'installation? -m.abort_confirm =\u00cates-vous s\u00fbr de vouloir annuler l'installation? \ - Vous pourrez reprendre l'installation en ex\u00e9cutant l'application de nouveau. +m.abort_confirm =Êtes-vous sûr de vouloir annuler l'installation? \ + Vous pourrez reprendre l'installation en exécutant l'application de nouveau. m.abort_ok = Quitter m.abort_cancel = Continuer l'installation -m.detecting_proxy = D\u00e9tection automatique des r\u00e9glages proxy +m.detecting_proxy = Détection automatique des réglages proxy m.configure_proxy =Connexion au serveur impossible. \ -

  • Veuillez v\u00e9rifier que javaw.exe n'est bloqu\u00e9 \ +
    • Veuillez vérifier que javaw.exe n'est bloqué \ par aucun pare-feu ou antivirus. \ Vous pouvez vous rendre sur la configuration du pare-feu windows via \ - (D\u00e9marrer -> Panneau de Configuration -> Pare-feu Windows ).
    \ -

    Il est \u00e9galement possible que vous soyez derri\u00e8re un proxy que l'application \ - est incapable de d\u00e9tecter automatiquement. \ - Si tel est le cas, veuillez saisir les r\u00e9glages proxy ci-dessous. + (Démarrer -> Panneau de Configuration -> Pare-feu Windows ).

\ +

Il est également possible que vous soyez derrière un proxy que l'application \ + est incapable de détecter automatiquement. \ + Si tel est le cas, veuillez saisir les réglages proxy ci-dessous. -m.proxy_extra =Si vous \u00eates certain de ne pas utiliser de proxy, il est \ - possible qu'une interruption temporaire de la connexion internet emp\u00fbche la \ +m.proxy_extra =Si vous êtes certain de ne pas utiliser de proxy, il est \ + possible qu'une interruption temporaire de la connexion internet empûche la \ communication avec les serveurs. Dans ce cas, vous pouvez relancer \ - l'installation ult\u00e9rieurement. + l'installation ultérieurement. m.proxy_host = Proxy IP m.proxy_port = Proxy port @@ -33,79 +31,77 @@ m.proxy_auth_required = Identification requise m.proxy_ok = OK m.proxy_cancel = Annuler -m.downloading_java = T\u00e9l\u00e9chargement en cours de la Machine Virtuelle Java -m.unpacking_java = D\u00e9compression en cours de la Machine Virtuelle Java +m.downloading_java = Téléchargement en cours de la Machine Virtuelle Java +m.unpacking_java = Décompression en cours de la Machine Virtuelle Java -m.resolving = R\u00e9solution des t\u00e9l\u00e9chargements en cours -m.downloading = T\u00e9l\u00e9chargement des donn\u00e9es en cours -m.failure = \u00c9chec du t\u00e9l\u00e9chargement: {0} +m.resolving = Résolution des téléchargements en cours +m.downloading = Téléchargement des données en cours +m.failure = Échec du téléchargement: {0} -m.checking = V\u00e9rification de la mise-\u00e0-jour en cours +m.checking = Vérification de la mise-à-jour en cours m.validating = Validation en cours m.patching = Modification en cours m.launching = Lancement en cours -m.patch_notes = Notes de mise-\u00e0-jour +m.patch_notes = Notes de mise-à-jour -m.complete = Complet \u00e0 {0}% +m.complete = Complet à {0}% m.remain = {0} restant -m.updating_metadata = T\u00e9l\u00e9chargement des fichiers de contr\u00f4les en cours +m.updating_metadata = Téléchargement des fichiers de contrôles en cours -m.init_failed = Notre fichier de configuration est perdu ou corrompu. T\u00e9l\u00e9chargement \ +m.init_failed = Notre fichier de configuration est perdu ou corrompu. Téléchargement \ d'une nouvelle copie en cours ... -m.java_download_failed = Impossible de t\u00e9l\u00e9charger automatiquement la \ - version de Java n\u00e9cessaire.\n\n\ - Veuillez vous rendre sur www.java.com et t\u00e9l\u00e9charger et installer la version \ - la plus r\u00e9cente de Java, avant d'ex\u00e9cuter l'application \u00e0 nouveau. +m.java_download_failed = Impossible de télécharger automatiquement la \ + version de Java nécessaire.\n\n\ + Veuillez vous rendre sur www.java.com et télécharger et installer la version \ + la plus récente de Java, avant d'exécuter l'application à nouveau. -m.java_unpack_failed = Impossible de d\u00e9compresser la version de \ - Java n\u00e9cessaire. Veuillez v\u00e9rifier que vous avez au moins 100 MB d'espace libre \ - sur votre disque dur puis tenter d'ex\u00e9cuter l'application \u00e0 nouveau.\n\n\ - Si le probl\u00e8me persiste, rendez vous www.java.com et t\u00e9l\u00e9chargez et \ - installez la version plus r\u00e9cente de Java puis essayez de nouveau. +m.java_unpack_failed = Impossible de décompresser la version de \ + Java nécessaire. Veuillez vérifier que vous avez au moins 100 MB d'espace libre \ + sur votre disque dur puis tenter d'exécuter l'application à nouveau.\n\n\ + Si le problème persiste, rendez vous www.java.com et téléchargez et \ + installez la version plus récente de Java puis essayez de nouveau. -m.unable_to_repair = Impossible de t\u00e9l\u00e9charger les fichiers n\u00e9cessaires apr\u00e8s \ - cinq tentatives. Vous pouvez tenter d'ex\u00e9cuter l'application \u00e0 nouveau, mais il est \ - possible qu'une d\u00e9sinstallation / r\u00e9installation soit n\u00e9cessaire. +m.unable_to_repair = Impossible de télécharger les fichiers nécessaires après \ + cinq tentatives. Vous pouvez tenter d'exécuter l'application à nouveau, mais il est \ + possible qu'une désinstallation / réinstallation soit nécessaire. -m.unknown_error = Une erreur inconnue a fait \u00e9chouer le lancement de l'application. \ +m.unknown_error = Une erreur inconnue a fait échouer le lancement de l'application. \ Veuillez visiter\n{0} pour plus d'informations. -m.init_error = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause de l'erreur \ +m.init_error = Le lancement de l'application a échoué à cause de l'erreur \ suivante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations. -m.readonly_error = Le r\u00e9pertoire d'installation de cette application: \ - \n{0}\nest en lecture seule. Veuillez installer l'application dans un r\u00e9pertoire avec \ - un acc\u00e8s en \u00e9criture. +m.readonly_error = Le répertoire d'installation de cette application: \ + \n{0}\nest en lecture seule. Veuillez installer l'application dans un répertoire avec \ + un accès en écriture. -m.missing_resource = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause d'une \ +m.missing_resource = Le lancement de l'application a échoué à cause d'une \ ressource manquante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations. m.insufficient_permissions_error = Vous n'avez pas accepter la signature \ - num\u00e9rique de cette application. Si vous souhaitez ex\u00e9cuter cette application, vous \ - devez accepter sa signature num\u00e9rique.\n\nAfin de le faire, vous devez quitter votre \ - navigateur, le red\u00e9marrer, retourner \u00e0 cette page puis relancer l'application. \ - Une fois la bo\u00eete de dialogue de s\u00e9curit\u00e9 affich\u00e9e, cliquez sur le bouton \ - pour accepter la signature num\u00e9rique et accorder les permissions n\u00e9cessaires au bon \ + numérique de cette application. Si vous souhaitez exécuter cette application, vous \ + devez accepter sa signature numérique.\n\nAfin de le faire, vous devez quitter votre \ + navigateur, le redémarrer, retourner à cette page puis relancer l'application. \ + Une fois la boîte de dialogue de sécurité affichée, cliquez sur le bouton \ + pour accepter la signature numérique et accorder les permissions nécessaires au bon \ fonctionnement de l'application. -m.corrupt_digest_signature_error = Nous ne pouvons pas v\u00e9rifier la signature num\u00e9rique \ - de l'application.\nVeuillez v\u00e9rifier que vous lancez l'application \ndepuis \ +m.corrupt_digest_signature_error = Nous ne pouvons pas vérifier la signature numérique \ + de l'application.\nVeuillez vérifier que vous lancez l'application \ndepuis \ la bonne adresse internet. m.default_install_error = la section de support du site m.another_getdown_running = Plusieurs instances d'installation de cette \ - application sont d\u00e9j\u00e0 en cours d'ex\u00e9cution. Cette instance va s'arr\u00eater \ + application sont déjà en cours d'exécution. Cette instance va s'arrêter \ afin de permettre aux autres d'aboutir. -m.applet_stopped = L'appelet Getdown a \u00e9t\u00e9 stopp\u00e9e. - # application/digest errors m.missing_appbase = Le fichier de configuration ne contient pas 'appbase'. -m.invalid_version = Le fichier de configuration sp\u00e9cifie une version invalide. -m.invalid_appbase = Le fichier de configuration sp\u00e9cifie un 'appbase' invalide. +m.invalid_version = Le fichier de configuration spécifie une version invalide. +m.invalid_appbase = Le fichier de configuration spécifie un 'appbase' invalide. m.missing_class = Le fichier de configuration ne contient pas la classe de l'application. -m.missing_code = Le fichier de configuration ne sp\u00e9cifie aucune ressource de code. +m.missing_code = Le fichier de configuration ne spécifie aucune ressource de code. m.invalid_digest_file = Le fichier digest est invalide. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties index 33b3260ce..aea9e9017 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties @@ -1,7 +1,5 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown Italian translation messages m.abort_title = Annullare l'installazione? m.abort_confirm = Sei sicuro di voler annullare l'installazione? \ @@ -22,7 +20,11 @@ m.configure_proxy = Impossibile collegarsi al server per \ questo potrebbe non essere stato riconosciuto automaticamente. Se conosci le \ tue impostazioni del proxy, puoi inserirle di seguito. -m.proxy_extra = Se sei sicuro di non usare proxy \ +m.update_proxy_auth = Combinazione User/Password salvata per il proxy non è valida \ + oppure obsoleta. \ +

Perfavore inserire una combinazione User/Password valida. + +m.proxy_extra = Se sei sicuro di non usare proxy \ potrebbe essere un problema di internet o di collegamento con il server. \ In questo caso puoi annullare e ripetere l'installazione più tardi. @@ -47,8 +49,6 @@ m.patching = Applico le patch m.launching = Avvio m.patch_notes = Note delle Patch -m.play_again = Avvia Nuovamente - m.complete = {0}% completato m.remain = {0} rimasto @@ -103,8 +103,6 @@ m.default_install_error = la sezione di supporto del sito m.another_getdown_running = E' già in esecuzione un'istanza del programma. \ Questa verrà chiusa. -m.applet_stopped = L'applet di Getdown è stata interrotta. - # application/digest errors m.missing_appbase = Il tag "appbase" è mancante. m.invalid_version = Il file di configurazione non contiene una versione valida (tag "version"). diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties index c344c16e0..f3538d0ac 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties @@ -1,107 +1,105 @@ # -# $Id$ -# -# Getdown translation messages - -m.abort_title = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f -m.abort_confirm = \u672c\u5f53\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f \ - \u5f8c\u3067\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u305f\u969b\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u518d\u958b\u3067\u304d\u307e\u3059\u3002 -m.abort_ok = \u4e2d\u6b62 -m.abort_cancel = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u7d9a\u884c - -m.detecting_proxy = \u81ea\u52d5\u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u5b9f\u884c\u4e2d - -m.configure_proxy = \u30b5\u30fc\u30d0\u306b\u63a5\u7d9a\u3067\u304d\u306a\u3044\u305f\u3081\u3001\u30b2\u30fc\u30e0\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306b \ - \u5931\u6557\u3057\u307e\u3057\u305f\u3002 \ -

  • \u30a6\u30a3\u30f3\u30c9\u30a6\u30ba\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u307e\u305f\u306f\u30ce\u30fc\u30c8\u30f3\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u304c \ - javaw.exe\u3092\u30d6\u30ed\u30c3\u30af\u3059\u308b\u3088\u3046\u8a2d\u5b9a\u3057\u3066\u3042\u308b\u5834\u5408\u306f\u3001\u30b2\u30fc\u30e0\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093\u3002 \u8a2d\u5b9a\u3092 \ - javaw.exe\u7d4c\u7531\u3067\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306b\u30a2\u30af\u30bb\u30b9\u3067\u304d\u308b\u3088\u3046\u306b\u5909\u66f4\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \u30b2\u30fc\u30e0\u3092\u518d\u8d77\u52d5 \ - \u3057\u305f\u5f8c\u3001\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u306e\u8a2d\u5b9a\u304b\u3089javaw.exe \u3092\u524a\u9664 \ - \u3057\u3066\u304f\u3060\u3055\u3044\uff08\u30b9\u30bf\u30fc\u30c8\u2192\u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d1\u30cd\u30eb\u2192\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\uff09\u3002
\ -

\u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u306e\u81ea\u52d5\u691c\u51fa\u304c\u3067\u304d\u307e\u305b\u3093\u3002\u304a\u4f7f\u3044\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u306f \ - \u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u3078\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u307e\u3059\u3002 \u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u306e\u8a73\u7d30\u304c \ - \u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u4e0b\u306b\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002 - -m.proxy_extra = \u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u4e00\u6642\u7684\u306a \ - \u30a4\u30f3\u30bf\u30fc\u30cd\u30c3\u30c8\u306e\u4e0d\u5177\u5408\u306b\u3088\u308a\u3001\u30b5\u30fc\u30d0\u3068\u4ea4\u4fe1\u3067\u304d\u306a\u3044\u72b6\u614b\u306b\u3042\u308b \ - \u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002 \u305d\u306e\u5834\u5408\u306f\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u3066\u3001 \ - \u5f8c\u307b\u3069\u6539\u3081\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 - -m.proxy_host = \u30d7\u30ed\u30ad\u30b7IP -m.proxy_port = \u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8 +# Getdown Japanese translation messages + +m.abort_title = インストールを中止しますか? +m.abort_confirm = 本当にインストールを中止しますか? \ + 後でアプリケーションを起動した際にインストールを再開できます。 +m.abort_ok = 中止 +m.abort_cancel = インストールの続行 + +m.detecting_proxy = 自動プロキシ設定実行中 + +m.configure_proxy = サーバに接続できないため、ゲームのダウンロードに \ + 失敗しました。 \ +

  • ウィンドウズファイアウォールまたはノートンインターネットセキュリティが \ + javaw.exeをブロックするよう設定してある場合は、ゲームをダウンロードできません。 設定を \ + javaw.exe経由でインターネットにアクセスできるように変更してください。 ゲームを再起動 \ + した後、ファイアウォールの設定からjavaw.exe を削除 \ + してください(スタート→コントロールパネル→ファイアウォール)。
\ +

プロキシ設定の自動検出ができません。お使いのコンピューターは \ + プロキシを使用してインターネットへアクセスしています。 プロキシ設定の詳細が \ + わかっている場合は、下に入力してください。 + +m.proxy_extra = プロキシを使用していない場合は、一時的な \ + インターネットの不具合により、サーバと交信できない状態にある \ + 可能性があります。 その場合はインストールをキャンセルして、 \ + 後ほど改めて実行してください。 + +m.proxy_host = プロキシIP +m.proxy_port = プロキシポート m.proxy_username = Username m.proxy_password = Password m.proxy_auth_required = Authentication required -m.proxy_ok = OK -m.proxy_cancel = \u30ad\u30e3\u30f3\u30bb\u30eb +m.proxy_ok = OK +m.proxy_cancel = キャンセル -m.downloading_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d -m.unpacking_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u89e3\u51cd\u4e2d +m.downloading_java = Javaバーチャルマシンのダウンロード中 +m.unpacking_java = Javaバーチャルマシンの解凍中 -m.resolving = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306e\u8a2d\u5b9a\u4e2d -m.downloading = \u30c7\u30fc\u30bf\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d -m.failure = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u5931\u6557\uff1a {0} +m.resolving = ダウンロードの設定中 +m.downloading = データのダウンロード中 +m.failure = ダウンロード失敗: {0} -m.checking = \u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u306e\u78ba\u8a8d\u4e2d -m.validating = \u8a8d\u8a3c\u4e2d -m.patching = \u4fee\u6b63\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5b9f\u884c\u4e2d -m.launching = \u5b9f\u884c\u4e2d +m.checking = アップデートの確認中 +m.validating = 認証中 +m.patching = 修正プログラムの実行中 +m.launching = 実行中 -m.complete = {0}\uff05\u5b8c\u4e86 -m.remain = \u3000\u6b8b\u308a{0} +m.complete = {0}%完了 +m.remain =  残り{0} -m.updating_metadata = \u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d +m.updating_metadata = コントロールファイルのダウンロード中 -m.init_failed = \u74b0\u5883\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u306a\u3044\u304b\u3001\u307e\u305f\u306f\u58ca\u308c\u3066\u3044\u307e\u3059\u3002 \u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092 \ - \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d\u2026 +m.init_failed = 環境設定ファイルが存在しないか、または壊れています。 新バージョンを \ + ダウンロード中… -m.java_download_failed = \u304a\u4f7f\u3044\u306e\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u30fc\u306b\u3001Java\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u6700\u65b0 \ - \u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u81ea\u52d5\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\n \ - www.java.com \u304b\u3089\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u624b\u52d5\u3067\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u3001 \ - \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.java_download_failed = お使いのコンピューターに、Javaプログラムの最新 \ + バージョンを自動インストールできませんでした。\n\n \ + www.java.com から最新バージョンを手動でダウンロードして、 \ + 再度アプリケーションを起動してください。 -m.java_unpack_failed = Java\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u89e3\u51cd \ - \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \u30cf\u30fc\u30c9\u30c9\u30e9\u30a4\u30d6\u306e\u30e1\u30e2\u30ea\u304c100MB\u4ee5\u4e0a\u3042\u308b\u3053\u3068\u3092\u78ba\u8a8d\u3057\u3066\u304b\u3089 \ - \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n \ - \u554f\u984c\u304c\u89e3\u6c7a\u3057\u306a\u3044\u5834\u5408\u306f\u3001www.java.com \u304b\u3089Java\u306e\u6700\u65b0\u30d0\u30fc\u30b8\u30e7\u30f3\u3092 \ - \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u304b\u3089\u3001\u518d\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002 +m.java_unpack_failed = Javaのアップデートバージョンが解凍 \ + できませんでした。 ハードドライブのメモリが100MB以上あることを確認してから \ + 再度アプリケーションを起動してください。\n\n \ + 問題が解決しない場合は、www.java.com からJavaの最新バージョンを \ + ダウンロードしてから、再度お試しください。 -m.unable_to_repair = 5\u56de\u8a66\u884c\u3057\u307e\u3057\u305f\u304c\u3001\u5fc5\u8981\u306a\u30d5\u30a1\u30a4\u30eb\u3092\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9 \ - \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \u5f8c\u307b\u3069\u6539\u3081\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 \ - \u518d\u5ea6\u5931\u6557\u3057\u305f\u5834\u5408\u306f\u3001\u30a2\u30f3\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5f8c\u306b\u518d\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.unable_to_repair = 5回試行しましたが、必要なファイルをダウンロード \ + できませんでした。 後ほど改めてアプリケーションを実行してください。 \ + 再度失敗した場合は、アンインストール後に再インストールしてください。 -m.unknown_error = \u539f\u56e0\u4e0d\u660e\u306e\u30a8\u30e9\u30fc\u306b\u3088\u308a\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c \ - \u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \u89e3\u6c7a\u65b9\u6cd5\u3092\n{0}\u3067 \ - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 -m.init_error = \u6b21\u306e\u30a8\u30e9\u30fc\u306b\u3088\u308a\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093 \ - \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067 \ - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.unknown_error = 原因不明のエラーにより、アプリケーションが \ + 実行できませんでした。 解決方法を\n{0}で \ + 確認してください。 +m.init_error = 次のエラーによりアプリケーションを実行できません \ + でした。\n{0}\n\n対処方法を\n{1}で \ + 確認してください。 -m.readonly_error = \u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3055\u308c\u305f\u30d5\u30a9\u30eb\u30c0\u306f \ - \n{0}\n\u8aad\u307f\u53d6\u308a\u5c02\u7528\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059\u3002 \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u66f8\u304d\u8fbc\u307f\u304c\u3067\u304d\u308b\u30d5\u30a9\u30eb\u30c0\u306b \ - \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.readonly_error = このアプリケーションがインストールされたフォルダは \ + \n{0}\n読み取り専用に設定されています。 アプリケーションを書き込みができるフォルダに \ + インストールしてください。 -m.missing_resource = \u30ea\u30bd\u30fc\u30b9\u4e0d\u660e\u306e\u305f\u3081\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3067\u304d\u307e\u305b\u3093 \ - \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067\u78ba\u8a8d \ - \u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.missing_resource = リソース不明のためアプリケーションを実行できません \ + でした。\n{0}\n\n対処方法を\n{1}で確認 \ + してください。 -m.insufficient_permissions_error = \u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u62d2\u5426 \ - \u3055\u308c\u307e\u3057\u305f\u3002 \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3059\u308b\u5834\u5408\u306f\u3001\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u306e\u627f\u8a8d\u304c \ - \u5fc5\u8981\u3067\u3059\u3002\n\n\u627f\u8a8d\u306b\u306f\u3001\u30d6\u30e9\u30a6\u30b6\u3092\u9589\u3058\u3066\u304b\u3089\u518d\u5ea6\u958b\u304d\u3001 \ - \u672c\u30db\u30fc\u30e0\u30da\u30fc\u30b8\u3092\u518d\u8868\u793a\u3057\u3066\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u518d\u5ea6\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044 \u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u306e \ - \u8b66\u544a\u304c\u8868\u793a\u3055\u308c\u305f\u6642\u306f\u3001\u5b9f\u884c\u3092\u30af\u30ea\u30c3\u30af\u3057\u3066\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u3092\u627f\u8a8d\u3057\u3001 \ - \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.insufficient_permissions_error = このアプリケーションのデジタル署名が拒否 \ + されました。 アプリケーションを実行する場合は、デジタル署名の承認が \ + 必要です。\n\n承認には、ブラウザを閉じてから再度開き、 \ + 本ホームページを再表示してアプリケーションを再度実行してください セキュリティの \ + 警告が表示された時は、実行をクリックしてデジタル署名を承認し、 \ + アプリケーションを実行してください。 -m.corrupt_digest_signature_error = \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u8a8d\u8a3c \ - \u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\n\u6307\u5b9a\u30db\u30fc\u30e0\u30da\u30fc\u30b8\u304b\u3089\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u3044\u308b\u304b\n \ - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002 +m.corrupt_digest_signature_error = アプリケーションのデジタル署名が認証 \ + できませんでした。\n指定ホームページからアプリケーションを実行しているか\n \ + 確認してください。 -m.default_install_error = \u30db\u30fc\u30e0\u30da\u30fc\u30b8\u3067\u306e\u30b5\u30dd\u30fc\u30c8\u8868\u793a +m.default_install_error = ホームページでのサポート表示 # application/digest errors -m.missing_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eappbase\u304c\u4e0d\u660e\u3067\u3059\u3002 -m.invalid_version = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306f\u7121\u52b9\u306a\u30d0\u30fc\u30b8\u30e7\u30f3\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002 -m.invalid_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u306aappbase\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002 -m.missing_class = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u30af\u30e9\u30b9\u304c\u4e0d\u660e\u3067\u3059\u3002 -m.missing_code = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3067\u30b3\u30fc\u30c9\u30ea\u30bd\u30fc\u30b9\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002 -m.invalid_digest_file = \u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u3067\u3059\u3002 +m.missing_appbase = 設定ファイルのappbaseが不明です。 +m.invalid_version = 設定ファイルは無効なバージョンを指定しています。 +m.invalid_appbase = 設定ファイルが無効なappbaseを指定しています。 +m.missing_class = 設定ファイルのアプリケーションクラスが不明です。 +m.missing_code = 設定ファイルでコードリソースが指定されていません。 +m.invalid_digest_file = ダイジェストファイルが無効です。 diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties index 3f8a47f35..05700d363 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties @@ -1,102 +1,96 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown Korean translation messages -m.abort_title = \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? -m.abort_confirm = \uC815\uB9D0\uB85C \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \ - \uB098\uC911\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC2E4\uD589\uD558\uC5EC \uC124\uCE58\uB97C \uC7AC\uAC1C\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. -m.abort_ok = \uC911\uC9C0 -m.abort_cancel = \uACC4\uC18D\uD558\uC5EC \uC124\uCE58 +m.abort_title = 설치를 취소하시겠습니까? +m.abort_confirm = 정말로 설치를 취소하시겠습니까? \ + 나중에 어플리케이션을 실행하여 설치를 재개하여 주십시오. +m.abort_ok = 중지 +m.abort_cancel = 계속하여 설치 -m.detecting_proxy = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4 +m.detecting_proxy = 자동 프록시를 설정을 시도 -m.configure_proxy = \uAC8C\uC784 \uB370\uC774\uD130\uB97C \uBC1B\uAE30 \uC704\uD55C \uC11C\uBC84 \uC811\uC18D\uC5D0 \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4.\ -

  • \uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD \uB610\uB294 \uB178\uD134 \uC778\uD130\uB137 \uC2DC\uD050\uB9AC\uD2F0\uAC00 javaw.exe\uC774 \uC124\uC815\uC5D0\uC11C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC744 \uACBD\uC6B0, \ - \uAC8C\uC784 \uB370\uC774\uD130\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \ - javaw.exe\uAC00 \uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD560 \uC218 \uC788\uB3C4\uB85D \uC124\uC815\uC744 \uBCC0\uACBD\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \ - \uAC8C\uC784\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD55C \uD6C4, \uBC29\uD654\uBCBD \uC124\uC815\uC5D0\uC11C javaw.exe\uB97C \uC0AD\uC81C\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \ - ( \uC2DC\uC791 -> \uC81C\uC5B4\uD310 -> \uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD )
\ -

\uCEF4\uD4E8\uD130\uAC00 \uD504\uB85D\uC2DC \uC11C\uBC84\uB97C \uD1B5\uD574 \uC778\uD130\uB137\uC5D0 \uC5F0\uACB0\uB418\uC5B4 \uC788\uB2E4\uBA74, \uD504\uB85D\uC2DC \uC124\uC815\uC758 \uC790\uB3D9 \uAD6C\uC131\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC73C\uBBC0\uB85C, \ - \uC0AC\uC6A9\uD558\uB294 \uD504\uB85D\uC2DC \uC124\uC815\uC744 \uC54C\uACE0 \uC788\uC744 \uACBD\uC6B0 \uC544\uB798\uC5D0 \uC785\uB825\uD558\uC5EC \uC8FC\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4. +m.configure_proxy = 게임 데이터를 받기 위한 서버 접속에 실패하였습니다.\ +

  • 윈도우 방화벽 또는 노턴 인터넷 시큐리티가 javaw.exe이 설정에서 차단되어 있을 경우, \ + 게임 데이터를 다운로드 할 수 없습니다. \ + javaw.exe가 인터넷 연결을 할 수 있도록 설정을 변경하여 주십시오. \ + 게임을 다시 실행한 후, 방화벽 설정에서 javaw.exe를 삭제하여 주십시오. \ + ( 시작 -> 제어판 -> 윈도우 방화벽 )
\ +

컴퓨터가 프록시 서버를 통해 인터넷에 연결되어 있다면, 프록시 설정의 자동 구성을 사용할 수 없으므로, \ + 사용하는 프록시 설정을 알고 있을 경우 아래에 입력하여 주시길 바랍니다. -m.proxy_extra = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4 +m.proxy_extra = 자동 프록시를 설정을 시도 -m.proxy_host = \uD504\uB85D\uC2DC IP -m.proxy_port = \uD504\uB85D\uC2DC \uD3EC\uD2B8 +m.proxy_host = 프록시 IP +m.proxy_port = 프록시 포트 m.proxy_username = Username m.proxy_password = Password m.proxy_auth_required = Authentication required m.proxy_ok = OK -m.proxy_cancel = \uCDE8\uC18C - -m.downloading_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uB2E4\uC6B4\uB85C\uB4DC \uC911 -m.unpacking_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uC555\uCD95\uC744 \uD574\uC81C\uD558\uB294 \uC911 - -m.resolving = \uB2E4\uC6B4\uB85C\uB4DC \uBD84\uC11D \uC911 -m.downloading = \uB370\uC774\uD130 \uB2E4\uC6B4\uB85C\uB4DC \uC911 -m.failure = \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: {0} +m.proxy_cancel = 취소 -m.checking = \uC5C5\uB370\uC774\uD2B8 \uCCB4\uD06C -m.validating = \uC720\uD6A8\uC131 \uAC80\uC0AC \uC911 -m.patching = \uD328\uCE58 \uC911 -m.launching = \uC2E4\uD589 \uC911 +m.downloading_java = 자바 가상 머신(JVM) 다운로드 중 +m.unpacking_java = 자바 가상 머신(JVM) 압축을 해제하는 중 -m.patch_notes = \uD328\uCE58 \uB178\uD2B8 -m.play_again = \uB2E4\uC2DC \uC2E4\uD589 +m.resolving = 다운로드 분석 중 +m.downloading = 데이터 다운로드 중 +m.failure = 다운로드 실패: {0} -m.complete = {0}% \uC644\uB8CC -m.remain = {0} \uB0A8\uC74C +m.checking = 업데이트 체크 +m.validating = 유효성 검사 중 +m.patching = 패치 중 +m.launching = 실행 중 -m.updating_metadata = \uCEE8\uD2B8\uB864 \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911 +m.patch_notes = 패치 노트 +m.complete = {0}% 완료 +m.remain = {0} 남음 -m.init_failed = \uC124\uC815 \uD30C\uC77C\uC774 \uB204\uB77D\uB418\uC5C8\uAC70\uB098 \uBCC0\uD615\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \ - \uC0C8\uB85C\uC6B4 \uBCF5\uC0AC\uBCF8\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911\uC785\uB2C8\uB2E4... +m.updating_metadata = 컨트롤 파일을 다운로드 중 -m.java_download_failed = \uC774 \uCEF4\uD4E8\uD130\uC5D0 \uD544\uC694\uD55C \uC0C8\uB85C\uC6B4 \uBC84\uC804\uC758 \uC790\uBC14\uB97C \uC790\uB3D9\uC73C\uB85C \uB2E4\uC6B4\uB85C\uB4DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n\n\ - \uC790\uBC14 \uC6F9\uC0AC\uC774\uD2B8(www.java.com)\uB85C \uAC00\uC11C \uCD5C\uC2E0\uC758 \uC790\uBC14\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uBC1B\uC73C\uC2E0 \uD6C4, \ - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624. +m.init_failed = 설정 파일이 누락되었거나 변형되었습니다. \ + 새로운 복사본을 다운로드 중입니다... -m.java_unpack_failed = \uC5C5\uB370\uC774\uD2B8\uB41C \uBC84\uC804\uC758 \uC790\uBC14\uC758 \uC555\uCD95\uC744 \uD480 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \ - \uD558\uB4DC\uB4DC\uB77C\uC774\uBE0C\uC5D0 \uCD5C\uC18C\uD55C 100MB\uC758 \uC6A9\uB7C9\uC744 \uD655\uBCF4\uD55C \uC774\uD6C4, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.\n\n\ - \uB9CC\uC57D \uBB38\uC81C\uAC00 \uD574\uACB0\uB418\uC9C0 \uC54A\uB294\uB2E4\uBA74, \uC790\uBC14 \uC6F9\uC0AC\uC774\uD2B8(www.java.com)\uB85C \uAC00\uC11C \uCD5C\uC2E0\uC758 \uC790\uBC14\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uBC1B\uC73C\uC2E0 \uD6C4, \ - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624. +m.java_download_failed = 이 컴퓨터에 필요한 새로운 버전의 자바를 자동으로 다운로드할 수 없습니다.\n\n\ + 자바 웹사이트(www.java.com)로 가서 최신의 자바를 다운로드 받으신 후, \ + 어플리케이션을 다시 실행해 주십시오. -m.unable_to_repair = \uB2E4\uC12F\uBC88\uC758 \uC2DC\uB3C4\uC5D0\uB3C4 \uD544\uC694\uD55C \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \ - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2DC\uC791\uD574\uBCF4\uC2DC\uACE0, \uADF8\uB798\uB3C4 \uB2E4\uC6B4\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD55C\uB2E4\uBA74, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC81C\uAC70\uD55C \uD6C4, \uB2E4\uC2DC \uC2E4\uD589\uD574\uBCF4\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. +m.java_unpack_failed = 업데이트된 버전의 자바의 압축을 풀 수 없습니다. \ + 하드드라이브에 최소한 100MB의 용량을 확보한 이후, 어플리케이션을 다시 실행해 주십시오.\n\n\ + 만약 문제가 해결되지 않는다면, 자바 웹사이트(www.java.com)로 가서 최신의 자바를 다운로드 받으신 후, \ + 어플리케이션을 다시 실행해 주십시오. -m.unknown_error = \uBCF5\uAD6C\uB420 \uC218 \uC5C6\uB294 \uC624\uB958\uB85C \uC778\uD558\uC5EC \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \ - \n{0}\uC5D0 \uB300\uD55C \uBCF5\uAD6C \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4. +m.unable_to_repair = 다섯번의 시도에도 필요한 파일을 다운로드하지 못했습니다. \ + 어플리케이션을 다시 시작해보시고, 그래도 다운로드에 실패한다면, 어플리케이션을 제거한 후, 다시 실행해보시기 바랍니다. -m.init_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC544\uB798\uC640 \uAC19\uC740 \uC5D0\uB7EC\uB85C \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC5D0\uB7EC:\ - \n{0}\n\n{1}\uC5D0 \uB300\uD55C \uBB38\uC81C \uD574\uACB0 \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4. +m.unknown_error = 복구될 수 없는 오류로 인하여 어플리케이션의 실행이 중단되었습니다. \ + \n{0}에 대한 복구 방법을 찾기 위해서 방문하시길 바랍니다. -m.readonly_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC124\uCE58\uB41C \uB514\uB809\uD1A0\uB9AC: \ - \n{0}\n\uAC00 \uC77D\uAE30 \uC804\uC6A9\uC785\uB2C8\uB2E4. \uC77D\uAE30 \uAD8C\uD55C\uC774 \uC2B9\uC778\uB41C \uB809\uD1A0\uB9AC\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC124\uCE58\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4. +m.init_error = 어플리케이션이 아래와 같은 에러로 실행이 중단되었습니다. 에러:\ + \n{0}\n\n{1}에 대한 문제 해결 방법을 찾기 위해서 방문하시길 바랍니다. -m.missing_resource = \uB9AC\uC18C\uC2A4\uC758 \uC190\uC2E4\uB85C \uC778\uD558\uC5EC \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. : \ - \n{0}\n\n{1}\uC5D0 \uB300\uD55C \uBB38\uC81C \uD574\uACB0 \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4. +m.readonly_error = 어플리케이션이 설치된 디렉토리: \ + \n{0}\n가 읽기 전용입니다. 읽기 권한이 승인된 렉토리에 어플리케이션을 설치하시길 바랍니다. -m.insufficient_permissions_error = \uC774 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \ - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC2E4\uD589\uD558\uAE30 \uC704\uD574\uC11C \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \ - \n\n\uADF8\uB9AC\uACE0 \uB098\uC11C \uC6F9 \uBE0C\uB77C\uC6B0\uC800\uB97C \uB2EB\uACE0 \uB2E4\uC2DC \uC2DC\uC791\uD558\uC5EC \uC6F9\uD398\uC774\uC9C0\uB85C \uB3CC\uC544\uC640 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uC7AC\uC2DC\uC791\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. \ - \uBCF4\uC548\uC5D0 \uB300\uD55C \uB300\uD654\uC0C1\uC790\uAC00 \uBCF4\uC774\uBA74, \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC5D0 \uB300\uD55C \uD655\uC778 \uBC84\uD2BC\uC744 \uD074\uB9AD\uD558\uACE0, \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC2E4\uD589\uB418\uAE30 \uC704\uD55C \ - \uAD8C\uD55C\uC744 \uBD80\uC5EC\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. +m.missing_resource = 리소스의 손실로 인하여 어플리케이션의 실행이 중단되었습니다. : \ + \n{0}\n\n{1}에 대한 문제 해결 방법을 찾기 위해서 방문하시길 바랍니다. -m.corrupt_digest_signature_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC758 \uB514\uC9C0\uD0C8 \uC11C\uBA85\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.\n \ - \uC62C\uBC14\uB978 \uC6F9\uC0AC\uC774\uD2B8\uC5D0\uC11C \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC2E4\uD589\uB418\uACE0 \uC788\uB294 \uC9C0 \uD655\uC778\uBC14\uB78D\uB2C8\uB2E4. +m.insufficient_permissions_error = 이 어플리케이션의 디지탈 서명을 확인하지 않았습니다. \ + 어플리케이션을 실행하기 위해서 디지탈 서명을 확인하여 주십시오. \ + \n\n그리고 나서 웹 브라우저를 닫고 다시 시작하여 웹페이지로 돌아와 어플리케이션을 재시작해주시기 바랍니다. \ + 보안에 대한 대화상자가 보이면, 디지탈 서명에 대한 확인 버튼을 클릭하고, 어플리케이션이 실행되기 위한 \ + 권한을 부여해주시기 바랍니다. -m.default_install_error = \uC6F9\uC0AC\uC774\uD2B8\uC758 \uC9C0\uC6D0 \uBA54\uB274(support section)\uB97C \uD655\uC778\uD558\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4. +m.corrupt_digest_signature_error = 어플리케이션의 디지탈 서명을 확인할 수 없습니다.\n \ + 올바른 웹사이트에서 어플리케이션이 실행되고 있는 지 확인바랍니다. -m.another_getdown_running = \uC774 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158 \uC778\uC2A4\uD1A8\uB7EC\uC758 \uB2E4\uC911 \uC778\uC2A4\uD134\uC2A4\uAC00 \uC2E4\uD589\uC911\uC785\uB2C8\uB2E4. \ - \uD558\uB098\uAC00 \uC644\uB8CC\uB420 \uB54C\uAE4C\uC9C0 \uC911\uB2E8\uB429\uB2C8\uB2E4. +m.default_install_error = 웹사이트의 지원 메뉴(support section)를 확인하시기 바랍니다. -m.applet_stopped = Getdown \uC560\uD50C\uB9BF \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4. +m.another_getdown_running = 이 어플리케이션 인스톨러의 다중 인스턴스가 실행중입니다. \ + 하나가 완료될 때까지 중단됩니다. # application/digest errors -m.missing_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 'appbase' \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. -m.invalid_version = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C \uBC84\uC804\uC774 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. -m.invalid_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C 'appbase'\uAC00 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. -m.missing_class = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158 \uD074\uB798\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. -m.missing_code = \uC124\uC815 \uD30C\uC77C\uC5D0 \uB9AC\uC18C\uC2A4\uC5D0 \uB300\uD55C \uCF54\uB4DC\uAC00 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. -m.invalid_digest_file = \uB2E4\uC774\uC81C\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4. +m.missing_appbase = 설정 파일에 'appbase' 가 없습니다. +m.invalid_version = 설정 파일에 잘못된 버전이 명시되어 있습니다. +m.invalid_appbase = 설정 파일에 잘못된 'appbase'가 명시되어 있습니다. +m.missing_class = 설정 파일에 어플리케이션 클래스가 없습니다. +m.missing_code = 설정 파일에 리소스에 대한 코드가 명시되어 있지 않습니다. +m.invalid_digest_file = 다이제스트 파일이 잘못되었습니다. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties index 47db91c90..e59ed20b2 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties @@ -1,118 +1,112 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown Portuguese translation messages -m.abort_title = Cancelar a instala\u00E7\u00E3o? -m.abort_confirm = Tem certeza que deseja cancelar a instala\u00E7\u00E3o? \ - Voc\u00EA pode continuar a instala\u00E7\u00E3o mais tarde, \ - basta executar a aplica\u00E7\u00E3o novamente. +m.abort_title = Cancelar a instalação? +m.abort_confirm = Tem certeza que deseja cancelar a instalação? \ + Você pode continuar a instalação mais tarde, \ + basta executar a aplicação novamente. m.abort_ok = Sair -m.abort_cancel = Continuar a instala\u00E7\u00E3o +m.abort_cancel = Continuar a instalação -m.detecting_proxy = Tentando detectar automaticamente as configura\u00E7\u00F5es de proxy +m.detecting_proxy = Tentando detectar automaticamente as configurações de proxy -m.configure_proxy = N\u00E3o foi poss\u00EDvel conectar aos nossos servidores para \ +m.configure_proxy = Não foi possível conectar aos nossos servidores para \ fazer o download dos dados. \ -

  • Se o Firewall do Windows ou o Norton Internet Security est\u00E1 configurado \ - para bloquear o programa javaw.exe n\u00E3o ser\u00E1 poss\u00EDvel realizar \ - o download. Voc\u00EA ter\u00E1 que permitir que o programa javaw.exe acesse \ - a internet. Voc\u00EA pode tentar executar o programa novamente, mas voc\u00EA precisa \ - remover o programa javaw.exe das configura\u00E7\u00F5es do firewall (Iniciar -> Painel \ +
    • Se o Firewall do Windows ou o Norton Internet Security está configurado \ + para bloquear o programa javaw.exe não será possível realizar \ + o download. Você terá que permitir que o programa javaw.exe acesse \ + a internet. Você pode tentar executar o programa novamente, mas você precisa \ + remover o programa javaw.exe das configurações do firewall (Iniciar -> Painel \ de controle -> Firewall do Windows).
    \ -

    Seu computador pode estar acessando a internet atrav\u00E9s de um proxy e n\u00E3o foi \ - capaz de detectar automaticamente as configura\u00E7\u00F5es de proxy. \ - Voc\u00EA pode informar esses dados abaixo. +

    Seu computador pode estar acessando a internet através de um proxy e não foi \ + capaz de detectar automaticamente as configurações de proxy. \ + Você pode informar esses dados abaixo. -m.proxy_extra = Se voc\u00EA tem certeza que n\u00E3o usa um proxy, ent\u00E3o pode ser \ - que exista um problema tempor\u00E1rio que est\u00E1 impedindo a comunica\u00E7\u00E3o \ - com os nossos servidores. Neste caso voc\u00EA pode cancelar e tentar instalar novamente \ +m.proxy_extra = Se você tem certeza que não usa um proxy, então pode ser \ + que exista um problema temporário que está impedindo a comunicação \ + com os nossos servidores. Neste caso você pode cancelar e tentar instalar novamente \ mais tarde. m.proxy_host = IP do Proxy m.proxy_port = Porta do Proxy -m.proxy_username = Nome de usu\u00e1rio +m.proxy_username = Nome de usuário m.proxy_password = Senha -m.proxy_auth_required = Autentifica\u00e7\u00e3o requerida +m.proxy_auth_required = Autentificação requerida m.proxy_ok = OK m.proxy_cancel = Cancelar -m.downloading_java = Fazendo o download da m\u00E1quina virtual Java -m.unpacking_java = Descompactando a m\u00E1quina virtual Java +m.downloading_java = Fazendo o download da máquina virtual Java +m.unpacking_java = Descompactando a máquina virtual Java m.resolving = Resolvendo downloads m.downloading = Transferindo dados m.failure = Download falhou: {0} -m.checking = Verificando atualiza\u00E7\u00F5es +m.checking = Verificando atualizações m.validating = Validando m.patching = Atualizando m.launching = Executando m.patch_notes = Corrigir notas -m.play_again = Jogar de novo - m.complete = {0}% completo m.remain = {0} Permanecer m.updating_metadata = Transferindo arquivos de controle -m.init_failed = Nosso arquivo de configura\u00E7\u00E3o est\u00E1 ausente ou corrompido. Tente \ - baixar uma nova c\u00F3pia... +m.init_failed = Nosso arquivo de configuração está ausente ou corrompido. Tente \ + baixar uma nova cópia... -m.java_download_failed = N\u00E3o conseguimos baixar automaticamente a\ - vers\u00E3o necess\u00E1ria do Java para o seu computador.\n\n\ - Por favor, acesse www.java.com, baixe e instale a \u00FAltima vers\u00E3o do \ +m.java_download_failed = Não conseguimos baixar automaticamente a\ + versão necessária do Java para o seu computador.\n\n\ + Por favor, acesse www.java.com, baixe e instale a última versão do \ Java, em seguida, tente executar o aplicativo novamente. -m.java_unpack_failed = N\u00E3o conseguimos descompactar uma vers\u00E3o atualizada do \ - Java. Por favor, certifique-se de ter pelo menos 100 MB de espa\u00E7o livre em seu \ - disco r\u00EDgido e tente executar o aplicativo novamente. \n\n\ - Se isso n\u00E3o resolver o problema, acesse www.java.com,baixe e \ - instale a \u00FAltima vers\u00E3o do Java e tente novamente. +m.java_unpack_failed = Não conseguimos descompactar uma versão atualizada do \ + Java. Por favor, certifique-se de ter pelo menos 100 MB de espaço livre em seu \ + disco rígido e tente executar o aplicativo novamente. \n\n\ + Se isso não resolver o problema, acesse www.java.com,baixe e \ + instale a última versão do Java e tente novamente. -m.unable_to_repair = N\u00E3o conseguimos baixar os arquivos necess\u00E1rios depois de \ - cinco tentativas. Voc\u00EA pode tentar executar o aplicativo novamente, mas se ele \ - falhar pode ser necess\u00E1rio desinstalar e reinstalar. +m.unable_to_repair = Não conseguimos baixar os arquivos necessários depois de \ + cinco tentativas. Você pode tentar executar o aplicativo novamente, mas se ele \ + falhar pode ser necessário desinstalar e reinstalar. -m.unknown_error = A aplica\u00E7\u00E3o falhou ao iniciar devido a algum erro estranho \ - do qual n\u00E3o conseguimos recuperar. Por favor, visite \n{0} para obter \ - informa\u00E7\u00F5es sobre como recuperar. -m.init_error = A aplica\u00E7\u00E3o falhou ao iniciar devido ao seguinte \ +m.unknown_error = A aplicação falhou ao iniciar devido a algum erro estranho \ + do qual não conseguimos recuperar. Por favor, visite \n{0} para obter \ + informações sobre como recuperar. +m.init_error = A aplicação falhou ao iniciar devido ao seguinte \ erro:\n{0}\n\nPor favor visite \n{1} para \ - informa\u00E7\u00F5es sobre como lidar com esse problema. + informações sobre como lidar com esse problema. -m.readonly_error =O diret\u00F3rio no qual este aplicativo est\u00E1 instalado: \ - \n{0}\n \u00E9 somente leitura. Por favor, instale o aplicativo em um diret\u00F3rio onde \ - voc\u00EA tem acesso de grava\u00E7\u00E3o. +m.readonly_error =O diretório no qual este aplicativo está instalado: \ + \n{0}\n é somente leitura. Por favor, instale o aplicativo em um diretório onde \ + você tem acesso de gravação. -m.missing_resource = A aplica\u00E7\u00E3o falhou ao iniciar devido a uma falta \ - de recurso:\n{0}\n\n Por favor, visite\n{1} para obter informa\u00E7\u00F5es sobre \ +m.missing_resource = A aplicação falhou ao iniciar devido a uma falta \ + de recurso:\n{0}\n\n Por favor, visite\n{1} para obter informações sobre \ como lidar com tal problema. -m.insufficient_permissions_error = Voc\u00EA n\u00E3o aceitou a assinatura digital \ - do aplicativo. Se voc\u00EA quiser executar o aplicativo, voc\u00EA ter\u00E1 que aceitar \ - a assinatura digital. \n\nPara fazer isso, voc\u00EA ter\u00E1 que sair do seu navegador, \ - reinici\u00E1-lo, e retornar a esta p\u00E1gina web para executar a aplica\u00E7\u00E3o. \ - Quando o di\u00E1logo de seguran\u00E7a aparecer, clique no bot\u00E3o para aceitar a \ - assinatura digital e conceder a este aplicativo os privil\u00E9gios necess\u00E1rios \ +m.insufficient_permissions_error = Você não aceitou a assinatura digital \ + do aplicativo. Se você quiser executar o aplicativo, você terá que aceitar \ + a assinatura digital. \n\nPara fazer isso, você terá que sair do seu navegador, \ + reiniciá-lo, e retornar a esta página web para executar a aplicação. \ + Quando o diálogo de segurança aparecer, clique no botão para aceitar a \ + assinatura digital e conceder a este aplicativo os privilégios necessários \ para executar. -m.corrupt_digest_signature_error = N\u00E3o conseguimos verificar a assinatura digital \ - do aplicativo.\nPor favor, verifique se voc\u00EA est\u00E1 utilizando o aplicativo \nde um \ +m.corrupt_digest_signature_error = Não conseguimos verificar a assinatura digital \ + do aplicativo.\nPor favor, verifique se você está utilizando o aplicativo \nde um \ site correto. -m.default_install_error = a se\u00E7\u00E3o de suporte do site - -m.another_getdown_running = V\u00E1rias inst\u00E2ncias desta aplica\u00E7\u00E3o \ - est\u00E3o em execu\u00E7\u00E3o. Esta ir\u00E1 parar e deixar outra completar suas atividades. +m.default_install_error = a seção de suporte do site -m.applet_stopped = Foi solicitado ao miniaplicativo GetDow que parasse de trabalhar. +m.another_getdown_running = Várias instâncias desta aplicação \ + estão em execução. Esta irá parar e deixar outra completar suas atividades. # application/digest errors -m.missing_appbase = O arquivo de configura\u00E7\u00E3o n\u00E3o possui o 'AppBase'. -m.invalid_version = O arquivo de configura\u00E7\u00E3o especifica uma vers\u00E3o inv\u00E1lida. -m.invalid_appbase = O arquivo de configura\u00E7\u00E3o especifica um 'AppBase' inv\u00E1lido. -m.missing_class = O arquivo de configura\u00E7\u00E3o n\u00E3o possui a classe de aplicativo. -m.missing_code = O arquivo de configura\u00E7\u00E3o n\u00E3o especifica um recurso de c\u00F3digo. -m.invalid_digest_file = O arquivo digest \u00E9 inv\u00E1lido. +m.missing_appbase = O arquivo de configuração não possui o 'AppBase'. +m.invalid_version = O arquivo de configuração especifica uma versão inválida. +m.invalid_appbase = O arquivo de configuração especifica um 'AppBase' inválido. +m.missing_class = O arquivo de configuração não possui a classe de aplicativo. +m.missing_code = O arquivo de configuração não especifica um recurso de código. +m.invalid_digest_file = O arquivo digest é inválido. diff --git a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties index 2c275437b..fa74fb293 100644 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties @@ -1,61 +1,57 @@ # -# $Id$ -# -# Getdown translation messages +# Getdown Chinese translation messages -m.detecting_proxy = \u641c\u5bfb\u4ee3\u7406\u670d\u52a1\u5668 +m.detecting_proxy = 搜寻代理服务器 -m.configure_proxy = \u6211\u4eec\u65e0\u6cd5\u8fde\u63a5\u5230\u670d\u52a1\u5668\u4e0b\u8f7d\u6e38\u620f\u6570\u636e\u3002\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e \ - \u60a8\u7684\u8ba1\u7b97\u673a\u662f\u901a\u8fc7\u4ee3\u7406\u670d\u52a1\u5668\u8fde\u63a5\u4e92\u8054\u7f51\u7684\uff0c\u5e76\u4e14\u6211\u4eec\u65e0\u6cd5\u81ea\u52a8\u83b7\u5f97\u4ee3\u7406\u670d\u52a1\u5668\u7684 \ - \u8bbe\u7f6e\u3002\u5982\u679c\u60a8\u77e5\u9053\u60a8\u4ee3\u7406\u670d\u52a1\u5668\u7684\u8bbe\u7f6e\uff0c\u60a8\u53ef\u4ee5\u5728\u4e0b\u9762\u8f93\u5165\u3002 +m.configure_proxy = 我们无法连接到服务器下载游戏数据。这可能是由于 \ + 您的计算机是通过代理服务器连接互联网的,并且我们无法自动获得代理服务器的 \ + 设置。如果您知道您代理服务器的设置,您可以在下面输入。 -m.proxy_extra = \u5982\u679c\u60a8\u786e\u5b9a\u60a8\u6ca1\u6709\u4f7f\u7528\u4ee3\u7406\u670d\u52a1\u5668\uff0c\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e\u6682\u65f6\u65e0\u6cd5 \ - \u8fde\u63a5\u5230\u4e92\u8054\u7f51\uff0c\u5bfc\u81f4\u65e0\u6cd5\u548c\u670d\u52a1\u5668\u901a\u8baf\u3002\u8fd9\u79cd\u60c5\u51b5\uff0c\u60a8\u53ef\u4ee5\u53d6\u6d88\uff0c\u7a0d\u5019\u518d\u91cd\u65b0\u5b89\u88c5\u3002

    \ - \u5982\u679c\u60a8\u65e0\u6cd5\u786e\u5b9a\u60a8\u662f\u5426\u4f7f\u7528\u4e86\u4ee3\u7406\u670d\u52a1\u5668\uff0c\u8bf7\u8bbf\u95ee\u6211\u4eec\u7f51\u7ad9\u4e2d\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c \ - \u4e86\u89e3\u5982\u4f55\u68c0\u6d4b\u60a8\u7684\u4ee3\u7406\u670d\u52a1\u5668\u8bbe\u7f6e\u3002 +m.proxy_extra = 如果您确定您没有使用代理服务器,这可能是由于暂时无法 \ + 连接到互联网,导致无法和服务器通讯。这种情况,您可以取消,稍候再重新安装。

    \ + 如果您无法确定您是否使用了代理服务器,请访问我们网站中的技术支持部份, \ + 了解如何检测您的代理服务器设置。 -m.proxy_host = \u4ee3\u7406\u670d\u52a1\u5668\u7684IP\u5730\u5740 -m.proxy_port = \u4ee3\u7406\u670d\u52a1\u5668\u7684\u7aef\u53e3\u53f7 +m.proxy_host = 代理服务器的IP地址 +m.proxy_port = 代理服务器的端口号 m.proxy_username = Username m.proxy_password = Password m.proxy_auth_required = Authentication required -m.proxy_ok = \u786e\u5b9a -m.proxy_cancel = \u53d6\u6d88 - -m.resolving = \u5206\u6790\u9700\u4e0b\u8f7d\u5185\u5bb9 -m.downloading = \u4e0b\u8f7d\u6570\u636e -m.failure = \u4e0b\u8f7d\u5931\u8d25: {0} - -m.checking = \u68c0\u67e5\u66f4\u65b0\u5185\u5bb9 -m.validating = \u786e\u8ba4 -m.patching = \u5347\u7ea7 -m.launching = \u542f\u52a8 +m.proxy_ok = 确定 +m.proxy_cancel = 取消 -m.complete = {0}% \u5b8c\u6210 -m.remain = {0} \u5269\u4f59\u65f6\u95f4 +m.resolving = 分析需下载内容 +m.downloading = 下载数据 +m.failure = 下载失败: {0} -m.updating_metadata = \u4e0b\u8f7d\u63a7\u5236\u6587\u4ef6 +m.checking = 检查更新内容 +m.validating = 确认 +m.patching = 升级 +m.launching = 启动 -m.init_failed = \u65e0\u6cd5\u627e\u5230\u914d\u7f6e\u6587\u4ef6\u6216\u5df2\u635f\u574f\u3002\u5c1d\u8bd5\u91cd\u65b0\u4e0b\u8f7d... +m.complete = {0}% 完成 +m.remain = {0} 剩余时间 -m.unable_to_repair = \u7ecf\u8fc75\u6b21\u5c1d\u8bd5\uff0c\u4f9d\u7136\u65e0\u6cd5\u4e0b\u8f7d\u6240\u9700\u7684\u6587\u4ef6\u3002\ -\u60a8\u53ef\u4ee5\u91cd\u65b0\u8fd0\u884c\u7a0b\u5e8f\uff0c\u4f46\u662f\u5982\u679c\u4f9d\u7136\u5931\u8d25\uff0c\u60a8\u53ef\u80fd\u9700\u8981\u53cd\u5b89\u88c5\u5e76\u91cd\u65b0\u5b89\u88c5\u3002 +m.updating_metadata = 下载控制文件 +m.init_failed = 无法找到配置文件或已损坏。尝试重新下载... -m.unknown_error = \u7531\u4e8e\u4e00\u4e9b\u65e0\u6cd5\u56de\u590d\u7684\u4e25\u91cd\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\u3002\ -\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u89e3\u51b3\u95ee\u9898\u3002 +m.unable_to_repair = 经过5次尝试,依然无法下载所需的文件。\ +您可以重新运行程序,但是如果依然失败,您可能需要反安装并重新安装。 -m.init_error = \u7531\u4e8e\u4e0b\u5217\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\uff1a\n{0}\n\n \ -\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u5904\u7406\u8fd9\u4e9b\u9519\u8bef\u3002 +m.unknown_error = 由于一些无法回复的严重错误,程序启动失败。\ +请访问我们的网站的技术支持部份,了解如何解决问题。 +m.init_error = 由于下列错误,程序启动失败:\n{0}\n\n \ +请访问我们的网站的技术支持部份,了解如何处理这些错误。 -m.missing_resource = \u7531\u4e8e\u65e0\u6cd5\u627e\u5230\u4e0b\u5217\u8d44\u6e90\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\uff1a\n{0}\n\n \ -\u8bf7\u8bbf\u95ee\u6211\u4eec\u7684\u7f51\u7ad9\u7684\u6280\u672f\u652f\u6301\u90e8\u4efd\uff0c\u4e86\u89e3\u5982\u4f55\u5904\u7406\u8fd9\u4e9b\u95ee\u9898\u3002 +m.missing_resource = 由于无法找到下列资源,程序启动失败:\n{0}\n\n \ +请访问我们的网站的技术支持部份,了解如何处理这些问题。 # application/digest errors -m.missing_appbase = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230 'appbase'\u3002 -m.invalid_version = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684\u7248\u672c\u3002 -m.invalid_appbase = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684 'appbase'\u3002 -m.missing_class = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u7a0b\u5e8f\u6587\u4ef6\u3002 -m.missing_code = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u6307\u5b9a\u7684\u8d44\u6e90\u3002 -m.invalid_digest_file = \u65e0\u6548\u7684\u914d\u7f6e\u6587\u4ef6\u3002 +m.missing_appbase = 配置文件中无法找到 'appbase'。 +m.invalid_version = 配置文件指定了无效的版本。 +m.invalid_appbase = 配置文件指定了无效的 'appbase'。 +m.missing_class = 配置文件中无法找到程序文件。 +m.missing_code = 配置文件中无法找到指定的资源。 +m.invalid_digest_file = 无效的配置文件。 diff --git a/getdown/src/getdown/lib/commons-compress-1.18.jar b/getdown/src/getdown/lib/commons-compress-1.18.jar deleted file mode 100644 index e401046b5..000000000 Binary files a/getdown/src/getdown/lib/commons-compress-1.18.jar and /dev/null differ diff --git a/getdown/src/getdown/mvn_cmd b/getdown/src/getdown/mvn_cmd deleted file mode 100644 index 0ce786ff8..000000000 --- a/getdown/src/getdown/mvn_cmd +++ /dev/null @@ -1 +0,0 @@ -mvn clean package -Dgetdown.host.whitelist="jalview.org,*.jalview.org" && cp launcher/target/getdown-launcher-1.8.3-SNAPSHOT.jar ../../../getdown/lib/getdown-launcher.jar && cp core/target/getdown-core-1.8.3-SNAPSHOT.jar ../../../getdown/lib/getdown-core-1.8.3-SNAPSHOT.jar && cp core/target/getdown-core-1.8.3-SNAPSHOT.jar ../../../j8lib/getdown-core.jar && cp core/target/getdown-core-1.8.3-SNAPSHOT.jar ../../../j11lib/getdown-core.jar diff --git a/getdown/src/getdown/pom.xml b/getdown/src/getdown/pom.xml index 78d67b0a5..ae1370dde 100644 --- a/getdown/src/getdown/pom.xml +++ b/getdown/src/getdown/pom.xml @@ -10,7 +10,7 @@ com.threerings.getdown getdown pom - 1.8.3-SNAPSHOT + 1.8.7-SNAPSHOT getdown An application installer and updater. @@ -35,6 +35,10 @@ + + UTF-8 + + scm:git:git://github.com/threerings/getdown.git scm:git:git@github.com:threerings/getdown.git @@ -47,28 +51,6 @@ ant - - - ej-technologies - https://maven.ej-technologies.com/repository - - - - - - - org.apache.commons - commons-compress - 1.18 - - - com.install4j - install4j-runtime - 7.0.11 - provided - - - @@ -83,6 +65,20 @@ aa555c46fc37d0 + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.0 + + true + public + + -Xdoclint:all + -Xdoclint:-missing + + + @@ -91,10 +87,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.7.0 - 1.8 - 1.8 + 1.7 + 1.7 true true true @@ -110,20 +106,12 @@ org.apache.maven.plugins maven-resources-plugin 3.0.2 - - UTF-8 - org.apache.maven.plugins - maven-javadoc-plugin - 3.0.0-M1 - - true - public - -Xdoclint:all -Xdoclint:-missing - + maven-gpg-plugin + 1.6 @@ -181,7 +169,6 @@ org.apache.maven.plugins maven-gpg-plugin - 1.6 sign-artifacts