source: josm/trunk/src/org/openstreetmap/josm/gui/MainApplication.java@ 7343

Last change on this file since 7343 was 7343, checked in by Don-vip, 10 years ago

see #10230, see #10033 - add "Install/uninstall certificate" buttons in remote control preferences (Windows only)

  • Property svn:eol-style set to native
File size: 24.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6import gnu.getopt.Getopt;
7import gnu.getopt.LongOpt;
8
9import java.awt.Dimension;
10import java.awt.Image;
11import java.awt.Toolkit;
12import java.awt.event.WindowAdapter;
13import java.awt.event.WindowEvent;
14import java.io.File;
15import java.io.IOException;
16import java.io.InputStream;
17import java.net.Authenticator;
18import java.net.ProxySelector;
19import java.net.URL;
20import java.security.AllPermission;
21import java.security.CodeSource;
22import java.security.KeyStoreException;
23import java.security.NoSuchAlgorithmException;
24import java.security.PermissionCollection;
25import java.security.Permissions;
26import java.security.Policy;
27import java.security.cert.CertificateException;
28import java.util.ArrayList;
29import java.util.Collection;
30import java.util.HashMap;
31import java.util.LinkedList;
32import java.util.List;
33import java.util.Map;
34import java.util.Set;
35import java.util.TreeSet;
36
37import javax.swing.JFrame;
38import javax.swing.JOptionPane;
39import javax.swing.RepaintManager;
40import javax.swing.SwingUtilities;
41
42import org.jdesktop.swinghelper.debug.CheckThreadViolationRepaintManager;
43import org.openstreetmap.josm.Main;
44import org.openstreetmap.josm.actions.PreferencesAction;
45import org.openstreetmap.josm.data.AutosaveTask;
46import org.openstreetmap.josm.data.CustomConfigurator;
47import org.openstreetmap.josm.data.Preferences;
48import org.openstreetmap.josm.data.Version;
49import org.openstreetmap.josm.gui.download.DownloadDialog;
50import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
51import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
52import org.openstreetmap.josm.gui.progress.ProgressMonitor;
53import org.openstreetmap.josm.gui.util.GuiHelper;
54import org.openstreetmap.josm.io.DefaultProxySelector;
55import org.openstreetmap.josm.io.MessageNotifier;
56import org.openstreetmap.josm.io.auth.CredentialsManager;
57import org.openstreetmap.josm.io.auth.DefaultAuthenticator;
58import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
59import org.openstreetmap.josm.plugins.PluginHandler;
60import org.openstreetmap.josm.plugins.PluginInformation;
61import org.openstreetmap.josm.tools.BugReportExceptionHandler;
62import org.openstreetmap.josm.tools.I18n;
63import org.openstreetmap.josm.tools.ImageProvider;
64import org.openstreetmap.josm.tools.OsmUrlToBounds;
65import org.openstreetmap.josm.tools.PlatformHookWindows;
66import org.openstreetmap.josm.tools.Utils;
67
68/**
69 * Main window class application.
70 *
71 * @author imi
72 */
73public class MainApplication extends Main {
74 /**
75 * Allow subclassing (see JOSM.java)
76 */
77 public MainApplication() {}
78
79 /**
80 * Constructs a main frame, ready sized and operating. Does not display the frame.
81 * @param mainFrame The main JFrame of the application
82 */
83 public MainApplication(JFrame mainFrame) {
84 addListener();
85 mainFrame.setContentPane(contentPanePrivate);
86 mainFrame.setJMenuBar(menu);
87 geometry.applySafe(mainFrame);
88 LinkedList<Image> l = new LinkedList<>();
89 l.add(ImageProvider.get("logo_16x16x32").getImage());
90 l.add(ImageProvider.get("logo_16x16x8").getImage());
91 l.add(ImageProvider.get("logo_32x32x32").getImage());
92 l.add(ImageProvider.get("logo_32x32x8").getImage());
93 l.add(ImageProvider.get("logo_48x48x32").getImage());
94 l.add(ImageProvider.get("logo_48x48x8").getImage());
95 l.add(ImageProvider.get("logo").getImage());
96 mainFrame.setIconImages(l);
97 mainFrame.addWindowListener(new WindowAdapter(){
98 @Override
99 public void windowClosing(final WindowEvent arg0) {
100 Main.exitJosm(true, 0);
101 }
102 });
103 mainFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
104 }
105
106 /**
107 * Displays help on the console
108 * @since 2748
109 */
110 public static void showHelp() {
111 // TODO: put in a platformHook for system that have no console by default
112 System.out.println(tr("Java OpenStreetMap Editor")+" ["
113 +Version.getInstance().getAgentString()+"]\n\n"+
114 tr("usage")+":\n"+
115 "\tjava -jar josm.jar <options>...\n\n"+
116 tr("options")+":\n"+
117 "\t--help|-h "+tr("Show this help")+"\n"+
118 "\t--geometry=widthxheight(+|-)x(+|-)y "+tr("Standard unix geometry argument")+"\n"+
119 "\t[--download=]minlat,minlon,maxlat,maxlon "+tr("Download the bounding box")+"\n"+
120 "\t[--download=]<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z)")+"\n"+
121 "\t[--download=]<filename> "+tr("Open a file (any file type that can be opened with File/Open)")+"\n"+
122 "\t--downloadgps=minlat,minlon,maxlat,maxlon "+tr("Download the bounding box as raw GPS")+"\n"+
123 "\t--downloadgps=<URL> "+tr("Download the location at the URL (with lat=x&lon=y&zoom=z) as raw GPS")+"\n"+
124 "\t--selection=<searchstring> "+tr("Select with the given search")+"\n"+
125 "\t--[no-]maximize "+tr("Launch in maximized mode")+"\n"+
126 "\t--reset-preferences "+tr("Reset the preferences to default")+"\n\n"+
127 "\t--load-preferences=<url-to-xml> "+tr("Changes preferences according to the XML file")+"\n\n"+
128 "\t--set=<key>=<value> "+tr("Set preference key to value")+"\n\n"+
129 "\t--language=<language> "+tr("Set the language")+"\n\n"+
130 "\t--version "+tr("Displays the JOSM version and exits")+"\n\n"+
131 "\t--debug "+tr("Print debugging messages to console")+"\n\n"+
132 tr("options provided as Java system properties")+":\n"+
133 "\t-Djosm.home="+tr("/PATH/TO/JOSM/FOLDER/ ")+tr("Change the folder for all user settings")+"\n\n"+
134 tr("note: For some tasks, JOSM needs a lot of memory. It can be necessary to add the following\n" +
135 " Java option to specify the maximum size of allocated memory in megabytes")+":\n"+
136 "\t-Xmx...m\n\n"+
137 tr("examples")+":\n"+
138 "\tjava -jar josm.jar track1.gpx track2.gpx london.osm\n"+
139 "\tjava -jar josm.jar "+OsmUrlToBounds.getURL(43.2, 11.1, 13)+"\n"+
140 "\tjava -jar josm.jar london.osm --selection=http://www.ostertag.name/osm/OSM_errors_node-duplicate.xml\n"+
141 "\tjava -jar josm.jar 43.2,11.1,43.4,11.4\n"+
142 "\tjava -Djosm.home=/home/user/.josm_dev -jar josm.jar\n"+
143 "\tjava -Xmx1024m -jar josm.jar\n\n"+
144 tr("Parameters --download, --downloadgps, and --selection are processed in this order.")+"\n"+
145 tr("Make sure you load some data if you use --selection.")+"\n"
146 );
147 }
148
149 /**
150 * JOSM command line options.
151 * @see <a href="https://josm.openstreetmap.de/wiki/Help/CommandLineOptions">Help/CommandLineOptions</a>
152 * @since 5279
153 */
154 public enum Option {
155 /** --help|-h Show this help */
156 HELP(false),
157 /** --version Displays the JOSM version and exits */
158 VERSION(false),
159 /** --debug Print debugging messages to console */
160 DEBUG(false),
161 /** --trace Print detailed debugging messages to console */
162 TRACE(false),
163 /** --language=&lt;language&gt; Set the language */
164 LANGUAGE(true),
165 /** --reset-preferences Reset the preferences to default */
166 RESET_PREFERENCES(false),
167 /** --load-preferences=&lt;url-to-xml&gt; Changes preferences according to the XML file */
168 LOAD_PREFERENCES(true),
169 /** --set=&lt;key&gt;=&lt;value&gt; Set preference key to value */
170 SET(true),
171 /** --geometry=widthxheight(+|-)x(+|-)y Standard unix geometry argument */
172 GEOMETRY(true),
173 /** --no-maximize Do not launch in maximized mode */
174 NO_MAXIMIZE(false),
175 /** --maximize Launch in maximized mode */
176 MAXIMIZE(false),
177 /** --download=minlat,minlon,maxlat,maxlon Download the bounding box <br>
178 * --download=&lt;URL&gt; Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) <br>
179 * --download=&lt;filename&gt; Open a file (any file type that can be opened with File/Open) */
180 DOWNLOAD(true),
181 /** --downloadgps=minlat,minlon,maxlat,maxlon Download the bounding box as raw GPS <br>
182 * --downloadgps=&lt;URL&gt; Download the location at the URL (with lat=x&amp;lon=y&amp;zoom=z) as raw GPS */
183 DOWNLOADGPS(true),
184 /** --selection=&lt;searchstring&gt; Select with the given search */
185 SELECTION(true);
186
187 private String name;
188 private boolean requiresArgument;
189
190 private Option(boolean requiresArgument) {
191 this.name = name().toLowerCase().replace("_", "-");
192 this.requiresArgument = requiresArgument;
193 }
194
195 /**
196 * Replies the option name
197 * @return The option name, in lowercase
198 */
199 public String getName() {
200 return name;
201 }
202
203 /**
204 * Determines if this option requires an argument.
205 * @return {@code true} if this option requires an argument, {@code false} otherwise
206 */
207 public boolean requiresArgument() {
208 return requiresArgument;
209 }
210
211 public static Map<Option, Collection<String>> fromStringMap(Map<String, Collection<String>> opts) {
212 Map<Option, Collection<String>> res = new HashMap<>();
213 for (Map.Entry<String, Collection<String>> e : opts.entrySet()) {
214 Option o = Option.valueOf(e.getKey().toUpperCase().replace("-", "_"));
215 if (o != null) {
216 res.put(o, e.getValue());
217 }
218 }
219 return res;
220 }
221 }
222
223 private static Map<Option, Collection<String>> buildCommandLineArgumentMap(String[] args) {
224
225 List<LongOpt> los = new ArrayList<>();
226 for (Option o : Option.values()) {
227 los.add(new LongOpt(o.getName(), o.requiresArgument() ? LongOpt.REQUIRED_ARGUMENT : LongOpt.NO_ARGUMENT, null, 0));
228 }
229
230 Getopt g = new Getopt("JOSM", args, "hv", los.toArray(new LongOpt[los.size()]));
231
232 Map<Option, Collection<String>> argMap = new HashMap<>();
233
234 int c;
235 while ((c = g.getopt()) != -1 ) {
236 Option opt = null;
237 switch (c) {
238 case 'h':
239 opt = Option.HELP;
240 break;
241 case 'v':
242 opt = Option.VERSION;
243 break;
244 case 0:
245 opt = Option.values()[g.getLongind()];
246 break;
247 }
248 if (opt != null) {
249 Collection<String> values = argMap.get(opt);
250 if (values == null) {
251 values = new ArrayList<>();
252 argMap.put(opt, values);
253 }
254 values.add(g.getOptarg());
255 } else
256 throw new IllegalArgumentException();
257 }
258 // positional arguments are a shortcut for the --download ... option
259 for (int i = g.getOptind(); i < args.length; ++i) {
260 Collection<String> values = argMap.get(Option.DOWNLOAD);
261 if (values == null) {
262 values = new ArrayList<>();
263 argMap.put(Option.DOWNLOAD, values);
264 }
265 values.add(args[i]);
266 }
267
268 return argMap;
269 }
270
271 /**
272 * Main application Startup
273 * @param argArray Command-line arguments
274 */
275 public static void main(final String[] argArray) {
276 I18n.init();
277 Main.checkJavaVersion();
278
279 // construct argument table
280 Map<Option, Collection<String>> args = null;
281 try {
282 args = buildCommandLineArgumentMap(argArray);
283 } catch (IllegalArgumentException e) {
284 System.exit(1);
285 }
286
287 final boolean languageGiven = args.containsKey(Option.LANGUAGE);
288
289 if (languageGiven) {
290 I18n.set(args.get(Option.LANGUAGE).iterator().next());
291 }
292
293 initApplicationPreferences();
294
295 Policy.setPolicy(new Policy() {
296 // Permissions for plug-ins loaded when josm is started via webstart
297 private PermissionCollection pc;
298
299 {
300 pc = new Permissions();
301 pc.add(new AllPermission());
302 }
303
304 @Override
305 public void refresh() { }
306
307 @Override
308 public PermissionCollection getPermissions(CodeSource codesource) {
309 return pc;
310 }
311 });
312
313 Thread.setDefaultUncaughtExceptionHandler(new BugReportExceptionHandler());
314
315 // initialize the platform hook, and
316 Main.determinePlatformHook();
317 // call the really early hook before we do anything else
318 Main.platform.preStartupHook();
319
320 Main.commandLineArgs = Utils.copyArray(argArray);
321
322 if (args.containsKey(Option.VERSION)) {
323 System.out.println(Version.getInstance().getAgentString());
324 System.exit(0);
325 }
326
327 if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) {
328 // Enable JOSM debug level
329 logLevel = 4;
330 Main.info(tr("Printing debugging messages to console"));
331 }
332
333 if (args.containsKey(Option.TRACE)) {
334 // Enable JOSM debug level
335 logLevel = 5;
336 // Enable debug in OAuth signpost via system preference, but only at trace level
337 Preferences.updateSystemProperty("debug", "true");
338 Main.info(tr("Enabled detailed debug level (trace)"));
339 }
340
341 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES));
342
343 if (!languageGiven) {
344 I18n.set(Main.pref.get("language", null));
345 }
346 Main.pref.updateSystemProperties();
347
348 final JFrame mainFrame = new JFrame(tr("Java OpenStreetMap Editor"));
349 Main.parent = mainFrame;
350
351 if (args.containsKey(Option.LOAD_PREFERENCES)) {
352 CustomConfigurator.XMLCommandProcessor config = new CustomConfigurator.XMLCommandProcessor(Main.pref);
353 for (String i : args.get(Option.LOAD_PREFERENCES)) {
354 info("Reading preferences from " + i);
355 try (InputStream is = Utils.openURL(new URL(i))) {
356 config.openAndReadXML(is);
357 } catch (Exception ex) {
358 throw new RuntimeException(ex);
359 }
360 }
361 }
362
363 if (args.containsKey(Option.SET)) {
364 for (String i : args.get(Option.SET)) {
365 String[] kv = i.split("=", 2);
366 Main.pref.put(kv[0], "null".equals(kv[1]) ? null : kv[1]);
367 }
368 }
369
370 DefaultAuthenticator.createInstance();
371 Authenticator.setDefault(DefaultAuthenticator.getInstance());
372 DefaultProxySelector proxySelector = new DefaultProxySelector(ProxySelector.getDefault());
373 ProxySelector.setDefault(proxySelector);
374 OAuthAccessTokenHolder.getInstance().init(Main.pref, CredentialsManager.getInstance());
375
376 // asking for help? show help and exit
377 if (args.containsKey(Option.HELP)) {
378 showHelp();
379 System.exit(0);
380 }
381
382 final SplashScreen splash = new SplashScreen();
383 final ProgressMonitor monitor = splash.getProgressMonitor();
384 monitor.beginTask(tr("Initializing"));
385 splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
386 Main.setInitStatusListener(new InitStatusListener() {
387
388 @Override
389 public void updateStatus(String event) {
390 monitor.indeterminateSubTask(event);
391 }
392 });
393
394 Collection<PluginInformation> pluginsToLoad = PluginHandler.buildListOfPluginsToLoad(splash,monitor.createSubTaskMonitor(1, false));
395 if (!pluginsToLoad.isEmpty() && PluginHandler.checkAndConfirmPluginUpdate(splash)) {
396 monitor.subTask(tr("Updating plugins"));
397 pluginsToLoad = PluginHandler.updatePlugins(splash, null, monitor.createSubTaskMonitor(1, false), false);
398 }
399
400 monitor.indeterminateSubTask(tr("Installing updated plugins"));
401 PluginHandler.installDownloadedPlugins(true);
402
403 monitor.indeterminateSubTask(tr("Loading early plugins"));
404 PluginHandler.loadEarlyPlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
405
406 monitor.indeterminateSubTask(tr("Setting defaults"));
407 preConstructorInit(args);
408
409 monitor.indeterminateSubTask(tr("Creating main GUI"));
410 final Main main = new MainApplication(mainFrame);
411
412 monitor.indeterminateSubTask(tr("Loading plugins"));
413 PluginHandler.loadLatePlugins(splash,pluginsToLoad, monitor.createSubTaskMonitor(1, false));
414 toolbar.refreshToolbarControl();
415
416 // Wait for splash disappearance (fix #9714)
417 GuiHelper.runInEDTAndWait(new Runnable() {
418 @Override
419 public void run() {
420 splash.setVisible(false);
421 splash.dispose();
422 mainFrame.setVisible(true);
423 }
424 });
425
426 Main.MasterWindowListener.setup();
427
428 boolean maximized = Main.pref.getBoolean("gui.maximized", false);
429 if ((!args.containsKey(Option.NO_MAXIMIZE) && maximized) || args.containsKey(Option.MAXIMIZE)) {
430 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) {
431 Main.windowState = JFrame.MAXIMIZED_BOTH;
432 mainFrame.setExtendedState(Main.windowState);
433 } else {
434 Main.debug("Main window: maximizing not supported");
435 }
436 }
437 if (main.menu.fullscreenToggleAction != null) {
438 main.menu.fullscreenToggleAction.initial();
439 }
440
441 SwingUtilities.invokeLater(new GuiFinalizationWorker(args, proxySelector));
442
443 if (Main.isPlatformWindows()) {
444 try {
445 // Check for insecure certificates to remove.
446 // This is Windows-dependant code but it can't go to preStartupHook (need i18n) neither startupHook (need to be called before remote control)
447 PlatformHookWindows.removeInsecureCertificates();
448 } catch (NoSuchAlgorithmException | CertificateException | KeyStoreException | IOException e) {
449 error(e);
450 }
451 }
452
453 if (RemoteControl.PROP_REMOTECONTROL_ENABLED.get()) {
454 RemoteControl.start();
455 }
456
457 if (MessageNotifier.PROP_NOTIFIER_ENABLED.get()) {
458 MessageNotifier.start();
459 }
460
461 if (Main.pref.getBoolean("debug.edt-checker.enable", Version.getInstance().isLocalBuild())) {
462 // Repaint manager is registered so late for a reason - there is lots of violation during startup process but they don't seem to break anything and are difficult to fix
463 info("Enabled EDT checker, wrongful access to gui from non EDT thread will be printed to console");
464 RepaintManager.setCurrentManager(new CheckThreadViolationRepaintManager());
465 }
466 }
467
468 private static class GuiFinalizationWorker implements Runnable {
469
470 private final Map<Option, Collection<String>> args;
471 private final DefaultProxySelector proxySelector;
472
473 public GuiFinalizationWorker(Map<Option, Collection<String>> args, DefaultProxySelector proxySelector) {
474 this.args = args;
475 this.proxySelector = proxySelector;
476 }
477
478 @Override
479 public void run() {
480
481 // Handle proxy/network errors early to inform user he should change settings to be able to use JOSM correctly
482 if (!handleProxyErrors()) {
483 handleNetworkErrors();
484 }
485
486 // Restore autosave layers after crash and start autosave thread
487 handleAutosave();
488
489 // Handle command line instructions
490 postConstructorProcessCmdLine(args);
491
492 // Show download dialog if autostart is enabled
493 DownloadDialog.autostartIfNeeded();
494 }
495
496 private void handleAutosave() {
497 if (AutosaveTask.PROP_AUTOSAVE_ENABLED.get()) {
498 AutosaveTask autosaveTask = new AutosaveTask();
499 List<File> unsavedLayerFiles = autosaveTask.getUnsavedLayersFiles();
500 if (!unsavedLayerFiles.isEmpty()) {
501 ExtendedDialog dialog = new ExtendedDialog(
502 Main.parent,
503 tr("Unsaved osm data"),
504 new String[] {tr("Restore"), tr("Cancel"), tr("Discard")}
505 );
506 dialog.setContent(
507 trn("JOSM found {0} unsaved osm data layer. ",
508 "JOSM found {0} unsaved osm data layers. ", unsavedLayerFiles.size(), unsavedLayerFiles.size()) +
509 tr("It looks like JOSM crashed last time. Would you like to restore the data?"));
510 dialog.setButtonIcons(new String[] {"ok", "cancel", "dialogs/delete"});
511 int selection = dialog.showDialog().getValue();
512 if (selection == 1) {
513 autosaveTask.recoverUnsavedLayers();
514 } else if (selection == 3) {
515 autosaveTask.discardUnsavedLayers();
516 }
517 }
518 autosaveTask.schedule();
519 }
520 }
521
522 private boolean handleNetworkOrProxyErrors(boolean hasErrors, String title, String message) {
523 if (hasErrors) {
524 ExtendedDialog ed = new ExtendedDialog(
525 Main.parent, title,
526 new String[]{tr("Change proxy settings"), tr("Cancel")});
527 ed.setButtonIcons(new String[]{"dialogs/settings.png", "cancel.png"}).setCancelButton(2);
528 ed.setMinimumSize(new Dimension(460, 260));
529 ed.setIcon(JOptionPane.WARNING_MESSAGE);
530 ed.setContent(message);
531
532 if (ed.showDialog().getValue() == 1) {
533 PreferencesAction.forPreferenceSubTab(null, null, ProxyPreference.class).run();
534 }
535 }
536 return hasErrors;
537 }
538
539 private boolean handleProxyErrors() {
540 return handleNetworkOrProxyErrors(proxySelector.hasErrors(), tr("Proxy errors occurred"),
541 tr("JOSM tried to access the following resources:<br>" +
542 "{0}" +
543 "but <b>failed</b> to do so, because of the following proxy errors:<br>" +
544 "{1}" +
545 "Would you like to change your proxy settings now?",
546 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorResources()),
547 Utils.joinAsHtmlUnorderedList(proxySelector.getErrorMessages())
548 ));
549 }
550
551 private boolean handleNetworkErrors() {
552 boolean condition = !NETWORK_ERRORS.isEmpty();
553 if (condition) {
554 Set<String> errors = new TreeSet<>();
555 for (Throwable t : NETWORK_ERRORS.values()) {
556 errors.add(t.toString());
557 }
558 return handleNetworkOrProxyErrors(condition, tr("Network errors occurred"),
559 tr("JOSM tried to access the following resources:<br>" +
560 "{0}" +
561 "but <b>failed</b> to do so, because of the following network errors:<br>" +
562 "{1}" +
563 "It may be due to a missing proxy configuration.<br>" +
564 "Would you like to change your proxy settings now?",
565 Utils.joinAsHtmlUnorderedList(NETWORK_ERRORS.keySet()),
566 Utils.joinAsHtmlUnorderedList(errors)
567 ));
568 }
569 return false;
570 }
571 }
572}
Note: See TracBrowser for help on using the repository browser.