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.awt.Image; 009import java.awt.Toolkit; 010import java.awt.event.WindowAdapter; 011import java.awt.event.WindowEvent; 012import java.io.File; 013import java.io.IOException; 014import java.io.InputStream; 015import java.net.Authenticator; 016import java.net.Inet6Address; 017import java.net.InetAddress; 018import java.net.InetSocketAddress; 019import java.net.ProxySelector; 020import java.net.Socket; 021import java.net.URL; 022import java.security.AllPermission; 023import java.security.CodeSource; 024import java.security.GeneralSecurityException; 025import java.security.KeyStoreException; 026import java.security.NoSuchAlgorithmException; 027import java.security.PermissionCollection; 028import java.security.Permissions; 029import java.security.Policy; 030import java.security.cert.CertificateException; 031import java.util.ArrayList; 032import java.util.Arrays; 033import java.util.Collection; 034import java.util.EnumMap; 035import java.util.LinkedList; 036import java.util.List; 037import java.util.Locale; 038import java.util.Map; 039import java.util.Set; 040import java.util.TreeSet; 041import java.util.concurrent.Callable; 042 043import javax.swing.JFrame; 044import javax.swing.JOptionPane; 045import javax.swing.RepaintManager; 046import javax.swing.SwingUtilities; 047 048import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager; 049import org.openstreetmap.josm.Main; 050import org.openstreetmap.josm.actions.PreferencesAction; 051import org.openstreetmap.josm.actions.RestartAction; 052import org.openstreetmap.josm.data.AutosaveTask; 053import org.openstreetmap.josm.data.CustomConfigurator; 054import org.openstreetmap.josm.data.Version; 055import org.openstreetmap.josm.gui.SplashScreen.SplashProgressMonitor; 056import org.openstreetmap.josm.gui.download.DownloadDialog; 057import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder; 058import org.openstreetmap.josm.gui.preferences.server.ProxyPreference; 059import org.openstreetmap.josm.gui.util.GuiHelper; 060import org.openstreetmap.josm.io.CertificateAmendment; 061import org.openstreetmap.josm.io.DefaultProxySelector; 062import org.openstreetmap.josm.io.MessageNotifier; 063import org.openstreetmap.josm.io.OnlineResource; 064import org.openstreetmap.josm.io.auth.CredentialsManager; 065import org.openstreetmap.josm.io.auth.DefaultAuthenticator; 066import org.openstreetmap.josm.io.remotecontrol.RemoteControl; 067import org.openstreetmap.josm.plugins.PluginHandler; 068import org.openstreetmap.josm.plugins.PluginInformation; 069import org.openstreetmap.josm.tools.FontsManager; 070import org.openstreetmap.josm.tools.HttpClient; 071import org.openstreetmap.josm.tools.I18n; 072import org.openstreetmap.josm.tools.ImageProvider; 073import org.openstreetmap.josm.tools.OsmUrlToBounds; 074import org.openstreetmap.josm.tools.PlatformHookWindows; 075import org.openstreetmap.josm.tools.Utils; 076import org.openstreetmap.josm.tools.bugreport.BugReportExceptionHandler; 077 078import gnu.getopt.Getopt; 079import gnu.getopt.LongOpt; 080 081/** 082 * Main window class application. 083 * 084 * @author imi 085 */ 086public class MainApplication extends Main { 087 088 /** 089 * Constructs a new {@code MainApplication}. 090 */ 091 public MainApplication() { 092 // Allow subclassing (see JOSM.java) 093 } 094 095 /** 096 * Constructs a main frame, ready sized and operating. Does not display the frame. 097 * @param mainFrame The main JFrame of the application 098 */ 099 public MainApplication(JFrame mainFrame) { 100 addListener(); 101 mainFrame.setContentPane(contentPanePrivate); 102 mainFrame.setJMenuBar(menu); 103 geometry.applySafe(mainFrame); 104 List<Image> l = new LinkedList<>(); 105 l.add(ImageProvider.get("logo_16x16x32").getImage()); 106 l.add(ImageProvider.get("logo_16x16x8").getImage()); 107 l.add(ImageProvider.get("logo_32x32x32").getImage()); 108 l.add(ImageProvider.get("logo_32x32x8").getImage()); 109 l.add(ImageProvider.get("logo_48x48x32").getImage()); 110 l.add(ImageProvider.get("logo_48x48x8").getImage()); 111 l.add(ImageProvider.get("logo").getImage()); 112 mainFrame.setIconImages(l); 113 mainFrame.addWindowListener(new WindowAdapter() { 114 @Override 115 public void windowClosing(final WindowEvent arg0) { 116 Main.exitJosm(true, 0); 117 } 118 }); 119 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 120 } 121 122 /** 123 * Displays help on the console 124 * @since 2748 125 */ 126 public static void showHelp() { 127 // TODO: put in a platformHook for system that have no console by default 128 System.out.println(tr("Java OpenStreetMap Editor")+" [" 129 +Version.getInstance().getAgentString()+"]\n\n"+ 130 tr("usage")+":\n"+ 131 "\tjava -jar josm.jar <options>...\n\n"+ 132 tr("options")+":\n"+ 133 "\t--help|-h "+tr("Show this help")+'\n'+ 134 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+'\n'+ 135 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+'\n'+ 136 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+'\n'+ 137 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+'\n'+ 138 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+'\n'+ 139 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+'\n'+ 140 "\t--selection=<searchstring> "+tr("Select with the given search")+'\n'+ 141 "\t--[no-]maximize "+tr("Launch in maximized mode")+'\n'+ 142 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+ 143 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+ 144 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+ 145 "\t--language=<language> "+tr("Set the language")+"\n\n"+ 146 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+ 147 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+ 148 "\t--skip-plugins "+tr("Skip loading plugins")+"\n\n"+ 149 "\t--offline=<osm_api|josm_website|all> "+tr("Disable access to the given resource(s), separated by comma")+"\n\n"+ 150 tr("options provided as Java system properties")+":\n"+ 151 "\t-Djosm.pref=" +tr("/PATH/TO/JOSM/PREF ")+tr("Set the preferences directory")+"\n\n"+ 152 "\t-Djosm.userdata="+tr("/PATH/TO/JOSM/USERDATA")+tr("Set the user data directory")+"\n\n"+ 153 "\t-Djosm.cache=" +tr("/PATH/TO/JOSM/CACHE ")+tr("Set the cache directory")+"\n\n"+ 154 "\t-Djosm.home=" +tr("/PATH/TO/JOSM/HOMEDIR ")+ 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 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor")); 400 Main.parent = mainFrame; 401 402 if (args.containsKey(Option.LOAD_PREFERENCES)) { 403 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref); 404 for (String i : args.get(Option.LOAD_PREFERENCES)) { 405 info("Reading preferences from " + i); 406 try (InputStream is = HttpClient.create(new URL(i)).connect().getContent()) { 407 config.openAndReadXML(is); 408 } catch (IOException ex) { 409 throw new RuntimeException(ex); 410 } 411 } 412 } 413 414 try { 415 CertificateAmendment.addMissingCertificates(); 416 } catch (IOException | GeneralSecurityException ex) { 417 Main.warn(ex); 418 Main.warn(getErrorMessage(Utils.getRootCause(ex))); 419 } 420 Authenticator.setDefault(DefaultAuthenticator.getInstance()); 421 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault()); 422 ProxySelector.setDefault(proxySelector); 423 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance()); 424 425 final SplashScreen splash = GuiHelper.runInEDTAndWaitAndReturn(new Callable<SplashScreen>() { 426 @Override 427 public SplashScreen call() { 428 return new SplashScreen(); 429 } 430 }); 431 final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor(); 432 monitor.beginTask(tr("Initializing")); 433 GuiHelper.runInEDT(new Runnable() { 434 @Override 435 public void run() { 436 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true)); 437 } 438 }); 439 Main.setInitStatusListener(new InitStatusListener() { 440 441 @Override 442 public Object updateStatus(String event) { 443 monitor.beginTask(event); 444 return event; 445 } 446 447 @Override 448 public void finish(Object status) { 449 if (status instanceof String) { 450 monitor.finishTask((String) status); 451 } 452 } 453 }); 454 455 Collection<PluginInformation> pluginsToLoad = null; 456 457 if (!skipLoadingPlugins) { 458 pluginsToLoad = updateAndLoadEarlyPlugins(splash, monitor); 459 } 460 461 monitor.indeterminateSubTask(tr("Setting defaults")); 462 preConstructorInit(args); 463 464 monitor.indeterminateSubTask(tr("Creating main GUI")); 465 final Main main = new MainApplication(mainFrame); 466 467 if (!skipLoadingPlugins) { 468 loadLatePlugins(splash, monitor, pluginsToLoad); 469 } 470 471 // Wait for splash disappearance (fix #9714) 472 GuiHelper.runInEDTAndWait(new Runnable() { 473 @Override 474 public void run() { 475 splash.setVisible(false); 476 splash.dispose(); 477 mainFrame.setVisible(true); 478 } 479 }); 480 481 Main.MasterWindowListener.setup(); 482 483 boolean maximized = Main.pref.getBoolean("gui.maximized", false); 484 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) { 485 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 486 Main.windowState = JFrame.MAXIMIZED_BOTH; 487 mainFrame.setExtendedState(Main.windowState); 488 } else { 489 Main.debug("Main window: maximizing not supported"); 490 } 491 } 492 if (main.menu.fullscreenToggleAction != null) { 493 main.menu.fullscreenToggleAction.initial(); 494 } 495 496 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector)); 497 498 if (Main.isPlatformWindows()) { 499 try { 500 // Check for insecure certificates to remove. 501 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) 502 // neither startupHook (need to be called before remote control) 503 PlatformHookWindows.removeInsecureCertificates(); 504 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) { 505 error(e); 506 } 507 } 508 509 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) { 510 RemoteControl.start(); 511 } 512 513 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) { 514 MessageNotifier.start(); 515 } 516 517 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) { 518 // Repaint manager is registered so late for a reason - there is lots of violation during startup process 519 // but they don't seem to break anything and are difficult to fix 520 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console"); 521 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager()); 522 } 523 } 524 525 static Collection<PluginInformation> updateAndLoadEarlyPlugins(SplashScreen splash, SplashProgressMonitor monitor) { 526 Collection<PluginInformation> pluginsToLoad; 527 pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash, monitor.createSubTaskMonitor(1, false)); 528 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) { 529 monitor.subTask(tr("Updating plugins")); 530 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false); 531 } 532 533 monitor.indeterminateSubTask(tr("Installing updated plugins")); 534 PluginHandler.installDownloadedPlugins(true); 535 536 monitor.indeterminateSubTask(tr("Loading early plugins")); 537 PluginHandler.loadEarlyPlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 538 return pluginsToLoad; 539 } 540 541 static void loadLatePlugins(SplashScreen splash, SplashProgressMonitor monitor, Collection<PluginInformation> pluginsToLoad) { 542 monitor.indeterminateSubTask(tr("Loading plugins")); 543 PluginHandler.loadLatePlugins(splash, pluginsToLoad, monitor.createSubTaskMonitor(1, false)); 544 toolbar.refreshToolbarControl(); 545 } 546 547 private static void processOffline(Map<Option, Collection<String>> args) { 548 if (args.containsKey(Option.OFFLINE)) { 549 for (String s : args.get(Option.OFFLINE).iterator().next().split(",")) { 550 try { 551 Main.setOffline(OnlineResource.valueOf(s.toUpperCase(Locale.ENGLISH))); 552 } catch (IllegalArgumentException e) { 553 Main.error(tr("''{0}'' is not a valid value for argument ''{1}''. Possible values are {2}, possibly delimited by commas.", 554 s.toUpperCase(Locale.ENGLISH), Option.OFFLINE.getName(), Arrays.toString(OnlineResource.values()))); 555 System.exit(1); 556 return; 557 } 558 } 559 Set<OnlineResource> offline = Main.getOfflineResources(); 560 if (!offline.isEmpty()) { 561 Main.warn(trn("JOSM is running in offline mode. This resource will not be available: {0}", 562 "JOSM is running in offline mode. These resources will not be available: {0}", 563 offline.size(), offline.size() == 1 ? offline.iterator().next() : Arrays.toString(offline.toArray()))); 564 } 565 } 566 } 567 568 /** 569 * Check if IPv6 can be safely enabled and do so. Because this cannot be done after network activation, 570 * disabling or enabling IPV6 may only be done with next start. 571 */ 572 private static void checkIPv6() { 573 if ("auto".equals(Main.pref.get("prefer.ipv6", "auto"))) { 574 new Thread(new Runnable() { /* this may take some time (DNS, Connect) */ 575 @Override 576 public void run() { 577 boolean hasv6 = false; 578 boolean wasv6 = Main.pref.getBoolean("validated.ipv6", false); 579 try { 580 /* Use the check result from last run of the software, as after the test, value 581 changes have no effect anymore */ 582 if (wasv6) { 583 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 584 } 585 for (InetAddress a : InetAddress.getAllByName("josm.openstreetmap.de")) { 586 if (a instanceof Inet6Address) { 587 if (a.isReachable(1000)) { 588 /* be sure it REALLY works */ 589 Socket s = new Socket(); 590 s.connect(new InetSocketAddress(a, 80), 1000); 591 s.close(); 592 Utils.updateSystemProperty("java.net.preferIPv6Addresses", "true"); 593 if (!wasv6) { 594 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4 after next restart.")); 595 } else { 596 Main.info(tr("Detected useable IPv6 network, prefering IPv6 over IPv4.")); 597 } 598 hasv6 = true; 599 } 600 break; /* we're done */ 601 } 602 } 603 } catch (IOException | SecurityException e) { 604 if (Main.isDebugEnabled()) { 605 Main.debug("Exception while checking IPv6 connectivity: "+e); 606 } 607 } 608 if (wasv6 && !hasv6) { 609 Main.info(tr("Detected no useable IPv6 network, prefering IPv4 over IPv6 after next restart.")); 610 Main.pref.put("validated.ipv6", hasv6); // be sure it is stored before the restart! 611 new RestartAction().actionPerformed(null); 612 } 613 Main.pref.put("validated.ipv6", hasv6); 614 } 615 }, "IPv6-checker").start(); 616 } 617 } 618 619 private static class GuiFinalizationWorker implements Runnable { 620 621 private final Map<Option, Collection<String>> args; 622 private final DefaultProxySelector proxySelector; 623 624 GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) { 625 this.args = args; 626 this.proxySelector = proxySelector; 627 } 628 629 @Override 630 public void run() { 631 632 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly 633 if (!handleProxyErrors()) { 634 handleNetworkErrors(); 635 } 636 637 // Restore autosave layers after crash and start autosave thread 638 handleAutosave(); 639 640 // Handle command line instructions 641 postConstructorProcessCmdLine(args); 642 643 // Show download dialog if autostart is enabled 644 DownloadDialog.autostartIfNeeded(); 645 } 646 647 private static void handleAutosave() { 648 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) { 649 AutosaveTask autosaveTask = new AutosaveTask(); 650 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles(); 651 if (!unsavedLayerFiles.isEmpty()) { 652 ExtendedDialog dialog = new ExtendedDialog( 653 Main.parent, 654 tr("Unsaved osm data"), 655 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")} 656 ); 657 dialog.setContent( 658 trn("JOSM found {0} unsaved osm data layer. ", 659 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) + 660 tr("It looks like JOSM crashed last time. Would you like to restore the data?")); 661 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"}); 662 int selection = dialog.showDialog().getValue(); 663 if (selection == 1) { 664 autosaveTask.recoverUnsavedLayers(); 665 } else if (selection == 3) { 666 autosaveTask.discardUnsavedLayers(); 667 } 668 } 669 autosaveTask.schedule(); 670 } 671 } 672 673 private static boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) { 674 if (hasErrors) { 675 ExtendedDialog ed = new ExtendedDialog( 676 Main.parent, title, 677 new String[]{tr("Change proxy settings"), tr("Cancel")}); 678 ed.setButtonIcons(new String[]{"dialogs/settings", "cancel"}).setCancelButton(2); 679 ed.setMinimumSize(new Dimension(460, 260)); 680 ed.setIcon(JOptionPane.WARNING_MESSAGE); 681 ed.setContent(message); 682 683 if (ed.showDialog().getValue() == 1) { 684 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run(); 685 } 686 } 687 return hasErrors; 688 } 689 690 private boolean handleProxyErrors() { 691 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"), 692 tr("JOSM tried to access the following resources:<br>" + 693 "{0}" + 694 "but <b>failed</b> to do so, because of the following proxy errors:<br>" + 695 "{1}" + 696 "Would you like to change your proxy settings now?", 697 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()), 698 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages()) 699 )); 700 } 701 702 private boolean handleNetworkErrors() { 703 boolean condition = !NETWORK_ERRORS.isEmpty(); 704 if (condition) { 705 Set<String> errors = new TreeSet<>(); 706 for (Throwable t : NETWORK_ERRORS.values()) { 707 errors.add(t.toString()); 708 } 709 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"), 710 tr("JOSM tried to access the following resources:<br>" + 711 "{0}" + 712 "but <b>failed</b> to do so, because of the following network errors:<br>" + 713 "{1}" + 714 "It may be due to a missing proxy configuration.<br>" + 715 "Would you like to change your proxy settings now?", 716 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()), 717 Utils.joinAsHtmlUnorderedList(errors) 718 )); 719 } 720 return false; 721 } 722 } 723}