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

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

fix #13777 - allow to copy/paste error message from most of our error dialogs

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