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

Last change on this file was 19216, checked in by taylor.smock, 22 months ago

Fix #23866: java.io.UncheckedIOException: java.nio.file.FileSystemException: The device is not ready

This does two things:

  1. Unwrap an unchecked exception in ImagesLoader so that we are throwing a checked exception
  2. Explain some IO exceptions (specifically "the device is not ready")

This is not the "best" solution, but it should mean that we are not ignoring
issues related to JOSM.

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