001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.awt.Dimension; 008import java.io.File; 009import java.io.IOException; 010import java.io.InputStream; 011import java.net.Authenticator; 012import java.net.Inet6Address; 013import java.net.InetAddress; 014import java.net.InetSocketAddress; 015import java.net.ProxySelector; 016import java.net.Socket; 017import java.net.URL; 018import java.security.AllPermission; 019import java.security.CodeSource; 020import java.security.GeneralSecurityException; 021import java.security.KeyStoreException; 022import java.security.NoSuchAlgorithmException; 023import java.security.PermissionCollection; 024import java.security.Permissions; 025import java.security.Policy; 026import java.security.cert.CertificateException; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.EnumMap; 031import java.util.List; 032import java.util.Locale; 033import java.util.Map; 034import java.util.Set; 035import java.util.TreeSet; 036import java.util.concurrent.Callable; 037 038import javax.swing.JOptionPane; 039import javax.swing.RepaintManager; 040import javax.swing.SwingUtilities; 041 042import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager; 043import org.openstreetmap.josm.Main; 044import org.openstreetmap.josm.actions.PreferencesAction; 045import org.openstreetmap.josm.actions.RestartAction; 046import org.openstreetmap.josm.data.AutosaveTask; 047import org.openstreetmap.josm.data.CustomConfigurator; 048import org.openstreetmap.josm.data.Version; 049import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor; 050import org.openstreetmap.josm.gui.download.DownloadDialog; 051import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 052import org.openstreetmap.josm.gui.preferences.server.ProxyPreference; 053import org.openstreetmap.josm.gui.util.GuiHelper; 054import org.openstreetmap.josm.io.CertificateAmendment; 055import org.openstreetmap.josm.io.DefaultProxySelector; 056import org.openstreetmap.josm.io.MessageNotifier; 057import org.openstreetmap.josm.io.OnlineResource; 058import org.openstreetmap.josm.io.auth.CredentialsManager; 059import org.openstreetmap.josm.io.auth.DefaultAuthenticator; 060import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 061import org.openstreetmap.josm.plugins.PluginHandler; 062import org.openstreetmap.josm.plugins.PluginInformation; 063import org.openstreetmap.josm.tools.FontsManager; 064import org.openstreetmap.josm.tools.HttpClient; 065import org.openstreetmap.josm.tools.I18n; 066import org.openstreetmap.josm.tools.OsmUrlToBounds; 067import org.openstreetmap.josm.tools.PlatformHookWindows; 068import org.openstreetmap.josm.tools.Utils; 069import org.openstreetmap.josm.tools.WindowGeometry; 070import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 071 072import gnu.getopt.Getopt; 073import gnu.getopt.LongOpt; 074 075/** 076 * Main window class application. 077 * 078 * @author imi 079 */ 080public class MainApplication extends Main { 081 082 private MainFrame mainFrame; 083 084 /** 085 * Constructs a new {@code MainApplication} without a window. 086 */ 087 public MainApplication() { 088 // Allow subclassing (see JOSM.java) 089 this(null); 090 } 091 092 /** 093 * Constructs a main frame, ready sized and operating. Does not display the frame. 094 * @param mainFrame The main JFrame of the application 095 * @since 10340 096 */ 097 public MainApplication(MainFrame mainFrame) { 098 this.mainFrame = mainFrame; 099 } 100 101 @Override 102 protected void initializeMainWindow() { 103 mainPanel.reAddListeners(); 104 if (mainFrame != null) { 105 mainFrame.initialize(); 106 107 menu = mainFrame.getMenu(); 108 } else { 109 // required for running some tests. 110 menu = new MainMenu(); 111 } 112 } 113 114 @Override 115 protected void shutdown() { 116 mainFrame.storeState(); 117 super.shutdown(); 118 } 119 120 /** 121 * Displays help on the console 122 * @since 2748 123 */ 124 public static void showHelp() { 125 // TODO: put in a platformHook for system that have no console by default 126 System.out.println(tr("Java OpenStreetMap Editor")+" [" 127 +Version.getInstance().getAgentString()+"]\n\n"+ 128 tr("usage")+":\n"+ 129 "\tjava -jar josm.jar <options>...\n\n"+ 130 tr("options")+":\n"+ 131 "\t--help|-h "+tr("Show this help")+'\n'+ 132 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+ 133 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+ 134 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+ 135 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+ 136 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+ 137 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+ 138 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+ 139 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+ 140 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 141 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+ 142 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+ 143 "\t--language=<language> "+tr("Set the language")+"\n\n"+ 144 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+ 145 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+ 146 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+ 147 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+ 148 tr("options provided as Java system properties")+":\n"+ 149 // CHECKSTYLE.OFF: SingleSpaceSeparator 150 "\t-Djosm.pref=" +tr("/PATH/TO/JOSM/PREF ")+tr("Set the preferences directory")+"\n\n"+ 151 "\t-Djosm.userdata="+tr("/PATH/TO/JOSM/USERDATA")+tr("Set the user data directory")+"\n\n"+ 152 "\t-Djosm.cache=" +tr("/PATH/TO/JOSM/CACHE ")+tr("Set the cache directory")+"\n\n"+ 153 "\t-Djosm.home=" +tr("/PATH/TO/JOSM/HOMEDIR ")+ 154 // CHECKSTYLE.ON: SingleSpaceSeparator 155 tr("Relocate all 3 directories to homedir. Cache directory will be in homedir/cache")+"\n\n"+ 156 tr("-Djosm.home has lower precedence, i.e. the specific setting overrides the general one")+"\n\n"+ 157 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" + 158 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+ 159 "\t-Xmx...m\n\n"+ 160 tr("examples")+":\n"+ 161 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+ 162 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+'\n'+ 163 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+ 164 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+ 165 "\tjava -Djosm.pref=$XDG_CONFIG_HOME -Djosm.userdata=$XDG_DATA_HOME -Djosm.cache=$XDG_CACHE_HOME -jar josm.jar\n"+ 166 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+ 167 "\tjava -Xmx1024m -jar josm.jar\n\n"+ 168 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+'\n'+ 169 tr("Make sure you load some data if you use --selection.")+'\n' 170 ); 171 } 172 173 /** 174 * JOSM command line options. 175 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a> 176 * @since 5279 177 */ 178 public enum Option { 179 /** --help|-h Show this help */ 180 HELP(false), 181 /** --version Displays the JOSM version and exits */ 182 VERSION(false), 183 /** --debug Print debugging messages to console */ 184 DEBUG(false), 185 /** --trace Print detailed debugging messages to console */ 186 TRACE(false), 187 /** --language=<language> Set the language */ 188 LANGUAGE(true), 189 /** --reset-preferences Reset the preferences to default */ 190 RESET_PREFERENCES(false), 191 /** --load-preferences=<url-to-xml> Changes preferences according to the XML file */ 192 LOAD_PREFERENCES(true), 193 /** --set=<key>=<value> Set preference key to value */ 194 SET(true), 195 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */ 196 GEOMETRY(true), 197 /** --no-maximize Do not launch in maximized mode */ 198 NO_MAXIMIZE(false), 199 /** --maximize Launch in maximized mode */ 200 MAXIMIZE(false), 201 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br> 202 * --download=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) <br> 203 * --download=<filename> Open a file (any file type that can be opened with File/Open) */ 204 DOWNLOAD(true), 205 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br> 206 * --downloadgps=<URL> Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS */ 207 DOWNLOADGPS(true), 208 /** --selection=<searchstring> Select with the given search */ 209 SELECTION(true), 210 /** --offline=<osm_api|josm_website|all> Disable access to the given resource(s), delimited by comma */ 211 OFFLINE(true), 212 /** --skip-plugins */ 213 SKIP_PLUGINS(false); 214 215 private final String name; 216 private final boolean requiresArg; 217 218 Option(boolean requiresArgument) { 219 this.name = name().toLowerCase(Locale.ENGLISH).replace('_', '-'); 220 this.requiresArg = requiresArgument; 221 } 222 223 /** 224 * Replies the option name 225 * @return The option name, in lowercase 226 */ 227 public String getName() { 228 return name; 229 } 230 231 /** 232 * Determines if this option requires an argument. 233 * @return {@code true} if this option requires an argument, {@code false} otherwise 234 */ 235 public boolean requiresArgument() { 236 return requiresArg; 237 } 238 } 239 240 /** 241 * Builds the command-line argument map. 242 * @param args command-line arguments array 243 * @return command-line argument map 244 */ 245 public static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) { 246 247 List<LongOpt> los = new ArrayList<>(); 248 for (Option o : Option.values()) { 249 los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0)); 250 } 251 252 Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()])); 253 254 Map<Option, Collection<String>> argMap = new EnumMap<>(Option.class); 255 256 int c; 257 while ((c = g.getopt()) != -1) { 258 Option opt; 259 switch (c) { 260 case 'h': 261 opt = Option.HELP; 262 break; 263 case 'v': 264 opt = Option.VERSION; 265 break; 266 case 0: 267 opt = Option.values()[g.getLongind()]; 268 break; 269 default: 270 opt = null; 271 } 272 if (opt != null) { 273 Collection<String> values = argMap.get(opt); 274 if (values == null) { 275 values = new ArrayList<>(); 276 argMap.put(opt, values); 277 } 278 values.add(g.getOptarg()); 279 } else 280 throw new IllegalArgumentException("Invalid option: "+c); 281 } 282 // positional arguments are a shortcut for the --download ... option 283 for (int i = g.getOptind(); i < args.length; ++i) { 284 Collection<String> values = argMap.get(Option.DOWNLOAD); 285 if (values == null) { 286 values = new ArrayList<>(); 287 argMap.put(Option.DOWNLOAD, values); 288 } 289 values.add(args[i]); 290 } 291 292 return argMap; 293 } 294 295 /** 296 * Main application Startup 297 * @param argArray Command-line arguments 298 */ 299 public static void main(final String[] argArray) { 300 I18n.init(); 301 Main.checkJavaVersion(); 302 303 // construct argument table 304 Map<Option, Collection<String>> args = null; 305 try { 306 args = buildCommandLineArgumentMap(argArray); 307 } catch (IllegalArgumentException e) { 308 System.exit(1); 309 return; 310 } 311 312 final boolean languageGiven = args.containsKey(Option.LANGUAGE); 313 314 if (languageGiven) { 315 I18n.set(args.get(Option.LANGUAGE).iterator().next()); 316 } 317 318 initApplicationPreferences(); 319 320 Policy.setPolicy(new Policy() { 321 // Permissions for plug-ins loaded when josm is started via webstart 322 private PermissionCollection pc; 323 324 { 325 pc = new Permissions(); 326 pc.add(new AllPermission()); 327 } 328 329 @Override 330 public PermissionCollection getPermissions(CodeSource codesource) { 331 return pc; 332 } 333 }); 334 335 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler()); 336 337 // initialize the platform hook, and 338 Main.determinePlatformHook(); 339 // call the really early hook before we do anything else 340 Main.platform.preStartupHook(); 341 342 Main.COMMAND_LINE_ARGS.addAll(Arrays.asList(argArray)); 343 344 if (args.containsKey(Option.VERSION)) { 345 System.out.println(Version.getInstance().getAgentString()); 346 System.exit(0); 347 } 348 349 if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) { 350 // Enable JOSM debug level 351 logLevel = 4; 352 Main.info(tr("Printing debugging messages to console")); 353 } 354 355 boolean skipLoadingPlugins = false; 356 if (args.containsKey(Option.SKIP_PLUGINS)) { 357 skipLoadingPlugins = true; 358 Main.info(tr("Plugin loading skipped")); 359 } 360 361 if (args.containsKey(Option.TRACE)) { 362 // Enable JOSM debug level 363 logLevel = 5; 364 // Enable debug in OAuth signpost via system preference, but only at trace level 365 Utils.updateSystemProperty("debug", "true"); 366 Main.info(tr("Enabled detailed debug level (trace)")); 367 } 368 369 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES)); 370 371 if (args.containsKey(Option.SET)) { 372 for (String i : args.get(Option.SET)) { 373 String[] kv = i.split("=", 2); 374 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]); 375 } 376 } 377 378 if (!languageGiven) { 379 I18n.set(Main.pref.get("language", null)); 380 } 381 Main.pref.updateSystemProperties(); 382 383 checkIPv6(); 384 385 // asking for help? show help and exit 386 if (args.containsKey(Option.HELP)) { 387 showHelp(); 388 System.exit(0); 389 } 390 391 processOffline(args); 392 393 Main.platform.afterPrefStartupHook(); 394 395 FontsManager.initialize(); 396 397 I18n.setupLanguageFonts(); 398 399 WindowGeometry geometry = WindowGeometry.mainWindow("gui.geometry", 400 args.containsKey(Option.GEOMETRY) ? args.get(Option.GEOMETRY).iterator().next() : null, 401 !args.containsKey(Option.NO_MAXIMIZE) && Main.pref.getBoolean("gui.maximized", false)); 402 final MainFrame mainFrame = new MainFrame(contentPanePrivate, mainPanel, geometry); 403 Main.parent = mainFrame; 404 405 if (args.containsKey(Option.LOAD_PREFERENCES)) { 406 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 407 for (String i : args.get(Option.LOAD_PREFERENCES)) { 408 info("Reading preferences from " + i); 409 try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) { 410 config.openAndReadXML(is); 411 } catch (IOException ex) { 412 throw new RuntimeException(ex); 413 } 414 } 415 } 416 417 try { 418 CertificateAmendment.addMissingCertificates(); 419 } catch (IOException | GeneralSecurityException ex) { 420 Main.warn(ex); 421 Main.warn(getErrorMessage(Utils.getRootCause(ex))); 422 } 423 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 424 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault()); 425 ProxySelector.setDefault(proxySelector); 426 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 427 428 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(new Callable<SplashScreen>() { 429 @Override 430 public SplashScreen call() { 431 return new SplashScreen(); 432 } 433 }); 434 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor(); 435 monitor.beginTask(tr("Initializing")); 436 GuiHelper.runInEDT(new Runnable() { 437 @Override 438 public void run() { 439 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)); 440 } 441 }); 442 Main.setInitStatusListener(new InitStatusListener() { 443 444 @Override 445 public Object updateStatus(String event) { 446 monitor.beginTask(event); 447 return event; 448 } 449 450 @Override 451 public void finish(Object status) { 452 if (status instanceof String) { 453 monitor.finishTask((String) status); 454 } 455 } 456 }); 457 458 Collection<PluginInformation> pluginsToLoad = null; 459 460 if (!skipLoadingPlugins) { 461 pluginsToLoad = updateAndLoadEarlyPlugins(splash, monitor); 462 } 463 464 monitor.indeterminateSubTask(tr("Setting defaults")); 465 preConstructorInit(args); 466 467 monitor.indeterminateSubTask(tr("Creating main GUI")); 468 final Main main = new MainApplication(mainFrame); 469 main.initialize(); 470 471 if (!skipLoadingPlugins) { 472 loadLatePlugins(splash, monitor, pluginsToLoad); 473 } 474 475 // Wait for splash disappearance (fix #9714) 476 GuiHelper.runInEDTAndWait(new Runnable() { 477 @Override 478 public void run() { 479 splash.setVisible(false); 480 splash.dispose(); 481 mainFrame.setVisible(true); 482 } 483 }); 484 485 Main.MasterWindowListener.setup(); 486 487 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 488 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) { 489 mainFrame.setMaximized(true); 490 } 491 if (main.menu.fullscreenToggleAction != null) { 492 main.menu.fullscreenToggleAction.initial(); 493 } 494 495 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 496 497 if (Main.isPlatformWindows()) { 498 try { 499 // Check for insecure certificates to remove. 500 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) 501 // neither startupHook (need to be called before remote control) 502 PlatformHookWindows.removeInsecureCertificates(); 503 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 504 error(e); 505 } 506 } 507 508 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 509 RemoteControl.start(); 510 } 511 512 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 513 MessageNotifier.start(); 514 } 515 516 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 517 // Repaint manager is registered so late for a reason - there is lots of violation during startup process 518 // but they don't seem to break anything and are difficult to fix 519 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 520 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 521 } 522 } 523 524 static Collection<PluginInformation> updateAndLoadEarlyPlugins(SplashScreen splash, SplashProgressMonitor monitor) { 525 Collection<PluginInformation> pluginsToLoad; 526 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false)); 527 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 528 monitor.subTask(tr("Updating plugins")); 529 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false); 530 } 531 532 monitor.indeterminateSubTask(tr("Installing updated plugins")); 533 PluginHandler.installDownloadedPlugins(true); 534 535 monitor.indeterminateSubTask(tr("Loading early plugins")); 536 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 537 return pluginsToLoad; 538 } 539 540 static void loadLatePlugins(SplashScreen splash, SplashProgressMonitor monitor, Collection<PluginInformation> pluginsToLoad) { 541 monitor.indeterminateSubTask(tr("Loading plugins")); 542 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 543 toolbar.refreshToolbarControl(); 544 } 545 546 private static void processOffline(Map<Option, Collection<String>> args) { 547 if (args.containsKey(Option.OFFLINE)) { 548 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) { 549 try { 550 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 551 } catch (IllegalArgumentException e) { 552 Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.", 553 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values()))); 554 System.exit(1); 555 return; 556 } 557 } 558 Set<OnlineResource> offline = Main.getOfflineResources(); 559 if (!offline.isEmpty()) { 560 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 561 "JOSM is running in offline mode. These resources will not be available: {0}", 562 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 563 } 564 } 565 } 566 567 /** 568 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation, 569 * disabling or enabling IPV6 may only be done with next start. 570 */ 571 private static void checkIPv6() { 572 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) { 573 new Thread(new Runnable() { /* this may take some time (DNS, Connect) */ 574 @Override 575 public void run() { 576 boolean hasv6 = false; 577 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false); 578 try { 579 /* Use the check result from last run of the software, as after the test, value 580 changes have no effect anymore */ 581 if (wasv6) { 582 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 583 } 584 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) { 585 if (a instanceof Inet6Address) { 586 if (a.isReachable(1000)) { 587 /* be sure it REALLY works */ 588 Socket s = new Socket(); 589 s.connect(new InetSocketAddress(a, 80), 1000); 590 s.close(); 591 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 592 if (!wasv6) { 593 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart.")); 594 } else { 595 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4.")); 596 } 597 hasv6 = true; 598 } 599 break; /* we're done */ 600 } 601 } 602 } catch (IOException | SecurityException e) { 603 if (Main.isDebugEnabled()) { 604 Main.debug("Exception while checking IPv6 connectivity: "+e); 605 } 606 } 607 if (wasv6 && !hasv6) { 608 Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart.")); 609 Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart! 610 new RestartAction().actionPerformed(null); 611 } 612 Main.pref.put("validated.ipv6", hasv6); 613 } 614 }, "IPv6-checker").start(); 615 } 616 } 617 618 private static class GuiFinalizationWorker implements Runnable { 619 620 private final Map<Option, Collection<String>> args; 621 private final DefaultProxySelector proxySelector; 622 623 GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) { 624 this.args = args; 625 this.proxySelector = proxySelector; 626 } 627 628 @Override 629 public void run() { 630 631 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly 632 if (!handleProxyErrors()) { 633 handleNetworkErrors(); 634 } 635 636 // Restore autosave layers after crash and start autosave thread 637 handleAutosave(); 638 639 // Handle command line instructions 640 postConstructorProcessCmdLine(args); 641 642 // Show download dialog if autostart is enabled 643 DownloadDialog.autostartIfNeeded(); 644 } 645 646 private static void handleAutosave() { 647 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 648 AutosaveTask autosaveTask = new AutosaveTask(); 649 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 650 if (!unsavedLayerFiles.isEmpty()) { 651 ExtendedDialog dialog = new ExtendedDialog( 652 Main.parent, 653 tr("Unsaved osm data"), 654 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 655 ); 656 dialog.setContent( 657 trn("JOSM found {0} unsaved osm data layer. ", 658 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 659 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 660 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 661 int selection = dialog.showDialog().getValue(); 662 if (selection == 1) { 663 autosaveTask.recoverUnsavedLayers(); 664 } else if (selection == 3) { 665 autosaveTask.discardUnsavedLayers(); 666 } 667 } 668 autosaveTask.schedule(); 669 } 670 } 671 672 private static boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) { 673 if (hasErrors) { 674 ExtendedDialog ed = new ExtendedDialog( 675 Main.parent, title, 676 new String[]{tr("Change proxy settings"), tr("Cancel")}); 677 ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2); 678 ed.setMinimumSize(new Dimension(460, 260)); 679 ed.setIcon(JOptionPane.WARNING_MESSAGE); 680 ed.setContent(message); 681 682 if (ed.showDialog().getValue() == 1) { 683 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run(); 684 } 685 } 686 return hasErrors; 687 } 688 689 private boolean handleProxyErrors() { 690 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"), 691 tr("JOSM tried to access the following resources:<br>" + 692 "{0}" + 693 "but <b>failed</b> to do so, because of the following proxy errors:<br>" + 694 "{1}" + 695 "Would you like to change your proxy settings now?", 696 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()), 697 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages()) 698 )); 699 } 700 701 private boolean handleNetworkErrors() { 702 boolean condition = !NETWORK_ERRORS.isEmpty(); 703 if (condition) { 704 Set<String> errors = new TreeSet<>(); 705 for (Throwable t : NETWORK_ERRORS.values()) { 706 errors.add(t.toString()); 707 } 708 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"), 709 tr("JOSM tried to access the following resources:<br>" + 710 "{0}" + 711 "but <b>failed</b> to do so, because of the following network errors:<br>" + 712 "{1}" + 713 "It may be due to a missing proxy configuration.<br>" + 714 "Would you like to change your proxy settings now?", 715 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()), 716 Utils.joinAsHtmlUnorderedList(errors) 717 )); 718 } 719 return false; 720 } 721 } 722}