1 diff --git a/getdown/src/getdown/CHANGELOG.md b/getdown/src/getdown/CHANGELOG.md
2 index 098651eb1..ec7a923e1 100644
3 --- a/getdown/src/getdown/CHANGELOG.md
4 +++ b/getdown/src/getdown/CHANGELOG.md
11 +* Reinstated env var support in `appbase` property.
13 +* Fixed issue with `myIpAddress()` in PAC proxy support.
15 +## 1.8.6 - June 4, 2019
17 +* Fixed issues with PAC proxy support: added `myIpAddress()`, fixed `dnsResolve()`, fixed crash
18 + when detecting PAC proxy.
20 +* Reverted env var support in `appbase` property. It's causing problems that need to be
23 +## 1.8.5 - May 29, 2019
25 +* Fixed issues with proxy information not getting properly passed through to app.
26 + Via [#216](//github.com/threerings/getdown/pull/216).
28 +* `appbase` and `latest` properties in `getdown.txt` now process env var subtitutions.
30 +* Added support for [Proxy Auto-config](https://en.wikipedia.org/wiki/Proxy_auto-config) via PAC
33 +* Proxy handling can now recover from credentials going out of date. It will detect the error and
34 + ask for updated credentials.
36 +* Added `try_no_proxy` system property. This instructs Getdown to always first try to run without a
37 + proxy, regardless of whether it has been configured to use a proxy in the past. And if it can run
38 + without a proxy, it does so for that session, but retains the proxy config for future sessions in
39 + which the proxy may again be needed.
41 +* Added `revalidate_policy` config to control when Getdown revalidates resources (by hashing them
42 + and comparing that hash to the values in `digest.txt`). The default, `after_update`, only
43 + validates resources after the app is updated. A new mode, `always`, validates resources prior to
44 + every application launch.
46 +## 1.8.4 - May 14, 2019
48 +* Added `verify_timeout` config to allow customization of the default (60 second) timeout during
49 + the resource verification process. Apparently in some pathological situations, this is needed.
50 + Woe betide the users who have to stare at an unmoving progress bar for more than 60 seconds.
51 + Via [#198](//github.com/threerings/getdown/pull/198)
52 + and [901682d](//github.com/threerings/getdown/commit/901682d).
54 +* Added `java_local_dir` config to allow custom location for Java if `java_location` is specified.
55 + Via [#206](//github.com/threerings/getdown/pull/206).
57 +* `messages_XX.properties` files are now all maintained in UTF-8 encoding and then converted to
58 + escaped ISO-8859-1 during the build process.
60 +* Resources and unpacked resources now support `.zip` files as well as `.jar` files.
61 + Via [#210](//github.com/threerings/getdown/pull/210).
63 +* Fixed issue when path to JVM contained spaces. Via [#214](//github.com/threerings/getdown/pull/214).
65 +## 1.8.3 - Apr 10, 2019
67 * Added support for `nresource` resources which must be jar files that contain native libraries.
68 Prior to launching the application, these resources will be unpacked and their contents added to
69 the `java.library.path` system property.
71 * When the app is updated to require a new version of the JVM, that JVM will be downloaded and used
72 - immediately during that app invocation (instead of one invocation later). Via PR#169.
73 + immediately during that app invocation (instead of one invocation later).
74 + Via [#169](//github.com/threerings/getdown/pull/169).
76 -* When a custom JVM is installed, old JVM files will be deleted prior to unpacking the new JVM. Via
78 +* When a custom JVM is installed, old JVM files will be deleted prior to unpacking the new JVM.
79 + Via [#170](//github.com/threerings/getdown/pull/170).
81 * Number of concurrent downloads now defaults to num-cores minus one. Though downloads are I/O
82 bound rather than CPU bound, this still turns out to be a decent default.
84 credentials supplied by the user. Otherwise they will be requested every time Getdown runs, which
85 is not a viable user experience.
87 +* The Getdown window can be now closed by pressing the `ESC` key.
88 + Via [#191](//github.com/threerings/getdown/pull/191).
90 +* If no `appdir` is specified via the command line or system property, the current working
91 + directory will be used as the `appdir`. Via [8d59367](//github.com/threerings/getdown/commit/8d59367)
93 +* A basic Russian translation has been added. Thanks [@sergiorussia](//github.com/sergiorussia)!
95 ## 1.8.2 - Nov 27, 2018
97 * Fixed a data corruption bug introduced at last minute into 1.8.1 release. Oops.
98 diff --git a/getdown/src/getdown/ant/.project-MOVED b/getdown/src/getdown/ant/.project-MOVED
99 deleted file mode 100644
100 index 097cb89db..000000000
101 --- a/getdown/src/getdown/ant/.project-MOVED
104 -<?xml version="1.0" encoding="UTF-8"?>
105 -<projectDescription>
106 - <name>getdown-ant</name>
107 - <comment></comment>
112 - <name>org.eclipse.jdt.core.javabuilder</name>
117 - <name>org.eclipse.m2e.core.maven2Builder</name>
123 - <nature>org.eclipse.jdt.core.javanature</nature>
124 - <nature>org.eclipse.m2e.core.maven2Nature</nature>
126 -</projectDescription>
127 diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs
128 deleted file mode 100644
129 index e9441bb12..000000000
130 --- a/getdown/src/getdown/ant/.settings/org.eclipse.core.resources.prefs
133 -eclipse.preferences.version=1
134 -encoding//src/main/java=UTF-8
135 -encoding/<project>=UTF-8
136 diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs
137 deleted file mode 100644
138 index 54e56721d..000000000
139 --- a/getdown/src/getdown/ant/.settings/org.eclipse.jdt.core.prefs
142 -eclipse.preferences.version=1
143 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
144 -org.eclipse.jdt.core.compiler.compliance=1.7
145 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
146 -org.eclipse.jdt.core.compiler.release=disabled
147 -org.eclipse.jdt.core.compiler.source=1.7
148 diff --git a/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs
149 deleted file mode 100644
150 index f897a7f1c..000000000
151 --- a/getdown/src/getdown/ant/.settings/org.eclipse.m2e.core.prefs
155 -eclipse.preferences.version=1
156 -resolveWorkspaceProjects=true
158 diff --git a/getdown/src/getdown/ant/pom.xml b/getdown/src/getdown/ant/pom.xml
159 index f8231aa2e..a72d95d87 100644
160 --- a/getdown/src/getdown/ant/pom.xml
161 +++ b/getdown/src/getdown/ant/pom.xml
164 <groupId>com.threerings.getdown</groupId>
165 <artifactId>getdown</artifactId>
166 - <version>1.8.3-SNAPSHOT</version>
167 + <version>1.8.7-SNAPSHOT</version>
170 <artifactId>getdown-ant</artifactId>
171 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
172 index 48cc8d426..76212ae89 100644
173 --- a/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java
174 +++ b/getdown/src/getdown/ant/src/main/java/com/threerings/getdown/tools/DigesterTask.java
175 @@ -7,16 +7,13 @@ package com.threerings.getdown.tools;
178 import java.io.IOException;
180 import java.security.GeneralSecurityException;
182 import org.apache.tools.ant.BuildException;
183 import org.apache.tools.ant.Task;
185 -import com.threerings.getdown.data.Digest;
188 - * An ant task used to create a <code>digest.txt</code> for a Getdown
189 + * An ant task used to create a {@code digest.txt} for a Getdown
190 * application deployment.
192 public class DigesterTask extends Task
193 diff --git a/getdown/src/getdown/core/.project-MOVED b/getdown/src/getdown/core/.project-MOVED
194 deleted file mode 100644
195 index 177252f5f..000000000
196 --- a/getdown/src/getdown/core/.project-MOVED
199 -<?xml version="1.0" encoding="UTF-8"?>
200 -<projectDescription>
201 - <name>getdown-core</name>
202 - <comment></comment>
207 - <name>org.eclipse.jdt.core.javabuilder</name>
212 - <name>org.eclipse.m2e.core.maven2Builder</name>
218 - <nature>org.eclipse.jdt.core.javanature</nature>
219 - <nature>org.eclipse.m2e.core.maven2Nature</nature>
221 -</projectDescription>
222 diff --git a/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs
223 deleted file mode 100644
224 index 0a9bbb889..000000000
225 --- a/getdown/src/getdown/core/.settings/org.eclipse.core.resources.prefs
228 -eclipse.preferences.version=1
229 -encoding//src/it/java=UTF-8
230 -encoding//src/main/java=UTF-8
231 -encoding//src/test/java=UTF-8
232 -encoding//src/test/resources=UTF-8
233 -encoding/<project>=UTF-8
234 diff --git a/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs
235 deleted file mode 100644
236 index 54e56721d..000000000
237 --- a/getdown/src/getdown/core/.settings/org.eclipse.jdt.core.prefs
240 -eclipse.preferences.version=1
241 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
242 -org.eclipse.jdt.core.compiler.compliance=1.7
243 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
244 -org.eclipse.jdt.core.compiler.release=disabled
245 -org.eclipse.jdt.core.compiler.source=1.7
246 diff --git a/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs
247 deleted file mode 100644
248 index f897a7f1c..000000000
249 --- a/getdown/src/getdown/core/.settings/org.eclipse.m2e.core.prefs
253 -eclipse.preferences.version=1
254 -resolveWorkspaceProjects=true
256 diff --git a/getdown/src/getdown/core/pom.xml b/getdown/src/getdown/core/pom.xml
257 index efec8b6ce..5e0f852ac 100644
258 --- a/getdown/src/getdown/core/pom.xml
259 +++ b/getdown/src/getdown/core/pom.xml
262 <groupId>com.threerings.getdown</groupId>
263 <artifactId>getdown</artifactId>
264 - <version>1.8.3-SNAPSHOT</version>
265 + <version>1.8.7-SNAPSHOT</version>
268 <artifactId>getdown-core</artifactId>
270 Wildcards can be used (*.mycompany.com) and multiple values can be
271 separated by commas (app1.foo.com,app2.bar.com,app3.baz.com). -->
273 - <getdown.host.whitelist>jalview.org,*.jalview.org</getdown.host.whitelist>
274 + <getdown.host.whitelist />
278 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
279 index 52b4b5ee3..d2ddaf272 100644
280 --- a/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java
281 +++ b/getdown/src/getdown/core/src/it/java/com/threerings/getdown/tests/DigesterIT.java
284 package com.threerings.getdown.tests;
286 -import java.io.File;
287 import java.nio.charset.StandardCharsets;
288 -import java.nio.file.*;
289 +import java.nio.file.Files;
290 +import java.nio.file.Path;
291 +import java.nio.file.Paths;
292 import java.util.Arrays;
293 import java.util.List;
296 -import static org.junit.Assert.*;
298 import com.threerings.getdown.tools.Digester;
299 +import org.junit.Test;
300 +import static org.junit.Assert.assertEquals;
302 public class DigesterIT {
304 @@ -32,23 +32,23 @@ public class DigesterIT {
305 Files.delete(digest2);
307 assertEquals(Arrays.asList(
308 - "getdown.txt = 779c74fb4b251e18faf6e240a0667964",
309 + "getdown.txt = 9c9b2494929c99d44ae51034d59e1a1b",
310 "testapp.jar = 404dafa55e78b25ec0e3a936357b1883",
311 "funny%test dir/some=file.txt = d8e8fca2dc0f896fd7cb4cb0031ba249",
312 "crazyhashfile#txt = f29d23fd5ab1781bd8d0760b3a516f16",
313 "foo.jar = 46ca4cc9079d9d019bb30cd21ebbc1ec",
314 "script.sh = f66e8ea25598e67e99c47d9b0b2a2cdf",
315 - "digest.txt = f5561d85e4d80cc85883963897e58ff6"
316 + "digest.txt = 11f9ba349cf9edacac4d72a3158447e5"
319 assertEquals(Arrays.asList(
320 - "getdown.txt = 4f0c657895c3c3a35fa55bf5951c64fa9b0694f8fc685af3f1d8635c639e066b",
321 + "getdown.txt = 1efecfae2a189002a6658f17d162b1922c7bde978944949276dc038a0df2461f",
322 "testapp.jar = c9cb1906afbf48f8654b416c3f831046bd3752a76137e5bf0a9af2f790bf48e0",
323 "funny%test dir/some=file.txt = f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2",
324 "crazyhashfile#txt = 6816889f922de38f145db215a28ad7c5e1badf7354b5cdab225a27486789fa3b",
325 "foo.jar = ea188b872e0496debcbe00aaadccccb12a8aa9b025bb62c130cd3d9b8540b062",
326 "script.sh = cca1c5c7628d9bf7533f655a9cfa6573d64afb8375f81960d1d832dc5135c988",
327 - "digest2.txt = 70b442c9f56660561921da3368e1a206f05c379182fab3062750b7ddcf303407"
328 + "digest2.txt = 41eacdabda8909bdbbf61e4f980867f4003c16a12f6770e6fc619b6af100e05b"
332 diff --git a/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt b/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt
333 index 3e0e5381a..ab0e47383 100644
334 --- a/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt
335 +++ b/getdown/src/getdown/core/src/it/resources/testapp/getdown.txt
336 @@ -13,6 +13,18 @@ apparg = %APPDIR%
337 # test the %env% mechanism
338 jvmarg = -Dusername=\%ENV.USER%
340 +# test various java_*** configs, they are not interesting for digester
341 +java_local_dir = jre
342 +java_max_version = 1089999
343 +java_min_version = [windows] 1080111
344 +java_min_version = [!windows] 1080192
345 +java_exact_version_required = [linux] true
346 +java_location = [linux-amd64] /files/java/java_linux_64.zip
347 +java_location = [linux-i386] /files/java/java_linux_32.zip
348 +java_location = [mac] /files/java/java_mac_64.zip
349 +java_location = [windows-amd64] /files/java/java_windows_64.zip
350 +java_location = [windows-x86] /files/java/java_windows_32.zip
352 strict_comments = true
353 resource = funny%test dir/some=file.txt
354 resource = crazyhashfile#txt
355 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
356 index 13b99564a..da98c9031 100644
357 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java
358 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/Log.java
359 @@ -15,7 +15,7 @@ import java.util.logging.*;
361 * A placeholder class that contains a reference to the log object used by the Getdown code.
364 +public final class Log
366 public static class Shim {
368 @@ -69,6 +69,13 @@ public class Log
369 /** We dispatch our log messages through this logging shim. */
370 public static final Shim log = new Shim();
373 + * Formats a message with key/value pairs. The pairs will be appended to the message as a
374 + * comma separated list of {@code key=value} in square brackets.
375 + * @param message the main log message.
376 + * @param args the key/value pairs. Any trailing key with no value will be ignored.
377 + * @return the formatted message, i.e. {@code Some log message [key=value, key=value]}.
379 public static String format (Object message, Object... args) {
380 if (args.length < 2) return String.valueOf(message);
381 StringBuilder buf = new StringBuilder(String.valueOf(message));
382 @@ -76,13 +83,13 @@ public class Log
386 - for (int ii = 0; ii < args.length; ii += 2) {
387 + for (int ii = 0, ll = args.length/2; ii < ll; ii += 1) {
389 buf.append(',').append(' ');
391 - buf.append(args[ii]).append('=');
392 + buf.append(args[2*ii]).append('=');
394 - buf.append(args[ii+1]);
395 + buf.append(args[2*ii+1]);
396 } catch (Throwable t) {
397 buf.append("<toString() failure: ").append(t).append(">");
399 @@ -136,6 +143,5 @@ public class Log
400 protected FieldPosition _fpos = new FieldPosition(SimpleDateFormat.DATE_FIELD);
403 - protected static final String DATE_FORMAT = "{0,date} {0,time}";
404 protected static final Level[] LEVELS = {Level.FINE, Level.INFO, Level.WARNING, Level.SEVERE};
406 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
407 index 67ea64575..7e01e87c5 100644
408 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java
409 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/GarbageCollector.java
411 package com.threerings.getdown.cache;
415 +import com.threerings.getdown.data.Resource;
416 import com.threerings.getdown.util.FileUtil;
419 @@ -55,9 +57,9 @@ public class GarbageCollector
420 if (subdirs != null) {
421 for (File dir : subdirs) {
422 if (dir.isDirectory()) {
423 - // Get all the native jars in the directory (there should only be one)
424 + // Get all the native jars or zips in the directory (there should only be one)
425 for (File file : dir.listFiles()) {
426 - if (!file.getName().endsWith(".jar")) {
427 + if (!Resource.isJar(file) && !Resource.isZip(file)) {
430 File cachedFile = getCachedFile(file);
431 @@ -94,6 +96,6 @@ public class GarbageCollector
432 private static File getCachedFile (File file)
434 return !isLastAccessedFile(file) ? file : new File(
435 - file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf(".")));
436 + file.getParentFile(), file.getName().substring(0, file.getName().lastIndexOf('.')));
439 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
440 index 0210e9a86..41f0c5f6d 100644
441 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java
442 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/cache/ResourceCache.java
443 @@ -69,7 +69,7 @@ public class ResourceCache
445 private String getFileSuffix (File fileToCache) {
446 String fileName = fileToCache.getName();
447 - int index = fileName.lastIndexOf(".");
448 + int index = fileName.lastIndexOf('.');
450 return index > -1 ? fileName.substring(index) : "";
452 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
453 index 0de5c8ac8..a93122553 100644
454 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
455 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Application.java
456 @@ -22,24 +22,17 @@ import java.util.*;
457 import java.util.concurrent.*;
458 import java.util.regex.Matcher;
459 import java.util.regex.Pattern;
460 -import java.util.zip.GZIPInputStream;
461 -import com.sun.management.OperatingSystemMXBean;
462 -import java.lang.management.ManagementFactory;
464 -import jalview.bin.MemorySetting;
466 +import com.threerings.getdown.net.Connector;
467 import com.threerings.getdown.util.*;
468 // avoid ambiguity with java.util.Base64 which we can't use as it's 1.8+
469 import com.threerings.getdown.util.Base64;
471 -import com.threerings.getdown.data.EnvConfig;
472 -import com.threerings.getdown.data.EnvConfig.Note;
474 import static com.threerings.getdown.Log.log;
475 import static java.nio.charset.StandardCharsets.UTF_8;
478 - * Parses and provide access to the information contained in the <code>getdown.txt</code>
479 + * Parses and provide access to the information contained in the {@code getdown.txt}
480 * configuration file.
482 public class Application
483 @@ -218,10 +211,10 @@ public class Application
484 * Used by {@link #verifyMetadata} to communicate status in circumstances where it needs to
485 * take network actions.
487 - public static interface StatusDisplay
488 + public interface StatusDisplay
490 /** Requests that the specified status message be displayed. */
491 - public void updateStatus (String message);
492 + void updateStatus (String message);
496 @@ -239,19 +232,56 @@ public class Application
500 - /** The proxy that should be used to do HTTP downloads. This must be configured prior to using
501 - * the application instance. Yes this is a public mutable field, no I'm not going to create a
503 + * Reads the {@code getdown.txt} config file into a {@code Config} object and returns it.
505 + public static Config readConfig (EnvConfig envc, boolean checkPlatform) throws IOException {
506 + Config config = null;
507 + File cfgfile = new File(envc.appDir, CONFIG_FILE);
508 + Config.ParseOpts opts = Config.createOpts(checkPlatform);
510 + // if we have a configuration file, read the data from it
511 + if (cfgfile.exists()) {
512 + config = Config.parseConfig(cfgfile, opts);
514 + // otherwise, try reading data from our backup config file; thanks to funny windows
515 + // bullshit, we have to do this backup file fiddling in case we got screwed while
516 + // updating getdown.txt during normal operation
517 + else if ((cfgfile = new File(envc.appDir, Application.CONFIG_FILE + "_old")).exists()) {
518 + config = Config.parseConfig(cfgfile, opts);
520 + // otherwise, issue a warning that we found no getdown file
522 + log.info("Found no getdown.txt file", "appdir", envc.appDir);
524 + } catch (Exception e) {
525 + log.warning("Failure reading config file", "file", config, e);
528 + // if we failed to read our config file, check for an appbase specified via a system
529 + // property; we can use that to bootstrap ourselves back into operation
530 + if (config == null) {
531 + log.info("Using 'appbase' from bootstrap config", "appbase", envc.appBase);
532 + Map<String, Object> cdata = new HashMap<>();
533 + cdata.put("appbase", envc.appBase);
534 + config = new Config(cdata);
540 + /** A helper that is used to do HTTP downloads. This must be configured prior to using the
541 + * application instance. Yes this is a public mutable field, no I'm not going to create a
542 * getter and setter just to pretend like that's not the case. */
543 - public Proxy proxy = Proxy.NO_PROXY;
544 + public Connector conn = Connector.DEFAULT;
547 - * Creates an application instance which records the location of the <code>getdown.txt</code>
548 + * Creates an application instance which records the location of the {@code getdown.txt}
549 * configuration file from the supplied application directory.
552 public Application (EnvConfig envc) {
554 - _config = getLocalPath(envc.appDir, CONFIG_FILE);
558 @@ -375,8 +405,7 @@ public class Application
560 public List<Resource> getActiveCodeResources ()
562 - ArrayList<Resource> codes = new ArrayList<>();
563 - codes.addAll(getCodeResources());
564 + List<Resource> codes = new ArrayList<>(getCodeResources());
565 for (AuxGroup aux : getAuxGroups()) {
566 if (isAuxGroupActive(aux.name)) {
567 codes.addAll(aux.codes);
568 @@ -404,8 +433,7 @@ public class Application
570 public List<Resource> getActiveResources ()
572 - ArrayList<Resource> rsrcs = new ArrayList<>();
573 - rsrcs.addAll(getResources());
574 + List<Resource> rsrcs = new ArrayList<>(getResources());
575 for (AuxGroup aux : getAuxGroups()) {
576 if (isAuxGroupActive(aux.name)) {
577 rsrcs.addAll(aux.rsrcs);
578 @@ -442,7 +470,15 @@ public class Application
582 - * Returns a resource for a zip file containing a Java VM that can be downloaded to use in
583 + * @return directory into which a local VM installation should be unpacked.
585 + public File getJavaLocalDir ()
587 + return _javaLocalDir;
591 + * @return a resource for a zip file containing a Java VM that can be downloaded to use in
592 * place of the installed VM (in the case where the VM that launched Getdown does not meet the
593 * application's version requirements) or null if no VM is available for this platform.
595 @@ -452,21 +488,16 @@ public class Application
599 - String extension = (_javaLocation.endsWith(".tgz"))?".tgz":".jar";
600 - String vmfile = LaunchUtil.LOCAL_JAVA_DIR + extension;
601 - log.info("vmfile is '"+vmfile+"'");
602 - System.out.println("vmfile is '"+vmfile+"'");
603 + // take extension from java location
604 + String vmfileExt = _javaLocation.substring(_javaLocation.lastIndexOf('.'));
605 + String vmfile = _javaLocalDir.getName() + vmfileExt;
607 URL remote = new URL(createVAppBase(_targetVersion), encodePath(_javaLocation));
608 - log.info("Attempting to fetch jvm at "+remote.toString());
609 - System.out.println("Attempting to fetch jvm at "+remote.toString());
610 return new Resource(vmfile, remote, getLocalPath(vmfile),
611 EnumSet.of(Resource.Attr.UNPACK, Resource.Attr.CLEAN));
612 } catch (Exception e) {
613 log.warning("Failed to create VM resource", "vmfile", vmfile, "appbase", _appbase,
614 "tvers", _targetVersion, "javaloc", _javaLocation, "error", e);
615 - System.out.println("Failed to create VM resource: vmfile="+vmfile+", appbase="+_appbase+
616 - ", tvers="+_targetVersion+", javaloc="+_javaLocation+", error="+e);
620 @@ -547,88 +578,52 @@ public class Application
621 * @exception IOException thrown if there is an error reading the file or an error encountered
622 * during its parsing.
624 - public Config init (boolean checkPlatform)
626 + public Config init (boolean checkPlatform) throws IOException
628 - Config config = null;
629 - File cfgfile = _config;
630 - Config.ParseOpts opts = Config.createOpts(checkPlatform);
632 - // if we have a configuration file, read the data from it
633 - if (cfgfile.exists()) {
634 - config = Config.parseConfig(_config, opts);
636 - // otherwise, try reading data from our backup config file; thanks to funny windows
637 - // bullshit, we have to do this backup file fiddling in case we got screwed while
638 - // updating getdown.txt during normal operation
639 - else if ((cfgfile = getLocalPath(CONFIG_FILE + "_old")).exists()) {
640 - config = Config.parseConfig(cfgfile, opts);
642 - // otherwise, issue a warning that we found no getdown file
644 - log.info("Found no getdown.txt file", "appdir", getAppDir());
646 - } catch (Exception e) {
647 - log.warning("Failure reading config file", "file", _config, e);
650 - // see if there's an override config from locator file
651 - Config locatorConfig = createLocatorConfig(opts);
653 - // merge the locator file config into config (or replace config with)
654 - if (locatorConfig != null) {
655 - if (config == null || locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_replace")) {
656 - config = locatorConfig;
658 - config.mergeConfig(locatorConfig, locatorConfig.getBoolean(LOCATOR_FILE_EXTENSION+"_merge"));
661 + Config config = readConfig(_envc, checkPlatform);
664 + initTracking(config);
665 + initResources(config);
670 - // if we failed to read our config file, check for an appbase specified via a system
671 - // property; we can use that to bootstrap ourselves back into operation
672 - if (config == null) {
673 - String appbase = _envc.appBase;
674 - log.info("Using 'appbase' from bootstrap config", "appbase", appbase);
675 - Map<String, Object> cdata = new HashMap<>();
676 - cdata.put("appbase", appbase);
677 - config = new Config(cdata);
680 + * Reads the basic config info from {@code config} into this instance. This includes things
681 + * like the appbase and version.
683 + public void initBase (Config config) throws IOException {
684 + // first extract our version information
685 + _version = config.getLong("version", -1L);
687 - // first determine our application base, this way if anything goes wrong later in the
688 + // determine our application base, this way if anything goes wrong later in the
689 // process, our caller can use the appbase to download a new configuration file
690 _appbase = config.getString("appbase");
692 - // see if locatorConfig override
693 - if (locatorConfig != null && !StringUtil.isBlank(locatorConfig.getString("appbase"))) {
694 - _appbase = locatorConfig.getString("appbase");
697 if (_appbase == null) {
698 throw new RuntimeException("m.missing_appbase");
701 - // check if we're overriding the domain in the appbase
702 - _appbase = SysProps.overrideAppbase(_appbase);
703 + // check if we're overriding the domain in the appbase, and sub envvars
704 + _appbase = resolveEnvVars(SysProps.overrideAppbase(_appbase));
706 // make sure there's a trailing slash
707 if (!_appbase.endsWith("/")) {
708 - _appbase = _appbase + "/";
712 - // extract our version information
713 - _version = config.getLong("version", -1L);
715 // if we are a versioned deployment, create a versioned appbase
717 _vappbase = createVAppBase(_version);
718 } catch (MalformedURLException mue) {
719 String err = MessageUtil.tcompose("m.invalid_appbase", _appbase);
720 - throw (IOException) new IOException(err).initCause(mue);
721 + throw new IOException(err, mue);
724 // check for a latest config URL
725 String latest = config.getString("latest");
726 if (latest != null) {
727 + latest = processArg(latest);
728 if (latest.startsWith(_appbase)) {
729 latest = _appbase + latest.substring(_appbase.length());
731 @@ -641,20 +636,25 @@ public class Application
735 - String appPrefix = _envc.appId == null ? "" : (_envc.appId + ".");
737 - // determine our application class name (use app-specific class _if_ one is provided)
738 - _class = config.getString("class");
739 - if (appPrefix.length() > 0) {
740 - _class = config.getString(appPrefix + "class", _class);
742 - if (_class == null) {
743 - throw new IOException("m.missing_class");
746 - // determine whether we want strict comments
747 + // read some miscellaneous configurations
748 _strictComments = config.getBoolean("strict_comments");
749 + _allowOffline = config.getBoolean("allow_offline");
750 + _revalidatePolicy = config.getEnum(
751 + "revalidate_policy", RevalidatePolicy.class, RevalidatePolicy.AFTER_UPDATE);
752 + int tpSize = SysProps.threadPoolSize();
753 + _maxConcDownloads = Math.max(1, config.getInt("max_concurrent_downloads", tpSize));
754 + _verifyTimeout = config.getInt("verify_timeout", 60);
756 + // whether to cache code resources and launch from cache
757 + _useCodeCache = config.getBoolean("use_code_cache");
758 + _codeCacheRetentionDays = config.getInt("code_cache_retention_days", 7);
762 + * Reads the JVM requirements from {@code config} into this instance. This includes things like
763 + * the min and max java version, location of a locally installed JRE, etc.
765 + public void initJava (Config config) {
766 // check to see if we're using a custom java.version property and regex
767 _javaVersionProp = config.getString("java_version_prop", _javaVersionProp);
768 _javaVersionRegex = config.getString("java_version_regex", _javaVersionRegex);
769 @@ -669,14 +669,16 @@ public class Application
770 // check to see if we require a particular JVM version and have a supplied JVM
771 _javaExactVersionRequired = config.getBoolean("java_exact_version_required");
773 - // this is a little weird, but when we're run from the digester, we see a String[] which
774 - // contains java locations for all platforms which we can't grok, but the digester doesn't
775 - // need to know about that; when we're run in a real application there will be only one!
776 - Object javaloc = config.getRaw("java_location");
777 - if (javaloc instanceof String) {
778 - _javaLocation = (String)javaloc;
780 + _javaLocation = config.getString("java_location");
782 + // used only in conjunction with java_location
783 + _javaLocalDir = getLocalPath(config.getString("java_local_dir", LaunchUtil.LOCAL_JAVA_DIR));
787 + * Reads the install tracking info from {@code config} into this instance.
789 + public void initTracking (Config config) {
790 // determine whether we have any tracking configuration
791 _trackingURL = config.getString("tracking_url");
793 @@ -701,14 +703,16 @@ public class Application
795 // Some app may need to generate google analytics code
796 _trackingGAHash = config.getString("tracking_ga_hash");
800 + * Reads the app resource info from {@code config} into this instance.
802 + public void initResources (Config config) throws IOException {
803 // clear our arrays as we may be reinitializing
809 - _txtJvmArgs.clear();
811 // parse our code resources
812 if (config.getMultiValue("code") == null &&
813 @@ -727,10 +731,10 @@ public class Application
815 // parse our auxiliary resource groups
816 for (String auxgroup : config.getList("auxgroups")) {
817 - ArrayList<Resource> codes = new ArrayList<>();
818 + List<Resource> codes = new ArrayList<>();
819 parseResources(config, auxgroup + ".code", Resource.NORMAL, codes);
820 parseResources(config, auxgroup + ".ucode", Resource.UNPACK, codes);
821 - ArrayList<Resource> rsrcs = new ArrayList<>();
822 + List<Resource> rsrcs = new ArrayList<>();
823 parseResources(config, auxgroup + ".resource", Resource.NORMAL, rsrcs);
824 parseResources(config, auxgroup + ".xresource", Resource.EXEC, rsrcs);
825 parseResources(config, auxgroup + ".uresource", Resource.UNPACK, rsrcs);
826 @@ -738,87 +742,48 @@ public class Application
827 parseResources(config, auxgroup + ".nresource", Resource.NATIVE, rsrcs);
828 _auxgroups.put(auxgroup, new AuxGroup(auxgroup, codes, rsrcs));
832 - // transfer our JVM arguments (we include both "global" args and app_id-prefixed args)
833 - String[] jvmargs = config.getMultiValue("jvmarg");
834 - addAll(jvmargs, _jvmargs);
836 + * Reads the command line arg info from {@code config} into this instance.
838 + public void initArgs (Config config) throws IOException {
841 + _txtJvmArgs.clear();
843 + String appPrefix = _envc.appId == null ? "" : (_envc.appId + ".");
845 + // determine our application class name (use app-specific class _if_ one is provided)
846 + _class = config.getString("class");
847 if (appPrefix.length() > 0) {
848 - jvmargs = config.getMultiValue(appPrefix + "jvmarg");
849 - addAll(jvmargs, _jvmargs);
850 + _class = config.getString(appPrefix + "class", _class);
852 + if (_class == null) {
853 + throw new IOException("m.missing_class");
856 - // see if a percentage of physical memory option exists
857 - int jvmmempc = config.getInt("jvmmempc", -1);
858 - // app_id prefixed setting overrides
859 + // transfer our JVM arguments (we include both "global" args and app_id-prefixed args)
860 + addAll(config.getMultiValue("jvmarg"), _jvmargs);
861 if (appPrefix.length() > 0) {
862 - jvmmempc = config.getInt(appPrefix + "jvmmempc", jvmmempc);
864 - if (0 <= jvmmempc && jvmmempc <= 100) {
866 - long maxMemLong = -1;
870 - maxMemLong = MemorySetting.memPercent(jvmmempc);
871 - } catch (Exception e)
873 - e.printStackTrace();
874 - } catch (Throwable t)
876 - t.printStackTrace();
879 - if (maxMemLong > 0)
882 - String[] maxMemHeapArg = new String[]{"-Xmx"+Long.toString(maxMemLong)};
883 - // remove other max heap size arg
884 - ARG: for (int i = 0; i < _jvmargs.size(); i++) {
885 - if (_jvmargs.get(i) instanceof java.lang.String && _jvmargs.get(i).startsWith("-Xmx")) {
886 - _jvmargs.remove(i);
889 - addAll(maxMemHeapArg, _jvmargs);
893 - } else if (jvmmempc != -1) {
894 - System.out.println("'jvmmempc' value must be in range 0 to 100 (read as '"+Integer.toString(jvmmempc)+"')");
895 + addAll(config.getMultiValue(appPrefix + "jvmarg"), _jvmargs);
898 // get the set of optimum JVM arguments
899 _optimumJvmArgs = config.getMultiValue("optimum_jvmarg");
901 // transfer our application arguments
902 - String[] appargs = config.getMultiValue(appPrefix + "apparg");
903 - addAll(appargs, _appargs);
904 + addAll(config.getMultiValue(appPrefix + "apparg"), _appargs);
906 // add the launch specific application arguments
907 _appargs.addAll(_envc.appArgs);
910 // look for custom arguments
911 fillAssignmentListFromPairs("extra.txt", _txtJvmArgs);
913 - // determine whether we want to allow offline operation (defaults to false)
914 - _allowOffline = config.getBoolean("allow_offline");
916 - // look for a debug.txt file which causes us to run in java.exe on Windows so that we can
917 - // obtain a thread dump of the running JVM
918 - _windebug = getLocalPath("debug.txt").exists();
920 - // whether to cache code resources and launch from cache
921 - _useCodeCache = config.getBoolean("use_code_cache");
922 - _codeCacheRetentionDays = config.getInt("code_cache_retention_days", 7);
924 - // maximum simultaneous downloads
925 - _maxConcDownloads = Math.max(1, config.getInt("max_concurrent_downloads",
926 - SysProps.threadPoolSize()));
928 // extract some info used to configure our child process on macOS
929 _dockName = config.getString("ui.name");
930 _dockIconPath = config.getString("ui.mac_dock_icon", "../desktop.icns");
936 @@ -847,8 +812,7 @@ public class Application
937 * Returns a URL from which the specified path can be fetched. Our application base URL is
938 * properly versioned and combined with the supplied path.
940 - public URL getRemoteURL (String path)
941 - throws MalformedURLException
942 + public URL getRemoteURL (String path) throws MalformedURLException
944 return new URL(_vappbase, encodePath(path));
946 @@ -858,7 +822,7 @@ public class Application
948 public File getLocalPath (String path)
950 - return getLocalPath(getAppDir(), path);
951 + return new File(getAppDir(), path);
955 @@ -881,8 +845,7 @@ public class Application
956 // if we have an unpacked VM, check the 'release' file for its version
957 Resource vmjar = getJavaVMResource();
958 if (vmjar != null && vmjar.isMarkedValid()) {
959 - File vmdir = new File(getAppDir(), LaunchUtil.LOCAL_JAVA_DIR);
960 - File relfile = new File(vmdir, "release");
961 + File relfile = new File(_javaLocalDir, "release");
962 if (!relfile.exists()) {
963 log.warning("Unpacked JVM missing 'release' file. Assuming valid version.");
965 @@ -939,7 +902,7 @@ public class Application
969 - * Attempts to redownload the <code>getdown.txt</code> file based on information parsed from a
970 + * Attempts to redownload the {@code getdown.txt} file based on information parsed from a
971 * previous call to {@link #init}.
973 public void attemptRecovery (StatusDisplay status)
974 @@ -950,7 +913,7 @@ public class Application
978 - * Downloads and replaces the <code>getdown.txt</code> and <code>digest.txt</code> files with
979 + * Downloads and replaces the {@code getdown.txt} and {@code digest.txt} files with
980 * those for the target version of our application.
982 public void updateMetadata ()
983 @@ -961,7 +924,7 @@ public class Application
984 _vappbase = createVAppBase(_targetVersion);
985 } catch (MalformedURLException mue) {
986 String err = MessageUtil.tcompose("m.invalid_appbase", _appbase);
987 - throw (IOException) new IOException(err).initCause(mue);
988 + throw new IOException(err, mue);
992 @@ -1000,7 +963,7 @@ public class Application
993 ArrayList<String> args = new ArrayList<>();
995 // reconstruct the path to the JVM
996 - args.add(LaunchUtil.getJVMPath(getAppDir(), _windebug || optimum));
997 + args.add(LaunchUtil.getJVMBinaryPath(_javaLocalDir, SysProps.debug() || optimum));
999 // check whether we're using -jar mode or -classpath mode
1000 boolean dashJarMode = MANIFEST_CLASS.equals(_class);
1001 @@ -1018,14 +981,8 @@ public class Application
1002 args.add("-Xdock:name=" + _dockName);
1005 - // pass along our proxy settings
1007 - if ((proxyHost = System.getProperty("http.proxyHost")) != null) {
1008 - args.add("-Dhttp.proxyHost=" + proxyHost);
1009 - args.add("-Dhttp.proxyPort=" + System.getProperty("http.proxyPort"));
1010 - args.add("-Dhttps.proxyHost=" + proxyHost);
1011 - args.add("-Dhttps.proxyPort=" + System.getProperty("http.proxyPort"));
1013 + // forward our proxy settings
1014 + conn.addProxyArgs(args);
1016 // add the marker indicating the app is running in getdown
1017 args.add("-D" + Properties.GETDOWN + "=true");
1018 @@ -1071,32 +1028,11 @@ public class Application
1022 - // almost finally check the startup file arguments
1023 - for (File f : _startupFiles) {
1024 - _appargs.add(f.getAbsolutePath());
1025 - break; // Only add one file to open
1028 - // check if one arg with recognised extension
1029 - if ( _appargs.size() == 1 && _appargs.get(0) != null ) {
1030 - String filename = _appargs.get(0);
1031 - String ext = null;
1032 - int j = filename.lastIndexOf('.');
1034 - ext = filename.substring(j+1);
1036 - if (LOCATOR_FILE_EXTENSION.equals(ext.toLowerCase())) {
1037 - // this file extension should have been dealt with in Getdown class
1039 - _appargs.add(0, "-open");
1043 // finally add the application arguments
1044 for (String string : _appargs) {
1045 args.add(processArg(string));
1049 String[] envp = createEnvironment();
1050 String[] sargs = args.toArray(new String[args.size()]);
1051 log.info("Running " + StringUtil.join(sargs, "\n "));
1052 @@ -1156,7 +1092,7 @@ public class Application
1053 for (String jvmarg : _jvmargs) {
1054 if (jvmarg.startsWith("-D")) {
1055 jvmarg = processArg(jvmarg.substring(2));
1056 - int eqidx = jvmarg.indexOf("=");
1057 + int eqidx = jvmarg.indexOf('=');
1059 log.warning("Bogus system property: '" + jvmarg + "'?");
1061 @@ -1194,32 +1130,36 @@ public class Application
1065 - /** Replaces the application directory and version in any argument. */
1066 + /** Replaces the application directory, version and env vars in any argument. */
1067 protected String processArg (String arg)
1069 arg = arg.replace("%APPDIR%", getAppDir().getAbsolutePath());
1070 arg = arg.replace("%VERSION%", String.valueOf(_version));
1071 + arg = resolveEnvVars(arg);
1075 - // if this argument contains %ENV.FOO% replace those with the associated values looked up
1076 - // from the environment
1077 - if (arg.contains(ENV_VAR_PREFIX)) {
1078 + /** Resolves env var substitutions in {@code text}. */
1079 + protected String resolveEnvVars (String text) {
1080 + // if the text contains %ENV.FOO% replace it with FOO looked up in the environment
1081 + if (text.contains(ENV_VAR_PREFIX)) {
1082 StringBuffer sb = new StringBuffer();
1083 - Matcher matcher = ENV_VAR_PATTERN.matcher(arg);
1084 + Matcher matcher = ENV_VAR_PATTERN.matcher(text);
1085 while (matcher.find()) {
1086 String varName = matcher.group(1), varValue = System.getenv(varName);
1087 String repValue = varValue == null ? "MISSING:"+varName : varValue;
1088 matcher.appendReplacement(sb, Matcher.quoteReplacement(repValue));
1090 matcher.appendTail(sb);
1091 - arg = sb.toString();
1092 + return sb.toString();
1101 - * Loads the <code>digest.txt</code> file and verifies the contents of both that file and the
1102 - * <code>getdown.text</code> file. Then it loads the <code>version.txt</code> and decides
1103 + * Loads the {@code digest.txt} file and verifies the contents of both that file and the
1104 + * {@code getdown.text} file. Then it loads the {@code version.txt} and decides
1105 * whether or not the application needs to be updated or whether we can proceed to verification
1108 @@ -1306,11 +1246,11 @@ public class Application
1111 if (_latest != null) {
1112 - try (InputStream in = ConnectionUtil.open(proxy, _latest, 0, 0).getInputStream();
1113 - InputStreamReader reader = new InputStreamReader(in, UTF_8);
1114 - BufferedReader bin = new BufferedReader(reader)) {
1115 - for (String[] pair : Config.parsePairs(bin, Config.createOpts(false))) {
1116 - if (pair[0].equals("version")) {
1118 + List<String[]> vdata = Config.parsePairs(
1119 + new StringReader(conn.fetch(_latest)), Config.createOpts(false));
1120 + for (String[] pair : vdata) {
1121 + if ("version".equals(pair[0])) {
1122 _targetVersion = Math.max(Long.parseLong(pair[1]), _targetVersion);
1123 if (fileVersion != -1 && _targetVersion > fileVersion) {
1124 // replace the file with the newest version
1125 @@ -1404,7 +1344,10 @@ public class Application
1126 while (completed[0] < rsrcs.size()) {
1127 // we should be getting progress completion updates WAY more often than one every
1128 // minute, so if things freeze up for 60 seconds, abandon ship
1129 - Runnable action = actions.poll(60, TimeUnit.SECONDS);
1130 + Runnable action = actions.poll(_verifyTimeout, TimeUnit.SECONDS);
1131 + if (action == null) {
1132 + throw new IllegalStateException("m.verify_timeout");
1137 @@ -1415,14 +1358,14 @@ public class Application
1138 unpacked.addAll(unpackedAsync);
1140 long complete = System.currentTimeMillis();
1141 - log.info("Verified resources", "count", rsrcs.size(), "size", (totalSize/1024) + "k",
1142 - "duration", (complete-start) + "ms");
1143 + log.info("Verified resources", "count", rsrcs.size(), "alreadyValid", alreadyValid[0],
1144 + "size", (totalSize/1024) + "k", "duration", (complete-start) + "ms");
1147 private void verifyResource (Resource rsrc, ProgressObserver obs, int[] alreadyValid,
1148 Set<Resource> unpacked,
1149 Set<Resource> toInstall, Set<Resource> toDownload) {
1150 - if (rsrc.isMarkedValid()) {
1151 + if (_revalidatePolicy != RevalidatePolicy.ALWAYS && rsrc.isMarkedValid()) {
1152 if (alreadyValid != null) {
1155 @@ -1513,7 +1456,7 @@ public class Application
1156 protected URL createVAppBase (long version)
1157 throws MalformedURLException
1159 - String url = version < 0 ? _appbase : _appbase.replace("%VERSION%", "" + version);
1160 + String url = version < 0 ? _appbase : _appbase.replace("%VERSION%", String.valueOf(version));
1161 return HostWhitelist.verify(new URL(url));
1164 @@ -1530,8 +1473,7 @@ public class Application
1166 * Downloads a new copy of CONFIG_FILE.
1168 - protected void downloadConfigFile ()
1169 - throws IOException
1170 + protected void downloadConfigFile () throws IOException
1172 downloadControlFile(CONFIG_FILE, 0);
1174 @@ -1673,8 +1615,7 @@ public class Application
1175 * Download a path to a temporary file, returning a {@link File} instance with the path
1178 - protected File downloadFile (String path)
1179 - throws IOException
1180 + protected File downloadFile (String path) throws IOException
1182 File target = getLocalPath(path + "_new");
1184 @@ -1684,30 +1625,11 @@ public class Application
1185 } catch (Exception e) {
1186 log.warning("Requested to download invalid control file",
1187 "appbase", _vappbase, "path", path, "error", e);
1188 - throw (IOException) new IOException("Invalid path '" + path + "'.").initCause(e);
1189 + throw new IOException("Invalid path '" + path + "'.", e);
1192 log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'.");
1194 - // stream the URL into our temporary file
1195 - URLConnection uconn = ConnectionUtil.open(proxy, targetURL, 0, 0);
1196 - // we have to tell Java not to use caches here, otherwise it will cache any request for
1197 - // same URL for the lifetime of this JVM (based on the URL string, not the URL object);
1198 - // if the getdown.txt file, for example, changes in the meanwhile, we would never hear
1199 - // about it; turning off caches is not a performance concern, because when Getdown asks
1200 - // to download a file, it expects it to come over the wire, not from a cache
1201 - uconn.setUseCaches(false);
1202 - uconn.setRequestProperty("Accept-Encoding", "gzip");
1203 - try (InputStream fin = uconn.getInputStream()) {
1204 - String encoding = uconn.getContentEncoding();
1205 - boolean gzip = "gzip".equalsIgnoreCase(encoding);
1206 - try (InputStream fin2 = (gzip ? new GZIPInputStream(fin) : fin)) {
1207 - try (FileOutputStream fout = new FileOutputStream(target)) {
1208 - StreamUtil.copy(fin2, fout);
1213 + conn.download(targetURL, target); // stream the URL into our temporary file
1217 @@ -1721,9 +1643,7 @@ public class Application
1218 /** Helper function to add all values in {@code values} (if non-null) to {@code target}. */
1219 protected static void addAll (String[] values, List<String> target) {
1220 if (values != null) {
1221 - for (String value : values) {
1222 - target.add(value);
1224 + Collections.addAll(target, values);
1228 @@ -1805,96 +1725,7 @@ public class Application
1232 - protected File getLocalPath (File appdir, String path)
1234 - return new File(appdir, path);
1237 - public static void setStartupFilesFromParameterString(String p) {
1238 - // multiple files *might* be passed in as space separated quoted filenames
1240 - if (!StringUtil.isBlank(p)) {
1241 - String[] filenames;
1242 - // split quoted params or treat as single string array
1243 - if (p.startsWith(q) && p.endsWith(q)) {
1244 - // this fails if, e.g.
1245 - // p=q("stupidfilename\" " "otherfilename")
1246 - // let's hope no-one ever ends a filename with '" '
1247 - filenames = p.substring(q.length(),p.length()-q.length()).split(q+" "+q);
1249 - // single unquoted filename
1250 - filenames = new String[]{p};
1253 - // check for locator file. Only allow one locator file to be double clicked (if multiple files opened, ignore locator files)
1254 - String locatorFilename = filenames.length >= 1 ? filenames[0] : null;
1256 - !StringUtil.isBlank(locatorFilename)
1257 - && locatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)
1259 - setLocatorFile(locatorFilename);
1260 - // remove the locator filename from the filenames array
1261 - String[] otherFilenames = new String[filenames.length - 1];
1262 - System.arraycopy(filenames, 1, otherFilenames, 0, otherFilenames.length);
1263 - filenames = otherFilenames;
1266 - for (int i = 0; i < filenames.length; i++) {
1267 - String filename = filenames[i];
1268 - // skip any other locator files in a multiple file list
1269 - if (! filename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)) {
1270 - addStartupFile(filename);
1276 - public static void setLocatorFile(String filename) {
1277 - _locatorFile = new File(filename);
1280 - public static void addStartupFile(String filename) {
1281 - _startupFiles.add(new File(filename));
1284 - private Config createLocatorConfig(Config.ParseOpts opts) {
1285 - if (_locatorFile == null) {
1289 - Config locatorConfig = null;
1292 - Config tmpConfig = null;
1293 - if (_locatorFile.exists()) {
1294 - tmpConfig = Config.parseConfig(_locatorFile, opts);
1296 - log.warning("Given locator file does not exist", "file", _locatorFile);
1299 - // appbase is sanitised in HostWhitelist
1300 - Map<String, Object> tmpData = new HashMap<>();
1301 - for (Map.Entry<String, Object> entry : tmpConfig.getData().entrySet()) {
1302 - String key = entry.getKey();
1303 - Object value = entry.getValue();
1304 - String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key;
1305 - if (Config.allowedReplaceKeys.contains(mkey) || Config.allowedMergeKeys.contains(mkey)) {
1306 - tmpData.put(key, value);
1309 - locatorConfig = new Config(tmpData);
1311 - } catch (Exception e) {
1312 - log.warning("Failure reading locator file", "file", _locatorFile, e);
1315 - log.info("Returning locatorConfig", locatorConfig);
1317 - return locatorConfig;
1320 protected final EnvConfig _envc;
1321 - protected File _config;
1322 protected Digest _digest;
1324 protected long _version = -1;
1325 @@ -1906,7 +1737,6 @@ public class Application
1326 protected String _dockName;
1327 protected String _dockIconPath;
1328 protected boolean _strictComments;
1329 - protected boolean _windebug;
1330 protected boolean _allowOffline;
1331 protected int _maxConcDownloads;
1333 @@ -1924,10 +1754,14 @@ public class Application
1334 protected long _javaMinVersion, _javaMaxVersion;
1335 protected boolean _javaExactVersionRequired;
1336 protected String _javaLocation;
1337 + protected File _javaLocalDir;
1339 protected List<Resource> _codes = new ArrayList<>();
1340 protected List<Resource> _resources = new ArrayList<>();
1342 + protected int _verifyTimeout = 60;
1344 + protected RevalidatePolicy _revalidatePolicy = RevalidatePolicy.AFTER_UPDATE;
1345 protected boolean _useCodeCache;
1346 protected int _codeCacheRetentionDays;
1348 @@ -1941,9 +1775,6 @@ public class Application
1350 protected List<String> _txtJvmArgs = new ArrayList<>();
1352 - /** If a warning has been issued about not being able to set modtimes. */
1353 - protected boolean _warnedAboutSetLastModified;
1355 /** Locks gettingdown.lock in the app dir. Held the entire time updating is going on.*/
1356 protected FileLock _lock;
1358 @@ -1956,8 +1787,6 @@ public class Application
1360 protected static final String ENV_VAR_PREFIX = "%ENV.";
1361 protected static final Pattern ENV_VAR_PATTERN = Pattern.compile("%ENV\\.(.*?)%");
1363 - protected static File _locatorFile;
1364 - protected static List<File> _startupFiles = new ArrayList<>();
1365 - public static final String LOCATOR_FILE_EXTENSION = "jvl";
1367 + protected static enum RevalidatePolicy { ALWAYS, AFTER_UPDATE }
1369 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
1370 index bc8d14052..e310a52a2 100644
1371 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java
1372 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Digest.java
1373 @@ -21,7 +21,7 @@ import static com.threerings.getdown.Log.log;
1374 import static java.nio.charset.StandardCharsets.UTF_8;
1377 - * Manages the <code>digest.txt</code> file and the computing and processing of digests for an
1378 + * Manages the {@code digest.txt} file and the computing and processing of digests for an
1382 @@ -72,8 +72,7 @@ public class Digest
1383 digests.put(rsrc, rsrc.computeDigest(fversion, md, null));
1384 completed.add(rsrc);
1385 } catch (Throwable t) {
1386 - completed.add(new IOException("Error computing digest for: " + rsrc).
1388 + completed.add(new IOException("Error computing digest for: " + rsrc, t));
1392 @@ -88,7 +87,7 @@ public class Digest
1393 if (done instanceof IOException) {
1394 throw (IOException)done;
1395 } else if (done instanceof Resource) {
1396 - pending.remove((Resource)done);
1397 + pending.remove(done);
1399 throw new AssertionError("What is this? " + done);
1401 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
1402 index 57b8d8493..a14b02c63 100644
1403 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java
1404 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/EnvConfig.java
1405 @@ -7,22 +7,19 @@ package com.threerings.getdown.data;
1407 import java.io.File;
1408 import java.io.FileInputStream;
1409 -import java.net.MalformedURLException;
1410 -import java.net.URL;
1411 import java.security.cert.Certificate;
1412 import java.security.cert.CertificateFactory;
1413 import java.security.cert.X509Certificate;
1416 import com.threerings.getdown.util.StringUtil;
1417 -import com.threerings.getdown.data.Application;
1419 /** Configuration that comes from our "environment" (command line args, sys props, etc.). */
1420 public final class EnvConfig {
1422 /** Used to report problems or feedback by {@link #create}. */
1423 public static final class Note {
1424 - public static enum Level { INFO, WARN, ERROR };
1425 + public enum Level { INFO, WARN, ERROR }
1426 public static Note info (String msg) { return new Note(Level.INFO, msg); }
1427 public static Note warn (String msg) { return new Note(Level.WARN, msg); }
1428 public static Note error (String msg) { return new Note(Level.ERROR, msg); }
1429 @@ -141,23 +138,11 @@ public final class EnvConfig {
1435 - // Look for locator file, pass to Application and remove from appArgs
1436 - String argvLocatorFilename = argv.length > 2 ? argv[2] : null;
1438 - !StringUtil.isBlank(argvLocatorFilename)
1439 - && argvLocatorFilename.toLowerCase().endsWith("."+Application.LOCATOR_FILE_EXTENSION)
1441 - notes.add(Note.info("locatorFilename in args: '"+argv[2]+"'"));
1442 - Application.setLocatorFile(argvLocatorFilename);
1447 - // ensure that we were able to find an app dir
1448 + // if no appdir was provided, default to the current working directory
1449 if (appDir == null) {
1450 - return null; // caller will report problem to user
1451 + appDir = System.getProperty("user.dir");
1452 + appDirProv = "default (cwd)";
1455 notes.add(Note.info("Using appdir from " + appDirProv + ": " + appDir));
1456 @@ -187,9 +172,9 @@ public final class EnvConfig {
1460 - // pass along anything after the first two (or three) args as extra app args
1461 - List<String> appArgs = argv.length > skipArgs ?
1462 - Arrays.asList(argv).subList(skipArgs, argv.length) :
1463 + // pass along anything after the first two args as extra app args
1464 + List<String> appArgs = argv.length > 2 ?
1465 + Arrays.asList(argv).subList(2, argv.length) :
1466 Collections.<String>emptyList();
1468 // load X.509 certificate if it exists
1469 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
1470 index b0a1dc920..57e9275be 100644
1471 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java
1472 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/PathBuilder.java
1473 @@ -10,7 +10,7 @@ import java.io.IOException;
1474 import java.util.LinkedHashSet;
1475 import java.util.List;
1476 import java.util.concurrent.TimeUnit;
1477 -import java.util.jar.JarFile;
1478 +import java.util.zip.ZipFile;
1480 import com.threerings.getdown.cache.GarbageCollector;
1481 import com.threerings.getdown.cache.ResourceCache;
1482 @@ -112,7 +112,7 @@ public class PathBuilder
1484 if (!unpackedIndicator.exists()) {
1486 - FileUtil.unpackJar(new JarFile(cachedFile), cachedParent, false);
1487 + FileUtil.unpackJar(new ZipFile(cachedFile), cachedParent, false);
1488 unpackedIndicator.createNewFile();
1489 } catch (IOException ioe) {
1490 log.warning("Failed to unpack native jar",
1491 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
1492 index adc2d4f21..d1ccba3ba 100644
1493 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java
1494 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/Resource.java
1495 @@ -7,25 +7,14 @@ package com.threerings.getdown.data;
1498 import java.net.URL;
1499 -import java.nio.file.Files;
1500 -import java.nio.file.Paths;
1501 import java.security.MessageDigest;
1502 -import java.util.Collections;
1503 -import java.util.Comparator;
1504 -import java.util.EnumSet;
1505 -import java.util.List;
1506 -import java.util.Locale;
1507 -import java.util.jar.JarEntry;
1508 -import java.util.jar.JarFile;
1510 -import org.apache.commons.compress.archivers.ArchiveInputStream;
1511 -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
1512 -import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
1513 +import java.util.*;
1514 +import java.util.zip.ZipEntry;
1515 +import java.util.zip.ZipFile;
1517 import com.threerings.getdown.util.FileUtil;
1518 import com.threerings.getdown.util.ProgressObserver;
1519 import com.threerings.getdown.util.StringUtil;
1521 import static com.threerings.getdown.Log.log;
1524 @@ -34,7 +23,7 @@ import static com.threerings.getdown.Log.log;
1525 public class Resource implements Comparable<Resource>
1527 /** Defines special attributes for resources. */
1528 - public static enum Attr {
1529 + public enum Attr {
1530 /** Indicates that the resource should be unpacked. */
1532 /** If present, when unpacking a resource, any directories created by the newly
1533 @@ -46,7 +35,7 @@ public class Resource implements Comparable<Resource>
1535 /** Indicates that the resource is a jar containing native libs. */
1540 public static final EnumSet<Attr> NORMAL = EnumSet.noneOf(Attr.class);
1541 public static final EnumSet<Attr> UNPACK = EnumSet.of(Attr.UNPACK);
1542 @@ -66,29 +55,30 @@ public class Resource implements Comparable<Resource>
1543 byte[] buffer = new byte[DIGEST_BUFFER_SIZE];
1546 - boolean isJar = isJar(target.getPath());
1547 - boolean isPacked200Jar = isPacked200Jar(target.getPath());
1548 + boolean isZip = isJar(target) || isZip(target); // jar is a zip too
1549 + boolean isPacked200Jar = isPacked200Jar(target);
1551 // if this is a jar, we need to compute the digest in a "timestamp and file order" agnostic
1552 // manner to properly correlate jardiff patched jars with their unpatched originals
1553 - if (isJar || isPacked200Jar){
1554 + if (isPacked200Jar || isZip){
1555 File tmpJarFile = null;
1556 - JarFile jar = null;
1557 + ZipFile zip = null;
1559 - // if this is a compressed jar file, uncompress it to compute the jar file digest
1560 + // if this is a compressed zip file, uncompress it to compute the zip file digest
1561 if (isPacked200Jar){
1562 tmpJarFile = new File(target.getPath() + ".tmp");
1563 + tmpJarFile.deleteOnExit();
1564 FileUtil.unpackPacked200Jar(target, tmpJarFile);
1565 - jar = new JarFile(tmpJarFile);
1566 + zip = new ZipFile(tmpJarFile);
1568 - jar = new JarFile(target);
1569 + zip = new ZipFile(target);
1572 - List<JarEntry> entries = Collections.list(jar.entries());
1573 + List<? extends ZipEntry> entries = Collections.list(zip.entries());
1574 Collections.sort(entries, ENTRY_COMP);
1577 - for (JarEntry entry : entries) {
1578 + for (ZipEntry entry : entries) {
1579 // old versions of the digest code skipped metadata
1581 if (entry.getName().startsWith("META-INF")) {
1582 @@ -97,7 +87,7 @@ public class Resource implements Comparable<Resource>
1586 - try (InputStream in = jar.getInputStream(entry)) {
1587 + try (InputStream in = zip.getInputStream(entry)) {
1588 while ((read = in.read(buffer)) != -1) {
1589 md.update(buffer, 0, read);
1591 @@ -107,11 +97,11 @@ public class Resource implements Comparable<Resource>
1595 - if (jar != null) {
1596 + if (zip != null) {
1600 } catch (IOException ioe) {
1601 - log.warning("Error closing jar", "path", target, "jar", jar, "error", ioe);
1602 + log.warning("Error closing", "path", target, "zip", zip, "error", ioe);
1605 if (tmpJarFile != null) {
1606 @@ -132,6 +122,34 @@ public class Resource implements Comparable<Resource>
1607 return StringUtil.hexlate(md.digest());
1611 + * Returns whether {@code file} is a {@code zip} file.
1613 + public static boolean isZip (File file)
1615 + String path = file.getName();
1616 + return path.endsWith(".zip") || path.endsWith(".zip_new");
1620 + * Returns whether {@code file} is a {@code jar} file.
1622 + public static boolean isJar (File file)
1624 + String path = file.getName();
1625 + return path.endsWith(".jar") || path.endsWith(".jar_new");
1629 + * Returns whether {@code file} is a {@code jar.pack} file.
1631 + public static boolean isPacked200Jar (File file)
1633 + String path = file.getName();
1634 + return path.endsWith(".jar.pack") || path.endsWith(".jar.pack_new") ||
1635 + path.endsWith(".jar.pack.gz") || path.endsWith(".jar.pack.gz_new");
1639 * Creates a resource with the supplied remote URL and local path.
1641 @@ -141,17 +159,13 @@ public class Resource implements Comparable<Resource>
1644 _localNew = new File(local.toString() + "_new");
1645 - String lpath = _local.getPath();
1646 - _marker = new File(lpath + "v");
1647 + _marker = new File(_local.getPath() + "v");
1650 - _isTgz = isTgz(lpath);
1651 - _isJar = isJar(lpath);
1652 - _isPacked200Jar = isPacked200Jar(lpath);
1653 + _isZip = isJar(local) || isZip(local);
1654 + _isPacked200Jar = isPacked200Jar(local);
1655 boolean unpack = attrs.contains(Attr.UNPACK);
1656 - if (unpack && _isJar) {
1657 - _unpacked = _local.getParentFile();
1658 - } else if(unpack && _isTgz) {
1659 + if (unpack && _isZip) {
1660 _unpacked = _local.getParentFile();
1661 } else if(unpack && _isPacked200Jar) {
1662 String dotJar = ".jar", lname = _local.getName();
1663 @@ -307,20 +321,13 @@ public class Resource implements Comparable<Resource>
1664 public void unpack () throws IOException
1667 - if (!_isJar && !_isPacked200Jar && !_isTgz) {
1668 - throw new IOException("Requested to unpack non-jar/tgz file '" + _local + "'.");
1669 + if (!_isZip && !_isPacked200Jar) {
1670 + throw new IOException("Requested to unpack non-jar file '" + _local + "'.");
1673 - try (JarFile jar = new JarFile(_local)) {
1675 + try (ZipFile jar = new ZipFile(_local)) {
1676 FileUtil.unpackJar(jar, _unpacked, _attrs.contains(Attr.CLEAN));
1678 - } else if (_isTgz) {
1679 - try (InputStream fi = Files.newInputStream(_local.toPath());
1680 - InputStream bi = new BufferedInputStream(fi);
1681 - InputStream gzi = new GzipCompressorInputStream(bi);
1682 - TarArchiveInputStream tgz = new TarArchiveInputStream(gzi)) {
1683 - FileUtil.unpackTgz(tgz, _unpacked, _attrs.contains(Attr.CLEAN));
1686 FileUtil.unpackPacked200Jar(_local, _unpacked);
1688 @@ -382,31 +389,15 @@ public class Resource implements Comparable<Resource>
1692 - protected static boolean isJar (String path)
1694 - return path.endsWith(".jar") || path.endsWith(".jar_new");
1697 - protected static boolean isTgz (String path)
1699 - return path.endsWith(".tgz") || path.endsWith(".tgz_new");
1702 - protected static boolean isPacked200Jar (String path)
1704 - return path.endsWith(".jar.pack") || path.endsWith(".jar.pack_new") ||
1705 - path.endsWith(".jar.pack.gz")|| path.endsWith(".jar.pack.gz_new");
1708 protected String _path;
1709 protected URL _remote;
1710 protected File _local, _localNew, _marker, _unpacked;
1711 protected EnumSet<Attr> _attrs;
1712 - protected boolean _isJar, _isPacked200Jar, _isTgz;
1713 + protected boolean _isZip, _isPacked200Jar;
1715 /** Used to sort the entries in a jar file. */
1716 - protected static final Comparator<JarEntry> ENTRY_COMP = new Comparator<JarEntry>() {
1717 - @Override public int compare (JarEntry e1, JarEntry e2) {
1718 + protected static final Comparator<ZipEntry> ENTRY_COMP = new Comparator<ZipEntry>() {
1719 + @Override public int compare (ZipEntry e1, ZipEntry e2) {
1720 return e1.getName().compareTo(e2.getName());
1723 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
1724 index 0d96ecb71..b36d40021 100644
1725 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java
1726 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/data/SysProps.java
1727 @@ -16,7 +16,7 @@ import com.threerings.getdown.util.VersionUtil;
1728 * accessor so that it's easy to see all of the secret system property arguments that Getdown makes
1731 -public class SysProps
1732 +public final class SysProps
1734 /** Configures the appdir (in lieu of passing it in argv). Usage: {@code -Dappdir=foo}. */
1735 public static String appDir () {
1736 @@ -40,6 +40,14 @@ public class SysProps
1737 return System.getProperty("no_log_redir") != null;
1740 + /** Used to debug Getdown's launching of an app. When set, it disables redirection of stdout
1741 + * and stderr into a log file, and on Windows it uses {@code java.exe} to launch the app so
1742 + * that its console output can be observed.
1743 + * Usage: {@code -Ddebug}. */
1744 + public static boolean debug () {
1745 + return System.getProperty("debug") != null;
1748 /** Overrides the domain on {@code appbase}. Usage: {@code -Dappbase_domain=foo}. */
1749 public static String appbaseDomain () {
1750 return System.getProperty("appbase_domain");
1751 @@ -101,6 +109,16 @@ public class SysProps
1752 return Boolean.getBoolean("direct");
1755 + /** If true, Getdown will always try to connect without proxy settings even it a proxy is set
1756 + * in {@code proxy.txt}. If direct access is possible it will not clear {@code proxy.txt}, it
1757 + * will preserve the settings. This is to support cases where a user uses a workstation in two
1758 + * different networks, one with proxy the other one without. They should not be asked for
1759 + * proxy settings again each time they switch back to the proxy network.
1760 + * Usage: {@code -Dtry_no_proxy}. */
1761 + public static boolean tryNoProxyFirst () {
1762 + return Boolean.getBoolean("try_no_proxy");
1765 /** Specifies the connection timeout (in seconds) to use when downloading control files from
1766 * the server. This is chiefly useful when you are running in versionless mode and want Getdown
1767 * to more quickly timeout its startup update check if the server with which it is
1768 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
1769 index 6033e2f6e..2298d6099 100644
1770 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java
1771 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/Downloader.java
1773 package com.threerings.getdown.net;
1775 import java.io.File;
1776 +import java.io.FileOutputStream;
1777 import java.io.IOException;
1778 +import java.io.InputStream;
1779 +import java.net.HttpURLConnection;
1780 +import java.net.URLConnection;
1781 +import java.nio.channels.Channels;
1782 +import java.nio.channels.ReadableByteChannel;
1784 import java.util.Collection;
1785 import java.util.HashMap;
1786 @@ -26,8 +32,13 @@ import static com.threerings.getdown.Log.log;
1787 * implementors must take care to only execute thread-safe code or simply pass a message to the AWT
1788 * thread, for example.
1790 -public abstract class Downloader
1791 +public class Downloader
1793 + public Downloader (Connector conn)
1799 * Start the downloading process.
1800 * @param resources the resources to download.
1801 @@ -129,10 +140,31 @@ public abstract class Downloader
1803 protected void downloadFailed (Resource rsrc, Exception cause) {}
1806 + * Called when a to-be-downloaded resource returns a 404 not found.
1808 + protected void resourceMissing (Resource rsrc) {}
1811 * Performs the protocol-specific portion of checking download size.
1813 - protected abstract long checkSize (Resource rsrc) throws IOException;
1814 + protected long checkSize (Resource rsrc) throws IOException {
1815 + URLConnection conn = _conn.open(rsrc.getRemote(), 0, 0);
1817 + // if we're accessing our data via HTTP, we only need a HEAD request
1818 + if (conn instanceof HttpURLConnection) {
1819 + ((HttpURLConnection)conn).setRequestMethod("HEAD");
1821 + // if we get a satisfactory response code, return a size; ignore errors as we'll report
1822 + // those when we actually attempt to download the resource
1823 + int code = _conn.checkConnectStatus(conn);
1824 + return code == HttpURLConnection.HTTP_OK ? conn.getContentLength() : 0;
1827 + // let it be known that we're done with this connection
1828 + conn.getInputStream().close();
1833 * Periodically called by the protocol-specific downloaders to update their progress. This
1834 @@ -203,13 +235,68 @@ public abstract class Downloader
1835 * protocol-specific code. This method should periodically check whether {@code _state} is set
1836 * to aborted and abort any in-progress download if so.
1838 - protected abstract void download (Resource rsrc) throws IOException;
1839 + protected void download (Resource rsrc) throws IOException {
1840 + URLConnection conn = _conn.open(rsrc.getRemote(), 0, 0);
1841 + // make sure we got a satisfactory response code
1842 + int code = _conn.checkConnectStatus(conn);
1843 + if (code == HttpURLConnection.HTTP_NOT_FOUND) {
1844 + resourceMissing(rsrc);
1845 + } else if (code != HttpURLConnection.HTTP_OK) {
1846 + throw new IOException(
1847 + "Resource returned HTTP error " + rsrc.getRemote() + " [code=" + code + "]");
1850 + // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a
1851 + // system property
1853 + // download the resource from the specified URL
1854 + long actualSize = conn.getContentLength();
1855 + log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize);
1856 + long currentSize = 0L;
1857 + byte[] buffer = new byte[4*4096];
1858 + try (InputStream in = conn.getInputStream();
1859 + FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) {
1861 + // TODO: look to see if we have a download info file
1862 + // containing info on potentially partially downloaded data;
1863 + // if so, use a "Range: bytes=HAVE-" header.
1865 + // read in the file data
1867 + while ((read = in.read(buffer)) != -1) {
1868 + // abort the download if the downloader is aborted
1869 + if (_state == State.ABORTED) {
1872 + // write it out to our local copy
1873 + out.write(buffer, 0, read);
1874 + // note that we've downloaded some data
1875 + currentSize += read;
1876 + reportProgress(rsrc, currentSize, actualSize);
1881 + log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown");
1882 + File localNew = rsrc.getLocalNew();
1883 + try (ReadableByteChannel rbc = Channels.newChannel(conn.getInputStream());
1884 + FileOutputStream fos = new FileOutputStream(localNew)) {
1885 + // TODO: more work is needed here, transferFrom can fail to transfer the entire
1886 + // file, in which case it's not clear what we're supposed to do.. call it again?
1887 + // will it repeatedly fail?
1888 + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
1889 + reportProgress(rsrc, localNew.length(), localNew.length());
1894 + protected final Connector _conn;
1896 /** The reported sizes of our resources. */
1897 - protected Map<Resource, Long> _sizes = new HashMap<>();
1898 + protected final Map<Resource, Long> _sizes = new HashMap<>();
1900 /** The bytes downloaded for each resource. */
1901 - protected Map<Resource, Long> _downloaded = new HashMap<>();
1902 + protected final Map<Resource, Long> _downloaded = new HashMap<>();
1904 /** The time at which the file transfer began. */
1905 protected long _start;
1906 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
1907 deleted file mode 100644
1908 index a7a3287a9..000000000
1909 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java
1913 -// Getdown - application installer, patcher and launcher
1914 -// Copyright (C) 2004-2018 Getdown authors
1915 -// https://github.com/threerings/getdown/blob/master/LICENSE
1917 -package com.threerings.getdown.net;
1919 -import java.io.File;
1920 -import java.io.FileOutputStream;
1921 -import java.io.IOException;
1922 -import java.io.InputStream;
1923 -import java.net.HttpURLConnection;
1924 -import java.net.Proxy;
1925 -import java.net.URL;
1926 -import java.net.URLConnection;
1927 -import java.nio.channels.Channels;
1928 -import java.nio.channels.ReadableByteChannel;
1930 -import com.threerings.getdown.data.Resource;
1931 -import com.threerings.getdown.util.ConnectionUtil;
1933 -import static com.threerings.getdown.Log.log;
1936 - * Implements downloading files over HTTP
1938 -public class HTTPDownloader extends Downloader
1940 - public HTTPDownloader (Proxy proxy)
1945 - @Override protected long checkSize (Resource rsrc) throws IOException
1947 - URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
1949 - // if we're accessing our data via HTTP, we only need a HEAD request
1950 - if (conn instanceof HttpURLConnection) {
1951 - HttpURLConnection hcon = (HttpURLConnection)conn;
1952 - hcon.setRequestMethod("HEAD");
1954 - // make sure we got a satisfactory response code
1955 - if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
1956 - throw new IOException("Unable to check up-to-date for " +
1957 - rsrc.getRemote() + ": " + hcon.getResponseCode());
1960 - return conn.getContentLength();
1963 - // let it be known that we're done with this connection
1964 - conn.getInputStream().close();
1968 - @Override protected void download (Resource rsrc) throws IOException
1970 - // TODO: make FileChannel download impl (below) robust and allow apps to opt-into it via a
1971 - // system property
1973 - // download the resource from the specified URL
1974 - URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0);
1977 - // make sure we got a satisfactory response code
1978 - if (conn instanceof HttpURLConnection) {
1979 - HttpURLConnection hcon = (HttpURLConnection)conn;
1980 - if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) {
1981 - throw new IOException("Unable to download resource " + rsrc.getRemote() + ": " +
1982 - hcon.getResponseCode());
1985 - long actualSize = conn.getContentLength();
1986 - log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize);
1987 - long currentSize = 0L;
1988 - byte[] buffer = new byte[4*4096];
1989 - try (InputStream in = conn.getInputStream();
1990 - FileOutputStream out = new FileOutputStream(rsrc.getLocalNew())) {
1992 - // TODO: look to see if we have a download info file
1993 - // containing info on potentially partially downloaded data;
1994 - // if so, use a "Range: bytes=HAVE-" header.
1996 - // read in the file data
1998 - while ((read = in.read(buffer)) != -1) {
1999 - // abort the download if the downloader is aborted
2000 - if (_state == State.ABORTED) {
2003 - // write it out to our local copy
2004 - out.write(buffer, 0, read);
2005 - // note that we've downloaded some data
2006 - currentSize += read;
2007 - reportProgress(rsrc, currentSize, actualSize);
2012 - log.info("Downloading resource", "url", rsrc.getRemote(), "size", "unknown");
2013 - File localNew = rsrc.getLocalNew();
2014 - try (ReadableByteChannel rbc = Channels.newChannel(rsrc.getRemote().openStream());
2015 - FileOutputStream fos = new FileOutputStream(localNew)) {
2016 - // TODO: more work is needed here, transferFrom can fail to transfer the entire
2017 - // file, in which case it's not clear what we're supposed to do.. call it again?
2018 - // will it repeatedly fail?
2019 - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
2020 - reportProgress(rsrc, localNew.length(), localNew.length());
2025 - protected final Proxy _proxy;
2027 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
2028 index 22446ec0a..8c6d84160 100644
2029 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java
2030 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java
2031 @@ -11,7 +11,7 @@ package com.threerings.getdown.spi;
2032 public interface ProxyAuth
2034 /** Credentials for a proxy server. */
2035 - public static class Credentials {
2036 + class Credentials {
2037 public final String username;
2038 public final String password;
2039 public Credentials (String username, String password) {
2040 @@ -23,10 +23,10 @@ public interface ProxyAuth
2042 * Loads the credentials for the app installed in {@code appDir}.
2044 - public Credentials loadCredentials (String appDir);
2045 + Credentials loadCredentials (String appDir);
2048 * Encrypts and saves the credentials for the app installed in {@code appDir}.
2050 - public void saveCredentials (String appDir, String username, String password);
2051 + void saveCredentials (String appDir, String username, String password);
2053 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
2054 index c2e740b6e..4f8e50ebd 100644
2055 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java
2056 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Differ.java
2057 @@ -11,15 +11,14 @@ import java.io.FileInputStream;
2058 import java.io.FileOutputStream;
2059 import java.io.IOException;
2060 import java.io.InputStream;
2062 +import java.io.OutputStream;
2063 +import java.security.MessageDigest;
2064 import java.util.ArrayList;
2065 import java.util.Enumeration;
2066 -import java.util.jar.JarEntry;
2067 -import java.util.jar.JarFile;
2068 -import java.util.jar.JarOutputStream;
2069 +import java.util.List;
2070 import java.util.zip.ZipEntry;
2072 -import java.security.MessageDigest;
2073 +import java.util.zip.ZipFile;
2074 +import java.util.zip.ZipOutputStream;
2076 import com.threerings.getdown.data.Application;
2077 import com.threerings.getdown.data.Digest;
2078 @@ -39,8 +38,8 @@ public class Differ
2080 * Creates a single patch file that contains the differences between
2081 * the two specified application directories. The patch file will be
2082 - * created in the <code>nvdir</code> directory with name
2083 - * <code>patchV.dat</code> where V is the old application version.
2084 + * created in the {@code nvdir} directory with name
2085 + * {@code patchV.dat} where V is the old application version.
2087 public void createDiff (File nvdir, File ovdir, boolean verbose)
2089 @@ -61,13 +60,13 @@ public class Differ
2091 Application oapp = new Application(new EnvConfig(ovdir));
2093 - ArrayList<Resource> orsrcs = new ArrayList<>();
2094 + List<Resource> orsrcs = new ArrayList<>();
2095 orsrcs.addAll(oapp.getCodeResources());
2096 orsrcs.addAll(oapp.getResources());
2098 Application napp = new Application(new EnvConfig(nvdir));
2100 - ArrayList<Resource> nrsrcs = new ArrayList<>();
2101 + List<Resource> nrsrcs = new ArrayList<>();
2102 nrsrcs.addAll(napp.getCodeResources());
2103 nrsrcs.addAll(napp.getResources());
2105 @@ -91,15 +90,15 @@ public class Differ
2109 - protected void createPatch (File patch, ArrayList<Resource> orsrcs,
2110 - ArrayList<Resource> nrsrcs, boolean verbose)
2111 + protected void createPatch (File patch, List<Resource> orsrcs,
2112 + List<Resource> nrsrcs, boolean verbose)
2115 int version = Digest.VERSION;
2116 MessageDigest md = Digest.getMessageDigest(version);
2117 try (FileOutputStream fos = new FileOutputStream(patch);
2118 BufferedOutputStream buffered = new BufferedOutputStream(fos);
2119 - JarOutputStream jout = new JarOutputStream(buffered)) {
2120 + ZipOutputStream jout = new ZipOutputStream(buffered)) {
2122 // for each file in the new application, it either already exists
2123 // in the old application, or it is new
2124 @@ -172,13 +171,13 @@ public class Differ
2127 File temp = File.createTempFile("differ", "jar");
2128 - try (JarFile jar = new JarFile(target);
2129 + try (ZipFile jar = new ZipFile(target);
2130 FileOutputStream tempFos = new FileOutputStream(temp);
2131 BufferedOutputStream tempBos = new BufferedOutputStream(tempFos);
2132 - JarOutputStream jout = new JarOutputStream(tempBos)) {
2133 + ZipOutputStream jout = new ZipOutputStream(tempBos)) {
2134 byte[] buffer = new byte[4096];
2135 - for (Enumeration< JarEntry > iter = jar.entries(); iter.hasMoreElements();) {
2136 - JarEntry entry = iter.nextElement();
2137 + for (Enumeration<? extends ZipEntry> iter = jar.entries(); iter.hasMoreElements();) {
2138 + ZipEntry entry = iter.nextElement();
2139 entry.setCompressedSize(-1);
2140 jout.putNextEntry(entry);
2141 try (InputStream in = jar.getInputStream(entry)) {
2142 @@ -193,8 +192,7 @@ public class Differ
2146 - protected void jarDiff (File ofile, File nfile, JarOutputStream jout)
2147 - throws IOException
2148 + protected void jarDiff (File ofile, File nfile, ZipOutputStream jout) throws IOException
2150 JarDiff.createPatch(ofile.getPath(), nfile.getPath(), jout, false);
2152 @@ -209,7 +207,7 @@ public class Differ
2153 Differ differ = new Differ();
2154 boolean verbose = false;
2156 - if (args[0].equals("-verbose")) {
2157 + if ("-verbose".equals(args[0])) {
2161 @@ -222,11 +220,10 @@ public class Differ
2165 - protected static void pipe (File file, JarOutputStream jout)
2166 - throws IOException
2167 + protected static void pipe (File file, OutputStream out) throws IOException
2169 try (FileInputStream fin = new FileInputStream(file)) {
2170 - StreamUtil.copy(fin, jout);
2171 + StreamUtil.copy(fin, out);
2175 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
2176 index b04a6539b..ae61b4333 100644
2177 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java
2178 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Digester.java
2179 @@ -23,6 +23,7 @@ import com.threerings.getdown.data.Digest;
2180 import com.threerings.getdown.data.EnvConfig;
2181 import com.threerings.getdown.data.Resource;
2182 import com.threerings.getdown.util.Base64;
2183 +import com.threerings.getdown.util.Config;
2185 import static java.nio.charset.StandardCharsets.UTF_8;
2187 @@ -74,8 +75,11 @@ public class Digester
2188 System.out.println("Generating digest file '" + target + "'...");
2190 // create our application and instruct it to parse its business
2191 - Application app = new Application(new EnvConfig(appdir));
2193 + EnvConfig envc = new EnvConfig(appdir);
2194 + Application app = new Application(envc);
2195 + Config config = Application.readConfig(envc, false);
2196 + app.initBase(config);
2197 + app.initResources(config);
2199 List<Resource> rsrcs = new ArrayList<>();
2200 rsrcs.add(app.getConfigResource());
2201 @@ -86,6 +90,9 @@ public class Digester
2202 rsrcs.addAll(ag.rsrcs);
2205 + // reinit app just to verify that getdown.txt has valid format
2208 // now generate the digest file
2209 Digest.createDigest(version, rsrcs, target);
2211 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
2212 index 1cea0eacd..f0db8ac03 100644
2213 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java
2214 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiff.java
2217 package com.threerings.getdown.tools;
2220 +import java.io.Closeable;
2221 +import java.io.File;
2222 +import java.io.IOException;
2223 +import java.io.InputStream;
2224 +import java.io.OutputStream;
2225 +import java.io.StringWriter;
2226 +import java.io.Writer;
2228 -import java.util.jar.*;
2229 +import java.util.zip.ZipEntry;
2230 +import java.util.zip.ZipFile;
2231 +import java.util.zip.ZipOutputStream;
2233 import static java.nio.charset.StandardCharsets.UTF_8;
2236 - * JarDiff is able to create a jar file containing the delta between two jar files (old and new).
2237 - * The delta jar file can then be applied to the old jar file to reconstruct the new jar file.
2238 + * JarDiff is able to create a zip file containing the delta between two jar or zip files (old
2239 + * and new). The delta file can then be applied to the old archive file to reconstruct the new
2242 * <p> Refer to the JNLP spec for details on how this is done.
2244 @@ -58,39 +67,37 @@ import static java.nio.charset.StandardCharsets.UTF_8;
2245 public class JarDiff implements JarDiffCodes
2247 private static final int DEFAULT_READ_SIZE = 2048;
2248 - private static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
2249 - private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
2250 + private static final byte[] newBytes = new byte[DEFAULT_READ_SIZE];
2251 + private static final byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
2253 // The JARDiff.java is the stand-alone jardiff.jar tool. Thus, we do not depend on Globals.java
2254 // and other stuff here. Instead, we use an explicit _debug flag.
2255 private static boolean _debug;
2258 - * Creates a patch from the two passed in files, writing the result to <code>os</code>.
2259 + * Creates a patch from the two passed in files, writing the result to {@code os}.
2261 public static void createPatch (String oldPath, String newPath,
2262 OutputStream os, boolean minimal) throws IOException
2264 - try (JarFile2 oldJar = new JarFile2(oldPath);
2265 - JarFile2 newJar = new JarFile2(newPath)) {
2266 + try (ZipFile2 oldArchive = new ZipFile2(oldPath);
2267 + ZipFile2 newArchive = new ZipFile2(newPath)) {
2269 - HashMap<String,String> moved = new HashMap<>();
2270 - HashSet<String> implicit = new HashSet<>();
2271 - HashSet<String> moveSrc = new HashSet<>();
2272 - HashSet<String> newEntries = new HashSet<>();
2273 + Map<String,String> moved = new HashMap<>();
2274 + Set<String> implicit = new HashSet<>();
2275 + Set<String> moveSrc = new HashSet<>();
2276 + Set<String> newEntries = new HashSet<>();
2279 - // Go through the entries in new jar and
2280 - // determine which files are candidates for implicit moves
2281 - // ( files that has the same filename and same content in old.jar
2283 - // and for files that cannot be implicitly moved, we will either
2284 - // find out whether it is moved or new (modified)
2285 - for (JarEntry newEntry : newJar) {
2286 + // Go through the entries in new archive and determine which files are candidates for
2287 + // implicit moves (files that have the same filename and same content in old and new)
2288 + // and for files that cannot be implicitly moved, we will either find out whether it is
2289 + // moved or new (modified)
2290 + for (ZipEntry newEntry : newArchive) {
2291 String newname = newEntry.getName();
2293 // Return best match of contents, will return a name match if possible
2294 - String oldname = oldJar.getBestMatch(newJar, newEntry);
2295 + String oldname = oldArchive.getBestMatch(newArchive, newEntry);
2296 if (oldname == null) {
2297 // New or modified entry
2299 @@ -101,7 +108,7 @@ public class JarDiff implements JarDiffCodes
2300 // Content already exist - need to do a move
2302 // Should do implicit move? Yes, if names are the same, and
2303 - // no move command already exist from oldJar
2304 + // no move command already exist from oldArchive
2305 if (oldname.equals(newname) && !moveSrc.contains(oldname)) {
2307 System.out.println(newname + " added to implicit set!");
2308 @@ -117,12 +124,9 @@ public class JarDiff implements JarDiffCodes
2309 // JarDiffPatcher also.
2310 if (!minimal && (implicit.contains(oldname) ||
2311 moveSrc.contains(oldname) )) {
2313 // generate non-minimal jardiff
2314 // for backward compatibility
2318 System.out.println("NEW: "+ newname);
2320 newEntries.add(newname);
2321 @@ -153,8 +157,8 @@ public class JarDiff implements JarDiffCodes
2323 // SECOND PASS: <deleted files> = <oldjarnames> - <implicitmoves> -
2324 // <source of move commands> - <new or modified entries>
2325 - ArrayList<String> deleted = new ArrayList<>();
2326 - for (JarEntry oldEntry : oldJar) {
2327 + List<String> deleted = new ArrayList<>();
2328 + for (ZipEntry oldEntry : oldArchive) {
2329 String oldName = oldEntry.getName();
2330 if (!implicit.contains(oldName) && !moveSrc.contains(oldName)
2331 && !newEntries.contains(oldName)) {
2332 @@ -180,7 +184,7 @@ public class JarDiff implements JarDiffCodes
2336 - JarOutputStream jos = new JarOutputStream(os);
2337 + ZipOutputStream jos = new ZipOutputStream(os);
2339 // Write out all the MOVEs and REMOVEs
2340 createIndex(jos, deleted, moved);
2341 @@ -190,7 +194,7 @@ public class JarDiff implements JarDiffCodes
2343 System.out.println("New File: " + newName);
2345 - writeEntry(jos, newJar.getEntryByName(newName), newJar);
2346 + writeEntry(jos, newArchive.getEntryByName(newName), newArchive);
2350 @@ -199,11 +203,11 @@ public class JarDiff implements JarDiffCodes
2354 - * Writes the index file out to <code>jos</code>.
2355 - * <code>oldEntries</code> gives the names of the files that were removed,
2356 - * <code>movedMap</code> maps from the new name to the old name.
2357 + * Writes the index file out to {@code jos}.
2358 + * {@code oldEntries} gives the names of the files that were removed,
2359 + * {@code movedMap} maps from the new name to the old name.
2361 - private static void createIndex (JarOutputStream jos, List<String> oldEntries,
2362 + private static void createIndex (ZipOutputStream jos, List<String> oldEntries,
2363 Map<String,String> movedMap)
2366 @@ -220,17 +224,17 @@ public class JarDiff implements JarDiffCodes
2369 // And those that have moved
2370 - for (String newName : movedMap.keySet()) {
2371 - String oldName = movedMap.get(newName);
2372 + for (Map.Entry<String, String> entry : movedMap.entrySet()) {
2373 + String oldName = entry.getValue();
2374 writer.write(MOVE_COMMAND);
2376 writeEscapedString(writer, oldName);
2378 - writeEscapedString(writer, newName);
2379 + writeEscapedString(writer, entry.getKey());
2380 writer.write("\r\n");
2383 - jos.putNextEntry(new JarEntry(INDEX_NAME));
2384 + jos.putNextEntry(new ZipEntry(INDEX_NAME));
2385 byte[] bytes = writer.toString().getBytes(UTF_8);
2386 jos.write(bytes, 0, bytes.length);
2388 @@ -264,10 +268,10 @@ public class JarDiff implements JarDiffCodes
2392 - private static void writeEntry (JarOutputStream jos, JarEntry entry, JarFile2 file)
2393 + private static void writeEntry (ZipOutputStream jos, ZipEntry entry, ZipFile2 file)
2396 - try (InputStream data = file.getJarFile().getInputStream(entry)) {
2397 + try (InputStream data = file.getArchive().getInputStream(entry)) {
2398 jos.putNextEntry(entry);
2399 int size = data.read(newBytes);
2400 while (size != -1) {
2401 @@ -278,31 +282,31 @@ public class JarDiff implements JarDiffCodes
2405 - * JarFile2 wraps a JarFile providing some convenience methods.
2406 + * ZipFile2 wraps a ZipFile providing some convenience methods.
2408 - private static class JarFile2 implements Iterable<JarEntry>, Closeable
2409 + private static class ZipFile2 implements Iterable<ZipEntry>, Closeable
2411 - private JarFile _jar;
2412 - private List<JarEntry> _entries;
2413 - private HashMap<String,JarEntry> _nameToEntryMap;
2414 - private HashMap<Long,LinkedList<JarEntry>> _crcToEntryMap;
2415 + private final ZipFile _archive;
2416 + private List<ZipEntry> _entries;
2417 + private HashMap<String,ZipEntry> _nameToEntryMap;
2418 + private HashMap<Long,LinkedList<ZipEntry>> _crcToEntryMap;
2420 - public JarFile2 (String path) throws IOException {
2421 - _jar = new JarFile(new File(path));
2422 + public ZipFile2 (String path) throws IOException {
2423 + _archive = new ZipFile(new File(path));
2427 - public JarFile getJarFile () {
2429 + public ZipFile getArchive () {
2433 - // from interface Iterable<JarEntry>
2434 + // from interface Iterable<ZipEntry>
2436 - public Iterator<JarEntry> iterator () {
2437 + public Iterator<ZipEntry> iterator () {
2438 return _entries.iterator();
2441 - public JarEntry getEntryByName (String name) {
2442 + public ZipEntry getEntryByName (String name) {
2443 return _nameToEntryMap.get(name);
2446 @@ -350,7 +354,7 @@ public class JarDiff implements JarDiffCodes
2450 - public String getBestMatch (JarFile2 file, JarEntry entry) throws IOException {
2451 + public String getBestMatch (ZipFile2 file, ZipEntry entry) throws IOException {
2452 // check for same name and same content, return name if found
2453 if (contains(file, entry)) {
2454 return (entry.getName());
2455 @@ -360,11 +364,10 @@ public class JarDiff implements JarDiffCodes
2456 return (hasSameContent(file,entry));
2459 - public boolean contains (JarFile2 f, JarEntry e) throws IOException {
2461 - JarEntry thisEntry = getEntryByName(e.getName());
2462 + public boolean contains (ZipFile2 f, ZipEntry e) throws IOException {
2463 + ZipEntry thisEntry = getEntryByName(e.getName());
2465 - // Look up name in 'this' Jar2File - if not exist return false
2466 + // Look up name in 'this' ZipFile2 - if not exist return false
2467 if (thisEntry == null)
2470 @@ -373,26 +376,26 @@ public class JarDiff implements JarDiffCodes
2473 // Check contents - if no match - return false
2474 - try (InputStream oldIS = getJarFile().getInputStream(thisEntry);
2475 - InputStream newIS = f.getJarFile().getInputStream(e)) {
2476 + try (InputStream oldIS = getArchive().getInputStream(thisEntry);
2477 + InputStream newIS = f.getArchive().getInputStream(e)) {
2478 return !differs(oldIS, newIS);
2482 - public String hasSameContent (JarFile2 file, JarEntry entry) throws IOException {
2483 + public String hasSameContent (ZipFile2 file, ZipEntry entry) throws IOException {
2484 String thisName = null;
2485 - Long crcL = Long.valueOf(entry.getCrc());
2486 - // check if this jar contains files with the passed in entry's crc
2487 + Long crcL = entry.getCrc();
2488 + // check if this archive contains files with the passed in entry's crc
2489 if (_crcToEntryMap.containsKey(crcL)) {
2490 // get the Linked List with files with the crc
2491 - LinkedList<JarEntry> ll = _crcToEntryMap.get(crcL);
2492 + LinkedList<ZipEntry> ll = _crcToEntryMap.get(crcL);
2493 // go through the list and check for content match
2494 - ListIterator<JarEntry> li = ll.listIterator(0);
2495 + ListIterator<ZipEntry> li = ll.listIterator(0);
2496 while (li.hasNext()) {
2497 - JarEntry thisEntry = li.next();
2498 + ZipEntry thisEntry = li.next();
2499 // check for content match
2500 - try (InputStream oldIS = getJarFile().getInputStream(thisEntry);
2501 - InputStream newIS = file.getJarFile().getInputStream(entry)) {
2502 + try (InputStream oldIS = getArchive().getInputStream(thisEntry);
2503 + InputStream newIS = file.getArchive().getInputStream(entry)) {
2504 if (!differs(oldIS, newIS)) {
2505 thisName = thisEntry.getName();
2507 @@ -404,19 +407,19 @@ public class JarDiff implements JarDiffCodes
2510 private void index () throws IOException {
2511 - Enumeration<JarEntry> entries = _jar.entries();
2512 + Enumeration<? extends ZipEntry> entries = _archive.entries();
2514 _nameToEntryMap = new HashMap<>();
2515 _crcToEntryMap = new HashMap<>();
2516 _entries = new ArrayList<>();
2518 - System.out.println("indexing: " + _jar.getName());
2519 + System.out.println("indexing: " + _archive.getName());
2521 if (entries != null) {
2522 while (entries.hasMoreElements()) {
2523 - JarEntry entry = entries.nextElement();
2524 + ZipEntry entry = entries.nextElement();
2525 long crc = entry.getCrc();
2526 - Long crcL = Long.valueOf(crc);
2529 System.out.println("\t" + entry.getName() + " CRC " + crc);
2531 @@ -427,13 +430,13 @@ public class JarDiff implements JarDiffCodes
2532 // generate the CRC to entries map
2533 if (_crcToEntryMap.containsKey(crcL)) {
2534 // key exist, add the entry to the correcponding linked list
2535 - LinkedList<JarEntry> ll = _crcToEntryMap.get(crcL);
2536 + LinkedList<ZipEntry> ll = _crcToEntryMap.get(crcL);
2538 _crcToEntryMap.put(crcL, ll);
2541 // create a new entry in the hashmap for the new key
2542 - LinkedList<JarEntry> ll = new LinkedList<JarEntry>();
2543 + LinkedList<ZipEntry> ll = new LinkedList<>();
2545 _crcToEntryMap.put(crcL, ll);
2547 @@ -443,7 +446,7 @@ public class JarDiff implements JarDiffCodes
2550 public void close() throws IOException {
2556 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
2557 index b5a0a1763..e55034bca 100644
2558 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java
2559 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/JarDiffPatcher.java
2560 @@ -16,27 +16,25 @@ import java.util.ArrayList;
2561 import java.util.Enumeration;
2562 import java.util.HashMap;
2563 import java.util.HashSet;
2564 -import java.util.Iterator;
2565 import java.util.List;
2566 import java.util.Map;
2567 import java.util.Set;
2569 -import java.util.jar.JarEntry;
2570 -import java.util.jar.JarFile;
2571 import java.util.jar.JarOutputStream;
2572 +import java.util.zip.ZipEntry;
2573 +import java.util.zip.ZipFile;
2574 +import java.util.zip.ZipOutputStream;
2576 import com.threerings.getdown.util.ProgressObserver;
2578 import static java.nio.charset.StandardCharsets.UTF_8;
2581 - * Applies a jardiff patch to a jar file.
2582 + * Applies a jardiff patch to a jar/zip file.
2584 public class JarDiffPatcher implements JarDiffCodes
2587 - * Patches the specified jar file using the supplied patch file and writing
2588 - * the new jar file to the supplied target.
2589 + * Patches the specified jar file using the supplied patch file and writing the new jar file to
2590 + * the supplied target.
2592 * @param jarPath the path to the original jar file.
2593 * @param diffPath the path to the jardiff patch file.
2594 @@ -49,11 +47,9 @@ public class JarDiffPatcher implements JarDiffCodes
2597 File oldFile = new File(jarPath), diffFile = new File(diffPath);
2599 - try (JarFile oldJar = new JarFile(oldFile);
2600 - JarFile jarDiff = new JarFile(diffFile);
2601 - JarOutputStream jos = new JarOutputStream(new FileOutputStream(target))) {
2603 + try (ZipFile oldJar = new ZipFile(oldFile);
2604 + ZipFile jarDiff = new ZipFile(diffFile);
2605 + ZipOutputStream jos = makeOutputStream(oldFile, target)) {
2606 Set<String> ignoreSet = new HashSet<>();
2607 Map<String, String> renameMap = new HashMap<>();
2608 determineNameMapping(jarDiff, ignoreSet, renameMap);
2609 @@ -63,7 +59,7 @@ public class JarDiffPatcher implements JarDiffCodes
2611 // Files to implicit move
2612 Set<String> oldjarNames = new HashSet<>();
2613 - Enumeration<JarEntry> oldEntries = oldJar.entries();
2614 + Enumeration<? extends ZipEntry> oldEntries = oldJar.entries();
2615 if (oldEntries != null) {
2616 while (oldEntries.hasMoreElements()) {
2617 oldjarNames.add(oldEntries.nextElement().getName());
2618 @@ -83,10 +79,10 @@ public class JarDiffPatcher implements JarDiffCodes
2619 size -= ignoreSet.size();
2621 // Add content from JARDiff
2622 - Enumeration<JarEntry> entries = jarDiff.entries();
2623 + Enumeration<? extends ZipEntry> entries = jarDiff.entries();
2624 if (entries != null) {
2625 while (entries.hasMoreElements()) {
2626 - JarEntry entry = entries.nextElement();
2627 + ZipEntry entry = entries.nextElement();
2628 if (!INDEX_NAME.equals(entry.getName())) {
2629 updateObserver(observer, currentEntry, size);
2631 @@ -114,15 +110,15 @@ public class JarDiffPatcher implements JarDiffCodes
2632 // Apply move <oldName> <newName> command
2633 String oldName = renameMap.get(newName);
2635 - // Get source JarEntry
2636 - JarEntry oldEntry = oldJar.getJarEntry(oldName);
2637 + // Get source ZipEntry
2638 + ZipEntry oldEntry = oldJar.getEntry(oldName);
2639 if (oldEntry == null) {
2640 String moveCmd = MOVE_COMMAND + oldName + " " + newName;
2641 throw new IOException("error.badmove: " + moveCmd);
2644 - // Create dest JarEntry
2645 - JarEntry newEntry = new JarEntry(newName);
2646 + // Create dest ZipEntry
2647 + ZipEntry newEntry = new ZipEntry(newName);
2648 newEntry.setTime(oldEntry.getTime());
2649 newEntry.setSize(oldEntry.getSize());
2650 newEntry.setCompressedSize(oldEntry.getCompressedSize());
2651 @@ -149,19 +145,15 @@ public class JarDiffPatcher implements JarDiffCodes
2655 - Iterator<String> iEntries = oldjarNames.iterator();
2656 - if (iEntries != null) {
2657 - while (iEntries.hasNext()) {
2658 - String name = iEntries.next();
2659 - JarEntry entry = oldJar.getJarEntry(name);
2660 - if (entry == null) {
2661 - // names originally retrieved from the JAR, so this should never happen
2662 - throw new AssertionError("JAR entry not found: " + name);
2664 - updateObserver(observer, currentEntry, size);
2666 - writeEntry(jos, entry, oldJar);
2667 + for (String name : oldjarNames) {
2668 + ZipEntry entry = oldJar.getEntry(name);
2669 + if (entry == null) {
2670 + // names originally retrieved from the archive, so this should never happen
2671 + throw new AssertionError("Archive entry not found: " + name);
2673 + updateObserver(observer, currentEntry, size);
2675 + writeEntry(jos, entry, oldJar);
2677 updateObserver(observer, currentEntry, size);
2679 @@ -175,7 +167,7 @@ public class JarDiffPatcher implements JarDiffCodes
2682 protected void determineNameMapping (
2683 - JarFile jarDiff, Set<String> ignoreSet, Map<String, String> renameMap)
2684 + ZipFile jarDiff, Set<String> ignoreSet, Map<String, String> renameMap)
2687 InputStream is = jarDiff.getInputStream(jarDiff.getEntry(INDEX_NAME));
2688 @@ -223,7 +215,7 @@ public class JarDiffPatcher implements JarDiffCodes
2691 int length = path.length();
2692 - ArrayList<String> sub = new ArrayList<>();
2693 + List<String> sub = new ArrayList<>();
2695 while (index < length) {
2696 while (index < length && Character.isWhitespace
2697 @@ -231,9 +223,8 @@ public class JarDiffPatcher implements JarDiffCodes
2700 if (index < length) {
2701 - int start = index;
2703 - String subString = null;
2705 + StringBuilder subString = null;
2707 while (index < length) {
2708 char aChar = path.charAt(index);
2709 @@ -241,9 +232,9 @@ public class JarDiffPatcher implements JarDiffCodes
2710 path.charAt(index + 1) == ' ') {
2712 if (subString == null) {
2713 - subString = path.substring(last, index);
2714 + subString = new StringBuilder(path.substring(last, index));
2716 - subString += path.substring(last, index);
2717 + subString.append(path, last, index);
2720 } else if (Character.isWhitespace(aChar)) {
2721 @@ -253,18 +244,20 @@ public class JarDiffPatcher implements JarDiffCodes
2723 if (last != index) {
2724 if (subString == null) {
2725 - subString = path.substring(last, index);
2726 + subString = new StringBuilder(path.substring(last, index));
2728 - subString += path.substring(last, index);
2729 + subString.append(path, last, index);
2732 - sub.add(subString);
2733 + if (subString != null) {
2734 + sub.add(subString.toString());
2741 - protected void writeEntry (JarOutputStream jos, JarEntry entry, JarFile file)
2742 + protected void writeEntry (ZipOutputStream jos, ZipEntry entry, ZipFile file)
2745 try (InputStream data = file.getInputStream(entry)) {
2746 @@ -272,10 +265,10 @@ public class JarDiffPatcher implements JarDiffCodes
2750 - protected void writeEntry (JarOutputStream jos, JarEntry entry, InputStream data)
2751 + protected void writeEntry (ZipOutputStream jos, ZipEntry entry, InputStream data)
2754 - jos.putNextEntry(new JarEntry(entry.getName()));
2755 + jos.putNextEntry(new ZipEntry(entry.getName()));
2758 int size = data.read(newBytes);
2759 @@ -285,6 +278,15 @@ public class JarDiffPatcher implements JarDiffCodes
2763 + protected static ZipOutputStream makeOutputStream (File source, File target)
2764 + throws IOException
2766 + FileOutputStream out = new FileOutputStream(target);
2767 + if (source.getName().endsWith(".jar")) return new JarOutputStream(out);
2768 + else if (source.getName().endsWith(".zip")) return new ZipOutputStream(out);
2769 + else throw new AssertionError("Unsupported source file '" + source + "'. Not a .jar or .zip?");
2772 protected static final int DEFAULT_READ_SIZE = 2048;
2774 protected static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
2775 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
2776 index 4ead59bb3..5c8baaaab 100644
2777 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java
2778 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/tools/Patcher.java
2779 @@ -9,16 +9,13 @@ import java.io.File;
2780 import java.io.FileOutputStream;
2781 import java.io.IOException;
2782 import java.io.InputStream;
2784 import java.util.Enumeration;
2785 -import java.util.jar.JarEntry;
2786 -import java.util.jar.JarFile;
2787 import java.util.zip.ZipEntry;
2788 +import java.util.zip.ZipFile;
2790 import com.threerings.getdown.util.FileUtil;
2791 import com.threerings.getdown.util.ProgressObserver;
2792 import com.threerings.getdown.util.StreamUtil;
2794 import static com.threerings.getdown.Log.log;
2797 @@ -55,10 +52,10 @@ public class Patcher
2799 _plength = patch.length();
2801 - try (JarFile file = new JarFile(patch)) {
2802 - Enumeration<JarEntry> entries = file.entries(); // old skool!
2803 + try (ZipFile file = new ZipFile(patch)) {
2804 + Enumeration<? extends ZipEntry> entries = file.entries();
2805 while (entries.hasMoreElements()) {
2806 - JarEntry entry = entries.nextElement();
2807 + ZipEntry entry = entries.nextElement();
2808 String path = entry.getName();
2809 long elength = entry.getCompressedSize();
2811 @@ -96,7 +93,7 @@ public class Patcher
2812 return path.substring(0, path.length() - suffix.length());
2815 - protected void createFile (JarFile file, ZipEntry entry, File target)
2816 + protected void createFile (ZipFile file, ZipEntry entry, File target)
2818 // create our copy buffer if necessary
2819 if (_buffer == null) {
2820 @@ -124,8 +121,7 @@ public class Patcher
2824 - protected void patchFile (JarFile file, ZipEntry entry,
2825 - File appdir, String path)
2826 + protected void patchFile (ZipFile file, ZipEntry entry, File appdir, String path)
2828 File target = new File(appdir, path);
2829 File patch = new File(appdir, entry.getName());
2830 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
2831 index 2a5db79ec..233f1e739 100644
2832 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java
2833 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Base64.java
2834 @@ -24,7 +24,7 @@ import static java.nio.charset.StandardCharsets.US_ASCII;
2835 * href="http://www.ietf.org/rfc/rfc2045.txt">2045</a> and <a
2836 * href="http://www.ietf.org/rfc/rfc3548.txt">3548</a>.
2838 -public class Base64 {
2839 +public final class Base64 {
2841 * Default values for encoder/decoder flags.
2843 @@ -68,7 +68,7 @@ public class Base64 {
2845 // --------------------------------------------------------
2847 - /* package */ static abstract class Coder {
2848 + /* package */ abstract static class Coder {
2849 public byte[] output;
2852 @@ -178,7 +178,7 @@ public class Base64 {
2853 * Lookup table for turning bytes into their position in the
2856 - private static final int DECODE[] = {
2857 + private static final int[] DECODE = {
2858 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2859 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2860 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
2861 @@ -201,7 +201,7 @@ public class Base64 {
2862 * Decode lookup table for the "web safe" variant (RFC 3548
2863 * sec. 4) where - and _ replace + and /.
2865 - private static final int DECODE_WEBSAFE[] = {
2866 + private static final int[] DECODE_WEBSAFE = {
2867 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2868 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2869 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
2870 @@ -236,7 +236,7 @@ public class Base64 {
2871 private int state; // state number (0 to 6)
2874 - final private int[] alphabet;
2875 + private final int[] alphabet;
2877 public Decoder(int flags, byte[] output) {
2878 this.output = output;
2879 @@ -541,7 +541,7 @@ public class Base64 {
2880 * Lookup table for turning Base64 alphabet positions (6 bits)
2881 * into output bytes.
2883 - private static final byte ENCODE[] = {
2884 + private static final byte[] ENCODE = {
2885 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
2886 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
2887 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
2888 @@ -552,21 +552,21 @@ public class Base64 {
2889 * Lookup table for turning Base64 alphabet positions (6 bits)
2890 * into output bytes.
2892 - private static final byte ENCODE_WEBSAFE[] = {
2893 + private static final byte[] ENCODE_WEBSAFE = {
2894 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
2895 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
2896 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
2897 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_',
2900 - final private byte[] tail;
2901 + private final byte[] tail;
2902 /* package */ int tailLen;
2905 - final public boolean do_padding;
2906 - final public boolean do_newline;
2907 - final public boolean do_cr;
2908 - final private byte[] alphabet;
2909 + public final boolean do_padding;
2910 + public final boolean do_newline;
2911 + public final boolean do_cr;
2912 + private final byte[] alphabet;
2914 public Encoder(int flags, byte[] output) {
2915 this.output = output;
2916 @@ -618,7 +618,7 @@ public class Base64 {
2917 ((input[p++] & 0xff) << 8) |
2918 (input[p++] & 0xff);
2925 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
2926 index 047cead76..416f77e58 100644
2927 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java
2928 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Color.java
2929 @@ -8,11 +8,11 @@ package com.threerings.getdown.util;
2931 * Utilities for handling ARGB colors.
2934 +public final class Color
2936 - public final static int CLEAR = 0x00000000;
2937 - public final static int WHITE = 0xFFFFFFFF;
2938 - public final static int BLACK = 0xFF000000;
2939 + public static final int CLEAR = 0x00000000;
2940 + public static final int WHITE = 0xFFFFFFFF;
2941 + public static final int BLACK = 0xFF000000;
2943 public static float brightness (int argb) {
2944 // TODO: we're ignoring alpha here...
2945 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
2946 index 6ad2b4fd9..75357e882 100644
2947 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java
2948 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/Config.java
2949 @@ -14,7 +14,6 @@ import java.net.MalformedURLException;
2950 import java.net.URL;
2951 import java.nio.charset.StandardCharsets;
2952 import java.util.ArrayList;
2953 -import java.util.Arrays;
2954 import java.util.HashMap;
2955 import java.util.List;
2956 import java.util.Locale;
2957 @@ -58,33 +57,17 @@ public class Config
2961 - * Parses a configuration file containing key/value pairs. The file must be in the UTF-8
2964 + * Parses configuration text containing key/value pairs.
2965 * @param opts options that influence the parsing. See {@link #createOpts}.
2967 - * @return a list of <code>String[]</code> instances containing the key/value pairs in the
2968 + * @return a list of {@code String[]} instances containing the key/value pairs in the
2969 * order they were parsed from the file.
2971 - public static List<String[]> parsePairs (File source, ParseOpts opts)
2972 - throws IOException
2974 - // annoyingly FileReader does not allow encoding to be specified (uses platform default)
2975 - try (FileInputStream fis = new FileInputStream(source);
2976 - InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
2977 - return parsePairs(input, opts);
2982 - * See {@link #parsePairs(File,ParseOpts)}.
2984 public static List<String[]> parsePairs (Reader source, ParseOpts opts) throws IOException
2986 List<String[]> pairs = new ArrayList<>();
2987 for (String line : FileUtil.readLines(source)) {
2989 - int cidx = line.indexOf("#");
2990 + int cidx = line.indexOf('#');
2991 if (opts.strictComments ? cidx == 0 : cidx != -1) {
2992 line = line.substring(0, cidx);
2994 @@ -98,7 +81,7 @@ public class Config
2995 // parse our key/value pair
2996 String[] pair = new String[2];
2997 // if we're biasing toward key, put all the extra = in the key rather than the value
2998 - int eidx = opts.biasToKey ? line.lastIndexOf("=") : line.indexOf("=");
2999 + int eidx = opts.biasToKey ? line.lastIndexOf('=') : line.indexOf('=');
3001 pair[0] = line.substring(0, eidx).trim();
3002 pair[1] = line.substring(eidx+1).trim();
3003 @@ -109,7 +92,7 @@ public class Config
3005 // if the pair has an os qualifier, we need to process it
3006 if (pair[1].startsWith("[")) {
3007 - int qidx = pair[1].indexOf("]");
3008 + int qidx = pair[1].indexOf(']');
3010 log.warning("Bogus platform specifier", "key", pair[0], "value", pair[1]);
3011 continue; // omit the pair entirely
3012 @@ -132,6 +115,21 @@ public class Config
3017 + * Parses configuration file containing key/value pairs.
3018 + * @param source the file containing the config text. Must be in the UTF-8 encoding.
3019 + * @param opts options that influence the parsing. See {@link #createOpts}.
3021 + public static List<String[]> parsePairs (File source, ParseOpts opts)
3022 + throws IOException
3024 + // annoyingly FileReader does not allow encoding to be specified (uses platform default)
3025 + try (FileInputStream fis = new FileInputStream(source);
3026 + InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
3027 + return parsePairs(input, opts);
3032 * Takes a comma-separated String of four integers and returns a rectangle using those ints as
3033 * the its x, y, width, and height.
3034 @@ -167,13 +165,10 @@ public class Config
3038 - * Parses a configuration file containing key/value pairs. The file must be in the UTF-8
3041 - * @return a map from keys to values, where a value will be an array of strings if more than
3042 - * one key/value pair in the config file was associated with the same key.
3043 + * Parses the data for a config instance from the supplied {@code source} reader.
3044 + * @return a map that can be used to create a {@link #Config}.
3046 - public static Config parseConfig (File source, ParseOpts opts)
3047 + public static Map<String, Object> parseData (Reader source, ParseOpts opts)
3050 Map<String, Object> data = new HashMap<>();
3051 @@ -196,15 +191,34 @@ public class Config
3055 - // special magic for the getdown.txt config: if the parsed data contains 'strict_comments =
3056 - // true' then we reparse the file with strict comments (i.e. # is only assumed to start a
3057 - // comment in column 0)
3058 - if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) {
3059 - opts.strictComments = true;
3060 - return parseConfig(source, opts);
3066 + * Parses a configuration file containing key/value pairs. The file must be in the UTF-8
3069 + * @return a map from keys to values, where a value will be an array of strings if more than
3070 + * one key/value pair in the config file was associated with the same key.
3072 + public static Config parseConfig (File source, ParseOpts opts)
3073 + throws IOException
3075 + // annoyingly FileReader does not allow encoding to be specified (uses platform default)
3076 + try (FileInputStream fis = new FileInputStream(source);
3077 + InputStreamReader input = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
3078 + Map<String, Object> data = parseData(input, opts);
3080 + // special magic for the getdown.txt config: if the parsed data contains
3081 + // 'strict_comments = true' then we reparse the file with strict comments (i.e. # is
3082 + // only assumed to start a comment in column 0)
3083 + if (!opts.strictComments && Boolean.parseBoolean((String)data.get("strict_comments"))) {
3084 + opts.strictComments = true;
3085 + return parseConfig(source, opts);
3088 - return new Config(data);
3089 + return new Config(data);
3093 public Config (Map<String, Object> data) {
3094 @@ -248,6 +262,20 @@ public class Config
3095 return Boolean.parseBoolean(getString(name));
3099 + * Returns the specified config value as an enum value. The string value of the config is
3100 + * converted to all upper case and then turned into an enum via {@link Enum#valueOf}.
3102 + public <T extends Enum<T>> T getEnum (String name, Class<T> eclass, T defval) {
3103 + String value = getString(name, defval.toString());
3105 + return Enum.valueOf(eclass, value.toUpperCase());
3106 + } catch (Exception e) {
3107 + log.warning("Invalid value for '" + name + "' config: '" + value + "'.");
3113 * Massages a single string into an array and leaves existing array values as is. Simplifies
3114 * access to parameters that are expected to be arrays.
3115 @@ -255,9 +283,6 @@ public class Config
3116 public String[] getMultiValue (String name)
3118 Object value = _data.get(name);
3119 - if (value == null) {
3120 - return new String[] {};
3122 if (value instanceof String) {
3123 return new String[] { (String)value };
3125 @@ -355,7 +380,7 @@ public class Config
3126 protected static boolean checkQualifiers (String quals, String osname, String osarch)
3128 if (quals.startsWith("!")) {
3129 - if (quals.indexOf(",") != -1) { // sanity check
3130 + if (quals.contains(",")) { // sanity check
3131 log.warning("Multiple qualifiers cannot be used when one of the qualifiers " +
3132 "is negative", "quals", quals);
3134 @@ -375,103 +400,8 @@ public class Config
3136 String[] bits = qual.trim().toLowerCase(Locale.ROOT).split("-");
3137 String os = bits[0], arch = (bits.length > 1) ? bits[1] : "";
3138 - return (osname.indexOf(os) != -1) && (osarch.indexOf(arch) != -1);
3141 - public void mergeConfig(Config newValues, boolean merge) {
3143 - for (Map.Entry<String, Object> entry : newValues.getData().entrySet()) {
3145 - String key = entry.getKey();
3146 - Object nvalue = entry.getValue();
3148 - String mkey = key.indexOf('.') > -1 ? key.substring(key.indexOf('.') + 1) : key;
3149 - if (merge && allowedMergeKeys.contains(mkey)) {
3151 - // merge multi values
3153 - Object value = _data.get(key);
3155 - if (value == null) {
3156 - _data.put(key, nvalue);
3157 - } else if (value instanceof String) {
3158 - if (nvalue instanceof String) {
3160 - // value is String, nvalue is String
3161 - _data.put(key, new String[] { (String)value, (String)nvalue });
3163 - } else if (nvalue instanceof String[]) {
3165 - // value is String, nvalue is String[]
3166 - String[] nvalues = (String[])nvalue;
3167 - String[] newvalues = new String[nvalues.length+1];
3168 - newvalues[0] = (String)value;
3169 - System.arraycopy(nvalues, 0, newvalues, 1, nvalues.length);
3170 - _data.put(key, newvalues);
3173 - } else if (value instanceof String[]) {
3174 - if (nvalue instanceof String) {
3176 - // value is String[], nvalue is String
3177 - String[] values = (String[])value;
3178 - String[] newvalues = new String[values.length+1];
3179 - System.arraycopy(values, 0, newvalues, 0, values.length);
3180 - newvalues[values.length] = (String)nvalue;
3181 - _data.put(key, newvalues);
3183 - } else if (nvalue instanceof String[]) {
3185 - // value is String[], nvalue is String[]
3186 - String[] values = (String[])value;
3187 - String[] nvalues = (String[])nvalue;
3188 - String[] newvalues = new String[values.length + nvalues.length];
3189 - System.arraycopy(values, 0, newvalues, 0, values.length);
3190 - System.arraycopy(nvalues, 0, newvalues, values.length, newvalues.length);
3191 - _data.put(key, newvalues);
3196 - } else if (allowedReplaceKeys.contains(mkey)){
3199 - _data.put(key, nvalue);
3202 - log.warning("Not merging key '"+key+"' into config");
3209 - public String toString() {
3210 - StringBuilder sb = new StringBuilder();
3211 - for (Map.Entry<String, Object> entry : getData().entrySet()) {
3212 - String key = entry.getKey();
3213 - Object val = entry.getValue();
3216 - if (val instanceof String) {
3217 - sb.append((String)val);
3218 - } else if (val instanceof String[]) {
3219 - sb.append(Arrays.toString((String[])val));
3221 - sb.append("Value not String or String[]");
3225 - return sb.toString();
3228 - public Map<String, Object> getData() {
3230 + return (osname.contains(os)) && (osarch.contains(arch));
3233 private final Map<String, Object> _data;
3235 - public static final List<String> allowedReplaceKeys = Arrays.asList("appbase","apparg","jvmarg"); // these are the ones we might use
3236 - public static final List<String> allowedMergeKeys = Arrays.asList("apparg","jvmarg"); // these are the ones we might use
3237 - //private final List<String> allowedMergeKeys = Arrays.asList("apparg","jvmarg","resource","code","java_location"); // (not exhaustive list here)
3239 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
3240 deleted file mode 100644
3241 index 21b056932..000000000
3242 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java
3246 -// Getdown - application installer, patcher and launcher
3247 -// Copyright (C) 2004-2018 Getdown authors
3248 -// https://github.com/threerings/getdown/blob/master/LICENSE
3250 -package com.threerings.getdown.util;
3252 -import java.io.IOException;
3253 -import java.net.HttpURLConnection;
3254 -import java.net.Proxy;
3255 -import java.net.URL;
3256 -import java.net.URLConnection;
3257 -import java.net.URLDecoder;
3259 -import com.threerings.getdown.data.SysProps;
3261 -import static java.nio.charset.StandardCharsets.UTF_8;
3263 -public class ConnectionUtil
3266 - * Opens a connection to a URL, setting the authentication header if user info is present.
3267 - * @param proxy the proxy via which to perform HTTP connections.
3268 - * @param url the URL to which to open a connection.
3269 - * @param connectTimeout if {@code > 0} then a timeout, in seconds, to use when opening the
3270 - * connection. If {@code 0} is supplied, the connection timeout specified via system properties
3271 - * will be used instead.
3272 - * @param readTimeout if {@code > 0} then a timeout, in seconds, to use while reading data from
3273 - * the connection. If {@code 0} is supplied, the read timeout specified via system properties
3274 - * will be used instead.
3276 - public static URLConnection open (Proxy proxy, URL url, int connectTimeout, int readTimeout)
3277 - throws IOException
3279 - URLConnection conn = url.openConnection(proxy);
3281 - // configure a connect timeout, if requested
3282 - int ctimeout = connectTimeout > 0 ? connectTimeout : SysProps.connectTimeout();
3283 - if (ctimeout > 0) {
3284 - conn.setConnectTimeout(ctimeout * 1000);
3287 - // configure a read timeout, if requested
3288 - int rtimeout = readTimeout > 0 ? readTimeout : SysProps.readTimeout();
3289 - if (rtimeout > 0) {
3290 - conn.setReadTimeout(rtimeout * 1000);
3293 - // If URL has a username:password@ before hostname, use HTTP basic auth
3294 - String userInfo = url.getUserInfo();
3295 - if (userInfo != null) {
3296 - // Remove any percent-encoding in the username/password
3297 - userInfo = URLDecoder.decode(userInfo, "UTF-8");
3298 - // Now base64 encode the auth info and make it a single line
3299 - String encoded = Base64.encodeToString(userInfo.getBytes(UTF_8), Base64.DEFAULT).
3300 - replaceAll("\\n","").replaceAll("\\r", "");
3301 - conn.setRequestProperty("Authorization", "Basic " + encoded);
3308 - * Opens a connection to a http or https URL, setting the authentication header if user info is
3309 - * present. Throws a class cast exception if the connection returned is not the right type. See
3310 - * {@link #open} for parameter documentation.
3312 - public static HttpURLConnection openHttp (
3313 - Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException
3315 - return (HttpURLConnection)open(proxy, url, connectTimeout, readTimeout);
3318 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
3319 index 67d033086..d7de78bbd 100644
3320 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java
3321 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/FileUtil.java
3323 package com.threerings.getdown.util;
3326 -import java.nio.file.Files;
3327 -import java.nio.file.Paths;
3329 import java.util.jar.*;
3330 -import java.util.zip.GZIPInputStream;
3331 +import java.util.zip.*;
3333 -import org.apache.commons.compress.archivers.ArchiveEntry;
3334 -import org.apache.commons.compress.archivers.ArchiveInputStream;
3335 -import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
3336 -import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
3338 -import com.threerings.getdown.util.StreamUtil;
3339 import com.threerings.getdown.Log;
3340 import static com.threerings.getdown.Log.log;
3343 * File related utilities.
3345 -public class FileUtil
3346 +public final class FileUtil
3349 * Gets the specified source file to the specified destination file by hook or crook. Windows
3350 @@ -122,13 +114,13 @@ public class FileUtil
3351 * @param cleanExistingDirs if true, all files in all directories contained in {@code jar} will
3352 * be deleted prior to unpacking the jar.
3354 - public static void unpackJar (JarFile jar, File target, boolean cleanExistingDirs)
3355 + public static void unpackJar (ZipFile jar, File target, boolean cleanExistingDirs)
3358 if (cleanExistingDirs) {
3359 - Enumeration<?> entries = jar.entries();
3360 + Enumeration<? extends ZipEntry> entries = jar.entries();
3361 while (entries.hasMoreElements()) {
3362 - JarEntry entry = (JarEntry)entries.nextElement();
3363 + ZipEntry entry = entries.nextElement();
3364 if (entry.isDirectory()) {
3365 File efile = new File(target, entry.getName());
3366 if (efile.exists()) {
3367 @@ -141,9 +133,9 @@ public class FileUtil
3371 - Enumeration<?> entries = jar.entries();
3372 + Enumeration<? extends ZipEntry> entries = jar.entries();
3373 while (entries.hasMoreElements()) {
3374 - JarEntry entry = (JarEntry)entries.nextElement();
3375 + ZipEntry entry = entries.nextElement();
3376 File efile = new File(target, entry.getName());
3378 // if we're unpacking a normal jar file, it will have special path
3379 @@ -164,7 +156,7 @@ public class FileUtil
3382 try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile));
3383 - InputStream jin = jar.getInputStream(entry)) {
3384 + InputStream jin = jar.getInputStream(entry)) {
3385 StreamUtil.copy(jin, fout);
3386 } catch (Exception e) {
3387 throw new IOException(
3388 @@ -174,82 +166,13 @@ public class FileUtil
3392 - * Unpacks the specified tgz file into the specified target directory.
3393 - * @param cleanExistingDirs if true, all files in all directories contained in {@code tgz} will
3394 - * be deleted prior to unpacking the tgz.
3396 - public static void unpackTgz (TarArchiveInputStream tgz, File target, boolean cleanExistingDirs)
3397 - throws IOException
3399 - TarArchiveEntry entry;
3400 - while ((entry = tgz.getNextTarEntry()) != null) {
3401 - // sanitize the entry name
3402 - String entryName = entry.getName();
3403 - if (entryName.startsWith(File.separator))
3405 - entryName = entryName.substring(File.separator.length());
3407 - File efile = new File(target, entryName);
3409 - // if we're unpacking a normal tgz file, it will have special path
3410 - // entries that allow us to create our directories first
3411 - if (entry.isDirectory()) {
3413 - if (cleanExistingDirs) {
3414 - if (efile.exists()) {
3415 - for (File f : efile.listFiles()) {
3416 - if (!f.isDirectory())
3422 - if (!efile.exists() && !efile.mkdir()) {
3423 - log.warning("Failed to create tgz entry path", "tgz", tgz, "entry", entry);
3428 - // but some do not, so we want to ensure that our directories exist
3429 - // prior to getting down and funky
3430 - File parent = new File(efile.getParent());
3431 - if (!parent.exists() && !parent.mkdirs()) {
3432 - log.warning("Failed to create tgz entry parent", "tgz", tgz, "parent", parent);
3436 - if (entry.isLink())
3438 - System.out.println("Creating hard link "+efile.getName()+" -> "+entry.getLinkName());
3439 - Files.createLink(efile.toPath(), Paths.get(entry.getLinkName()));
3443 - if (entry.isSymbolicLink())
3445 - System.out.println("Creating symbolic link "+efile.getName()+" -> "+entry.getLinkName());
3446 - Files.createSymbolicLink(efile.toPath(), Paths.get(entry.getLinkName()));
3450 - try (BufferedOutputStream fout = new BufferedOutputStream(new FileOutputStream(efile));
3451 - InputStream tin = tgz;) {
3452 - StreamUtil.copy(tin, fout);
3453 - } catch (Exception e) {
3454 - throw new IOException(
3455 - Log.format("Failure unpacking", "tgz", tgz, "entry", efile), e);
3461 - * Unpacks a pack200 packed jar file from {@code packedJar} into {@code target}. If {@code
3462 - * packedJar} has a {@code .gz} extension, it will be gunzipped first.
3463 + * Unpacks a pack200 packed jar file from {@code packedJar} into {@code target}.
3464 + * If {@code packedJar} has a {@code .gz} extension, it will be gunzipped first.
3466 public static void unpackPacked200Jar (File packedJar, File target) throws IOException
3468 try (InputStream packJarIn = new FileInputStream(packedJar);
3469 - JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(target))) {
3470 + JarOutputStream jarOut = new JarOutputStream(new FileOutputStream(target))) {
3471 boolean gz = (packedJar.getName().endsWith(".gz") ||
3472 packedJar.getName().endsWith(".gz_new"));
3473 try (InputStream packJarIn2 = (gz ? new GZIPInputStream(packJarIn) : packJarIn)) {
3474 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
3475 index f2f7ef39f..c05992abd 100644
3476 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java
3477 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/HostWhitelist.java
3478 @@ -25,8 +25,6 @@ public final class HostWhitelist
3480 public static URL verify (URL url) throws MalformedURLException
3484 return verify(Build.hostWhitelist(), url);
3487 @@ -43,12 +41,6 @@ public final class HostWhitelist
3490 String urlHost = url.getHost();
3491 - String protocol = url.getProtocol();
3493 - if (ALLOW_LOCATOR_FILE_PROTOCOL && protocol.equals("file") && urlHost.equals("")) {
3497 for (String host : hosts) {
3498 String regex = host.replace(".", "\\.").replace("*", ".*");
3499 if (urlHost.matches(regex)) {
3500 @@ -59,5 +51,4 @@ public final class HostWhitelist
3501 throw new MalformedURLException(
3502 "The host for the specified URL (" + url + ") is not in the host whitelist: " + hosts);
3504 - private static boolean ALLOW_LOCATOR_FILE_PROTOCOL = true;
3506 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
3507 index f2cd5736e..cc51794ef 100644
3508 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java
3509 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/LaunchUtil.java
3510 @@ -17,21 +17,23 @@ import static com.threerings.getdown.Log.log;
3511 * Useful routines for launching Java applications from within other Java
3514 -public class LaunchUtil
3515 +public final class LaunchUtil
3517 - /** The directory into which a local VM installation should be unpacked. */
3518 - public static final String LOCAL_JAVA_DIR = "jre";
3519 + /** The default directory into which a local VM installation should be unpacked. */
3520 + public static final String LOCAL_JAVA_DIR = "java_vm";
3523 - * Writes a <code>version.txt</code> file into the specified application directory and
3524 + * Writes a {@code version.txt} file into the specified application directory and
3525 * attempts to relaunch Getdown in that directory which will cause it to upgrade to the newly
3526 * specified version and relaunch the application.
3528 * @param appdir the directory in which the application is installed.
3529 * @param getdownJarName the name of the getdown jar file in the application directory. This is
3530 - * probably <code>getdown-pro.jar</code> or <code>getdown-retro-pro.jar</code> if you are using
3531 + * probably {@code getdown-pro.jar} or {@code getdown-retro-pro.jar} if you are using
3532 * the results of the standard build.
3533 * @param newVersion the new version to which Getdown will update when it is executed.
3534 + * @param javaLocalDir the name of the directory (inside {@code appdir}) that contains a
3535 + * locally installed JRE. Defaults to {@link #LOCAL_JAVA_DIR} if null is passed.
3537 * @return true if the relaunch succeeded, false if we were unable to relaunch due to being on
3538 * Windows 9x where we cannot launch subprocesses without waiting around for them to exit,
3539 @@ -39,13 +41,13 @@ public class LaunchUtil
3540 * after making this call as it will be upgraded and restarted. If false is returned, the
3541 * application should tell the user that they must restart the application manually.
3543 - * @exception IOException thrown if we were unable to create the <code>version.txt</code> file
3544 + * @exception IOException thrown if we were unable to create the {@code version.txt} file
3545 * in the supplied application directory. If the version.txt file cannot be created, restarting
3546 * Getdown will not cause the application to be upgraded, so the application will have to
3547 * resort to telling the user that it is in a bad way.
3549 public static boolean updateVersionAndRelaunch (
3550 - File appdir, String getdownJarName, String newVersion)
3551 + File appdir, String getdownJarName, String newVersion, String javaLocalDir)
3554 // create the file that instructs Getdown to upgrade
3555 @@ -61,9 +63,9 @@ public class LaunchUtil
3559 - String[] args = new String[] {
3560 - getJVMPath(appdir), "-jar", pro.toString(), appdir.getPath()
3562 + String javaDir = StringUtil.isBlank(javaLocalDir) ? LOCAL_JAVA_DIR : javaLocalDir;
3563 + String javaBin = getJVMBinaryPath(new File(appdir, javaDir), false);
3564 + String[] args = { javaBin, "-jar", pro.toString(), appdir.getPath() };
3565 log.info("Running " + StringUtil.join(args, "\n "));
3567 Runtime.getRuntime().exec(args, null);
3568 @@ -75,25 +77,15 @@ public class LaunchUtil
3572 - * Reconstructs the path to the JVM used to launch this process.
3574 - public static String getJVMPath (File appdir)
3576 - return getJVMPath(appdir, false);
3580 - * Reconstructs the path to the JVM used to launch this process.
3582 + * Resolves a path to a JVM binary.
3583 + * @param javaLocalDir JRE location within appdir.
3584 * @param windebug if true we will use java.exe instead of javaw.exe on Windows.
3585 + * @return the path to the JVM binary used to launch this process.
3587 - public static String getJVMPath (File appdir, boolean windebug)
3588 + public static String getJVMBinaryPath (File javaLocalDir, boolean windebug)
3590 // first look in our application directory for an installed VM
3591 - String vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR).getAbsolutePath(), windebug);
3592 - if (vmpath == null && isMacOS()) {
3593 - vmpath = checkJVMPath(new File(appdir, LOCAL_JAVA_DIR + "/Contents/Home").getAbsolutePath(), windebug);
3595 + String vmpath = checkJVMPath(javaLocalDir.getAbsolutePath(), windebug);
3597 // then fall back to the VM in which we're already running
3598 if (vmpath == null) {
3599 @@ -102,7 +94,7 @@ public class LaunchUtil
3601 // then throw up our hands and hope for the best
3602 if (vmpath == null) {
3603 - log.warning("Unable to find java [appdir=" + appdir +
3604 + log.warning("Unable to find java [local=" + javaLocalDir +
3605 ", java.home=" + System.getProperty("java.home") + "]!");
3608 @@ -153,7 +145,7 @@ public class LaunchUtil
3609 if (newgd.renameTo(curgd)) {
3610 FileUtil.deleteHarder(oldgd); // yay!
3612 - // copy the moved file back to getdown-dop-new.jar so that we don't end up
3613 + // copy the moved file back to newgd so that we don't end up
3614 // downloading another copy next time
3615 FileUtil.copy(curgd, newgd);
3616 } catch (IOException e) {
3617 @@ -185,23 +177,23 @@ public class LaunchUtil
3618 public static boolean mustMonitorChildren ()
3620 String osname = System.getProperty("os.name", "").toLowerCase(Locale.ROOT);
3621 - return (osname.indexOf("windows 98") != -1 || osname.indexOf("windows me") != -1);
3622 + return (osname.contains("windows 98") || osname.contains("windows me"));
3626 * Returns true if we're running in a JVM that identifies its operating system as Windows.
3628 - public static final boolean isWindows () { return _isWindows; }
3629 + public static boolean isWindows () { return _isWindows; }
3632 * Returns true if we're running in a JVM that identifies its operating system as MacOS.
3634 - public static final boolean isMacOS () { return _isMacOS; }
3635 + public static boolean isMacOS () { return _isMacOS; }
3638 * Returns true if we're running in a JVM that identifies its operating system as Linux.
3640 - public static final boolean isLinux () { return _isLinux; }
3641 + public static boolean isLinux () { return _isLinux; }
3644 * Checks whether a Java Virtual Machine can be located in the supplied path.
3645 @@ -240,10 +232,9 @@ public class LaunchUtil
3647 String osname = System.getProperty("os.name");
3648 osname = (osname == null) ? "" : osname;
3649 - _isWindows = (osname.indexOf("Windows") != -1);
3650 - _isMacOS = (osname.indexOf("Mac OS") != -1 ||
3651 - osname.indexOf("MacOS") != -1);
3652 - _isLinux = (osname.indexOf("Linux") != -1);
3653 + _isWindows = (osname.contains("Windows"));
3654 + _isMacOS = (osname.contains("Mac OS") || osname.contains("MacOS"));
3655 + _isLinux = (osname.contains("Linux"));
3656 } catch (Exception e) {
3657 // can't grab system properties; we'll just pretend we're not on any of these OSes
3659 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
3660 index 28dbdcff5..f2ee9a265 100644
3661 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java
3662 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/MessageUtil.java
3665 package com.threerings.getdown.util;
3667 -public class MessageUtil {
3668 +public final class MessageUtil {
3671 * Returns whether or not the provided string is tainted. See {@link #taint}. Null strings
3672 @@ -101,11 +101,11 @@ public class MessageUtil {
3676 - * Used to escape single quotes so that they are not interpreted by <code>MessageFormat</code>.
3677 + * Used to escape single quotes so that they are not interpreted by {@code MessageFormat}.
3678 * As we assume all single quotes are to be escaped, we cannot use the characters
3679 * <code>{</code> and <code>}</code> in our translation strings, but this is a small price to
3680 * pay to have to differentiate between messages that will and won't eventually be parsed by a
3681 - * <code>MessageFormat</code> instance.
3682 + * {@code MessageFormat} instance.
3684 public static String escape (String message)
3686 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
3687 index ad4c560a4..ac3e0a3de 100644
3688 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java
3689 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/ProgressObserver.java
3690 @@ -14,5 +14,5 @@ public interface ProgressObserver
3691 * Informs the observer that we have completed the specified
3692 * percentage of the process.
3694 - public void progress (int percent);
3695 + void progress (int percent);
3697 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
3698 index 373cfff00..5cb1a405b 100644
3699 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java
3700 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StreamUtil.java
3701 @@ -11,11 +11,10 @@ import java.io.InputStream;
3702 import java.io.OutputStream;
3703 import java.io.Reader;
3704 import java.io.Writer;
3705 -import java.nio.charset.Charset;
3707 import static com.threerings.getdown.Log.log;
3709 -public class StreamUtil {
3710 +public final class StreamUtil {
3712 * Convenient close for a stream. Use in a finally clause and love life.
3714 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
3715 index 03d3c9ccd..86277c881 100644
3716 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java
3717 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/StringUtil.java
3718 @@ -7,14 +7,14 @@ package com.threerings.getdown.util;
3720 import java.util.StringTokenizer;
3722 -public class StringUtil {
3723 +public final class StringUtil {
3726 * @return true if the specified string could be a valid URL (contains no illegal characters)
3728 public static boolean couldBeValidUrl (String url)
3730 - return url.matches("[A-Za-z0-9\\-\\._~:/\\?#\\[\\]@!$&'\\(\\)\\*\\+,;=%]+");
3731 + return url.matches("[A-Za-z0-9\\-._~:/?#\\[\\]@!$&'()*+,;=%]+");
3735 @@ -84,7 +84,7 @@ public class StringUtil {
3736 source = source.replace(",,", "%COMMA%");
3738 // count up the number of tokens
3739 - while ((tpos = source.indexOf(",", tpos+1)) != -1) {
3740 + while ((tpos = source.indexOf(',', tpos+1)) != -1) {
3744 @@ -92,7 +92,7 @@ public class StringUtil {
3745 tpos = -1; tcount = 0;
3748 - while ((tpos = source.indexOf(",", tpos+1)) != -1) {
3749 + while ((tpos = source.indexOf(',', tpos+1)) != -1) {
3750 tokens[tcount] = source.substring(tstart, tpos);
3751 tokens[tcount] = tokens[tcount].trim().replace("%COMMA%", ",");
3753 @@ -119,7 +119,7 @@ public class StringUtil {
3756 * Generates a string from the supplied bytes that is the HEX encoded representation of those
3757 - * bytes. Returns the empty string for a <code>null</code> or empty byte array.
3758 + * bytes. Returns the empty string for a {@code null} or empty byte array.
3760 * @param bytes the bytes for which we want a string representation.
3761 * @param count the number of bytes to stop at (which will be coerced into being {@code <=} the
3762 @@ -185,7 +185,7 @@ public class StringUtil {
3766 - * Helper function for the various <code>join</code> methods.
3767 + * Helper function for the various {@code join} methods.
3769 protected static String join (Object[] values, String separator, boolean escape)
3771 @@ -201,6 +201,6 @@ public class StringUtil {
3772 return buf.toString();
3775 - /** Used by {@link #hexlate} and {@link #unhexlate}. */
3776 + /** Used by {@link #hexlate}. */
3777 protected static final String XLATE = "0123456789abcdef";
3779 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
3780 index 49e4e6e0e..b2f289415 100644
3781 --- a/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java
3782 +++ b/getdown/src/getdown/core/src/main/java/com/threerings/getdown/util/VersionUtil.java
3783 @@ -22,7 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
3785 * Version related utilities.
3787 -public class VersionUtil
3788 +public final class VersionUtil
3791 * Reads a version number from a file.
3792 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
3793 deleted file mode 100644
3794 index 8af09da6d..000000000
3795 --- a/getdown/src/getdown/core/src/main/java/jalview/bin/MemorySetting.java
3798 -package jalview.bin;
3800 -import java.lang.management.ManagementFactory;
3801 -import java.lang.management.OperatingSystemMXBean;
3803 -public class MemorySetting
3805 - public static final long leaveFreeMinMemory = 536870912; // 0.5 GB
3807 - public static final long applicationMinMemory = 536870912; // 0.5 GB
3809 - protected static long getPhysicalMemory()
3811 - final OperatingSystemMXBean o = ManagementFactory
3812 - .getOperatingSystemMXBean();
3816 - if (o instanceof com.sun.management.OperatingSystemMXBean)
3818 - final com.sun.management.OperatingSystemMXBean osb = (com.sun.management.OperatingSystemMXBean) o;
3819 - return osb.getTotalPhysicalMemorySize();
3821 - } catch (NoClassDefFoundError e)
3823 - // com.sun.management.OperatingSystemMXBean doesn't exist in this JVM
3824 - System.err.println("No com.sun.management.OperatingSystemMXBean");
3827 - // We didn't get a com.sun.management.OperatingSystemMXBean.
3831 - public static long memPercent(int percent)
3833 - long memPercent = -1;
3835 - long physicalMem = getPhysicalMemory();
3836 - if (physicalMem > applicationMinMemory)
3838 - // try and set at least applicationMinMemory and thereafter ensure
3839 - // leaveFreeMinMemory is left for the OS
3840 - memPercent = Math.max(applicationMinMemory,
3841 - physicalMem - Math.max(physicalMem * (100 - percent) / 100,
3842 - leaveFreeMinMemory));
3845 - return memPercent;
3849 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
3850 index d5a393745..c7fcf7271 100644
3851 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java
3852 +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/GarbageCollectorTest.java
3853 @@ -7,23 +7,36 @@ package com.threerings.getdown.cache;
3855 import java.io.File;
3856 import java.io.IOException;
3857 +import java.util.Arrays;
3858 +import java.util.Collection;
3859 import java.util.concurrent.TimeUnit;
3862 import org.junit.rules.TemporaryFolder;
3864 +import org.junit.runner.RunWith;
3865 +import org.junit.runners.Parameterized;
3866 import static org.junit.Assert.*;
3867 import static org.junit.Assume.assumeTrue;
3870 * Validates that cache garbage is collected and deleted correctly.
3872 +@RunWith(Parameterized.class)
3873 public class GarbageCollectorTest
3875 + @Parameterized.Parameters(name = "{0}")
3876 + public static Collection<Object[]> data() {
3877 + return Arrays.asList(new Object[][] {{ ".jar" }, { ".zip" }});
3880 + @Parameterized.Parameter
3881 + public String extension;
3883 @Before public void setupFiles () throws IOException
3885 - _cachedFile = _folder.newFile("abc123.jar");
3886 - _lastAccessedFile = _folder.newFile("abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
3887 + _cachedFile = _folder.newFile("abc123" + extension);
3888 + _lastAccessedFile = _folder.newFile(
3889 + "abc123" + extension + ResourceCache.LAST_ACCESSED_FILE_SUFFIX);
3892 @Test public void shouldDeleteCacheEntryIfRetentionPeriodIsReached ()
3893 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
3894 index 860c72a37..6acffda32 100644
3895 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java
3896 +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/cache/ResourceCacheTest.java
3897 @@ -7,26 +7,38 @@ package com.threerings.getdown.cache;
3899 import java.io.File;
3900 import java.io.IOException;
3901 +import java.util.Arrays;
3902 +import java.util.Collection;
3903 import java.util.concurrent.TimeUnit;
3906 import org.junit.rules.TemporaryFolder;
3908 +import org.junit.runner.RunWith;
3909 +import org.junit.runners.Parameterized;
3910 import static org.junit.Assert.*;
3913 * Asserts the correct functionality of the {@link ResourceCache}.
3915 +@RunWith(Parameterized.class)
3916 public class ResourceCacheTest
3918 + @Parameterized.Parameters(name = "{0}")
3919 + public static Collection<Object[]> data() {
3920 + return Arrays.asList(new Object[][] {{ ".jar" }, { ".zip" }});
3923 + @Parameterized.Parameter
3924 + public String extension;
3926 @Before public void setupCache () throws IOException {
3927 - _fileToCache = _folder.newFile("filetocache.jar");
3928 + _fileToCache = _folder.newFile("filetocache" + extension);
3929 _cache = new ResourceCache(_folder.newFolder(".cache"));
3932 @Test public void shouldCacheFile () throws IOException
3934 - assertEquals("abc123.jar", cacheFile().getName());
3935 + assertEquals("abc123" + extension, cacheFile().getName());
3938 private File cacheFile() throws IOException
3939 @@ -36,7 +48,7 @@ public class ResourceCacheTest
3941 @Test public void shouldTrackFileUsage () throws IOException
3943 - String name = "abc123.jar" + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
3944 + String name = "abc123" + extension + ResourceCache.LAST_ACCESSED_FILE_SUFFIX;
3945 File lastAccessedFile = new File(cacheFile().getParentFile(), name);
3946 assertTrue(lastAccessedFile.exists());
3948 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
3949 index 61786518c..04a73d38b 100644
3950 --- a/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java
3951 +++ b/getdown/src/getdown/core/src/test/java/com/threerings/getdown/data/EnvConfigTest.java
3952 @@ -19,13 +19,13 @@ public class EnvConfigTest {
3953 static String TESTID = "testid";
3954 static String TESTBASE = "https://test.com/test";
3956 - private void debugNotes(List<EnvConfig.Note> notes) {
3957 + private void debugNotes (List<EnvConfig.Note> notes) {
3958 for (EnvConfig.Note note : notes) {
3959 System.out.println(note.message);
3963 - private void checkNoNotes (List<EnvConfig.Note> notes) {
3964 + static void checkNoNotes (List<EnvConfig.Note> notes) {
3965 StringBuilder msg = new StringBuilder();
3966 for (EnvConfig.Note note : notes) {
3967 if (note.level != EnvConfig.Note.Level.INFO) {
3968 diff --git a/getdown/src/getdown/launcher/.project-MOVED b/getdown/src/getdown/launcher/.project-MOVED
3969 deleted file mode 100644
3970 index d77a6e8db..000000000
3971 --- a/getdown/src/getdown/launcher/.project-MOVED
3974 -<?xml version="1.0" encoding="UTF-8"?>
3975 -<projectDescription>
3976 - <name>getdown-launcher</name>
3977 - <comment></comment>
3982 - <name>org.eclipse.jdt.core.javabuilder</name>
3987 - <name>org.eclipse.m2e.core.maven2Builder</name>
3993 - <nature>org.eclipse.jdt.core.javanature</nature>
3994 - <nature>org.eclipse.m2e.core.maven2Nature</nature>
3996 -</projectDescription>
3997 diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs
3998 deleted file mode 100644
3999 index abdea9ac0..000000000
4000 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.core.resources.prefs
4003 -eclipse.preferences.version=1
4004 -encoding//src/main/java=UTF-8
4005 -encoding//src/main/resources=UTF-8
4006 -encoding/<project>=UTF-8
4007 diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs
4008 deleted file mode 100644
4009 index 54e56721d..000000000
4010 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.jdt.core.prefs
4013 -eclipse.preferences.version=1
4014 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4015 -org.eclipse.jdt.core.compiler.compliance=1.7
4016 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
4017 -org.eclipse.jdt.core.compiler.release=disabled
4018 -org.eclipse.jdt.core.compiler.source=1.7
4019 diff --git a/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs b/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs
4020 deleted file mode 100644
4021 index f897a7f1c..000000000
4022 --- a/getdown/src/getdown/launcher/.settings/org.eclipse.m2e.core.prefs
4026 -eclipse.preferences.version=1
4027 -resolveWorkspaceProjects=true
4029 diff --git a/getdown/src/getdown/launcher/pom.xml b/getdown/src/getdown/launcher/pom.xml
4030 index e77061a3b..f55234977 100644
4031 --- a/getdown/src/getdown/launcher/pom.xml
4032 +++ b/getdown/src/getdown/launcher/pom.xml
4035 <groupId>com.threerings.getdown</groupId>
4036 <artifactId>getdown</artifactId>
4037 - <version>1.8.3-SNAPSHOT</version>
4038 + <version>1.8.7-SNAPSHOT</version>
4041 <artifactId>getdown-launcher</artifactId>
4043 <groupId>com.threerings.getdown</groupId>
4044 <artifactId>getdown-core</artifactId>
4045 <version>${project.version}</version>
4046 + <optional>true</optional>
4049 <groupId>com.samskivert</groupId>
4050 <artifactId>samskivert</artifactId>
4051 <version>1.2</version>
4052 + <optional>true</optional>
4055 <groupId>jregistrykey</groupId>
4057 <version>1.0</version>
4058 <optional>true</optional>
4061 + <groupId>junit</groupId>
4062 + <artifactId>junit</artifactId>
4063 + <version>4.12</version>
4064 + <scope>test</scope>
4072 + <groupId>org.codehaus.mojo</groupId>
4073 + <artifactId>native2ascii-maven-plugin</artifactId>
4074 + <version>2.0.1</version>
4077 + <id>utf8-to-latin1</id>
4079 + <goal>inplace</goal>
4081 + <phase>process-resources</phase>
4083 + <dir>${project.build.outputDirectory}</dir>
4084 + <encoding>${project.build.sourceEncoding}</encoding>
4086 + <include>**/*.properties</include>
4094 <groupId>com.github.wvengen</groupId>
4095 <artifactId>proguard-maven-plugin</artifactId>
4096 <version>2.0.14</version>
4099 - <phase>package</phase>
4101 - <goal>proguard</goal>
4105 + <phase>package</phase>
4106 + <goals><goal>proguard</goal></goals>
4112 <addMavenDescriptor>false</addMavenDescriptor>
4118 <groupId>org.apache.maven.plugins</groupId>
4119 <artifactId>maven-jar-plugin</artifactId>
4120 @@ -130,40 +157,6 @@
4126 - <groupId>org.apache.maven.plugins</groupId>
4127 - <artifactId>maven-shade-plugin</artifactId>
4128 - <version>3.2.1</version>
4130 - <!-- put your configurations here -->
4134 - <phase>package</phase>
4136 - <goal>shade</goal>
4140 - <minimizeJar>true</minimizeJar>
4143 - <artifact>install4j-runtime</artifact>
4145 - <include>**</include>
4162 <lib>${java.home}/jmods/java.base.jmod</lib>
4163 <lib>${java.home}/jmods/java.desktop.jmod</lib>
4164 <lib>${java.home}/jmods/java.logging.jmod</lib>
4165 + <lib>${java.home}/jmods/java.scripting.jmod</lib>
4166 <lib>${java.home}/jmods/jdk.jsobject.jmod</lib>
4169 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
4170 index dc1e54e46..4bd9f90b0 100644
4171 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java
4172 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/AbortPanel.java
4173 @@ -72,7 +72,7 @@ public final class AbortPanel extends JFrame
4174 public void actionPerformed (ActionEvent e)
4176 String cmd = e.getActionCommand();
4177 - if (cmd.equals("ok")) {
4178 + if ("ok".equals(cmd)) {
4182 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
4183 index 5750fce66..1cc6922c8 100644
4184 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java
4185 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java
4186 @@ -9,51 +9,73 @@ import java.awt.BorderLayout;
4187 import java.awt.Container;
4188 import java.awt.Dimension;
4189 import java.awt.EventQueue;
4190 -import java.awt.Graphics;
4191 import java.awt.GraphicsEnvironment;
4192 import java.awt.Image;
4193 import java.awt.event.ActionEvent;
4194 import java.awt.image.BufferedImage;
4195 import java.io.BufferedReader;
4196 import java.io.File;
4197 -import java.io.FileInputStream;
4198 import java.io.FileNotFoundException;
4199 -import java.io.FileReader;
4200 import java.io.IOException;
4201 import java.io.InputStream;
4202 import java.io.InputStreamReader;
4203 import java.io.PrintStream;
4204 import java.net.HttpURLConnection;
4205 -import java.net.MalformedURLException;
4206 import java.net.URL;
4207 -import java.util.*;
4208 +import java.util.ArrayList;
4209 +import java.util.Arrays;
4210 +import java.util.Collection;
4211 +import java.util.HashSet;
4212 +import java.util.List;
4213 +import java.util.Locale;
4214 +import java.util.ResourceBundle;
4215 +import java.util.Set;
4216 +import java.util.concurrent.TimeUnit;
4218 import javax.imageio.ImageIO;
4219 import javax.swing.AbstractAction;
4220 import javax.swing.JButton;
4221 import javax.swing.JFrame;
4222 import javax.swing.JLayeredPane;
4223 -import javax.swing.JPanel;
4225 import com.samskivert.swing.util.SwingUtil;
4226 -import com.threerings.getdown.data.*;
4227 +import com.threerings.getdown.data.Application;
4228 import com.threerings.getdown.data.Application.UpdateInterface.Step;
4229 +import com.threerings.getdown.data.Build;
4230 +import com.threerings.getdown.data.EnvConfig;
4231 +import com.threerings.getdown.data.Resource;
4232 +import com.threerings.getdown.data.SysProps;
4233 import com.threerings.getdown.net.Downloader;
4234 -import com.threerings.getdown.net.HTTPDownloader;
4235 import com.threerings.getdown.tools.Patcher;
4236 -import com.threerings.getdown.util.*;
4238 +import com.threerings.getdown.util.Config;
4239 +import com.threerings.getdown.util.FileUtil;
4240 +import com.threerings.getdown.util.LaunchUtil;
4241 +import com.threerings.getdown.util.MessageUtil;
4242 +import com.threerings.getdown.util.ProgressAggregator;
4243 +import com.threerings.getdown.util.ProgressObserver;
4244 +import com.threerings.getdown.util.StringUtil;
4245 +import com.threerings.getdown.util.VersionUtil;
4246 import static com.threerings.getdown.Log.log;
4249 * Manages the main control for the Getdown application updater and deployment system.
4251 -public abstract class Getdown extends Thread
4252 +public abstract class Getdown
4253 implements Application.StatusDisplay, RotatingBackgrounds.ImageLoader
4256 + * Starts a thread to run Getdown and ultimately (hopefully) launch the target app.
4258 + public static void run (final Getdown getdown) {
4259 + new Thread("Getdown") {
4260 + @Override public void run () {
4266 public Getdown (EnvConfig envc)
4270 // If the silent property exists, install without bringing up any gui. If it equals
4271 // launch, start the application after installing. Otherwise, just install and exit.
4272 @@ -80,7 +102,7 @@ public abstract class Getdown extends Thread
4273 // welcome to hell, where java can't cope with a classpath that contains jars that live
4274 // in a directory that contains a !, at least the same bug happens on all platforms
4275 String dir = envc.appDir.toString();
4276 - if (dir.equals(".")) {
4277 + if (".".equals(dir)) {
4278 dir = System.getProperty("user.dir");
4280 String errmsg = "The directory in which this application is installed:\n" + dir +
4281 @@ -107,7 +129,7 @@ public abstract class Getdown extends Thread
4283 if (SysProps.noInstall()) {
4284 log.info("Skipping install due to 'no_install' sysprop.");
4285 - } else if (_readyToInstall) {
4286 + } else if (isUpdateAvailable()) {
4287 log.info("Installing " + _toInstallResources.size() + " downloaded resources:");
4288 for (Resource resource : _toInstallResources) {
4289 resource.install(true);
4290 @@ -120,8 +142,28 @@ public abstract class Getdown extends Thread
4295 - public void run ()
4297 + * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher.
4299 + public void configProxy (String host, String port, String username, String password)
4301 + log.info("User configured proxy", "host", host, "port", port);
4302 + ProxyUtil.configProxy(_app, host, port, username, password);
4304 + // clear out our UI
4305 + disposeContainer();
4306 + _container = null;
4308 + // fire up a new thread
4313 + * The main entry point of Getdown: does some sanity checks and preparation, then delegates the
4314 + * actual getting down to {@link #getdown}. This is not called directly, but rather via the
4315 + * static {@code run} method as Getdown does its main work on a separate thread.
4317 + protected void run ()
4319 // if we have no messages, just bail because we're hosed; the error message will be
4320 // displayed to the user already
4321 @@ -135,95 +177,48 @@ public abstract class Getdown extends Thread
4322 File instdir = _app.getLocalPath("");
4323 if (!instdir.canWrite()) {
4324 String path = instdir.getPath();
4325 - if (path.equals(".")) {
4326 + if (".".equals(path)) {
4327 path = System.getProperty("user.dir");
4329 fail(MessageUtil.tcompose("m.readonly_error", path));
4335 - // if we fail to detect a proxy, but we're allowed to run offline, then go ahead and
4336 - // run the app anyway because we're prepared to cope with not being able to update
4337 - if (detectProxy() || _app.allowOffline()) {
4339 - } else if (_silent) {
4340 - log.warning("Need a proxy, but we don't want to bother anyone. Exiting.");
4342 - // create a panel they can use to configure the proxy settings
4343 - _container = createContainer();
4344 - // allow them to close the window to abort the proxy configuration
4346 - configureContainer();
4347 - ProxyPanel panel = new ProxyPanel(this, _msgs);
4348 - // set up any existing configured proxy
4349 - String[] hostPort = ProxyUtil.loadProxy(_app);
4350 - panel.setProxy(hostPort[0], hostPort[1]);
4351 - _container.add(panel, BorderLayout.CENTER);
4355 - } catch (Exception e) {
4356 - log.warning("run() failed.", e);
4357 - String msg = e.getMessage();
4358 - if (msg == null) {
4359 - msg = MessageUtil.compose("m.unknown_error", _ifc.installError);
4360 - } else if (!msg.startsWith("m.")) {
4361 - // try to do something sensible based on the type of error
4362 - if (e instanceof FileNotFoundException) {
4363 - msg = MessageUtil.compose(
4364 - "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
4366 - msg = MessageUtil.compose(
4367 - "m.init_error", MessageUtil.taint(msg), _ifc.installError);
4375 - * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher.
4377 - public void configProxy (String host, String port, String username, String password)
4379 - log.info("User configured proxy", "host", host, "port", port);
4381 - if (!StringUtil.isBlank(host)) {
4382 - ProxyUtil.configProxy(_app, host, port, username, password);
4385 - // clear out our UI
4386 - disposeContainer();
4387 - _container = null;
4389 - // fire up a new thread
4390 - new Thread(this).start();
4392 + // if we fail to detect a proxy, but we're allowed to run offline, then go ahead and
4393 + // run the app anyway because we're prepared to cope with not being able to update
4394 + if (detectProxy() || _app.allowOffline()) getdown();
4395 + else requestProxyInfo(false);
4398 protected boolean detectProxy () {
4399 - if (ProxyUtil.autoDetectProxy(_app)) {
4403 - // otherwise see if we actually need a proxy; first we have to initialize our application
4404 - // to get some sort of interface configuration and the appbase URL
4405 + // first we have to initialize our application to get the appbase URL, etc.
4406 log.info("Checking whether we need to use a proxy...");
4409 } catch (IOException ioe) {
4413 + boolean tryNoProxy = SysProps.tryNoProxyFirst();
4414 + if (!tryNoProxy && ProxyUtil.autoDetectProxy(_app)) {
4418 + // see if we actually need a proxy
4419 updateStatus("m.detecting_proxy");
4420 - if (!ProxyUtil.canLoadWithoutProxy(_app.getConfigResource().getRemote())) {
4422 + URL configURL = _app.getConfigResource().getRemote();
4423 + if (!ProxyUtil.canLoadWithoutProxy(configURL, tryNoProxy ? 2 : 5)) {
4424 + // if we didn't auto-detect proxy first thing, do auto-detect now
4425 + return tryNoProxy ? ProxyUtil.autoDetectProxy(_app) : false;
4428 - // we got through, so we appear not to require a proxy; make a blank proxy config so that
4429 - // we don't go through this whole detection process again next time
4430 log.info("No proxy appears to be needed.");
4431 - ProxyUtil.saveProxy(_app, null, null);
4432 + if (!tryNoProxy) {
4433 + // we got through, so we appear not to require a proxy; make a blank proxy config so
4434 + // that we don't go through this whole detection process again next time
4435 + ProxyUtil.saveProxy(_app, null, null);
4440 @@ -233,6 +228,25 @@ public abstract class Getdown extends Thread
4441 _ifc = new Application.UpdateInterface(config);
4444 + protected void requestProxyInfo (boolean reinitAuth) {
4446 + log.warning("Need a proxy, but we don't want to bother anyone. Exiting.");
4450 + // create a panel they can use to configure the proxy settings
4451 + _container = createContainer();
4452 + // allow them to close the window to abort the proxy configuration
4454 + configureContainer();
4455 + ProxyPanel panel = new ProxyPanel(this, _msgs, reinitAuth);
4456 + // set up any existing configured proxy
4457 + String[] hostPort = ProxyUtil.loadProxy(_app);
4458 + panel.setProxy(hostPort[0], hostPort[1]);
4459 + _container.add(panel, BorderLayout.CENTER);
4464 * Downloads and installs (without verifying) any resources that are marked with a
4465 * {@code PRELOAD} attribute.
4466 @@ -247,13 +261,15 @@ public abstract class Getdown extends Thread
4471 - download(predownloads);
4472 - for (Resource rsrc : predownloads) {
4473 - rsrc.install(false); // install but don't validate yet
4474 + if (!predownloads.isEmpty()) {
4476 + download(predownloads);
4477 + for (Resource rsrc : predownloads) {
4478 + rsrc.install(false); // install but don't validate yet
4480 + } catch (IOException ioe) {
4481 + log.warning("Failed to predownload resources. Continuing...", ioe);
4483 - } catch (IOException ioe) {
4484 - log.warning("Failed to predownload resources. Continuing...", ioe);
4488 @@ -263,7 +279,7 @@ public abstract class Getdown extends Thread
4489 protected void getdown ()
4492 - // first parses our application deployment file
4493 + // first parse our application deployment file
4496 } catch (IOException ioe) {
4497 @@ -278,7 +294,7 @@ public abstract class Getdown extends Thread
4498 throw new MultipleGetdownRunning();
4501 - // Update the config modtime so a sleeping getdown will notice the change.
4502 + // update the config modtime so a sleeping getdown will notice the change
4503 File config = _app.getLocalPath(Application.CONFIG_FILE);
4504 if (!config.setLastModified(System.currentTimeMillis())) {
4505 log.warning("Unable to set modtime on config file, will be unable to check for " +
4506 @@ -290,7 +306,7 @@ public abstract class Getdown extends Thread
4507 // Store the config modtime before waiting the delay amount of time
4508 long lastConfigModtime = config.lastModified();
4509 log.info("Waiting " + _delay + " minutes before beginning actual work.");
4510 - Thread.sleep(_delay * 60 * 1000);
4511 + TimeUnit.MINUTES.sleep(_delay);
4512 if (lastConfigModtime < config.lastModified()) {
4513 log.warning("getdown.txt was modified while getdown was waiting.");
4514 throw new MultipleGetdownRunning();
4515 @@ -339,11 +355,7 @@ public abstract class Getdown extends Thread
4517 if (toDownload.size() > 0) {
4518 // we have resources to download, also note them as to-be-installed
4519 - for (Resource r : toDownload) {
4520 - if (!_toInstallResources.contains(r)) {
4521 - _toInstallResources.add(r);
4524 + _toInstallResources.addAll(toDownload);
4527 // if any of our resources have already been marked valid this is not a
4528 @@ -427,24 +439,20 @@ public abstract class Getdown extends Thread
4529 throw new IOException("m.unable_to_repair");
4531 } catch (Exception e) {
4532 - log.warning("getdown() failed.", e);
4533 - String msg = e.getMessage();
4534 - if (msg == null) {
4535 - msg = MessageUtil.compose("m.unknown_error", _ifc.installError);
4536 - } else if (!msg.startsWith("m.")) {
4537 - // try to do something sensible based on the type of error
4538 - if (e instanceof FileNotFoundException) {
4539 - msg = MessageUtil.compose(
4540 - "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
4542 - msg = MessageUtil.compose(
4543 - "m.init_error", MessageUtil.taint(msg), _ifc.installError);
4545 + // if we failed due to proxy errors, ask for proxy info
4546 + switch (_app.conn.state) {
4548 + requestProxyInfo(false);
4550 + case NEED_PROXY_AUTH:
4551 + requestProxyInfo(true);
4554 + log.warning("getdown() failed.", e);
4556 + _app.releaseLock();
4559 - // Since we're dead, clear off the 'time remaining' label along with displaying the
4562 - _app.releaseLock();
4566 @@ -499,6 +507,18 @@ public abstract class Getdown extends Thread
4567 throw new IOException("m.java_download_failed");
4570 + // on Windows, if the local JVM is in use, we will not be able to replace it with an
4571 + // updated JVM; we detect this by attempting to rename the java.dll to its same name, which
4572 + // will fail on Windows for in use files; hackery!
4573 + File javaLocalDir = _app.getJavaLocalDir();
4574 + File javaDll = new File(javaLocalDir, "bin" + File.separator + "java.dll");
4575 + if (javaDll.exists()) {
4576 + if (!javaDll.renameTo(javaDll)) {
4577 + log.info("Cannot update local Java VM as it is in use.");
4582 reportTrackingEvent("jvm_start", -1);
4584 updateStatus("m.downloading_java");
4585 @@ -507,21 +527,24 @@ public abstract class Getdown extends Thread
4588 reportTrackingEvent("jvm_unpack", -1);
4590 updateStatus("m.unpacking_java");
4591 - vmjar.install(true);
4593 + vmjar.install(true);
4594 + } catch (IOException ioe) {
4595 + throw new IOException("m.java_unpack_failed", ioe);
4598 // these only run on non-Windows platforms, so we use Unix file separators
4599 - String localJavaDir = LaunchUtil.LOCAL_JAVA_DIR + "/";
4600 - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "bin/java"));
4601 - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/jspawnhelper"));
4602 - FileUtil.makeExecutable(_app.getLocalPath(localJavaDir + "lib/amd64/jspawnhelper"));
4603 + FileUtil.makeExecutable(new File(javaLocalDir, "bin/java"));
4604 + FileUtil.makeExecutable(new File(javaLocalDir, "lib/jspawnhelper"));
4605 + FileUtil.makeExecutable(new File(javaLocalDir, "lib/amd64/jspawnhelper"));
4607 // lastly regenerate the .jsa dump file that helps Java to start up faster
4608 - String vmpath = LaunchUtil.getJVMPath(_app.getLocalPath(""));
4609 + String vmpath = LaunchUtil.getJVMBinaryPath(javaLocalDir, false);
4610 + String[] command = { vmpath, "-Xshare:dump" };
4612 log.info("Regenerating classes.jsa for " + vmpath + "...");
4613 - Runtime.getRuntime().exec(vmpath + " -Xshare:dump");
4614 + Runtime.getRuntime().exec(command);
4615 } catch (Exception e) {
4616 log.warning("Failed to regenerate .jsa dump file", "error", e);
4618 @@ -578,6 +601,8 @@ public abstract class Getdown extends Thread
4619 int ii = 0; for (Resource prsrc : list) {
4620 ProgressObserver pobs = pragg.startElement(ii++);
4622 + // if this patch file failed to download, skip it
4623 + if (!prsrc.getLocalNew().exists()) continue;
4624 // install the patch file (renaming them from _new)
4625 prsrc.install(false);
4626 // now apply the patch
4627 @@ -613,7 +638,7 @@ public abstract class Getdown extends Thread
4628 // create our user interface
4629 createInterfaceAsync(false);
4631 - Downloader dl = new HTTPDownloader(_app.proxy) {
4632 + Downloader dl = new Downloader(_app.conn) {
4633 @Override protected void resolvingDownloads () {
4634 updateStatus("m.resolving");
4636 @@ -640,6 +665,10 @@ public abstract class Getdown extends Thread
4637 log.warning("Download failed", "rsrc", rsrc, e);
4640 + @Override protected void resourceMissing (Resource rsrc) {
4641 + log.warning("Resource missing (got 404)", "rsrc", rsrc);
4644 /** The last percentage at which we checked for another getdown running, or -1 for not
4645 * having checked at all. */
4646 protected int _lastCheck = -1;
4647 @@ -725,7 +754,7 @@ public abstract class Getdown extends Thread
4648 long minshow = _ifc.minShowSeconds * 1000L;
4649 if (_container != null && uptime < minshow) {
4651 - Thread.sleep(minshow - uptime);
4652 + TimeUnit.MILLISECONDS.sleep(minshow - uptime);
4653 } catch (Exception e) {
4656 @@ -750,10 +779,9 @@ public abstract class Getdown extends Thread
4657 if (_silent || (_container != null && !reinit)) {
4662 EventQueue.invokeLater(new Runnable() {
4663 public void run () {
4665 if (_container == null || reinit) {
4666 if (_container == null) {
4667 _container = createContainer();
4668 @@ -762,42 +790,6 @@ public abstract class Getdown extends Thread
4670 configureContainer();
4671 _layers = new JLayeredPane();
4675 - // added in the instant display of a splashscreen
4677 - readConfig(false);
4678 - Graphics g = _container.getGraphics();
4679 - String imageFile = _ifc.backgroundImage;
4680 - BufferedImage bgImage = loadImage(_ifc.backgroundImage);
4681 - int bwidth = bgImage.getWidth();
4682 - int bheight = bgImage.getHeight();
4684 - instantSplashPane = new JPanel() {
4686 - protected void paintComponent(Graphics g)
4688 - super.paintComponent(g);
4689 - // attempt to draw a background image...
4690 - if (bgImage != null) {
4691 - g.drawImage(bgImage, 0, 0, this);
4696 - instantSplashPane.setSize(bwidth,bheight);
4697 - instantSplashPane.setPreferredSize(new Dimension(bwidth,bheight));
4699 - _layers.add(instantSplashPane, Integer.valueOf(0));
4701 - _container.setPreferredSize(new Dimension(bwidth,bheight));
4702 - } catch (Exception e) {
4703 - log.warning("Failed to set instant background image", "bg", _ifc.backgroundImage);
4708 _container.add(_layers, BorderLayout.CENTER);
4709 _patchNotes = new JButton(new AbstractAction(_msgs.getString("m.patch_notes")) {
4710 @Override public void actionPerformed (ActionEvent event) {
4711 @@ -807,14 +799,12 @@ public abstract class Getdown extends Thread
4712 _patchNotes.setFont(StatusPanel.FONT);
4713 _layers.add(_patchNotes);
4714 _status = new StatusPanel(_msgs);
4715 - _layers.add(_status, Integer.valueOf(10));
4716 + _layers.add(_status);
4727 @@ -845,12 +835,13 @@ public abstract class Getdown extends Thread
4729 protected RotatingBackgrounds getBackground ()
4731 - if (_ifc.rotatingBackgrounds != null && _ifc.rotatingBackgrounds.size() > 0) {
4732 + if (_ifc.rotatingBackgrounds != null) {
4733 if (_ifc.backgroundImage != null) {
4734 log.warning("ui.background_image and ui.rotating_background were both specified. " +
4735 - "The background image is being used.");
4736 + "The rotating images are being used.");
4738 - return new RotatingBackgrounds(_ifc.rotatingBackgrounds, _ifc.errorBackground, Getdown.this);
4739 + return new RotatingBackgrounds(_ifc.rotatingBackgrounds, _ifc.errorBackground,
4741 } else if (_ifc.backgroundImage != null) {
4742 return new RotatingBackgrounds(loadImage(_ifc.backgroundImage));
4744 @@ -879,8 +870,23 @@ public abstract class Getdown extends Thread
4748 + private void fail (Exception e) {
4749 + String msg = e.getMessage();
4750 + if (msg == null) {
4751 + msg = MessageUtil.compose("m.unknown_error", _ifc.installError);
4752 + } else if (!msg.startsWith("m.")) {
4753 + // try to do something sensible based on the type of error
4754 + msg = MessageUtil.taint(msg);
4755 + msg = e instanceof FileNotFoundException ?
4756 + MessageUtil.compose("m.missing_resource", msg, _ifc.installError) :
4757 + MessageUtil.compose("m.init_error", msg, _ifc.installError);
4759 + // since we're dead, clear off the 'time remaining' label along with displaying the error
4764 - * Update the status to indicate getdown has failed for the reason in <code>message</code>.
4765 + * Update the status to indicate getdown has failed for the reason in {@code message}.
4767 protected void fail (String message)
4769 @@ -961,14 +967,14 @@ public abstract class Getdown extends Thread
4771 URL url = _app.getTrackingProgressURL(++_reportedProgress);
4773 - new ProgressReporter(url).start();
4774 + reportProgress(url);
4776 } while (_reportedProgress <= progress);
4779 URL url = _app.getTrackingURL(event);
4781 - new ProgressReporter(url).start();
4782 + reportProgress(url);
4786 @@ -1031,44 +1037,40 @@ public abstract class Getdown extends Thread
4789 /** Used to fetch a progress report URL. */
4790 - protected class ProgressReporter extends Thread
4792 - public ProgressReporter (URL url) {
4798 - public void run () {
4800 - HttpURLConnection ucon = ConnectionUtil.openHttp(_app.proxy, _url, 0, 0);
4802 - // if we have a tracking cookie configured, configure the request with it
4803 - if (_app.getTrackingCookieName() != null &&
4804 - _app.getTrackingCookieProperty() != null) {
4805 - String val = System.getProperty(_app.getTrackingCookieProperty());
4806 - if (val != null) {
4807 - ucon.setRequestProperty("Cookie", _app.getTrackingCookieName() + "=" + val);
4808 + protected void reportProgress (final URL url) {
4809 + Thread reporter = new Thread("Progress reporter") {
4810 + public void run () {
4812 + HttpURLConnection ucon = _app.conn.openHttp(url, 0, 0);
4814 + // if we have a tracking cookie configured, configure the request with it
4815 + if (_app.getTrackingCookieName() != null &&
4816 + _app.getTrackingCookieProperty() != null) {
4817 + String val = System.getProperty(_app.getTrackingCookieProperty());
4818 + if (val != null) {
4819 + ucon.setRequestProperty(
4820 + "Cookie", _app.getTrackingCookieName() + "=" + val);
4825 - // now request our tracking URL and ensure that we get a non-error response
4828 - if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) {
4829 - log.warning("Failed to report tracking event",
4830 - "url", _url, "rcode", ucon.getResponseCode());
4831 + // now request our tracking URL and ensure that we get a non-error response
4834 + if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) {
4835 + log.warning("Failed to report tracking event",
4836 + "url", url, "rcode", ucon.getResponseCode());
4839 + ucon.disconnect();
4842 - ucon.disconnect();
4845 - } catch (IOException ioe) {
4846 - log.warning("Failed to report tracking event", "url", _url, "error", ioe);
4847 + } catch (IOException ioe) {
4848 + log.warning("Failed to report tracking event", "url", url, "error", ioe);
4853 - protected URL _url;
4855 + reporter.setDaemon(true);
4859 /** Used to pass progress on to our user interface. */
4860 @@ -1084,7 +1086,6 @@ public abstract class Getdown extends Thread
4861 protected ResourceBundle _msgs;
4862 protected Container _container;
4863 protected JLayeredPane _layers;
4864 - protected JPanel instantSplashPane;
4865 protected StatusPanel _status;
4866 protected JButton _patchNotes;
4867 protected AbortPanel _abort;
4868 @@ -1112,5 +1113,4 @@ public abstract class Getdown extends Thread
4870 protected static final int MAX_LOOPS = 5;
4871 protected static final long FALLBACK_CHECK_TIME = 1000L;
4874 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
4875 index 2d1908904..3859f6a09 100644
4876 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
4877 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java
4878 @@ -28,258 +28,225 @@ import javax.swing.KeyStroke;
4879 import javax.swing.WindowConstants;
4881 import com.samskivert.swing.util.SwingUtil;
4882 -import com.threerings.getdown.data.Application;
4883 import com.threerings.getdown.data.EnvConfig;
4884 import com.threerings.getdown.data.SysProps;
4885 import com.threerings.getdown.util.LaunchUtil;
4886 import com.threerings.getdown.util.StringUtil;
4887 import static com.threerings.getdown.Log.log;
4888 -import jalview.bin.StartupNotificationListener;
4891 * The main application entry point for Getdown.
4893 public class GetdownApp
4895 - public static String startupFilesParameterString = "";
4897 - * The main entry point of the Getdown launcher application.
4899 - public static void main (String[] argv) {
4902 - } catch (Exception e) {
4903 - log.warning("main() failed.", e);
4908 - * Runs Getdown as an application, using the arguments supplie as {@code argv}.
4909 - * @return the {@code Getdown} instance that is running. {@link Getdown#start} will have been
4911 - * @throws Exception if anything goes wrong starting Getdown.
4913 - public static Getdown start (String[] argv) throws Exception {
4914 - List<EnvConfig.Note> notes = new ArrayList<>();
4915 - EnvConfig envc = EnvConfig.create(argv, notes);
4916 - if (envc == null) {
4917 - if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
4918 - else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
4922 - // pipe our output into a file in the application directory
4923 - if (!SysProps.noLogRedir()) {
4924 - File logFile = new File(envc.appDir, "launcher.log");
4926 - PrintStream logOut = new PrintStream(
4927 - new BufferedOutputStream(new FileOutputStream(logFile)), true);
4928 - System.setOut(logOut);
4929 - System.setErr(logOut);
4930 - } catch (IOException ioe) {
4931 - log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
4935 - // report any notes from reading our env config, and abort if necessary
4936 - boolean abort = false;
4937 - for (EnvConfig.Note note : notes) {
4938 - switch (note.level) {
4939 - case INFO: log.info(note.message); break;
4940 - case WARN: log.warning(note.message); break;
4941 - case ERROR: log.error(note.message); abort = true; break;
4944 + * The main entry point of the Getdown launcher application.
4946 + public static void main (String[] argv) {
4949 + } catch (Exception e) {
4950 + log.warning("main() failed.", e);
4953 - if (abort) System.exit(-1);
4957 - jalview.bin.StartupNotificationListener.setListener();
4958 - } catch (Exception e)
4960 - e.printStackTrace();
4961 - } catch (NoClassDefFoundError e)
4963 - log.warning("Starting without install4j classes");
4964 - } catch (Throwable t)
4966 - t.printStackTrace();
4969 - // record a few things for posterity
4970 - log.info("------------------ VM Info ------------------");
4971 - log.info("-- OS Name: " + System.getProperty("os.name"));
4972 - log.info("-- OS Arch: " + System.getProperty("os.arch"));
4973 - log.info("-- OS Vers: " + System.getProperty("os.version"));
4974 - log.info("-- Java Vers: " + System.getProperty("java.version"));
4975 - log.info("-- Java Home: " + System.getProperty("java.home"));
4976 - log.info("-- User Name: " + System.getProperty("user.name"));
4977 - log.info("-- User Home: " + System.getProperty("user.home"));
4978 - log.info("-- Cur dir: " + System.getProperty("user.dir"));
4979 - log.info("-- startupFilesParameterString: " + startupFilesParameterString);
4980 - log.info("---------------------------------------------");
4982 + * Runs Getdown as an application, using the arguments supplie as {@code argv}.
4983 + * @return the {@code Getdown} instance that is running. {@link Getdown#run} will have been
4985 + * @throws Exception if anything goes wrong starting Getdown.
4987 + public static Getdown start (String[] argv) throws Exception {
4988 + List<EnvConfig.Note> notes = new ArrayList<>();
4989 + EnvConfig envc = EnvConfig.create(argv, notes);
4990 + if (envc == null) {
4991 + if (!notes.isEmpty()) for (EnvConfig.Note n : notes) System.err.println(n.message);
4992 + else System.err.println("Usage: java -jar getdown.jar [app_dir] [app_id] [app args]");
4996 - Getdown app = new Getdown(envc) {
4998 - protected Container createContainer () {
4999 - // create our user interface, and display it
5000 - if (_frame == null) {
5001 - _frame = new JFrame("");
5002 - _frame.addWindowListener(new WindowAdapter() {
5004 - public void windowClosing (WindowEvent evt) {
5005 - handleWindowClose();
5008 - // handle close on ESC
5009 - String cancelId = "Cancel"; // $NON-NLS-1$
5010 - _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
5011 - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
5012 - _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
5013 - public void actionPerformed (ActionEvent e) {
5014 - handleWindowClose();
5015 + // pipe our output into a file in the application directory
5016 + if (!SysProps.noLogRedir() && !SysProps.debug()) {
5017 + File logFile = new File(envc.appDir, "launcher.log");
5019 + PrintStream logOut = new PrintStream(
5020 + new BufferedOutputStream(new FileOutputStream(logFile)), true);
5021 + System.setOut(logOut);
5022 + System.setErr(logOut);
5023 + } catch (IOException ioe) {
5024 + log.warning("Unable to redirect output to '" + logFile + "': " + ioe);
5027 - // this cannot be called in configureContainer as it is only allowed before the
5028 - // frame has been displayed for the first time
5029 - _frame.setUndecorated(_ifc.hideDecorations);
5030 - _frame.setResizable(false);
5032 - _frame.getContentPane().removeAll();
5034 - _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
5035 - return _frame.getContentPane();
5039 - protected void configureContainer () {
5040 - if (_frame == null) return;
5042 - _frame.setTitle(_ifc.name);
5045 - _frame.setBackground(new Color(_ifc.background, true));
5046 - } catch (Exception e) {
5047 - log.warning("Failed to set background", "bg", _ifc.background, e);
5048 + // report any notes from reading our env config, and abort if necessary
5049 + boolean abort = false;
5050 + for (EnvConfig.Note note : notes) {
5051 + switch (note.level) {
5052 + case INFO: log.info(note.message); break;
5053 + case WARN: log.warning(note.message); break;
5054 + case ERROR: log.error(note.message); abort = true; break;
5057 + if (abort) System.exit(-1);
5059 + // record a few things for posterity
5060 + log.info("------------------ VM Info ------------------");
5061 + log.info("-- OS Name: " + System.getProperty("os.name"));
5062 + log.info("-- OS Arch: " + System.getProperty("os.arch"));
5063 + log.info("-- OS Vers: " + System.getProperty("os.version"));
5064 + log.info("-- Java Vers: " + System.getProperty("java.version"));
5065 + log.info("-- Java Home: " + System.getProperty("java.home"));
5066 + log.info("-- User Name: " + System.getProperty("user.name"));
5067 + log.info("-- User Home: " + System.getProperty("user.home"));
5068 + log.info("-- Cur dir: " + System.getProperty("user.dir"));
5069 + log.info("---------------------------------------------");
5071 + Getdown getdown = new Getdown(envc) {
5073 + protected Container createContainer () {
5074 + // create our user interface, and display it
5075 + if (_frame == null) {
5076 + _frame = new JFrame("");
5077 + _frame.addWindowListener(new WindowAdapter() {
5079 + public void windowClosing (WindowEvent evt) {
5080 + handleWindowClose();
5083 + // handle close on ESC
5084 + String cancelId = "Cancel"; // $NON-NLS-1$
5085 + _frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
5086 + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelId);
5087 + _frame.getRootPane().getActionMap().put(cancelId, new AbstractAction() {
5088 + public void actionPerformed (ActionEvent e) {
5089 + handleWindowClose();
5092 + // this cannot be called in configureContainer as it is only allowed before the
5093 + // frame has been displayed for the first time
5094 + _frame.setUndecorated(_ifc.hideDecorations);
5095 + _frame.setResizable(false);
5097 + _frame.getContentPane().removeAll();
5099 + _frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
5100 + return _frame.getContentPane();
5103 - if (_ifc.iconImages != null && _ifc.iconImages.size() > 0) {
5104 - ArrayList<Image> icons = new ArrayList<>();
5105 - for (String path : _ifc.iconImages) {
5106 - Image img = loadImage(path);
5107 - if (img == null) {
5108 - log.warning("Error loading icon image", "path", path);
5112 + protected void configureContainer () {
5113 + if (_frame == null) return;
5115 + _frame.setTitle(_ifc.name);
5118 + _frame.setBackground(new Color(_ifc.background, true));
5119 + } catch (Exception e) {
5120 + log.warning("Failed to set background", "bg", _ifc.background, e);
5123 + if (_ifc.iconImages != null) {
5124 + List<Image> icons = new ArrayList<>();
5125 + for (String path : _ifc.iconImages) {
5126 + Image img = loadImage(path);
5127 + if (img == null) {
5128 + log.warning("Error loading icon image", "path", path);
5133 + if (icons.isEmpty()) {
5134 + log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
5136 + _frame.setIconImages(icons);
5141 - if (icons.isEmpty()) {
5142 - log.warning("Failed to load any icons", "iconImages", _ifc.iconImages);
5144 - _frame.setIconImages(icons);
5150 - protected void showContainer () {
5151 - if (_frame != null) {
5153 - SwingUtil.centerWindow(_frame);
5154 - _frame.setVisible(true);
5158 + protected void showContainer () {
5159 + if (_frame != null) {
5161 + SwingUtil.centerWindow(_frame);
5162 + _frame.setVisible(true);
5167 - protected void disposeContainer () {
5168 - if (_frame != null) {
5174 + protected void disposeContainer () {
5175 + if (_frame != null) {
5182 - protected void showDocument (String url) {
5183 - if (!StringUtil.couldBeValidUrl(url)) {
5184 - // command injection would be possible if we allowed e.g. spaces and double quotes
5185 - log.warning("Invalid document URL.", "url", url);
5188 - String[] cmdarray;
5189 - if (LaunchUtil.isWindows()) {
5190 - String osName = System.getProperty("os.name", "");
5191 - if (osName.indexOf("9") != -1 || osName.indexOf("Me") != -1) {
5192 - cmdarray = new String[] {
5193 - "command.com", "/c", "start", "\"" + url + "\"" };
5195 - cmdarray = new String[] {
5196 - "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
5198 - } else if (LaunchUtil.isMacOS()) {
5199 - cmdarray = new String[] { "open", url };
5200 - } else { // Linux, Solaris, etc.
5201 - cmdarray = new String[] { "firefox", url };
5204 - Runtime.getRuntime().exec(cmdarray);
5205 - } catch (Exception e) {
5206 - log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
5210 + protected void showDocument (String url) {
5211 + if (!StringUtil.couldBeValidUrl(url)) {
5212 + // command injection would be possible if we allowed e.g. spaces and double quotes
5213 + log.warning("Invalid document URL.", "url", url);
5216 + String[] cmdarray;
5217 + if (LaunchUtil.isWindows()) {
5218 + String osName = System.getProperty("os.name", "");
5219 + if (osName.contains("9") || osName.contains("Me")) {
5220 + cmdarray = new String[] {
5221 + "command.com", "/c", "start", "\"" + url + "\"" };
5223 + cmdarray = new String[] {
5224 + "cmd.exe", "/c", "start", "\"\"", "\"" + url + "\"" };
5226 + } else if (LaunchUtil.isMacOS()) {
5227 + cmdarray = new String[] { "open", url };
5228 + } else { // Linux, Solaris, etc.
5229 + cmdarray = new String[] { "firefox", url };
5232 + Runtime.getRuntime().exec(cmdarray);
5233 + } catch (Exception e) {
5234 + log.warning("Failed to open browser.", "cmdarray", cmdarray, e);
5239 - protected void exit (int exitCode) {
5240 - // if we're running the app in the same JVM, don't call System.exit, but do
5241 - // make double sure that the download window is closed.
5242 - if (invokeDirect()) {
5243 - disposeContainer();
5245 - System.exit(exitCode);
5249 + protected void exit (int exitCode) {
5250 + // if we're running the app in the same JVM, don't call System.exit, but do
5251 + // make double sure that the download window is closed.
5252 + if (invokeDirect()) {
5253 + disposeContainer();
5255 + System.exit(exitCode);
5260 - protected void fail (String message) {
5261 - super.fail(message);
5262 - // super.fail causes the UI to be created (if needed) on the next UI tick, so we
5263 - // want to wait until that happens before we attempt to redecorate the window
5264 - EventQueue.invokeLater(new Runnable() {
5266 - public void run() {
5267 - // if the frame was set to be undecorated, make window decoration available
5268 - // to allow the user to close the window
5269 - if (_frame != null && _frame.isUndecorated()) {
5271 - Color bg = _frame.getBackground();
5272 - if (bg != null && bg.getAlpha() < 255) {
5273 - // decorated windows do not allow alpha backgrounds
5274 - _frame.setBackground(
5275 - new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
5277 - _frame.setUndecorated(false);
5280 + protected void fail (String message) {
5281 + super.fail(message);
5282 + // super.fail causes the UI to be created (if needed) on the next UI tick, so we
5283 + // want to wait until that happens before we attempt to redecorate the window
5284 + EventQueue.invokeLater(new Runnable() {
5285 + @Override public void run () {
5286 + // if the frame was set to be undecorated, make window decoration available
5287 + // to allow the user to close the window
5288 + if (_frame != null && _frame.isUndecorated()) {
5290 + Color bg = _frame.getBackground();
5291 + if (bg != null && bg.getAlpha() < 255) {
5292 + // decorated windows do not allow alpha backgrounds
5293 + _frame.setBackground(
5294 + new Color(bg.getRed(), bg.getGreen(), bg.getBlue()));
5296 + _frame.setUndecorated(false);
5306 - protected JFrame _frame;
5309 - String startupFile = getStartupFilesParameterString();
5310 - if (!StringUtil.isBlank(startupFile)) {
5311 - Application.setStartupFilesFromParameterString(startupFile);
5312 + protected JFrame _frame;
5314 + Getdown.run(getdown);
5322 - public static void setStartupFilesParameterString(String parameters) {
5323 - startupFilesParameterString = parameters;
5326 - public static String getStartupFilesParameterString() {
5327 - return startupFilesParameterString;
5330 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
5331 index 217827364..5f18896c4 100644
5332 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java
5333 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java
5334 @@ -35,14 +35,16 @@ import static com.threerings.getdown.Log.log;
5336 public final class ProxyPanel extends JPanel implements ActionListener
5338 - public ProxyPanel (Getdown getdown, ResourceBundle msgs)
5339 + public ProxyPanel (Getdown getdown, ResourceBundle msgs, boolean updateAuth)
5343 + _updateAuth = updateAuth;
5345 setLayout(new VGroupLayout());
5346 setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
5347 - add(new SaneLabelField(get("m.configure_proxy")));
5348 + String title = get(updateAuth ? "m.update_proxy_auth" : "m.configure_proxy");
5349 + add(new SaneLabelField(title));
5350 add(new Spacer(5, 5));
5352 JPanel row = new JPanel(new GridLayout());
5353 @@ -61,19 +63,20 @@ public final class ProxyPanel extends JPanel implements ActionListener
5354 row.add(new SaneLabelField(get("m.proxy_auth_required")), BorderLayout.WEST);
5355 _useAuth = new JCheckBox();
5357 + _useAuth.setSelected(updateAuth);
5360 row = new JPanel(new GridLayout());
5361 row.add(new SaneLabelField(get("m.proxy_username")), BorderLayout.WEST);
5362 _username = new SaneTextField();
5363 - _username.setEnabled(false);
5364 + _username.setEnabled(updateAuth);
5368 row = new JPanel(new GridLayout());
5369 row.add(new SaneLabelField(get("m.proxy_password")), BorderLayout.WEST);
5370 _password = new SanePasswordField();
5371 - _password.setEnabled(false);
5372 + _password.setEnabled(updateAuth);
5376 @@ -112,7 +115,13 @@ public final class ProxyPanel extends JPanel implements ActionListener
5377 public void addNotify ()
5380 - _host.requestFocusInWindow();
5381 + if (_updateAuth) {
5382 + // we are asking the user to update the credentials for an existing proxy
5383 + // configuration, so focus that instead of the proxy host config
5384 + _username.requestFocusInWindow();
5386 + _host.requestFocusInWindow();
5390 // documentation inherited
5391 @@ -131,7 +140,7 @@ public final class ProxyPanel extends JPanel implements ActionListener
5392 public void actionPerformed (ActionEvent e)
5394 String cmd = e.getActionCommand();
5395 - if (cmd.equals("ok")) {
5396 + if ("ok".equals(cmd)) {
5397 String user = null, pass = null;
5398 if (_useAuth.isSelected()) {
5399 user = _username.getText();
5400 @@ -184,8 +193,9 @@ public final class ProxyPanel extends JPanel implements ActionListener
5404 - protected Getdown _getdown;
5405 - protected ResourceBundle _msgs;
5406 + protected final Getdown _getdown;
5407 + protected final ResourceBundle _msgs;
5408 + protected final boolean _updateAuth;
5410 protected JTextField _host;
5411 protected JTextField _port;
5412 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
5413 index a36b5fa67..8962d35b9 100644
5414 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java
5415 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java
5416 @@ -8,31 +8,41 @@ package com.threerings.getdown.launcher;
5417 import java.io.File;
5418 import java.io.FileOutputStream;
5419 import java.io.IOException;
5420 +import java.io.InputStreamReader;
5421 import java.io.PrintStream;
5422 +import java.io.Reader;
5423 import java.net.Authenticator;
5424 import java.net.HttpURLConnection;
5425 +import java.net.InetAddress;
5426 import java.net.InetSocketAddress;
5427 import java.net.PasswordAuthentication;
5428 import java.net.Proxy;
5429 import java.net.URL;
5430 import java.net.URLConnection;
5431 +import java.net.UnknownHostException;
5432 import java.util.Iterator;
5433 import java.util.ServiceLoader;
5435 +import javax.script.Bindings;
5436 +import javax.script.Invocable;
5437 +import javax.script.ScriptContext;
5438 +import javax.script.ScriptEngine;
5439 +import javax.script.ScriptEngineManager;
5441 import ca.beq.util.win32.registry.RegistryKey;
5442 import ca.beq.util.win32.registry.RegistryValue;
5443 import ca.beq.util.win32.registry.RootKey;
5445 import com.threerings.getdown.data.Application;
5446 +import com.threerings.getdown.net.Connector;
5447 import com.threerings.getdown.spi.ProxyAuth;
5448 import com.threerings.getdown.util.Config;
5449 -import com.threerings.getdown.util.ConnectionUtil;
5450 import com.threerings.getdown.util.LaunchUtil;
5451 import com.threerings.getdown.util.StringUtil;
5453 import static com.threerings.getdown.Log.log;
5455 -public class ProxyUtil {
5456 +public final class ProxyUtil {
5458 public static boolean autoDetectProxy (Application app)
5460 @@ -57,25 +67,40 @@ public class ProxyUtil {
5461 RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY);
5462 for (Iterator<?> iter = r.values(); iter.hasNext(); ) {
5463 RegistryValue value = (RegistryValue)iter.next();
5464 - if (value.getName().equals("ProxyEnable")) {
5465 - enabled = value.getStringValue().equals("1");
5466 + if ("ProxyEnable".equals(value.getName())) {
5467 + enabled = "1".equals(value.getStringValue());
5469 if (value.getName().equals("ProxyServer")) {
5470 - String strval = value.getStringValue();
5471 - int cidx = strval.indexOf(":");
5473 - rport = strval.substring(cidx+1);
5474 - strval = strval.substring(0, cidx);
5475 + String[] hostPort = splitHostPort(value.getStringValue());
5476 + rhost = hostPort[0];
5477 + rport = hostPort[1];
5479 + if (value.getName().equals("AutoConfigURL")) {
5480 + String acurl = value.getStringValue();
5481 + Reader acjs = new InputStreamReader(new URL(acurl).openStream());
5482 + // technically we should be returning all this info and trying each proxy
5483 + // in succession, but that's complexity we'll leave for another day
5484 + URL configURL = app.getConfigResource().getRemote();
5485 + for (String proxy : findPACProxiesForURL(acjs, configURL)) {
5486 + if (proxy.startsWith("PROXY ")) {
5487 + String[] hostPort = splitHostPort(proxy.substring(6));
5488 + rhost = hostPort[0];
5489 + rport = hostPort[1];
5490 + // TODO: is this valid? Does AutoConfigURL imply proxy enabled?
5503 log.info("Detected no proxy settings in the registry.");
5506 } catch (Throwable t) {
5507 log.info("Failed to find proxy settings in Windows registry", "error", t);
5509 @@ -97,32 +122,31 @@ public class ProxyUtil {
5513 - public static boolean canLoadWithoutProxy (URL rurl)
5514 + public static boolean canLoadWithoutProxy (URL rurl, int timeoutSeconds)
5516 - log.info("Testing whether proxy is needed, via: " + rurl);
5517 + log.info("Attempting to fetch without proxy: " + rurl);
5519 - // try to make a HEAD request for this URL (use short connect and read timeouts)
5520 - URLConnection conn = ConnectionUtil.open(Proxy.NO_PROXY, rurl, 5, 5);
5521 - if (conn instanceof HttpURLConnection) {
5522 - HttpURLConnection hcon = (HttpURLConnection)conn;
5524 - hcon.setRequestMethod("HEAD");
5526 - // make sure we got a satisfactory response code
5527 - int rcode = hcon.getResponseCode();
5528 - if (rcode == HttpURLConnection.HTTP_PROXY_AUTH ||
5529 - rcode == HttpURLConnection.HTTP_FORBIDDEN) {
5530 - log.warning("Got an 'HTTP credentials needed' response", "code", rcode);
5535 - hcon.disconnect();
5538 - // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy
5539 + URLConnection conn = Connector.DEFAULT.open(rurl, timeoutSeconds, timeoutSeconds);
5540 + // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy
5541 + if (!(conn instanceof HttpURLConnection)) {
5544 + // otherwise, try to make a HEAD request for this URL
5545 + HttpURLConnection hcon = (HttpURLConnection)conn;
5547 + hcon.setRequestMethod("HEAD");
5549 + // make sure we got a satisfactory response code
5550 + int rcode = hcon.getResponseCode();
5551 + if (rcode == HttpURLConnection.HTTP_PROXY_AUTH ||
5552 + rcode == HttpURLConnection.HTTP_FORBIDDEN) {
5553 + log.warning("Got an 'HTTP credentials needed' response", "code", rcode);
5558 + hcon.disconnect();
5560 } catch (IOException ioe) {
5561 log.info("Failed to HEAD " + rurl + ": " + ioe);
5562 log.info("We probably need a proxy, but auto-detection failed.");
5563 @@ -190,9 +214,14 @@ public class ProxyUtil {
5565 boolean haveCreds = !StringUtil.isBlank(username) && !StringUtil.isBlank(password);
5567 - int pport = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port);
5568 - log.info("Using proxy", "host", host, "port", pport, "haveCreds", haveCreds);
5569 - app.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pport));
5570 + if (StringUtil.isBlank(host)) {
5571 + log.info("Using no proxy");
5572 + app.conn = new Connector(Proxy.NO_PROXY);
5574 + int pp = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port);
5575 + log.info("Using proxy", "host", host, "port", pp, "haveCreds", haveCreds);
5576 + app.conn = new Connector(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pp)));
5580 final String fuser = username;
5581 @@ -205,6 +234,61 @@ public class ProxyUtil {
5585 + public static class Resolver {
5586 + public String dnsResolve (String host) {
5588 + return InetAddress.getByName(host).getHostAddress();
5589 + } catch (UnknownHostException uhe) {
5593 + public String myIpAddress () {
5595 + return InetAddress.getLocalHost().getHostAddress();
5596 + } catch (UnknownHostException uhe) {
5602 + public static String[] findPACProxiesForURL (Reader pac, URL url) {
5603 + ScriptEngineManager manager = new ScriptEngineManager();
5604 + ScriptEngine engine = manager.getEngineByName("javascript");
5605 + Bindings globals = engine.createBindings();
5606 + globals.put("resolver", new Resolver());
5607 + engine.setBindings(globals, ScriptContext.GLOBAL_SCOPE);
5609 + URL utils = ProxyUtil.class.getResource("PacUtils.js");
5610 + if (utils == null) {
5611 + log.error("Unable to load PacUtils.js");
5612 + return new String[0];
5614 + engine.eval(new InputStreamReader(utils.openStream()));
5615 + Object res = engine.eval(pac);
5616 + if (engine instanceof Invocable) {
5617 + Object[] args = new Object[] { url.toString(), url.getHost() };
5618 + res = ((Invocable) engine).invokeFunction("FindProxyForURL", args);
5620 + String[] proxies = res.toString().split(";");
5621 + for (int ii = 0; ii < proxies.length; ii += 1) {
5622 + proxies[ii] = proxies[ii].trim();
5625 + } catch (Exception e) {
5626 + log.warning("Failed to resolve PAC proxy", e);
5628 + return new String[0];
5631 + private static String[] splitHostPort (String hostPort) {
5632 + int cidx = hostPort.indexOf(":");
5634 + return new String[] { hostPort, null};
5636 + return new String[] { hostPort.substring(0, cidx), hostPort.substring(cidx+1) };
5640 protected static final String PROXY_REGISTRY =
5641 "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
5643 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
5644 index d3aa2bd25..d64e5f02d 100644
5645 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java
5646 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java
5647 @@ -14,7 +14,7 @@ public final class RotatingBackgrounds
5649 public interface ImageLoader {
5650 /** Loads and returns the image with the supplied path. */
5651 - public Image loadImage (String path);
5652 + Image loadImage (String path);
5656 @@ -35,7 +35,7 @@ public final class RotatingBackgrounds
5660 - * Create a sequence of images to be rotated through from <code>backgrounds</code>.
5661 + * Create a sequence of images to be rotated through from {@code backgrounds}.
5663 * Each String in backgrounds should be the path to the image, a semicolon, and the minimum
5664 * amount of time to display the image in seconds. Each image will be active for an equal
5665 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
5666 index 99f44ca51..197dc9170 100644
5667 --- a/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java
5668 +++ b/getdown/src/getdown/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java
5669 @@ -26,12 +26,9 @@ import com.samskivert.swing.Label;
5670 import com.samskivert.swing.LabelStyleConstants;
5671 import com.samskivert.swing.util.SwingUtil;
5672 import com.samskivert.util.Throttle;
5674 import com.threerings.getdown.data.Application.UpdateInterface;
5675 import com.threerings.getdown.util.MessageUtil;
5676 import com.threerings.getdown.util.Rectangle;
5677 -import com.threerings.getdown.util.StringUtil;
5679 import static com.threerings.getdown.Log.log;
5682 @@ -344,7 +341,7 @@ public final class StatusPanel extends JComponent
5684 String msg = get(key);
5685 if (msg != null) return MessageFormat.format(MessageUtil.escape(msg), (Object[])args);
5686 - return key + String.valueOf(Arrays.asList(args));
5687 + return key + Arrays.asList(args);
5690 /** Used by {@link #setStatus}, and {@link #setProgress}. */
5691 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
5692 deleted file mode 100644
5693 index 5c6c7c393..000000000
5694 --- a/getdown/src/getdown/launcher/src/main/java/jalview/bin/StartupNotificationListener.java
5697 -package jalview.bin;
5699 -import com.threerings.getdown.launcher.GetdownApp;
5700 -import static com.threerings.getdown.Log.log;
5702 -public class StartupNotificationListener {
5704 - public static void setListener() {
5708 - com.install4j.api.launcher.StartupNotification.registerStartupListener(
5709 - new com.install4j.api.launcher.StartupNotification.Listener() {
5711 - public void startupPerformed(String parameters) {
5712 - log.info("StartupNotification.Listener.startupPerformed: '"+parameters+"'");
5713 - GetdownApp.setStartupFilesParameterString(parameters);
5717 - } catch (Exception e) {
5718 - e.printStackTrace();
5719 - } catch (NoClassDefFoundError t) {
5720 - log.warning("Starting without install4j classes");
5726 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
5727 index 19b2999e1..7a33ca0e4 100644
5728 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties
5729 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages.properties
5730 @@ -12,11 +12,14 @@ m.abort_cancel = Continue installation
5731 m.detecting_proxy = Trying to auto-detect proxy settings
5733 m.configure_proxy = <html>We were unable to connect to the application server to download data. \
5734 - <p> Please make sure that no virus scanner or firewall is blocking network communicaton with \
5735 + <p> Please make sure that no virus scanner or firewall is blocking network communication with \
5737 <p> Your computer may access the Internet through a proxy and we were unable to automatically \
5738 detect your proxy settings. If you know your proxy settings, you can enter them below.</html>
5740 +m.update_proxy_auth = <html>The stored proxy user/password is wrong or obsolete. \
5741 + <p>Please provide an updated user/password combination.</html>
5743 m.proxy_extra = <html>If you are sure that you don't use a proxy then \
5744 perhaps there is a temporary Internet outage that is preventing us from \
5745 communicating with the servers. In this case, you can cancel and try \
5746 @@ -41,10 +44,7 @@ m.checking = Checking for update
5747 m.validating = Validating
5748 m.patching = Patching
5749 m.launching = Launching
5751 m.patch_notes = Patch Notes
5752 -m.play_again = Play Again
5754 m.complete = {0}% complete
5755 m.remain = {0} remaining
5757 @@ -99,7 +99,7 @@ m.default_install_error = the support section of the website
5758 m.another_getdown_running = Multiple instances of this application's \
5759 installer are running. This one will stop and let another complete.
5761 -m.applet_stopped = Getdown's applet was told to stop working.
5762 +m.verify_timeout = Verifying resources took too long.
5764 # application/digest errors
5765 m.missing_appbase = The configuration file is missing the 'appbase'.
5766 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
5767 index 8e3683594..db35593b2 100644
5768 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties
5769 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_de.properties
5774 -# Getdown translation messages
5775 +# Getdown German translation messages
5777 m.abort_title = Installation abbrechen?
5778 -m.abort_confirm = <html>Bist du sicher, dass du die Installation abbrechen \
5780 - Du kannst sp\u00e4ter fortfahren, indem du die Anwendung erneut \
5781 -ausf\u00fchrst.</html>
5782 +m.abort_confirm = <html>Bist du sicher, dass du die Installation abbrechen möchtest? \
5783 + Du kannst später fortfahren, indem du die Anwendung erneut ausführst.</html>
5784 m.abort_ok = Beenden
5785 m.abort_cancel = Installation fortsetzen
5787 @@ -17,9 +13,12 @@ m.configure_proxy = <html>Es konnte keine Verbindung zum Applikations-Server auf
5788 <p>Bitte kontrollieren Sie die Proxyeinstellungen und stellen Sie sicher, dass keine lokal oder \
5789 im Netzwerk betriebene Sicherheitsanwendung (Virenscanner, Firewall, etc.) die Kommunikation \
5790 mit dem Server blockiert.<br> \
5791 - Wenn kein Proxy verwendet werden soll, l\u00f6schen Sie bitte alle Eintr\u00e4ge in den unten \
5792 + Wenn kein Proxy verwendet werden soll, löschen Sie bitte alle Einträge in den unten \
5793 stehenden Feldern und klicken sie auf OK.</html>
5795 +m.update_proxy_auth = <html>Gespeicherte Proxy User/Passwort Kombination ungültig oder obsolet. \
5796 + <p>Bitte geben Sie die korrekte User/Passwort Kombination ein.</html>
5798 m.proxy_extra = <html>Sollten Sie keine Proxyeinstellungen gesetzt haben wenden Sie sich bitte \
5799 an Ihren Administrator.</html>
5801 @@ -46,71 +45,68 @@ m.launching = Starte
5802 m.patch_notes = Patchnotes
5804 m.complete = {0}% abgeschlossen
5805 -m.remain = {0} \u00fcbrig
5806 +m.remain = {0} übrig
5808 m.updating_metadata = Lade Steuerungsdateien herunter
5810 -m.init_failed = Unsere Konfigurationsdatei fehlt oder ist besch\u00e4digt. \
5811 -Versuche, eine neue Kopie herunterzuladen...
5812 +m.init_failed = Unsere Konfigurationsdatei fehlt oder ist beschädigt. \
5813 + Versuche, eine neue Kopie herunterzuladen...
5815 -m.java_download_failed = Wir konnten die notwendige Javaversion f\u00fcr deinen \
5816 -Computer nicht automatisch herunterladen. \n\n \
5817 -Bitte auf www.java.com die aktuelle Javaversion herunterladen und dann die \
5818 -Anwendung erneut starten.
5819 +m.java_download_failed = Wir konnten die notwendige Javaversion für deinen \
5820 + Computer nicht automatisch herunterladen. \n\n \
5821 + Bitte auf www.java.com die aktuelle Javaversion herunterladen und dann die \
5822 + Anwendung erneut starten.
5824 m.java_unpack_failed = Wir konnten die aktualisierte Javaversion nicht \
5825 -entpacken. Bitte stelle sicher, dass wenigstens 100MB Platz auf der \
5826 -Festplatte frei sind und versuche dann die Anwendung erneut zu \
5828 -Falls das das Problem nicht beseitigt, bitte auf www.java.com die aktuelle \
5829 -Javaversion herunterladen und installieren und dann erneut versuchen.
5830 + entpacken. Bitte stelle sicher, dass wenigstens 100MB Platz auf der \
5831 + Festplatte frei sind und versuche dann die Anwendung erneut zu \
5833 + Falls das das Problem nicht beseitigt, bitte auf www.java.com die aktuelle \
5834 + Javaversion herunterladen und installieren und dann erneut versuchen.
5836 m.unable_to_repair = Wir konnten die notwendigen Dateien nach 5 Versuchen \
5837 -nicht herunterladen. Du kannst versuchen, die Anwendung erneut zu starten, \
5838 -aber wenn dies erneut fehlschl\u00e4gt, musst du die Anwendung deinstallieren \
5839 -und erneut installieren.
5840 + nicht herunterladen. Du kannst versuchen, die Anwendung erneut zu starten, \
5841 + aber wenn dies erneut fehlschlägt, musst du die Anwendung deinstallieren \
5842 + und erneut installieren.
5844 m.unknown_error = Die Anwendung konnte wegen eines unbekannten Fehlers \
5845 -nicht gestartet werden. Bitte auf \n{0} weiterlesen.
5846 + nicht gestartet werden. Bitte auf \n{0} weiterlesen.
5848 m.init_error = Die Anwendung konnte wegen folgendem Fehler nicht gestartet \
5849 -werden:\n{0}\n\n Bitte auf \n{1} weiterlesen, um zu erfahren, wie bei \
5850 -solchen Problemen vorzugehen ist.
5851 + werden:\n{0}\n\n Bitte auf \n{1} weiterlesen, um zu erfahren, wie bei \
5852 + solchen Problemen vorzugehen ist.
5854 -m.readonly_error = Das Verzeichnis, in dem die Anwendung installiert ist: \
5855 - \n{0}\nist nicht schreibberechtigt. Bitte in ein Verzeichnis mit \
5856 -Schreibzugriff installieren.
5857 +m.readonly_error = Das Verzeichnis, in dem die Anwendung installiert ist:\n{0}\n \
5858 + ist nicht schreibberechtigt. Bitte in ein Verzeichnis mit Schreibzugriff installieren.
5860 m.missing_resource = Die Anwendung konnte nicht gestartet werden, da die \
5861 -folgende Quelle nicht gefunden wurde:\n{0}\n\n\ Bitte auf \n{1} \
5862 -weiterlesen, um zu erfahren, wie bei solchen Problemen vorzugehen ist.
5863 + folgende Quelle nicht gefunden wurde:\n{0}\n\n\ Bitte auf \n{1} \
5864 + weiterlesen, um zu erfahren, wie bei solchen Problemen vorzugehen ist.
5866 m.insufficient_permissions_error = Du hast die digitale Signatur dieser \
5867 -Anwendung nicht akzeptiert. Falls du diese Anwendung benutzen willst, \
5868 -musst du ihre digitale Signatur akzeptieren. \n\Um das zu tun, musst du \
5869 -deinen Browser beenden, neu starten und erneut die Anwendung von dieser \
5870 -Webseite aus starten. Wenn die Sicherheitsabfrage erscheint, bitte die \
5871 -digitale Signatur akzeptieren, um der Anwendung die n\u00f6tigen Rechte zu \
5872 -geben, die sie braucht, um zu laufen.
5873 + Anwendung nicht akzeptiert. Falls du diese Anwendung benutzen willst, \
5874 + musst du ihre digitale Signatur akzeptieren. \n\Um das zu tun, musst du \
5875 + deinen Browser beenden, neu starten und erneut die Anwendung von dieser \
5876 + Webseite aus starten. Wenn die Sicherheitsabfrage erscheint, bitte die \
5877 + digitale Signatur akzeptieren, um der Anwendung die nötigen Rechte zu \
5878 + geben, die sie braucht, um zu laufen.
5880 m.corrupt_digest_signature_error = Wir konnten die digitale Signatur \
5881 -dieser Anwendung nicht \u00fcberpr\u00fcfen.\nBitte \u00fcberpr\u00fcfe, ob du die Anwendung \
5882 -von der richtigen Webseite aus startest.
5883 + dieser Anwendung nicht überprüfen.\nBitte überprüfe, ob du die Anwendung \
5884 + von der richtigen Webseite aus startest.
5886 m.default_install_error = der Support-Webseite
5888 -m.another_getdown_running = Diese Installationsanwendung l\u00e4uft in mehreren \
5889 -Instanzen. Diese Instanz wird sich beenden und eine andere Instanz den \
5890 -Vorgang erledigen lassen.
5892 -m.applet_stopped = Die Anwendung wurde beendet.
5893 +m.another_getdown_running = Diese Installationsanwendung läuft in mehreren \
5894 + Instanzen. Diese Instanz wird sich beenden und eine andere Instanz den \
5895 + Vorgang erledigen lassen.
5897 +m.verify_timeout = Timeout beim Verifizieren der Resourcen.
5899 # application/digest errors
5900 m.missing_appbase = In der Konfigurationsdatei fehlt die 'appbase'.
5901 m.invalid_version = In der Konfigurationsdatei steht die falsche Version.
5902 m.invalid_appbase = In der Konfigurationsdatei steht die falsche 'appbase'.
5903 m.missing_class = In der Konfigurationsdatei fehlt die Anwendungsklasse.
5904 -m.missing_code = Die Konfigurationsdatei enth\u00e4lt keine Codequellen.
5905 -m.invalid_digest_file = Die Hashwertedatei ist ung\u00fcltig.
5907 +m.missing_code = Die Konfigurationsdatei enthält keine Codequellen.
5908 +m.invalid_digest_file = Die Hashwertedatei ist ungültig.
5909 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
5910 index 609b02524..46cd64ac9 100644
5911 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties
5912 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_es.properties
5917 -# Getdown translation messages
5918 +# Getdown Spanish translation messages
5920 -m.abort_title = \u00bfCancelar la instalaci\u00f3n?
5921 -m.abort_confirm = <html>\u00bfEst\u00e1s seguro de querer cancelar la instalaci\u00f3n? \
5922 - Puedes continuarla despu\u00e9s si corres de nuevo la aplicaci\u00f3n.</html>
5923 +m.abort_title = ¿Cancelar la instalación?
5924 +m.abort_confirm = <html>¿Estás seguro de querer cancelar la instalación? \
5925 + Puedes continuarla después si corres de nuevo la aplicación.</html>
5926 m.abort_ok = Cancelar
5927 -m.abort_cancel = Continuar la instalaci\u00f3n
5928 +m.abort_cancel = Continuar la instalación
5930 -m.detecting_proxy = Detectando autom\u00e1ticamente la configuraci\u00f3n proxy
5931 +m.detecting_proxy = Detectando automáticamente la configuración proxy
5933 m.configure_proxy = <html>No ha sido posible conectar con nuestros servidores para \
5934 descargar los datos del juego. \
5935 <ul><li> Si el cortafuegos de Windows o Norton Internet Security tiene instrucciones \
5936 - de bloquear <code>javaw.exe</code> no podemos descargar el juego. Necesitar\u00e1s \
5937 + de bloquear <code>javaw.exe</code> no podemos descargar el juego. Necesitarás \
5938 permitir que <code>javaw.exe</code> tenga acceso al Internet. Puedes intentar \
5939 correr el juego de nuevo, pero es posible que debas dar permisos a javaw.exe en la \
5940 - configuraci\u00f3n de tu cortafuegos ( Inicio -> Panel de control -> Firewall de Windows ).</ul> \
5941 + configuración de tu cortafuegos ( Inicio -> Panel de control -> Firewall de Windows ).</ul> \
5942 <p> Es posible que tu computadora tenga acceso al Internet por medio de un proxy por lo que \
5943 - no ha sido posible detectar autom\u00e1ticamente tu configuraci\u00f3n. Si conoces tu \
5944 - configuraci\u00f3n proxy, puedes anotarla abajo.</html>
5945 + no ha sido posible detectar automáticamente tu configuración. Si conoces tu \
5946 + configuración proxy, puedes anotarla abajo.</html>
5948 -m.proxy_extra = <html>Si est\u00e1s seguro de que no tienes un proxy entonces \
5949 - tal vez exista un falla temporal en el Internet que est\u00e1 evitando que podamos \
5950 +m.proxy_extra = <html>Si estás seguro de que no tienes un proxy entonces \
5951 + tal vez exista un falla temporal en el Internet que está evitando que podamos \
5952 comunicarnos con los servidores. En este caso, puedes cancelar e intentar \
5953 - instalarla de nuevo m\u00e1s tarde.</html>
5954 + instalarla de nuevo más tarde.</html>
5956 m.proxy_host = IP proxy
5957 m.proxy_port = Puerto proxy
5958 m.proxy_username = Nombre de usuario
5959 -m.proxy_password = Contrase\u00f1a
5960 +m.proxy_password = Contraseña
5961 m.proxy_auth_required = Autenticacion requerida
5963 m.proxy_cancel = Cancelar
5964 @@ -54,62 +52,59 @@ m.remain = {0} restante
5966 m.updating_metadata = Descargando los archivos de control
5968 -m.init_failed = Un archivo de configuraci\u00f3n est\u00e1 faltante o est\u00e1 corrupto. Intentando \
5969 +m.init_failed = Un archivo de configuración está faltante o está corrupto. Intentando \
5970 descargar una nueva copia...
5972 -m.java_download_failed = No ha sido posible descargar autom\u00e1ticamente la \
5973 - versi\u00f3n de Java necesaria para tu computadora.\n\n\
5974 - Por favor ve a www.java.com y descarga la \u00faltima versi\u00f3n de \
5975 - Java, despu\u00e9s intenta correr de nuevo la aplicaci\u00f3n.
5976 +m.java_download_failed = No ha sido posible descargar automáticamente la \
5977 + versión de Java necesaria para tu computadora.\n\n\
5978 + Por favor ve a www.java.com y descarga la última versión de \
5979 + Java, después intenta correr de nuevo la aplicación.
5981 -m.java_unpack_failed = No ha sido posible desempacar una versi\u00f3n actualizada de \
5982 - Java. Por favor aseg\u00farate de tener al menos 100 MB de espacio libre en tu \
5983 - disco duro e intenta correr de nuevo la aplicaci\u00f3n.\n\n\
5984 +m.java_unpack_failed = No ha sido posible desempacar una versión actualizada de \
5985 + Java. Por favor asegúrate de tener al menos 100 MB de espacio libre en tu \
5986 + disco duro e intenta correr de nuevo la aplicación.\n\n\
5987 Si eso no soluciona el problema, ve a www.java.com y descarga e \
5988 - instala la \u00faltima versi\u00f3n de Java e intenta de nuevo.
5989 + instala la última versión de Java e intenta de nuevo.
5991 -m.unable_to_repair = No ha sido posible descargar los archivos necesarios despu\u00e9s de \
5992 - cinco intentos. Puedes intentar correr de nuevo la aplicaci\u00f3n, pero si falla \
5993 - de nuevo podr\u00edas necesitar desinstalar y reinstalar.
5994 +m.unable_to_repair = No ha sido posible descargar los archivos necesarios después de \
5995 + cinco intentos. Puedes intentar correr de nuevo la aplicación, pero si falla \
5996 + de nuevo podrías necesitar desinstalar y reinstalar.
5998 -m.unknown_error = La aplicaci\u00f3n no ha podido iniciar debido a un extra\u00f1o \
5999 - error del que no se pudo recobrar. Por favor visita\n{0} para ver informaci\u00f3n acerca \
6000 +m.unknown_error = La aplicación no ha podido iniciar debido a un extraño \
6001 + error del que no se pudo recobrar. Por favor visita\n{0} para ver información acerca \
6002 de como recuperarla.
6003 -m.init_error = La aplicaci\u00f3n no ha podido iniciar debido al siguiente \
6004 +m.init_error = La aplicación no ha podido iniciar debido al siguiente \
6005 error:\n{0}\n\nPor favor visita\n{1} para \
6006 - ver informaci\u00f3n acerca de como manejar ese tipo de problemas.
6007 + ver información acerca de como manejar ese tipo de problemas.
6009 -m.readonly_error = El directorio en el que esta aplicaci\u00f3n est\u00e1 instalada: \
6010 - \n{0}\nes solo lectura. Por favor instala la aplicaci\u00f3n en un directorio en el cual \
6011 +m.readonly_error = El directorio en el que esta aplicación está instalada: \
6012 + \n{0}\nes solo lectura. Por favor instala la aplicación en un directorio en el cual \
6013 tengas acceso de escritura.
6015 -m.missing_resource = La aplicaci\u00f3n no ha podido iniciar debido a un recurso \
6016 - faltante:\n{0}\n\nPor favor visita\n{1} para informaci\u00f3n acerca de como solucionar \
6017 +m.missing_resource = La aplicación no ha podido iniciar debido a un recurso \
6018 + faltante:\n{0}\n\nPor favor visita\n{1} para información acerca de como solucionar \
6021 m.insufficient_permissions_error = No aceptaste la firma digital de \
6022 - esta aplicaci\u00f3n. Si quieres correr la aplicaci\u00f3n, necesitas aceptar \
6023 + esta aplicación. Si quieres correr la aplicación, necesitas aceptar \
6024 su firma digital.\n\nPara hacerlo, necesitas cerrar tu navegador, \
6025 - reiniciarlo, y regresar a esta p\u00e1gina web para reiniciar la aplicaci\u00f3n. Cuando se muestre \
6026 - el di\u00e1logo de seguridad, haz clic en el bot\u00f3n para aceptar la firmar digital \
6027 - y otorgar a esta aplicaci\u00f3n los privilegios que necesita para correr.
6028 + reiniciarlo, y regresar a esta página web para reiniciar la aplicación. Cuando se muestre \
6029 + el diálogo de seguridad, haz clic en el botón para aceptar la firmar digital \
6030 + y otorgar a esta aplicación los privilegios que necesita para correr.
6032 m.corrupt_digest_signature_error = No pudimos verificar la firma digital \
6033 - de la aplicaci\u00f3n.\nPor favor revisa que est\u00e9s lanzando la aplicaci\u00f3n desde\nel \
6034 + de la aplicación.\nPor favor revisa que estés lanzando la aplicación desde\nel \
6037 -m.default_install_error = la secci\u00f3n de asistencia de este sitio web
6039 -m.another_getdown_running = Est\u00e1n corriendo m\u00faltiples instancias de \
6040 - este instalador. Este se detendr\u00e1 para permitir que otra contin\u00fae.
6041 +m.default_install_error = la sección de asistencia de este sitio web
6043 -m.applet_stopped = Se le dijo al applet de Getdown que dejara de trabajar.
6044 +m.another_getdown_running = Están corriendo múltiples instancias de \
6045 + este instalador. Este se detendrá para permitir que otra continúe.
6047 # application/digest errors
6048 -m.missing_appbase = Al archivo de configuraci\u00f3n le falta el 'appbase'.
6049 -m.invalid_version = El archivo de configuraci\u00f3n especifica una versi\u00f3n no v\u00e1lida.
6050 -m.invalid_appbase = El archivo de configuraci\u00f3n especifica un 'appbase' no v\u00e1lido.
6051 -m.missing_class = Al archivo de configuraci\u00f3n le falta la clase de aplicaci\u00f3n.
6052 -m.missing_code = El archivo de configuraci\u00f3n especifica que no hay recursos de c\u00f3digo.
6053 -m.invalid_digest_file = El archivo digest no es v\u00e1lido.
6055 +m.missing_appbase = Al archivo de configuración le falta el 'appbase'.
6056 +m.invalid_version = El archivo de configuración especifica una versión no válida.
6057 +m.invalid_appbase = El archivo de configuración especifica un 'appbase' no válido.
6058 +m.missing_class = Al archivo de configuración le falta la clase de aplicación.
6059 +m.missing_code = El archivo de configuración especifica que no hay recursos de código.
6060 +m.invalid_digest_file = El archivo digest no es válido.
6061 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
6062 index 3666204e2..5eb8ec9cd 100644
6063 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties
6064 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties
6067 -# $Id: messages.properties 485 2012-03-08 22:05:30Z ray.j.greenwell $
6069 -# Getdown translation messages
6070 +# Getdown French translation messages
6072 m.abort_title = Annuler l'installation?
6073 -m.abort_confirm =<html>\u00cates-vous s\u00fbr de vouloir annuler l'installation? \
6074 - Vous pourrez reprendre l'installation en ex\u00e9cutant l'application de nouveau.</html>
6075 +m.abort_confirm =<html>Êtes-vous sûr de vouloir annuler l'installation? \
6076 + Vous pourrez reprendre l'installation en exécutant l'application de nouveau.</html>
6077 m.abort_ok = Quitter
6078 m.abort_cancel = Continuer l'installation
6080 -m.detecting_proxy = D\u00e9tection automatique des r\u00e9glages proxy
6081 +m.detecting_proxy = Détection automatique des réglages proxy
6083 m.configure_proxy =<html>Connexion au serveur impossible. \
6084 - <ul><li> Veuillez v\u00e9rifier que <code>javaw.exe</code> n'est bloqu\u00e9 \
6085 + <ul><li> Veuillez vérifier que <code>javaw.exe</code> n'est bloqué \
6086 par aucun pare-feu ou antivirus. \
6087 Vous pouvez vous rendre sur la configuration du pare-feu windows via \
6088 - (D\u00e9marrer -> Panneau de Configuration -> Pare-feu Windows ).</ul> \
6089 - <p> Il est \u00e9galement possible que vous soyez derri\u00e8re un proxy que l'application \
6090 - est incapable de d\u00e9tecter automatiquement. \
6091 - Si tel est le cas, veuillez saisir les r\u00e9glages proxy ci-dessous.</html>
6092 + (Démarrer -> Panneau de Configuration -> Pare-feu Windows ).</ul> \
6093 + <p> Il est également possible que vous soyez derrière un proxy que l'application \
6094 + est incapable de détecter automatiquement. \
6095 + Si tel est le cas, veuillez saisir les réglages proxy ci-dessous.</html>
6097 -m.proxy_extra =<html>Si vous \u00eates certain de ne pas utiliser de proxy, il est \
6098 - possible qu'une interruption temporaire de la connexion internet emp\u00fbche la \
6099 +m.proxy_extra =<html>Si vous êtes certain de ne pas utiliser de proxy, il est \
6100 + possible qu'une interruption temporaire de la connexion internet empûche la \
6101 communication avec les serveurs. Dans ce cas, vous pouvez relancer \
6102 - l'installation ult\u00e9rieurement.</html>
6103 + l'installation ultérieurement.</html>
6105 m.proxy_host = Proxy IP
6106 m.proxy_port = Proxy port
6107 @@ -33,79 +31,77 @@ m.proxy_auth_required = Identification requise
6109 m.proxy_cancel = Annuler
6111 -m.downloading_java = T\u00e9l\u00e9chargement en cours de la Machine Virtuelle Java
6112 -m.unpacking_java = D\u00e9compression en cours de la Machine Virtuelle Java
6113 +m.downloading_java = Téléchargement en cours de la Machine Virtuelle Java
6114 +m.unpacking_java = Décompression en cours de la Machine Virtuelle Java
6116 -m.resolving = R\u00e9solution des t\u00e9l\u00e9chargements en cours
6117 -m.downloading = T\u00e9l\u00e9chargement des donn\u00e9es en cours
6118 -m.failure = \u00c9chec du t\u00e9l\u00e9chargement: {0}
6119 +m.resolving = Résolution des téléchargements en cours
6120 +m.downloading = Téléchargement des données en cours
6121 +m.failure = Échec du téléchargement: {0}
6123 -m.checking = V\u00e9rification de la mise-\u00e0-jour en cours
6124 +m.checking = Vérification de la mise-à-jour en cours
6125 m.validating = Validation en cours
6126 m.patching = Modification en cours
6127 m.launching = Lancement en cours
6129 -m.patch_notes = Notes de mise-\u00e0-jour
6130 +m.patch_notes = Notes de mise-à-jour
6132 -m.complete = Complet \u00e0 {0}%
6133 +m.complete = Complet à {0}%
6134 m.remain = {0} restant
6136 -m.updating_metadata = T\u00e9l\u00e9chargement des fichiers de contr\u00f4les en cours
6137 +m.updating_metadata = Téléchargement des fichiers de contrôles en cours
6139 -m.init_failed = Notre fichier de configuration est perdu ou corrompu. T\u00e9l\u00e9chargement \
6140 +m.init_failed = Notre fichier de configuration est perdu ou corrompu. Téléchargement \
6141 d'une nouvelle copie en cours ...
6143 -m.java_download_failed = Impossible de t\u00e9l\u00e9charger automatiquement la \
6144 - version de Java n\u00e9cessaire.\n\n\
6145 - Veuillez vous rendre sur www.java.com et t\u00e9l\u00e9charger et installer la version \
6146 - la plus r\u00e9cente de Java, avant d'ex\u00e9cuter l'application \u00e0 nouveau.
6147 +m.java_download_failed = Impossible de télécharger automatiquement la \
6148 + version de Java nécessaire.\n\n\
6149 + Veuillez vous rendre sur www.java.com et télécharger et installer la version \
6150 + la plus récente de Java, avant d'exécuter l'application à nouveau.
6152 -m.java_unpack_failed = Impossible de d\u00e9compresser la version de \
6153 - Java n\u00e9cessaire. Veuillez v\u00e9rifier que vous avez au moins 100 MB d'espace libre \
6154 - sur votre disque dur puis tenter d'ex\u00e9cuter l'application \u00e0 nouveau.\n\n\
6155 - Si le probl\u00e8me persiste, rendez vous www.java.com et t\u00e9l\u00e9chargez et \
6156 - installez la version plus r\u00e9cente de Java puis essayez de nouveau.
6157 +m.java_unpack_failed = Impossible de décompresser la version de \
6158 + Java nécessaire. Veuillez vérifier que vous avez au moins 100 MB d'espace libre \
6159 + sur votre disque dur puis tenter d'exécuter l'application à nouveau.\n\n\
6160 + Si le problème persiste, rendez vous www.java.com et téléchargez et \
6161 + installez la version plus récente de Java puis essayez de nouveau.
6163 -m.unable_to_repair = Impossible de t\u00e9l\u00e9charger les fichiers n\u00e9cessaires apr\u00e8s \
6164 - cinq tentatives. Vous pouvez tenter d'ex\u00e9cuter l'application \u00e0 nouveau, mais il est \
6165 - possible qu'une d\u00e9sinstallation / r\u00e9installation soit n\u00e9cessaire.
6166 +m.unable_to_repair = Impossible de télécharger les fichiers nécessaires après \
6167 + cinq tentatives. Vous pouvez tenter d'exécuter l'application à nouveau, mais il est \
6168 + possible qu'une désinstallation / réinstallation soit nécessaire.
6170 -m.unknown_error = Une erreur inconnue a fait \u00e9chouer le lancement de l'application. \
6171 +m.unknown_error = Une erreur inconnue a fait échouer le lancement de l'application. \
6172 Veuillez visiter\n{0} pour plus d'informations.
6173 -m.init_error = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause de l'erreur \
6174 +m.init_error = Le lancement de l'application a échoué à cause de l'erreur \
6175 suivante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations.
6177 -m.readonly_error = Le r\u00e9pertoire d'installation de cette application: \
6178 - \n{0}\nest en lecture seule. Veuillez installer l'application dans un r\u00e9pertoire avec \
6179 - un acc\u00e8s en \u00e9criture.
6180 +m.readonly_error = Le répertoire d'installation de cette application: \
6181 + \n{0}\nest en lecture seule. Veuillez installer l'application dans un répertoire avec \
6182 + un accès en écriture.
6184 -m.missing_resource = Le lancement de l'application a \u00e9chou\u00e9 \u00e0 cause d'une \
6185 +m.missing_resource = Le lancement de l'application a échoué à cause d'une \
6186 ressource manquante:\n{0}\n\nVeuillez visiter\n{1} pour plus d'informations.
6188 m.insufficient_permissions_error = Vous n'avez pas accepter la signature \
6189 - num\u00e9rique de cette application. Si vous souhaitez ex\u00e9cuter cette application, vous \
6190 - devez accepter sa signature num\u00e9rique.\n\nAfin de le faire, vous devez quitter votre \
6191 - navigateur, le red\u00e9marrer, retourner \u00e0 cette page puis relancer l'application. \
6192 - Une fois la bo\u00eete de dialogue de s\u00e9curit\u00e9 affich\u00e9e, cliquez sur le bouton \
6193 - pour accepter la signature num\u00e9rique et accorder les permissions n\u00e9cessaires au bon \
6194 + numérique de cette application. Si vous souhaitez exécuter cette application, vous \
6195 + devez accepter sa signature numérique.\n\nAfin de le faire, vous devez quitter votre \
6196 + navigateur, le redémarrer, retourner à cette page puis relancer l'application. \
6197 + Une fois la boîte de dialogue de sécurité affichée, cliquez sur le bouton \
6198 + pour accepter la signature numérique et accorder les permissions nécessaires au bon \
6199 fonctionnement de l'application.
6201 -m.corrupt_digest_signature_error = Nous ne pouvons pas v\u00e9rifier la signature num\u00e9rique \
6202 - de l'application.\nVeuillez v\u00e9rifier que vous lancez l'application \ndepuis \
6203 +m.corrupt_digest_signature_error = Nous ne pouvons pas vérifier la signature numérique \
6204 + de l'application.\nVeuillez vérifier que vous lancez l'application \ndepuis \
6205 la bonne adresse internet.
6207 m.default_install_error = la section de support du site
6209 m.another_getdown_running = Plusieurs instances d'installation de cette \
6210 - application sont d\u00e9j\u00e0 en cours d'ex\u00e9cution. Cette instance va s'arr\u00eater \
6211 + application sont déjà en cours d'exécution. Cette instance va s'arrêter \
6212 afin de permettre aux autres d'aboutir.
6214 -m.applet_stopped = L'appelet Getdown a \u00e9t\u00e9 stopp\u00e9e.
6216 # application/digest errors
6217 m.missing_appbase = Le fichier de configuration ne contient pas 'appbase'.
6218 -m.invalid_version = Le fichier de configuration sp\u00e9cifie une version invalide.
6219 -m.invalid_appbase = Le fichier de configuration sp\u00e9cifie un 'appbase' invalide.
6220 +m.invalid_version = Le fichier de configuration spécifie une version invalide.
6221 +m.invalid_appbase = Le fichier de configuration spécifie un 'appbase' invalide.
6222 m.missing_class = Le fichier de configuration ne contient pas la classe de l'application.
6223 -m.missing_code = Le fichier de configuration ne sp\u00e9cifie aucune ressource de code.
6224 +m.missing_code = Le fichier de configuration ne spécifie aucune ressource de code.
6225 m.invalid_digest_file = Le fichier digest est invalide.
6226 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
6227 index 33b3260ce..aea9e9017 100644
6228 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties
6229 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_it.properties
6234 -# Getdown translation messages
6235 +# Getdown Italian translation messages
6237 m.abort_title = Annullare l'installazione?
6238 m.abort_confirm = <html>Sei sicuro di voler annullare l'installazione? \
6239 @@ -22,7 +20,11 @@ m.configure_proxy = <html>Impossibile collegarsi al server per \
6240 questo potrebbe non essere stato riconosciuto automaticamente. Se conosci le \
6241 tue impostazioni del proxy, puoi inserirle di seguito.</html>
6243 -m.proxy_extra = <html>Se sei sicuro di non usare proxy \
6244 +m.update_proxy_auth = <html>Combinazione User/Password salvata per il proxy non è valida \
6245 + oppure obsoleta. \
6246 + <p>Perfavore inserire una combinazione User/Password valida.</html>
6248 +m.proxy_extra = <html>Se sei sicuro di non usare proxy \
6249 potrebbe essere un problema di internet o di collegamento con il server. \
6250 In questo caso puoi annullare e ripetere l'installazione più tardi.</html>
6252 @@ -47,8 +49,6 @@ m.patching = Applico le patch
6255 m.patch_notes = Note delle Patch
6256 -m.play_again = Avvia Nuovamente
6258 m.complete = {0}% completato
6259 m.remain = {0} rimasto
6261 @@ -103,8 +103,6 @@ m.default_install_error = la sezione di supporto del sito
6262 m.another_getdown_running = E' già in esecuzione un'istanza del programma. \
6263 Questa verrà chiusa.
6265 -m.applet_stopped = L'applet di Getdown è stata interrotta.
6267 # application/digest errors
6268 m.missing_appbase = Il tag "appbase" è mancante.
6269 m.invalid_version = Il file di configurazione non contiene una versione valida (tag "version").
6270 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
6271 index c344c16e0..f3538d0ac 100644
6272 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties
6273 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties
6278 -# Getdown translation messages
6280 -m.abort_title = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f
6281 -m.abort_confirm = <html>\u672c\u5f53\u306b\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3092\u4e2d\u6b62\u3057\u307e\u3059\u304b\uff1f \
6282 - \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</html>
6283 -m.abort_ok = \u4e2d\u6b62
6284 -m.abort_cancel = \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u7d9a\u884c
6286 -m.detecting_proxy = \u81ea\u52d5\u30d7\u30ed\u30ad\u30b7\u8a2d\u5b9a\u5b9f\u884c\u4e2d
6288 -m.configure_proxy = <html>\u30b5\u30fc\u30d0\u306b\u63a5\u7d9a\u3067\u304d\u306a\u3044\u305f\u3081\u3001\u30b2\u30fc\u30e0\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306b \
6289 - \u5931\u6557\u3057\u307e\u3057\u305f\u3002 \
6290 - <ul><li>\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 \
6291 - <code>javaw.exe</code>\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 \
6292 - <code>javaw.exe</code>\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 \
6293 - \u3057\u305f\u5f8c\u3001\u30d5\u30a1\u30a4\u30a2\u30a6\u30a9\u30fc\u30eb\u306e\u8a2d\u5b9a\u304b\u3089javaw.exe \u3092\u524a\u9664 \
6294 - \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</ul> \
6295 - <p>\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 \
6296 - \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 \
6297 - \u308f\u304b\u3063\u3066\u3044\u308b\u5834\u5408\u306f\u3001\u4e0b\u306b\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044\u3002</html>
6299 -m.proxy_extra = <html>\u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u3044\u306a\u3044\u5834\u5408\u306f\u3001\u4e00\u6642\u7684\u306a \
6300 - \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 \
6301 - \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 \
6302 - \u5f8c\u307b\u3069\u6539\u3081\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002</html>
6304 -m.proxy_host = \u30d7\u30ed\u30ad\u30b7IP
6305 -m.proxy_port = \u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8
6306 +# Getdown Japanese translation messages
6308 +m.abort_title = インストールを中止しますか?
6309 +m.abort_confirm = <html>本当にインストールを中止しますか? \
6310 + 後でアプリケーションを起動した際にインストールを再開できます。</html>
6312 +m.abort_cancel = インストールの続行
6314 +m.detecting_proxy = 自動プロキシ設定実行中
6316 +m.configure_proxy = <html>サーバに接続できないため、ゲームのダウンロードに \
6318 + <ul><li>ウィンドウズファイアウォールまたはノートンインターネットセキュリティが \
6319 + <code>javaw.exe</code>をブロックするよう設定してある場合は、ゲームをダウンロードできません。 設定を \
6320 + <code>javaw.exe</code>経由でインターネットにアクセスできるように変更してください。 ゲームを再起動 \
6321 + した後、ファイアウォールの設定からjavaw.exe を削除 \
6322 + してください(スタート→コントロールパネル→ファイアウォール)。</ul> \
6323 + <p>プロキシ設定の自動検出ができません。お使いのコンピューターは \
6324 + プロキシを使用してインターネットへアクセスしています。 プロキシ設定の詳細が \
6325 + わかっている場合は、下に入力してください。</html>
6327 +m.proxy_extra = <html>プロキシを使用していない場合は、一時的な \
6328 + インターネットの不具合により、サーバと交信できない状態にある \
6329 + 可能性があります。 その場合はインストールをキャンセルして、 \
6330 + 後ほど改めて実行してください。</html>
6332 +m.proxy_host = プロキシIP
6333 +m.proxy_port = プロキシポート
6334 m.proxy_username = Username
6335 m.proxy_password = Password
6336 m.proxy_auth_required = Authentication required
6338 -m.proxy_cancel = \u30ad\u30e3\u30f3\u30bb\u30eb
6340 +m.proxy_cancel = キャンセル
6342 -m.downloading_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
6343 -m.unpacking_java = Java\u30d0\u30fc\u30c1\u30e3\u30eb\u30de\u30b7\u30f3\u306e\u89e3\u51cd\u4e2d
6344 +m.downloading_java = Javaバーチャルマシンのダウンロード中
6345 +m.unpacking_java = Javaバーチャルマシンの解凍中
6347 -m.resolving = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u306e\u8a2d\u5b9a\u4e2d
6348 -m.downloading = \u30c7\u30fc\u30bf\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
6349 -m.failure = \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u5931\u6557\uff1a {0}
6350 +m.resolving = ダウンロードの設定中
6351 +m.downloading = データのダウンロード中
6352 +m.failure = ダウンロード失敗: {0}
6354 -m.checking = \u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u306e\u78ba\u8a8d\u4e2d
6355 -m.validating = \u8a8d\u8a3c\u4e2d
6356 -m.patching = \u4fee\u6b63\u30d7\u30ed\u30b0\u30e9\u30e0\u306e\u5b9f\u884c\u4e2d
6357 -m.launching = \u5b9f\u884c\u4e2d
6358 +m.checking = アップデートの確認中
6360 +m.patching = 修正プログラムの実行中
6363 -m.complete = {0}\uff05\u5b8c\u4e86
6364 -m.remain = \u3000\u6b8b\u308a{0}
6365 +m.complete = {0}%完了
6368 -m.updating_metadata = \u30b3\u30f3\u30c8\u30ed\u30fc\u30eb\u30d5\u30a1\u30a4\u30eb\u306e\u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d
6369 +m.updating_metadata = コントロールファイルのダウンロード中
6371 -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 \
6372 - \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u4e2d\u2026
6373 +m.init_failed = 環境設定ファイルが存在しないか、または壊れています。 新バージョンを \
6376 -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 \
6377 - \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 \
6378 - 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 \
6379 - \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6380 +m.java_download_failed = お使いのコンピューターに、Javaプログラムの最新 \
6381 + バージョンを自動インストールできませんでした。\n\n \
6382 + www.java.com から最新バージョンを手動でダウンロードして、 \
6383 + 再度アプリケーションを起動してください。
6385 -m.java_unpack_failed = Java\u306e\u30a2\u30c3\u30d7\u30c7\u30fc\u30c8\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u89e3\u51cd \
6386 - \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 \
6387 - \u518d\u5ea6\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u8d77\u52d5\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\n \
6388 - \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 \
6389 - \u30c0\u30a6\u30f3\u30ed\u30fc\u30c9\u3057\u3066\u304b\u3089\u3001\u518d\u5ea6\u304a\u8a66\u3057\u304f\u3060\u3055\u3044\u3002
6390 +m.java_unpack_failed = Javaのアップデートバージョンが解凍 \
6391 + できませんでした。 ハードドライブのメモリが100MB以上あることを確認してから \
6392 + 再度アプリケーションを起動してください。\n\n \
6393 + 問題が解決しない場合は、www.java.com からJavaの最新バージョンを \
6394 + ダウンロードしてから、再度お試しください。
6396 -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 \
6397 - \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 \
6398 - \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
6399 +m.unable_to_repair = 5回試行しましたが、必要なファイルをダウンロード \
6400 + できませんでした。 後ほど改めてアプリケーションを実行してください。 \
6401 + 再度失敗した場合は、アンインストール後に再インストールしてください。
6403 -m.unknown_error = \u539f\u56e0\u4e0d\u660e\u306e\u30a8\u30e9\u30fc\u306b\u3088\u308a\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u304c \
6404 - \u5b9f\u884c\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 \u89e3\u6c7a\u65b9\u6cd5\u3092\n{0}\u3067 \
6405 - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6406 -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 \
6407 - \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067 \
6408 - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6409 +m.unknown_error = 原因不明のエラーにより、アプリケーションが \
6410 + 実行できませんでした。 解決方法を\n{0}で \
6412 +m.init_error = 次のエラーによりアプリケーションを実行できません \
6413 + でした。\n{0}\n\n対処方法を\n{1}で \
6416 -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 \
6417 - \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 \
6418 - \u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6419 +m.readonly_error = このアプリケーションがインストールされたフォルダは \
6420 + \n{0}\n読み取り専用に設定されています。 アプリケーションを書き込みができるフォルダに \
6423 -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 \
6424 - \u3067\u3057\u305f\u3002\n{0}\n\n\u5bfe\u51e6\u65b9\u6cd5\u3092\n{1}\u3067\u78ba\u8a8d \
6425 - \u3057\u3066\u304f\u3060\u3055\u3044\u3002
6426 +m.missing_resource = リソース不明のためアプリケーションを実行できません \
6427 + でした。\n{0}\n\n対処方法を\n{1}で確認 \
6430 -m.insufficient_permissions_error = \u3053\u306e\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u62d2\u5426 \
6431 - \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 \
6432 - \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 \
6433 - \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 \
6434 - \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 \
6435 - \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6436 +m.insufficient_permissions_error = このアプリケーションのデジタル署名が拒否 \
6437 + されました。 アプリケーションを実行する場合は、デジタル署名の承認が \
6438 + 必要です。\n\n承認には、ブラウザを閉じてから再度開き、 \
6439 + 本ホームページを再表示してアプリケーションを再度実行してください セキュリティの \
6440 + 警告が表示された時は、実行をクリックしてデジタル署名を承認し、 \
6441 + アプリケーションを実行してください。
6443 -m.corrupt_digest_signature_error = \u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30c7\u30b8\u30bf\u30eb\u7f72\u540d\u304c\u8a8d\u8a3c \
6444 - \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 \
6445 - \u78ba\u8a8d\u3057\u3066\u304f\u3060\u3055\u3044\u3002
6446 +m.corrupt_digest_signature_error = アプリケーションのデジタル署名が認証 \
6447 + できませんでした。\n指定ホームページからアプリケーションを実行しているか\n \
6450 -m.default_install_error = \u30db\u30fc\u30e0\u30da\u30fc\u30b8\u3067\u306e\u30b5\u30dd\u30fc\u30c8\u8868\u793a
6451 +m.default_install_error = ホームページでのサポート表示
6453 # application/digest errors
6454 -m.missing_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u306eappbase\u304c\u4e0d\u660e\u3067\u3059\u3002
6455 -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
6456 -m.invalid_appbase = \u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u306aappbase\u3092\u6307\u5b9a\u3057\u3066\u3044\u307e\u3059\u3002
6457 -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
6458 -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
6459 -m.invalid_digest_file = \u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30d5\u30a1\u30a4\u30eb\u304c\u7121\u52b9\u3067\u3059\u3002
6460 +m.missing_appbase = 設定ファイルのappbaseが不明です。
6461 +m.invalid_version = 設定ファイルは無効なバージョンを指定しています。
6462 +m.invalid_appbase = 設定ファイルが無効なappbaseを指定しています。
6463 +m.missing_class = 設定ファイルのアプリケーションクラスが不明です。
6464 +m.missing_code = 設定ファイルでコードリソースが指定されていません。
6465 +m.invalid_digest_file = ダイジェストファイルが無効です。
6466 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
6467 index 3f8a47f35..05700d363 100644
6468 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties
6469 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties
6474 -# Getdown translation messages
6475 +# Getdown Korean translation messages
6477 -m.abort_title = \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?
6478 -m.abort_confirm = <html>\uC815\uB9D0\uB85C \uC124\uCE58\uB97C \uCDE8\uC18C\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? \
6479 - \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.</html>
6480 -m.abort_ok = \uC911\uC9C0
6481 -m.abort_cancel = \uACC4\uC18D\uD558\uC5EC \uC124\uCE58
6482 +m.abort_title = 설치를 취소하시겠습니까?
6483 +m.abort_confirm = <html>정말로 설치를 취소하시겠습니까? \
6484 + 나중에 어플리케이션을 실행하여 설치를 재개하여 주십시오.</html>
6486 +m.abort_cancel = 계속하여 설치
6488 -m.detecting_proxy = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4
6489 +m.detecting_proxy = 자동 프록시를 설정을 시도
6491 -m.configure_proxy = <html>\uAC8C\uC784 \uB370\uC774\uD130\uB97C \uBC1B\uAE30 \uC704\uD55C \uC11C\uBC84 \uC811\uC18D\uC5D0 \uC2E4\uD328\uD558\uC600\uC2B5\uB2C8\uB2E4.\
6492 - <ul><li>\uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD \uB610\uB294 \uB178\uD134 \uC778\uD130\uB137 \uC2DC\uD050\uB9AC\uD2F0\uAC00 <code>javaw.exe</code>\uC774 \uC124\uC815\uC5D0\uC11C \uCC28\uB2E8\uB418\uC5B4 \uC788\uC744 \uACBD\uC6B0, \
6493 - \uAC8C\uC784 \uB370\uC774\uD130\uB97C \uB2E4\uC6B4\uB85C\uB4DC \uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \
6494 - <code>javaw.exe</code>\uAC00 \uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD560 \uC218 \uC788\uB3C4\uB85D \uC124\uC815\uC744 \uBCC0\uACBD\uD558\uC5EC \uC8FC\uC2ED\uC2DC\uC624. \
6495 - \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. \
6496 - ( \uC2DC\uC791 -> \uC81C\uC5B4\uD310 -> \uC708\uB3C4\uC6B0 \uBC29\uD654\uBCBD )</ul> \
6497 - <p> \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, \
6498 - \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.</html>
6499 +m.configure_proxy = <html>게임 데이터를 받기 위한 서버 접속에 실패하였습니다.\
6500 + <ul><li>윈도우 방화벽 또는 노턴 인터넷 시큐리티가 <code>javaw.exe</code>이 설정에서 차단되어 있을 경우, \
6501 + 게임 데이터를 다운로드 할 수 없습니다. \
6502 + <code>javaw.exe</code>가 인터넷 연결을 할 수 있도록 설정을 변경하여 주십시오. \
6503 + 게임을 다시 실행한 후, 방화벽 설정에서 javaw.exe를 삭제하여 주십시오. \
6504 + ( 시작 -> 제어판 -> 윈도우 방화벽 )</ul> \
6505 + <p> 컴퓨터가 프록시 서버를 통해 인터넷에 연결되어 있다면, 프록시 설정의 자동 구성을 사용할 수 없으므로, \
6506 + 사용하는 프록시 설정을 알고 있을 경우 아래에 입력하여 주시길 바랍니다.</html>
6508 -m.proxy_extra = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\uB3C4
6509 +m.proxy_extra = 자동 프록시를 설정을 시도
6511 -m.proxy_host = \uD504\uB85D\uC2DC IP
6512 -m.proxy_port = \uD504\uB85D\uC2DC \uD3EC\uD2B8
6513 +m.proxy_host = 프록시 IP
6514 +m.proxy_port = 프록시 포트
6515 m.proxy_username = Username
6516 m.proxy_password = Password
6517 m.proxy_auth_required = Authentication required
6519 -m.proxy_cancel = \uCDE8\uC18C
6521 -m.downloading_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uB2E4\uC6B4\uB85C\uB4DC \uC911
6522 -m.unpacking_java = \uC790\uBC14 \uAC00\uC0C1 \uBA38\uC2E0(JVM) \uC555\uCD95\uC744 \uD574\uC81C\uD558\uB294 \uC911
6524 -m.resolving = \uB2E4\uC6B4\uB85C\uB4DC \uBD84\uC11D \uC911
6525 -m.downloading = \uB370\uC774\uD130 \uB2E4\uC6B4\uB85C\uB4DC \uC911
6526 -m.failure = \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328: {0}
6527 +m.proxy_cancel = 취소
6529 -m.checking = \uC5C5\uB370\uC774\uD2B8 \uCCB4\uD06C
6530 -m.validating = \uC720\uD6A8\uC131 \uAC80\uC0AC \uC911
6531 -m.patching = \uD328\uCE58 \uC911
6532 -m.launching = \uC2E4\uD589 \uC911
6533 +m.downloading_java = 자바 가상 머신(JVM) 다운로드 중
6534 +m.unpacking_java = 자바 가상 머신(JVM) 압축을 해제하는 중
6536 -m.patch_notes = \uD328\uCE58 \uB178\uD2B8
6537 -m.play_again = \uB2E4\uC2DC \uC2E4\uD589
6538 +m.resolving = 다운로드 분석 중
6539 +m.downloading = 데이터 다운로드 중
6540 +m.failure = 다운로드 실패: {0}
6542 -m.complete = {0}% \uC644\uB8CC
6543 -m.remain = {0} \uB0A8\uC74C
6544 +m.checking = 업데이트 체크
6545 +m.validating = 유효성 검사 중
6549 -m.updating_metadata = \uCEE8\uD2B8\uB864 \uD30C\uC77C\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911
6550 +m.patch_notes = 패치 노트
6551 +m.complete = {0}% 완료
6554 -m.init_failed = \uC124\uC815 \uD30C\uC77C\uC774 \uB204\uB77D\uB418\uC5C8\uAC70\uB098 \uBCC0\uD615\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \
6555 - \uC0C8\uB85C\uC6B4 \uBCF5\uC0AC\uBCF8\uC744 \uB2E4\uC6B4\uB85C\uB4DC \uC911\uC785\uB2C8\uB2E4...
6556 +m.updating_metadata = 컨트롤 파일을 다운로드 중
6558 -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\
6559 - \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, \
6560 - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.
6561 +m.init_failed = 설정 파일이 누락되었거나 변형되었습니다. \
6562 + 새로운 복사본을 다운로드 중입니다...
6564 -m.java_unpack_failed = \uC5C5\uB370\uC774\uD2B8\uB41C \uBC84\uC804\uC758 \uC790\uBC14\uC758 \uC555\uCD95\uC744 \uD480 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \
6565 - \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\
6566 - \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, \
6567 - \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC2ED\uC2DC\uC624.
6568 +m.java_download_failed = 이 컴퓨터에 필요한 새로운 버전의 자바를 자동으로 다운로드할 수 없습니다.\n\n\
6569 + 자바 웹사이트(www.java.com)로 가서 최신의 자바를 다운로드 받으신 후, \
6570 + 어플리케이션을 다시 실행해 주십시오.
6572 -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. \
6573 - \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.
6574 +m.java_unpack_failed = 업데이트된 버전의 자바의 압축을 풀 수 없습니다. \
6575 + 하드드라이브에 최소한 100MB의 용량을 확보한 이후, 어플리케이션을 다시 실행해 주십시오.\n\n\
6576 + 만약 문제가 해결되지 않는다면, 자바 웹사이트(www.java.com)로 가서 최신의 자바를 다운로드 받으신 후, \
6577 + 어플리케이션을 다시 실행해 주십시오.
6579 -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. \
6580 - \n{0}\uC5D0 \uB300\uD55C \uBCF5\uAD6C \uBC29\uBC95\uC744 \uCC3E\uAE30 \uC704\uD574\uC11C \uBC29\uBB38\uD558\uC2DC\uAE38 \uBC14\uB78D\uB2C8\uB2E4.
6581 +m.unable_to_repair = 다섯번의 시도에도 필요한 파일을 다운로드하지 못했습니다. \
6582 + 어플리케이션을 다시 시작해보시고, 그래도 다운로드에 실패한다면, 어플리케이션을 제거한 후, 다시 실행해보시기 바랍니다.
6584 -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:\
6585 - \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.
6586 +m.unknown_error = 복구될 수 없는 오류로 인하여 어플리케이션의 실행이 중단되었습니다. \
6587 + \n{0}에 대한 복구 방법을 찾기 위해서 방문하시길 바랍니다.
6589 -m.readonly_error = \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158\uC774 \uC124\uCE58\uB41C \uB514\uB809\uD1A0\uB9AC: \
6590 - \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.
6591 +m.init_error = 어플리케이션이 아래와 같은 에러로 실행이 중단되었습니다. 에러:\
6592 + \n{0}\n\n{1}에 대한 문제 해결 방법을 찾기 위해서 방문하시길 바랍니다.
6594 -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. : \
6595 - \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.
6596 +m.readonly_error = 어플리케이션이 설치된 디렉토리: \
6597 + \n{0}\n가 읽기 전용입니다. 읽기 권한이 승인된 렉토리에 어플리케이션을 설치하시길 바랍니다.
6599 -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. \
6600 - \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. \
6601 - \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. \
6602 - \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 \
6603 - \uAD8C\uD55C\uC744 \uBD80\uC5EC\uD574\uC8FC\uC2DC\uAE30 \uBC14\uB78D\uB2C8\uB2E4.
6604 +m.missing_resource = 리소스의 손실로 인하여 어플리케이션의 실행이 중단되었습니다. : \
6605 + \n{0}\n\n{1}에 대한 문제 해결 방법을 찾기 위해서 방문하시길 바랍니다.
6607 -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 \
6608 - \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.
6609 +m.insufficient_permissions_error = 이 어플리케이션의 디지탈 서명을 확인하지 않았습니다. \
6610 + 어플리케이션을 실행하기 위해서 디지탈 서명을 확인하여 주십시오. \
6611 + \n\n그리고 나서 웹 브라우저를 닫고 다시 시작하여 웹페이지로 돌아와 어플리케이션을 재시작해주시기 바랍니다. \
6612 + 보안에 대한 대화상자가 보이면, 디지탈 서명에 대한 확인 버튼을 클릭하고, 어플리케이션이 실행되기 위한 \
6615 -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.
6616 +m.corrupt_digest_signature_error = 어플리케이션의 디지탈 서명을 확인할 수 없습니다.\n \
6617 + 올바른 웹사이트에서 어플리케이션이 실행되고 있는 지 확인바랍니다.
6619 -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. \
6620 - \uD558\uB098\uAC00 \uC644\uB8CC\uB420 \uB54C\uAE4C\uC9C0 \uC911\uB2E8\uB429\uB2C8\uB2E4.
6621 +m.default_install_error = 웹사이트의 지원 메뉴(support section)를 확인하시기 바랍니다.
6623 -m.applet_stopped = Getdown \uC560\uD50C\uB9BF \uC2E4\uD589\uC774 \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
6624 +m.another_getdown_running = 이 어플리케이션 인스톨러의 다중 인스턴스가 실행중입니다. \
6625 + 하나가 완료될 때까지 중단됩니다.
6627 # application/digest errors
6628 -m.missing_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 'appbase' \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
6629 -m.invalid_version = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C \uBC84\uC804\uC774 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
6630 -m.invalid_appbase = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC798\uBABB\uB41C 'appbase'\uAC00 \uBA85\uC2DC\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.
6631 -m.missing_class = \uC124\uC815 \uD30C\uC77C\uC5D0 \uC5B4\uD50C\uB9AC\uCF00\uC774\uC158 \uD074\uB798\uC2A4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.
6632 -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.
6633 -m.invalid_digest_file = \uB2E4\uC774\uC81C\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.
6634 +m.missing_appbase = 설정 파일에 'appbase' 가 없습니다.
6635 +m.invalid_version = 설정 파일에 잘못된 버전이 명시되어 있습니다.
6636 +m.invalid_appbase = 설정 파일에 잘못된 'appbase'가 명시되어 있습니다.
6637 +m.missing_class = 설정 파일에 어플리케이션 클래스가 없습니다.
6638 +m.missing_code = 설정 파일에 리소스에 대한 코드가 명시되어 있지 않습니다.
6639 +m.invalid_digest_file = 다이제스트 파일이 잘못되었습니다.
6640 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
6641 index 47db91c90..e59ed20b2 100644
6642 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties
6643 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties
6648 -# Getdown translation messages
6649 +# Getdown Portuguese translation messages
6651 -m.abort_title = Cancelar a instala\u00E7\u00E3o?
6652 -m.abort_confirm = <html>Tem certeza que deseja cancelar a instala\u00E7\u00E3o? \
6653 - Voc\u00EA pode continuar a instala\u00E7\u00E3o mais tarde, \
6654 - basta executar a aplica\u00E7\u00E3o novamente.</html>
6655 +m.abort_title = Cancelar a instalação?
6656 +m.abort_confirm = <html>Tem certeza que deseja cancelar a instalação? \
6657 + Você pode continuar a instalação mais tarde, \
6658 + basta executar a aplicação novamente.</html>
6660 -m.abort_cancel = Continuar a instala\u00E7\u00E3o
6661 +m.abort_cancel = Continuar a instalação
6663 -m.detecting_proxy = Tentando detectar automaticamente as configura\u00E7\u00F5es de proxy
6664 +m.detecting_proxy = Tentando detectar automaticamente as configurações de proxy
6666 -m.configure_proxy = <html>N\u00E3o foi poss\u00EDvel conectar aos nossos servidores para \
6667 +m.configure_proxy = <html>Não foi possível conectar aos nossos servidores para \
6668 fazer o download dos dados. \
6669 - <ul><li> Se o Firewall do Windows ou o Norton Internet Security est\u00E1 configurado \
6670 - para bloquear o programa <code>javaw.exe</code> n\u00E3o ser\u00E1 poss\u00EDvel realizar \
6671 - o download. Voc\u00EA ter\u00E1 que permitir que o programa <code>javaw.exe</code> acesse \
6672 - a internet. Voc\u00EA pode tentar executar o programa novamente, mas voc\u00EA precisa \
6673 - remover o programa javaw.exe das configura\u00E7\u00F5es do firewall (Iniciar -> Painel \
6674 + <ul><li> Se o Firewall do Windows ou o Norton Internet Security está configurado \
6675 + para bloquear o programa <code>javaw.exe</code> não será possível realizar \
6676 + o download. Você terá que permitir que o programa <code>javaw.exe</code> acesse \
6677 + a internet. Você pode tentar executar o programa novamente, mas você precisa \
6678 + remover o programa javaw.exe das configurações do firewall (Iniciar -> Painel \
6679 de controle -> Firewall do Windows).</ul> \
6680 - <p> Seu computador pode estar acessando a internet atrav\u00E9s de um proxy e n\u00E3o foi \
6681 - capaz de detectar automaticamente as configura\u00E7\u00F5es de proxy. \
6682 - Voc\u00EA pode informar esses dados abaixo.</html>
6683 + <p> Seu computador pode estar acessando a internet através de um proxy e não foi \
6684 + capaz de detectar automaticamente as configurações de proxy. \
6685 + Você pode informar esses dados abaixo.</html>
6687 -m.proxy_extra = <html>Se voc\u00EA tem certeza que n\u00E3o usa um proxy, ent\u00E3o pode ser \
6688 - que exista um problema tempor\u00E1rio que est\u00E1 impedindo a comunica\u00E7\u00E3o \
6689 - com os nossos servidores. Neste caso voc\u00EA pode cancelar e tentar instalar novamente \
6690 +m.proxy_extra = <html>Se você tem certeza que não usa um proxy, então pode ser \
6691 + que exista um problema temporário que está impedindo a comunicação \
6692 + com os nossos servidores. Neste caso você pode cancelar e tentar instalar novamente \
6695 m.proxy_host = IP do Proxy
6696 m.proxy_port = Porta do Proxy
6697 -m.proxy_username = Nome de usu\u00e1rio
6698 +m.proxy_username = Nome de usuário
6699 m.proxy_password = Senha
6700 -m.proxy_auth_required = Autentifica\u00e7\u00e3o requerida
6701 +m.proxy_auth_required = Autentificação requerida
6703 m.proxy_cancel = Cancelar
6705 -m.downloading_java = Fazendo o download da m\u00E1quina virtual Java
6706 -m.unpacking_java = Descompactando a m\u00E1quina virtual Java
6707 +m.downloading_java = Fazendo o download da máquina virtual Java
6708 +m.unpacking_java = Descompactando a máquina virtual Java
6710 m.resolving = Resolvendo downloads
6711 m.downloading = Transferindo dados
6712 m.failure = Download falhou: {0}
6714 -m.checking = Verificando atualiza\u00E7\u00F5es
6715 +m.checking = Verificando atualizações
6716 m.validating = Validando
6717 m.patching = Atualizando
6718 m.launching = Executando
6720 m.patch_notes = Corrigir notas
6721 -m.play_again = Jogar de novo
6723 m.complete = {0}% completo
6724 m.remain = {0} Permanecer
6726 m.updating_metadata = Transferindo arquivos de controle
6728 -m.init_failed = Nosso arquivo de configura\u00E7\u00E3o est\u00E1 ausente ou corrompido. Tente \
6729 - baixar uma nova c\u00F3pia...
6730 +m.init_failed = Nosso arquivo de configuração está ausente ou corrompido. Tente \
6731 + baixar uma nova cópia...
6733 -m.java_download_failed = N\u00E3o conseguimos baixar automaticamente a\
6734 - vers\u00E3o necess\u00E1ria do Java para o seu computador.\n\n\
6735 - Por favor, acesse www.java.com, baixe e instale a \u00FAltima vers\u00E3o do \
6736 +m.java_download_failed = Não conseguimos baixar automaticamente a\
6737 + versão necessária do Java para o seu computador.\n\n\
6738 + Por favor, acesse www.java.com, baixe e instale a última versão do \
6739 Java, em seguida, tente executar o aplicativo novamente.
6741 -m.java_unpack_failed = N\u00E3o conseguimos descompactar uma vers\u00E3o atualizada do \
6742 - Java. Por favor, certifique-se de ter pelo menos 100 MB de espa\u00E7o livre em seu \
6743 - disco r\u00EDgido e tente executar o aplicativo novamente. \n\n\
6744 - Se isso n\u00E3o resolver o problema, acesse www.java.com,baixe e \
6745 - instale a \u00FAltima vers\u00E3o do Java e tente novamente.
6746 +m.java_unpack_failed = Não conseguimos descompactar uma versão atualizada do \
6747 + Java. Por favor, certifique-se de ter pelo menos 100 MB de espaço livre em seu \
6748 + disco rígido e tente executar o aplicativo novamente. \n\n\
6749 + Se isso não resolver o problema, acesse www.java.com,baixe e \
6750 + instale a última versão do Java e tente novamente.
6752 -m.unable_to_repair = N\u00E3o conseguimos baixar os arquivos necess\u00E1rios depois de \
6753 - cinco tentativas. Voc\u00EA pode tentar executar o aplicativo novamente, mas se ele \
6754 - falhar pode ser necess\u00E1rio desinstalar e reinstalar.
6755 +m.unable_to_repair = Não conseguimos baixar os arquivos necessários depois de \
6756 + cinco tentativas. Você pode tentar executar o aplicativo novamente, mas se ele \
6757 + falhar pode ser necessário desinstalar e reinstalar.
6759 -m.unknown_error = A aplica\u00E7\u00E3o falhou ao iniciar devido a algum erro estranho \
6760 - do qual n\u00E3o conseguimos recuperar. Por favor, visite \n{0} para obter \
6761 - informa\u00E7\u00F5es sobre como recuperar.
6762 -m.init_error = A aplica\u00E7\u00E3o falhou ao iniciar devido ao seguinte \
6763 +m.unknown_error = A aplicação falhou ao iniciar devido a algum erro estranho \
6764 + do qual não conseguimos recuperar. Por favor, visite \n{0} para obter \
6765 + informações sobre como recuperar.
6766 +m.init_error = A aplicação falhou ao iniciar devido ao seguinte \
6767 erro:\n{0}\n\nPor favor visite \n{1} para \
6768 - informa\u00E7\u00F5es sobre como lidar com esse problema.
6769 + informações sobre como lidar com esse problema.
6771 -m.readonly_error =O diret\u00F3rio no qual este aplicativo est\u00E1 instalado: \
6772 - \n{0}\n \u00E9 somente leitura. Por favor, instale o aplicativo em um diret\u00F3rio onde \
6773 - voc\u00EA tem acesso de grava\u00E7\u00E3o.
6774 +m.readonly_error =O diretório no qual este aplicativo está instalado: \
6775 + \n{0}\n é somente leitura. Por favor, instale o aplicativo em um diretório onde \
6776 + você tem acesso de gravação.
6778 -m.missing_resource = A aplica\u00E7\u00E3o falhou ao iniciar devido a uma falta \
6779 - de recurso:\n{0}\n\n Por favor, visite\n{1} para obter informa\u00E7\u00F5es sobre \
6780 +m.missing_resource = A aplicação falhou ao iniciar devido a uma falta \
6781 + de recurso:\n{0}\n\n Por favor, visite\n{1} para obter informações sobre \
6782 como lidar com tal problema.
6784 -m.insufficient_permissions_error = Voc\u00EA n\u00E3o aceitou a assinatura digital \
6785 - do aplicativo. Se voc\u00EA quiser executar o aplicativo, voc\u00EA ter\u00E1 que aceitar \
6786 - a assinatura digital. \n\nPara fazer isso, voc\u00EA ter\u00E1 que sair do seu navegador, \
6787 - reinici\u00E1-lo, e retornar a esta p\u00E1gina web para executar a aplica\u00E7\u00E3o. \
6788 - Quando o di\u00E1logo de seguran\u00E7a aparecer, clique no bot\u00E3o para aceitar a \
6789 - assinatura digital e conceder a este aplicativo os privil\u00E9gios necess\u00E1rios \
6790 +m.insufficient_permissions_error = Você não aceitou a assinatura digital \
6791 + do aplicativo. Se você quiser executar o aplicativo, você terá que aceitar \
6792 + a assinatura digital. \n\nPara fazer isso, você terá que sair do seu navegador, \
6793 + reiniciá-lo, e retornar a esta página web para executar a aplicação. \
6794 + Quando o diálogo de segurança aparecer, clique no botão para aceitar a \
6795 + assinatura digital e conceder a este aplicativo os privilégios necessários \
6798 -m.corrupt_digest_signature_error = N\u00E3o conseguimos verificar a assinatura digital \
6799 - do aplicativo.\nPor favor, verifique se voc\u00EA est\u00E1 utilizando o aplicativo \nde um \
6800 +m.corrupt_digest_signature_error = Não conseguimos verificar a assinatura digital \
6801 + do aplicativo.\nPor favor, verifique se você está utilizando o aplicativo \nde um \
6804 -m.default_install_error = a se\u00E7\u00E3o de suporte do site
6806 -m.another_getdown_running = V\u00E1rias inst\u00E2ncias desta aplica\u00E7\u00E3o \
6807 - est\u00E3o em execu\u00E7\u00E3o. Esta ir\u00E1 parar e deixar outra completar suas atividades.
6808 +m.default_install_error = a seção de suporte do site
6810 -m.applet_stopped = Foi solicitado ao miniaplicativo GetDow que parasse de trabalhar.
6811 +m.another_getdown_running = Várias instâncias desta aplicação \
6812 + estão em execução. Esta irá parar e deixar outra completar suas atividades.
6814 # application/digest errors
6815 -m.missing_appbase = O arquivo de configura\u00E7\u00E3o n\u00E3o possui o 'AppBase'.
6816 -m.invalid_version = O arquivo de configura\u00E7\u00E3o especifica uma vers\u00E3o inv\u00E1lida.
6817 -m.invalid_appbase = O arquivo de configura\u00E7\u00E3o especifica um 'AppBase' inv\u00E1lido.
6818 -m.missing_class = O arquivo de configura\u00E7\u00E3o n\u00E3o possui a classe de aplicativo.
6819 -m.missing_code = O arquivo de configura\u00E7\u00E3o n\u00E3o especifica um recurso de c\u00F3digo.
6820 -m.invalid_digest_file = O arquivo digest \u00E9 inv\u00E1lido.
6821 +m.missing_appbase = O arquivo de configuração não possui o 'AppBase'.
6822 +m.invalid_version = O arquivo de configuração especifica uma versão inválida.
6823 +m.invalid_appbase = O arquivo de configuração especifica um 'AppBase' inválido.
6824 +m.missing_class = O arquivo de configuração não possui a classe de aplicativo.
6825 +m.missing_code = O arquivo de configuração não especifica um recurso de código.
6826 +m.invalid_digest_file = O arquivo digest é inválido.
6827 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
6828 index 2c275437b..fa74fb293 100644
6829 --- a/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties
6830 +++ b/getdown/src/getdown/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties
6835 -# Getdown translation messages
6836 +# Getdown Chinese translation messages
6838 -m.detecting_proxy = \u641c\u5bfb\u4ee3\u7406\u670d\u52a1\u5668
6839 +m.detecting_proxy = 搜寻代理服务器
6841 -m.configure_proxy = <html>\u6211\u4eec\u65e0\u6cd5\u8fde\u63a5\u5230\u670d\u52a1\u5668\u4e0b\u8f7d\u6e38\u620f\u6570\u636e\u3002\u8fd9\u53ef\u80fd\u662f\u7531\u4e8e \
6842 - \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 \
6843 - \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</html>
6844 +m.configure_proxy = <html>我们无法连接到服务器下载游戏数据。这可能是由于 \
6845 + 您的计算机是通过代理服务器连接互联网的,并且我们无法自动获得代理服务器的 \
6846 + 设置。如果您知道您代理服务器的设置,您可以在下面输入。</html>
6848 -m.proxy_extra = <html>\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 \
6849 - \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<br><br> \
6850 - \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 \
6851 - \u4e86\u89e3\u5982\u4f55\u68c0\u6d4b\u60a8\u7684\u4ee3\u7406\u670d\u52a1\u5668\u8bbe\u7f6e\u3002</html>
6852 +m.proxy_extra = <html>如果您确定您没有使用代理服务器,这可能是由于暂时无法 \
6853 + 连接到互联网,导致无法和服务器通讯。这种情况,您可以取消,稍候再重新安装。<br><br> \
6854 + 如果您无法确定您是否使用了代理服务器,请访问我们网站中的技术支持部份, \
6855 + 了解如何检测您的代理服务器设置。</html>
6857 -m.proxy_host = \u4ee3\u7406\u670d\u52a1\u5668\u7684IP\u5730\u5740
6858 -m.proxy_port = \u4ee3\u7406\u670d\u52a1\u5668\u7684\u7aef\u53e3\u53f7
6859 +m.proxy_host = 代理服务器的IP地址
6860 +m.proxy_port = 代理服务器的端口号
6861 m.proxy_username = Username
6862 m.proxy_password = Password
6863 m.proxy_auth_required = Authentication required
6864 -m.proxy_ok = \u786e\u5b9a
6865 -m.proxy_cancel = \u53d6\u6d88
6867 -m.resolving = \u5206\u6790\u9700\u4e0b\u8f7d\u5185\u5bb9
6868 -m.downloading = \u4e0b\u8f7d\u6570\u636e
6869 -m.failure = \u4e0b\u8f7d\u5931\u8d25: {0}
6871 -m.checking = \u68c0\u67e5\u66f4\u65b0\u5185\u5bb9
6872 -m.validating = \u786e\u8ba4
6873 -m.patching = \u5347\u7ea7
6874 -m.launching = \u542f\u52a8
6876 +m.proxy_cancel = 取消
6878 -m.complete = {0}% \u5b8c\u6210
6879 -m.remain = {0} \u5269\u4f59\u65f6\u95f4
6880 +m.resolving = 分析需下载内容
6881 +m.downloading = 下载数据
6882 +m.failure = 下载失败: {0}
6884 -m.updating_metadata = \u4e0b\u8f7d\u63a7\u5236\u6587\u4ef6
6885 +m.checking = 检查更新内容
6890 -m.init_failed = \u65e0\u6cd5\u627e\u5230\u914d\u7f6e\u6587\u4ef6\u6216\u5df2\u635f\u574f\u3002\u5c1d\u8bd5\u91cd\u65b0\u4e0b\u8f7d...
6891 +m.complete = {0}% 完成
6892 +m.remain = {0} 剩余时间
6894 -m.unable_to_repair = \u7ecf\u8fc75\u6b21\u5c1d\u8bd5\uff0c\u4f9d\u7136\u65e0\u6cd5\u4e0b\u8f7d\u6240\u9700\u7684\u6587\u4ef6\u3002\
6895 -\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
6896 +m.updating_metadata = 下载控制文件
6898 +m.init_failed = 无法找到配置文件或已损坏。尝试重新下载...
6900 -m.unknown_error = \u7531\u4e8e\u4e00\u4e9b\u65e0\u6cd5\u56de\u590d\u7684\u4e25\u91cd\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\u3002\
6901 -\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
6902 +m.unable_to_repair = 经过5次尝试,依然无法下载所需的文件。\
6903 +您可以重新运行程序,但是如果依然失败,您可能需要反安装并重新安装。
6905 -m.init_error = \u7531\u4e8e\u4e0b\u5217\u9519\u8bef\uff0c\u7a0b\u5e8f\u542f\u52a8\u5931\u8d25\uff1a\n{0}\n\n \
6906 -\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
6907 +m.unknown_error = 由于一些无法回复的严重错误,程序启动失败。\
6908 +请访问我们的网站的技术支持部份,了解如何解决问题。
6910 +m.init_error = 由于下列错误,程序启动失败:\n{0}\n\n \
6911 +请访问我们的网站的技术支持部份,了解如何处理这些错误。
6913 -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 \
6914 -\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
6915 +m.missing_resource = 由于无法找到下列资源,程序启动失败:\n{0}\n\n \
6916 +请访问我们的网站的技术支持部份,了解如何处理这些问题。
6918 # application/digest errors
6919 -m.missing_appbase = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230 'appbase'\u3002
6920 -m.invalid_version = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684\u7248\u672c\u3002
6921 -m.invalid_appbase = \u914d\u7f6e\u6587\u4ef6\u6307\u5b9a\u4e86\u65e0\u6548\u7684 'appbase'\u3002
6922 -m.missing_class = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u7a0b\u5e8f\u6587\u4ef6\u3002
6923 -m.missing_code = \u914d\u7f6e\u6587\u4ef6\u4e2d\u65e0\u6cd5\u627e\u5230\u6307\u5b9a\u7684\u8d44\u6e90\u3002
6924 -m.invalid_digest_file = \u65e0\u6548\u7684\u914d\u7f6e\u6587\u4ef6\u3002
6925 +m.missing_appbase = 配置文件中无法找到 'appbase'。
6926 +m.invalid_version = 配置文件指定了无效的版本。
6927 +m.invalid_appbase = 配置文件指定了无效的 'appbase'。
6928 +m.missing_class = 配置文件中无法找到程序文件。
6929 +m.missing_code = 配置文件中无法找到指定的资源。
6930 +m.invalid_digest_file = 无效的配置文件。
6931 diff --git a/getdown/src/getdown/lib/commons-compress-1.18.jar b/getdown/src/getdown/lib/commons-compress-1.18.jar
6932 deleted file mode 100644
6933 index e401046b5..000000000
6934 Binary files a/getdown/src/getdown/lib/commons-compress-1.18.jar and /dev/null differ
6935 diff --git a/getdown/src/getdown/mvn_cmd b/getdown/src/getdown/mvn_cmd
6936 deleted file mode 100644
6937 index 0ce786ff8..000000000
6938 --- a/getdown/src/getdown/mvn_cmd
6941 -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
6942 diff --git a/getdown/src/getdown/pom.xml b/getdown/src/getdown/pom.xml
6943 index 78d67b0a5..ae1370dde 100644
6944 --- a/getdown/src/getdown/pom.xml
6945 +++ b/getdown/src/getdown/pom.xml
6947 <groupId>com.threerings.getdown</groupId>
6948 <artifactId>getdown</artifactId>
6949 <packaging>pom</packaging>
6950 - <version>1.8.3-SNAPSHOT</version>
6951 + <version>1.8.7-SNAPSHOT</version>
6953 <name>getdown</name>
6954 <description>An application installer and updater.</description>
6960 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
6964 <connection>scm:git:git://github.com/threerings/getdown.git</connection>
6965 <developerConnection>scm:git:git@github.com:threerings/getdown.git</developerConnection>
6967 <module>ant</module>
6972 - <id>ej-technologies</id>
6973 - <url>https://maven.ej-technologies.com/repository</url>
6980 - <groupId>org.apache.commons</groupId>
6981 - <artifactId>commons-compress</artifactId>
6982 - <version>1.18</version>
6985 - <groupId>com.install4j</groupId>
6986 - <artifactId>install4j-runtime</artifactId>
6987 - <version>7.0.11</version>
6988 - <scope>provided</scope>
6996 <stagingProfileId>aa555c46fc37d0</stagingProfileId>
7001 + <groupId>org.apache.maven.plugins</groupId>
7002 + <artifactId>maven-javadoc-plugin</artifactId>
7003 + <version>3.1.0</version>
7005 + <quiet>true</quiet>
7006 + <show>public</show>
7007 + <additionalOptions>
7008 + <additionalOption>-Xdoclint:all</additionalOption>
7009 + <additionalOption>-Xdoclint:-missing</additionalOption>
7010 + </additionalOptions>
7015 <!-- Common plugin configuration for all children -->
7018 <groupId>org.apache.maven.plugins</groupId>
7019 <artifactId>maven-compiler-plugin</artifactId>
7020 - <version>3.8.1</version>
7021 + <version>3.7.0</version>
7023 - <source>1.8</source>
7024 - <target>1.8</target>
7025 + <source>1.7</source>
7026 + <target>1.7</target>
7028 <showDeprecation>true</showDeprecation>
7029 <showWarnings>true</showWarnings>
7030 @@ -110,20 +106,12 @@
7031 <groupId>org.apache.maven.plugins</groupId>
7032 <artifactId>maven-resources-plugin</artifactId>
7033 <version>3.0.2</version>
7035 - <encoding>UTF-8</encoding>
7040 <groupId>org.apache.maven.plugins</groupId>
7041 - <artifactId>maven-javadoc-plugin</artifactId>
7042 - <version>3.0.0-M1</version>
7044 - <quiet>true</quiet>
7045 - <show>public</show>
7046 - <additionalparam>-Xdoclint:all -Xdoclint:-missing</additionalparam>
7048 + <artifactId>maven-gpg-plugin</artifactId>
7049 + <version>1.6</version>
7055 <groupId>org.apache.maven.plugins</groupId>
7056 <artifactId>maven-gpg-plugin</artifactId>
7057 - <version>1.6</version>
7060 <id>sign-artifacts</id>