source: josm/trunk/src/org/openstreetmap/josm/gui/ExceptionDialogUtil.java@ 8394

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

fix #8885 (see #4614) - add offline mode with new command line argument --offline which can take one of several of these values (comma separated):

  • josm_website: to disable all accesses to JOSM website (when not cached, disables Getting Started page, help, plugin list, styles, imagery, presets, rules)
  • osm_api: to disable all accesses to OSM API (disables download, upload, changeset queries, history, user message notification)
  • all: alias to disable all values. Currently equivalent to "josm_website,osm_api"

Plus improved javadoc, fixed EDT violations, and fixed a bug with HTTP redirection sent without "Location" header

  • Property svn:eol-style set to native
File size: 18.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.io.IOException;
8import java.lang.reflect.InvocationTargetException;
9import java.net.HttpURLConnection;
10import java.net.SocketException;
11import java.net.UnknownHostException;
12import java.util.regex.Matcher;
13import java.util.regex.Pattern;
14
15import javax.swing.JOptionPane;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.osm.OsmPrimitive;
19import org.openstreetmap.josm.io.ChangesetClosedException;
20import org.openstreetmap.josm.io.IllegalDataException;
21import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
22import org.openstreetmap.josm.io.OfflineAccessException;
23import org.openstreetmap.josm.io.OsmApi;
24import org.openstreetmap.josm.io.OsmApiException;
25import org.openstreetmap.josm.io.OsmApiInitializationException;
26import org.openstreetmap.josm.io.OsmTransferException;
27import org.openstreetmap.josm.tools.BugReportExceptionHandler;
28import org.openstreetmap.josm.tools.ExceptionUtil;
29
30/**
31 * This utility class provides static methods which explain various exceptions to the user.
32 *
33 */
34public final class ExceptionDialogUtil {
35
36 /**
37 * just static utility functions. no constructor
38 */
39 private ExceptionDialogUtil() {
40 }
41
42 /**
43 * handles an exception caught during OSM API initialization
44 *
45 * @param e the exception
46 */
47 public static void explainOsmApiInitializationException(OsmApiInitializationException e) {
48 HelpAwareOptionPane.showOptionDialog(
49 Main.parent,
50 ExceptionUtil.explainOsmApiInitializationException(e),
51 tr("Error"),
52 JOptionPane.ERROR_MESSAGE,
53 ht("/ErrorMessages#OsmApiInitializationException")
54 );
55 }
56
57 /**
58 * handles a ChangesetClosedException
59 *
60 * @param e the exception
61 */
62 public static void explainChangesetClosedException(ChangesetClosedException e) {
63 HelpAwareOptionPane.showOptionDialog(
64 Main.parent,
65 ExceptionUtil.explainChangesetClosedException(e),
66 tr("Error"),
67 JOptionPane.ERROR_MESSAGE,
68 ht("/Action/Upload#ChangesetClosed")
69 );
70 }
71
72 /**
73 * Explains an upload error due to a violated precondition, i.e. a HTTP return code 412
74 *
75 * @param e the exception
76 */
77 public static void explainPreconditionFailed(OsmApiException e) {
78 HelpAwareOptionPane.showOptionDialog(
79 Main.parent,
80 ExceptionUtil.explainPreconditionFailed(e),
81 tr("Precondition violation"),
82 JOptionPane.ERROR_MESSAGE,
83 ht("/ErrorMessages#OsmApiException")
84 );
85 }
86
87 /**
88 * Explains an exception with a generic message dialog
89 *
90 * @param e the exception
91 */
92 public static void explainGeneric(Exception e) {
93 Main.error(e);
94 BugReportExceptionHandler.handleException(e);
95 }
96
97 /**
98 * Explains a {@link SecurityException} which has caused an {@link OsmTransferException}.
99 * This is most likely happening when user tries to access the OSM API from within an
100 * applet which wasn't loaded from the API server.
101 *
102 * @param e the exception
103 */
104
105 public static void explainSecurityException(OsmTransferException e) {
106 HelpAwareOptionPane.showOptionDialog(
107 Main.parent,
108 ExceptionUtil.explainSecurityException(e),
109 tr("Security exception"),
110 JOptionPane.ERROR_MESSAGE,
111 ht("/ErrorMessages#SecurityException")
112 );
113 }
114
115 /**
116 * Explains a {@link SocketException} which has caused an {@link OsmTransferException}.
117 * This is most likely because there's not connection to the Internet or because
118 * the remote server is not reachable.
119 *
120 * @param e the exception
121 */
122
123 public static void explainNestedSocketException(OsmTransferException e) {
124 HelpAwareOptionPane.showOptionDialog(
125 Main.parent,
126 ExceptionUtil.explainNestedSocketException(e),
127 tr("Network exception"),
128 JOptionPane.ERROR_MESSAGE,
129 ht("/ErrorMessages#NestedSocketException")
130 );
131 }
132
133 /**
134 * Explains a {@link IOException} which has caused an {@link OsmTransferException}.
135 * This is most likely happening when the communication with the remote server is
136 * interrupted for any reason.
137 *
138 * @param e the exception
139 */
140
141 public static void explainNestedIOException(OsmTransferException e) {
142 HelpAwareOptionPane.showOptionDialog(
143 Main.parent,
144 ExceptionUtil.explainNestedIOException(e),
145 tr("IO Exception"),
146 JOptionPane.ERROR_MESSAGE,
147 ht("/ErrorMessages#NestedIOException")
148 );
149 }
150
151 /**
152 * Explains a {@link IllegalDataException} which has caused an {@link OsmTransferException}.
153 * This is most likely happening when JOSM tries to load data in an unsupported format.
154 *
155 * @param e the exception
156 */
157 public static void explainNestedIllegalDataException(OsmTransferException e) {
158 HelpAwareOptionPane.showOptionDialog(
159 Main.parent,
160 ExceptionUtil.explainNestedIllegalDataException(e),
161 tr("Illegal Data"),
162 JOptionPane.ERROR_MESSAGE,
163 ht("/ErrorMessages#IllegalDataException")
164 );
165 }
166
167 /**
168 * Explains a {@link OfflineAccessException} which has caused an {@link OsmTransferException}.
169 * This is most likely happening when JOSM tries to access OSM API or JOSM website while in offline mode.
170 *
171 * @param e the exception
172 * @since 7434
173 */
174 public static void explainNestedOfflineAccessException(OsmTransferException e) {
175 HelpAwareOptionPane.showOptionDialog(
176 Main.parent,
177 ExceptionUtil.explainOfflineAccessException(e),
178 tr("Offline mode"),
179 JOptionPane.ERROR_MESSAGE,
180 ht("/ErrorMessages#OfflineAccessException")
181 );
182 }
183
184 /**
185 * Explains a {@link InvocationTargetException }
186 *
187 * @param e the exception
188 */
189 public static void explainNestedInvocationTargetException(Exception e) {
190 InvocationTargetException ex = getNestedException(e, InvocationTargetException.class);
191 if (ex != null) {
192 // Users should be able to submit a bug report for an invocation target exception
193 //
194 BugReportExceptionHandler.handleException(ex);
195 return;
196 }
197 }
198
199 /**
200 * Explains a {@link OsmApiException} which was thrown because of an internal server
201 * error in the OSM API server.
202 *
203 * @param e the exception
204 */
205
206 public static void explainInternalServerError(OsmTransferException e) {
207 HelpAwareOptionPane.showOptionDialog(
208 Main.parent,
209 ExceptionUtil.explainInternalServerError(e),
210 tr("Internal Server Error"),
211 JOptionPane.ERROR_MESSAGE,
212 ht("/ErrorMessages#InternalServerError")
213 );
214 }
215
216 /**
217 * Explains a {@link OsmApiException} which was thrown because of a bad
218 * request
219 *
220 * @param e the exception
221 */
222 public static void explainBadRequest(OsmApiException e) {
223 HelpAwareOptionPane.showOptionDialog(
224 Main.parent,
225 ExceptionUtil.explainBadRequest(e),
226 tr("Bad Request"),
227 JOptionPane.ERROR_MESSAGE,
228 ht("/ErrorMessages#BadRequest")
229 );
230 }
231
232 /**
233 * Explains a {@link OsmApiException} which was thrown because a resource wasn't found
234 * on the server
235 *
236 * @param e the exception
237 */
238 public static void explainNotFound(OsmApiException e) {
239 HelpAwareOptionPane.showOptionDialog(
240 Main.parent,
241 ExceptionUtil.explainNotFound(e),
242 tr("Not Found"),
243 JOptionPane.ERROR_MESSAGE,
244 ht("/ErrorMessages#NotFound")
245 );
246 }
247
248 /**
249 * Explains a {@link OsmApiException} which was thrown because of a conflict
250 *
251 * @param e the exception
252 */
253 public static void explainConflict(OsmApiException e) {
254 HelpAwareOptionPane.showOptionDialog(
255 Main.parent,
256 ExceptionUtil.explainConflict(e),
257 tr("Conflict"),
258 JOptionPane.ERROR_MESSAGE,
259 ht("/ErrorMessages#Conflict")
260 );
261 }
262
263 /**
264 * Explains a {@link OsmApiException} which was thrown because the authentication at
265 * the OSM server failed
266 *
267 * @param e the exception
268 */
269 public static void explainAuthenticationFailed(OsmApiException e) {
270 String msg;
271 if (OsmApi.isUsingOAuth()) {
272 msg = ExceptionUtil.explainFailedOAuthAuthentication(e);
273 } else {
274 msg = ExceptionUtil.explainFailedBasicAuthentication(e);
275 }
276
277 HelpAwareOptionPane.showOptionDialog(
278 Main.parent,
279 msg,
280 tr("Authentication Failed"),
281 JOptionPane.ERROR_MESSAGE,
282 ht("/ErrorMessages#AuthenticationFailed")
283 );
284 }
285
286 /**
287 * Explains a {@link OsmApiException} which was thrown because accessing a protected
288 * resource was forbidden (HTTP 403).
289 *
290 * @param e the exception
291 */
292 public static void explainAuthorizationFailed(OsmApiException e) {
293
294 Matcher m;
295 String msg;
296 String url = e.getAccessedUrl();
297 Pattern p = Pattern.compile("https?://.*/api/0.6/(node|way|relation)/(\\d+)/(\\d+)");
298
299 // Special case for individual access to redacted versions
300 // See http://wiki.openstreetmap.org/wiki/Open_Database_License/Changes_in_the_API
301 if (url != null && (m = p.matcher(url)).matches()) {
302 String type = m.group(1);
303 String id = m.group(2);
304 String version = m.group(3);
305 // {1} is the translation of "node", "way" or "relation"
306 msg = tr("Access to redacted version ''{0}'' of {1} {2} is forbidden.",
307 version, tr(type), id);
308 } else if (OsmApi.isUsingOAuth()) {
309 msg = ExceptionUtil.explainFailedOAuthAuthorisation(e);
310 } else {
311 msg = ExceptionUtil.explainFailedAuthorisation(e);
312 }
313
314 HelpAwareOptionPane.showOptionDialog(
315 Main.parent,
316 msg,
317 tr("Authorisation Failed"),
318 JOptionPane.ERROR_MESSAGE,
319 ht("/ErrorMessages#AuthorizationFailed")
320 );
321 }
322
323 /**
324 * Explains a {@link OsmApiException} which was thrown because of a
325 * client timeout (HTTP 408)
326 *
327 * @param e the exception
328 */
329 public static void explainClientTimeout(OsmApiException e) {
330 HelpAwareOptionPane.showOptionDialog(
331 Main.parent,
332 ExceptionUtil.explainClientTimeout(e),
333 tr("Client Time Out"),
334 JOptionPane.ERROR_MESSAGE,
335 ht("/ErrorMessages#ClientTimeOut")
336 );
337 }
338
339 /**
340 * Explains a {@link OsmApiException} which was thrown because of a
341 * bandwidth limit (HTTP 509)
342 *
343 * @param e the exception
344 */
345 public static void explainBandwidthLimitExceeded(OsmApiException e) {
346 HelpAwareOptionPane.showOptionDialog(
347 Main.parent,
348 ExceptionUtil.explainBandwidthLimitExceeded(e),
349 tr("Bandwidth Limit Exceeded"),
350 JOptionPane.ERROR_MESSAGE,
351 ht("/ErrorMessages#BandwidthLimit")
352 );
353 }
354
355 /**
356 * Explains a {@link OsmApiException} with a generic error
357 * message.
358 *
359 * @param e the exception
360 */
361 public static void explainGenericHttpException(OsmApiException e) {
362 HelpAwareOptionPane.showOptionDialog(
363 Main.parent,
364 ExceptionUtil.explainClientTimeout(e),
365 tr("Communication with OSM server failed"),
366 JOptionPane.ERROR_MESSAGE,
367 ht("/ErrorMessages#GenericCommunicationError")
368 );
369 }
370
371 /**
372 * Explains a {@link OsmApiException} which was thrown because accessing a protected
373 * resource was forbidden.
374 *
375 * @param e the exception
376 */
377 public static void explainMissingOAuthAccessTokenException(MissingOAuthAccessTokenException e) {
378 HelpAwareOptionPane.showOptionDialog(
379 Main.parent,
380 ExceptionUtil.explainMissingOAuthAccessTokenException(e),
381 tr("Authentication failed"),
382 JOptionPane.ERROR_MESSAGE,
383 ht("/ErrorMessages#MissingOAuthAccessToken")
384 );
385 }
386
387 /**
388 * Explains a {@link UnknownHostException} which has caused an {@link OsmTransferException}.
389 * This is most likely happening when there is an error in the API URL or when
390 * local DNS services are not working.
391 *
392 * @param e the exception
393 */
394
395 public static void explainNestedUnkonwnHostException(OsmTransferException e) {
396 HelpAwareOptionPane.showOptionDialog(
397 Main.parent,
398 ExceptionUtil.explainNestedUnknownHostException(e),
399 tr("Unknown host"),
400 JOptionPane.ERROR_MESSAGE,
401 ht("/ErrorMessages#UnknownHost")
402 );
403 }
404
405 /**
406 * Replies the first nested exception of type <code>nestedClass</code> (including
407 * the root exception <code>e</code>) or null, if no such exception is found.
408 *
409 * @param <T>
410 * @param e the root exception
411 * @param nestedClass the type of the nested exception
412 * @return the first nested exception of type <code>nestedClass</code> (including
413 * the root exception <code>e</code>) or null, if no such exception is found.
414 */
415 protected static <T> T getNestedException(Exception e, Class<T> nestedClass) {
416 Throwable t = e;
417 while (t != null && !(nestedClass.isInstance(t))) {
418 t = t.getCause();
419 }
420 if (t == null)
421 return null;
422 else if (nestedClass.isInstance(t))
423 return nestedClass.cast(t);
424 return null;
425 }
426
427 /**
428 * Explains an {@link OsmTransferException} to the user.
429 *
430 * @param e the {@link OsmTransferException}
431 */
432 public static void explainOsmTransferException(OsmTransferException e) {
433 if (getNestedException(e, SecurityException.class) != null) {
434 explainSecurityException(e);
435 return;
436 }
437 if (getNestedException(e, SocketException.class) != null) {
438 explainNestedSocketException(e);
439 return;
440 }
441 if (getNestedException(e, UnknownHostException.class) != null) {
442 explainNestedUnkonwnHostException(e);
443 return;
444 }
445 if (getNestedException(e, IOException.class) != null) {
446 explainNestedIOException(e);
447 return;
448 }
449 if (getNestedException(e, IllegalDataException.class) != null) {
450 explainNestedIllegalDataException(e);
451 return;
452 }
453 if (getNestedException(e, OfflineAccessException.class) != null) {
454 explainNestedOfflineAccessException(e);
455 return;
456 }
457 if (e instanceof OsmApiInitializationException) {
458 explainOsmApiInitializationException((OsmApiInitializationException) e);
459 return;
460 }
461
462 if (e instanceof ChangesetClosedException) {
463 explainChangesetClosedException((ChangesetClosedException)e);
464 return;
465 }
466
467 if (e instanceof MissingOAuthAccessTokenException) {
468 explainMissingOAuthAccessTokenException((MissingOAuthAccessTokenException)e);
469 return;
470 }
471
472 if (e instanceof OsmApiException) {
473 OsmApiException oae = (OsmApiException) e;
474 switch(oae.getResponseCode()) {
475 case HttpURLConnection.HTTP_PRECON_FAILED:
476 explainPreconditionFailed(oae);
477 return;
478 case HttpURLConnection.HTTP_GONE:
479 explainGoneForUnknownPrimitive(oae);
480 return;
481 case HttpURLConnection.HTTP_INTERNAL_ERROR:
482 explainInternalServerError(oae);
483 return;
484 case HttpURLConnection.HTTP_BAD_REQUEST:
485 explainBadRequest(oae);
486 return;
487 case HttpURLConnection.HTTP_NOT_FOUND:
488 explainNotFound(oae);
489 return;
490 case HttpURLConnection.HTTP_CONFLICT:
491 explainConflict(oae);
492 return;
493 case HttpURLConnection.HTTP_UNAUTHORIZED:
494 explainAuthenticationFailed(oae);
495 return;
496 case HttpURLConnection.HTTP_FORBIDDEN:
497 explainAuthorizationFailed(oae);
498 return;
499 case HttpURLConnection.HTTP_CLIENT_TIMEOUT:
500 explainClientTimeout(oae);
501 return;
502 case 509:
503 explainBandwidthLimitExceeded(oae);
504 return;
505 default:
506 explainGenericHttpException(oae);
507 return;
508 }
509 }
510 explainGeneric(e);
511 }
512
513 /**
514 * explains the case of an error due to a delete request on an already deleted
515 * {@link OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which
516 * {@link OsmPrimitive} is causing the error.
517 *
518 * @param e the exception
519 */
520 public static void explainGoneForUnknownPrimitive(OsmApiException e) {
521 HelpAwareOptionPane.showOptionDialog(
522 Main.parent,
523 ExceptionUtil.explainGoneForUnknownPrimitive(e),
524 tr("Object deleted"),
525 JOptionPane.ERROR_MESSAGE,
526 ht("/ErrorMessages#GoneForUnknownPrimitive")
527 );
528 }
529
530 /**
531 * Explains an {@link Exception} to the user.
532 *
533 * @param e the {@link Exception}
534 */
535 public static void explainException(Exception e) {
536 if (getNestedException(e, InvocationTargetException.class) != null) {
537 explainNestedInvocationTargetException(e);
538 return;
539 }
540 if (e instanceof OsmTransferException) {
541 explainOsmTransferException((OsmTransferException) e);
542 return;
543 }
544 explainGeneric(e);
545 }
546}
Note: See TracBrowser for help on using the repository browser.