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

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

do not block start when plugin list cannot be retrieved, save the error for later display in global network errors message dialog

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