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

Last change on this file since 13717 was 13499, checked in by Don-vip, 6 years ago

fix #16050 - nicer display of HTTP errors from OSM API

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