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

Last change on this file since 7082 was 7043, checked in by bastiK, 10 years ago

see #9691 - added command line option to enable debug level trace
added map paint performance output for trace level debug

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