source: josm/trunk/src/org/openstreetmap/josm/tools/ExceptionUtil.java@ 2876

Last change on this file since 2876 was 2862, checked in by Gubaer, 14 years ago

fixed #4322: No error message if trying to upload without OAuth token

File size: 18.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.IOException;
7import java.net.HttpURLConnection;
8import java.net.MalformedURLException;
9import java.net.SocketException;
10import java.net.URL;
11import java.net.UnknownHostException;
12import java.text.DateFormat;
13import java.text.ParseException;
14import java.text.SimpleDateFormat;
15import java.util.Date;
16import java.util.Locale;
17import java.util.regex.Matcher;
18import java.util.regex.Pattern;
19
20import org.openstreetmap.josm.Main;
21import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
22import org.openstreetmap.josm.io.ChangesetClosedException;
23import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
24import org.openstreetmap.josm.io.OsmApi;
25import org.openstreetmap.josm.io.OsmApiException;
26import org.openstreetmap.josm.io.OsmApiInitializationException;
27import org.openstreetmap.josm.io.OsmTransferException;
28
29public class ExceptionUtil {
30 private ExceptionUtil() {
31 }
32
33 /**
34 * handles an exception caught during OSM API initialization
35 *
36 * @param e the exception
37 */
38 public static String explainOsmApiInitializationException(OsmApiInitializationException e) {
39 e.printStackTrace();
40 String msg = tr(
41 "<html>Failed to initialize communication with the OSM server {0}.<br>"
42 + "Check the server URL in your preferences and your internet connection.</html>", Main.pref.get(
43 "osm-server.url", "http://api.openstreetmap.org/api"));
44 return msg;
45 }
46
47
48 /**
49 * Creates the error message
50 *
51 * @param e the exception
52 */
53 public static String explainMissingOAuthAccessTokenException(MissingOAuthAccessTokenException e) {
54 e.printStackTrace();
55 String msg = tr(
56 "<html>Failed to authenticate at the OSM server ''{0}''.<br>"
57 + "You are using OAuth to authenticate but currently there is no<br>"
58 + "OAuth Access Token configured.<br>"
59 + "Please open the Preferences Dialog and generate or enter an Access Token."
60 + "</html>",
61 Main.pref.get("osm-server.url", "http://api.openstreetmap.org/api")
62 );
63 return msg;
64 }
65
66 /**
67 * Explains a precondition exception when a child relation could not be deleted because
68 * it is still referred to by an undeleted parent relation.
69 *
70 * @param e the exception
71 * @param childRelation the child relation
72 * @param parentRelation the parent relation
73 * @return
74 */
75 public static String explainDeletedRelationStillInUse(OsmApiException e, long childRelation, long parentRelation) {
76 String msg = tr(
77 "<html><strong>Failed</strong> to delete <strong>relation {0}</strong>."
78 + " It is still referred to by relation {1}.<br>"
79 + "Please load relation {1}, remove the reference to relation {0}, and upload again.</html>",
80 childRelation,parentRelation
81 );
82 return msg;
83 }
84
85 /**
86 * Explains an upload error due to a violated precondition, i.e. a HTTP return code 412
87 *
88 * @param e the exception
89 */
90 public static String explainPreconditionFailed(OsmApiException e) {
91 e.printStackTrace();
92 String msg = e.getErrorHeader();
93 if (msg != null) {
94 String pattern = "Precondition failed: The relation (\\d+) is used in relation (\\d+)\\.";
95 Pattern p = Pattern.compile(pattern);
96 Matcher m = p.matcher(msg);
97 if (m.matches()) {
98 long childRelation = Long.parseLong(m.group(1));
99 long parentRelation = Long.parseLong(m.group(2));
100 return explainDeletedRelationStillInUse(e, childRelation, parentRelation);
101 }
102 }
103 msg = tr(
104 "<html>Uploading to the server <strong>failed</strong> because your current<br>"
105 + "dataset violates a precondition.<br>" + "The error message is:<br>" + "{0}" + "</html>", e
106 .getMessage().replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"));
107 return msg;
108 }
109
110 public static String explainFailedBasicAuthentication(OsmApiException e) {
111 e.printStackTrace();
112 return tr("<html>"
113 + "Authentication at the OSM server with the username ''{0}'' failed.<br>"
114 + "Please check the username and the password in the JOSM preferences."
115 + "</html>",
116 Main.pref.get("osm-server.username")
117 );
118 }
119
120 public static String explainFailedOAuthAuthentication(OsmApiException e) {
121 e.printStackTrace();
122 return tr("<html>"
123 + "Authentication at the OSM server with the OAuth token ''{0}'' failed.<br>"
124 + "Please launch the preferences dialog and retrieve another OAuth token."
125 + "</html>",
126 OAuthAccessTokenHolder.getInstance().getAccessTokenKey()
127 );
128 }
129
130 public static String explainFailedOAuthAuthorisation(OsmApiException e) {
131 e.printStackTrace();
132 return tr("<html>"
133 + "Authorisation at the OSM server with the OAuth token ''{0}'' failed.<br>"
134 + "The token is not authorised to access the protected resource<br>"
135 + "''{1}''.<br>"
136 + "Please launch the preferences dialog and retrieve another OAuth token."
137 + "</html>",
138 OAuthAccessTokenHolder.getInstance().getAccessTokenKey(),
139 e.getAccessedUrl() == null ? tr("unknown") : e.getAccessedUrl()
140 );
141 }
142 /**
143 * Explains an error due to a 409 conflict
144 *
145 * @param e the exception
146 */
147 public static String explainConflict(OsmApiException e) {
148 e.printStackTrace();
149 String msg = e.getErrorHeader();
150 if (msg != null) {
151 String pattern = "The changeset (\\d+) was closed at (.*)";
152 Pattern p = Pattern.compile(pattern);
153 Matcher m = p.matcher(msg);
154 if (m.matches()) {
155 long changesetId = Long.parseLong(m.group(1));
156 // Example: Tue Oct 15 10:00:00 UTC 2009. Always parsed with english locale, regardless
157 // of the current locale in JOSM
158 DateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
159 Date closeDate = null;
160 try {
161 closeDate = formatter.parse(m.group(2));
162 } catch(ParseException ex) {
163 System.err.println(tr("Failed to parse date ''{0}'' replied by server.", m.group(2)));
164 ex.printStackTrace();
165 }
166 if (closeDate == null) {
167 msg = tr(
168 "<html>Closing of changeset <strong>{0}</strong> failed <br>because it has already been closed.</html>",
169 changesetId
170 );
171 } else {
172 SimpleDateFormat dateFormat = new SimpleDateFormat();
173 msg = tr(
174 "<html>Closing of changeset <strong>{0}</strong> failed<br>"
175 +" because it has already been closed on {1}.</html>",
176 changesetId,
177 dateFormat.format(closeDate)
178 );
179 }
180 return msg;
181 }
182 msg = tr(
183 "<html>The server reported that it has detected a conflict.<br>" +
184 "Error message (untranslated):<br>{0}</html>",
185 msg
186 );
187 }
188 msg = tr(
189 "<html>The server reported that it has detected a conflict.</html>"
190 );
191 return msg;
192 }
193
194 /**
195 * Explains an exception thrown during upload because the changeset which data is
196 * uploaded to is already closed.
197 *
198 * @param e the exception
199 */
200 public static String explainChangesetClosedException(ChangesetClosedException e) {
201 String msg;
202 SimpleDateFormat dateFormat = new SimpleDateFormat();
203 msg = tr(
204 "<html>Failed to upload to changeset <strong>{0}</strong><br>"
205 +"because it has already been closed on {1}.</html>",
206 e.getChangesetId(),
207 dateFormat.format(e.getClosedOn())
208 );
209 e.printStackTrace();
210 return msg;
211 }
212
213 /**
214 * Explains an exception with a generic message dialog
215 *
216 * @param e the exception
217 */
218 public static String explainGeneric(Exception e) {
219 String msg = e.getMessage();
220 if (msg == null || msg.trim().equals("")) {
221 msg = e.toString();
222 }
223 e.printStackTrace();
224 return msg;
225 }
226
227 /**
228 * Explains a {@see SecurityException} which has caused an {@see OsmTransferException}.
229 * This is most likely happening when user tries to access the OSM API from within an
230 * applet which wasn't loaded from the API server.
231 *
232 * @param e the exception
233 */
234
235 public static String explainSecurityException(OsmTransferException e) {
236 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
237 String host = tr("unknown");
238 try {
239 host = new URL(apiUrl).getHost();
240 } catch (MalformedURLException ex) {
241 // shouldn't happen
242 }
243
244 String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''<br>"
245 + "for security reasons. This is most likely because you are running<br>"
246 + "in an applet and because you did not load your applet from ''{1}''.</html>", apiUrl, host);
247 return message;
248 }
249
250 /**
251 * Explains a {@see SocketException} which has caused an {@see OsmTransferException}.
252 * This is most likely because there's not connection to the Internet or because
253 * the remote server is not reachable.
254 *
255 * @param e the exception
256 */
257
258 public static String explainNestedSocketException(OsmTransferException e) {
259 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
260 String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
261 + "Please check your internet connection.</html>", apiUrl);
262 e.printStackTrace();
263 return message;
264 }
265
266 /**
267 * Explains a {@see IOException} which has caused an {@see OsmTransferException}.
268 * This is most likely happening when the communication with the remote server is
269 * interrupted for any reason.
270 *
271 * @param e the exception
272 */
273
274 public static String explainNestedIOException(OsmTransferException e) {
275 IOException ioe = getNestedException(e, IOException.class);
276 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
277 String message = tr("<html>Failed to upload data to or download data from<br>" + "''{0}''<br>"
278 + "due to a problem with transferring data.<br>" + "Details(untranslated): {1}</html>", apiUrl, ioe
279 .getMessage());
280 e.printStackTrace();
281 return message;
282 }
283
284 /**
285 * Explains a {@see OsmApiException} which was thrown because of an internal server
286 * error in the OSM API server..
287 *
288 * @param e the exception
289 */
290
291 public static String explainInternalServerError(OsmTransferException e) {
292 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
293 String message = tr("<html>The OSM server<br>" + "''{0}''<br>" + "reported an internal server error.<br>"
294 + "This is most likely a temporary problem. Please try again later.</html>", apiUrl);
295 e.printStackTrace();
296 return message;
297 }
298
299 /**
300 * Explains a {@see OsmApiException} which was thrown because of a bad
301 * request
302 *
303 * @param e the exception
304 */
305 public static String explainBadRequest(OsmApiException e) {
306 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
307 String message = tr("The OSM server ''{0}'' reported a bad request.<br>", apiUrl);
308 if (e.getErrorHeader() != null && e.getErrorHeader().startsWith("The maximum bbox")) {
309 message += "<br>"
310 + tr("The area you tried to download is too big or your request was too large."
311 + "<br>Either request a smaller area or use an export file provided by the OSM community.");
312 } else if (e.getErrorHeader() != null) {
313 message += tr("<br>Error message(untranslated): {0}", e.getErrorHeader());
314 }
315 message = "<html>" + message + "</html>";
316 e.printStackTrace();
317 return message;
318 }
319
320 /**
321 * Explains a {@see OsmApiException} which was thrown because a resource wasn't found.
322 *
323 * @param e the exception
324 */
325 public static String explainNotFound(OsmApiException e) {
326 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
327 String message = tr("The OSM server ''{0}'' does not know about an object<br>"
328 + "you tried to read, update, or delete. Either the respective object<br>"
329 + "does not exist on the server or you are using an invalid URL to access<br>"
330 + "it. Please carefully check the server''s address ''{0}'' for typos."
331 , apiUrl);
332 message = "<html>" + message + "</html>";
333 e.printStackTrace();
334 return message;
335 }
336
337 /**
338 * Explains a {@see UnknownHostException} which has caused an {@see OsmTransferException}.
339 * This is most likely happening when there is an error in the API URL or when
340 * local DNS services are not working.
341 *
342 * @param e the exception
343 */
344
345 public static String explainNestedUnkonwnHostException(OsmTransferException e) {
346 String apiUrl = OsmApi.getOsmApi().getBaseUrl();
347 String host = tr("unknown");
348 try {
349 host = new URL(apiUrl).getHost();
350 } catch (MalformedURLException ex) {
351 // shouldn't happen
352 }
353
354 String message = tr("<html>Failed to open a connection to the remote server<br>" + "''{0}''.<br>"
355 + "Host name ''{1}'' could not be resolved. <br>"
356 + "Please check the API URL in your preferences and your internet connection.</html>", apiUrl, host);
357 e.printStackTrace();
358 return message;
359 }
360
361 /**
362 * Replies the first nested exception of type <code>nestedClass</code> (including
363 * the root exception <code>e</code>) or null, if no such exception is found.
364 *
365 * @param <T>
366 * @param e the root exception
367 * @param nestedClass the type of the nested exception
368 * @return the first nested exception of type <code>nestedClass</code> (including
369 * the root exception <code>e</code>) or null, if no such exception is found.
370 */
371 protected static <T> T getNestedException(Exception e, Class<T> nestedClass) {
372 Throwable t = e;
373 while (t != null && !(nestedClass.isInstance(t))) {
374 t = t.getCause();
375 }
376 if (t == null)
377 return null;
378 else if (nestedClass.isInstance(t))
379 return nestedClass.cast(t);
380 return null;
381 }
382
383 /**
384 * Explains an {@see OsmTransferException} to the user.
385 *
386 * @param e the {@see OsmTransferException}
387 */
388 public static String explainOsmTransferException(OsmTransferException e) {
389 if (getNestedException(e, SecurityException.class) != null)
390 return explainSecurityException(e);
391 if (getNestedException(e, SocketException.class) != null)
392 return explainNestedSocketException(e);
393 if (getNestedException(e, UnknownHostException.class) != null)
394 return explainNestedUnkonwnHostException(e);
395 if (getNestedException(e, IOException.class) != null)
396 return explainNestedIOException(e);
397 if (e instanceof OsmApiInitializationException)
398 return explainOsmApiInitializationException((OsmApiInitializationException) e);
399
400 if (e instanceof ChangesetClosedException)
401 return explainChangesetClosedException((ChangesetClosedException)e);
402
403 if (e instanceof OsmApiException) {
404 OsmApiException oae = (OsmApiException) e;
405 if (oae.getResponseCode() == HttpURLConnection.HTTP_PRECON_FAILED)
406 return explainPreconditionFailed(oae);
407 if (oae.getResponseCode() == HttpURLConnection.HTTP_GONE)
408 return explainGoneForUnknownPrimitive(oae);
409 if (oae.getResponseCode() == HttpURLConnection.HTTP_INTERNAL_ERROR)
410 return explainInternalServerError(oae);
411 if (oae.getResponseCode() == HttpURLConnection.HTTP_BAD_REQUEST)
412 return explainBadRequest(oae);
413 }
414 return explainGeneric(e);
415 }
416
417 /**
418 * explains the case of an error due to a delete request on an already deleted
419 * {@see OsmPrimitive}, i.e. a HTTP response code 410, where we don't know which
420 * {@see OsmPrimitive} is causing the error.
421 *
422 * @param e the exception
423 */
424 public static String explainGoneForUnknownPrimitive(OsmApiException e) {
425 String msg = tr(
426 "<html>The server reports that an object is deleted.<br>"
427 + "<strong>Uploading failed</strong> if you tried to update or delete this object.<br> "
428 + "<strong>Downloading failed</strong> if you tried to download this object.<br>"
429 + "<br>"
430 + "The error message is:<br>" + "{0}"
431 + "</html>", e.getMessage().replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"));
432 return msg;
433
434 }
435
436 /**
437 * Explains an {@see Exception} to the user.
438 *
439 * @param e the {@see Exception}
440 */
441 public static String explainException(Exception e) {
442 String msg = "";
443 if (e instanceof OsmTransferException) {
444 msg = explainOsmTransferException((OsmTransferException) e);
445 } else {
446 msg = explainGeneric(e);
447 }
448 e.printStackTrace();
449 return msg;
450 }
451}
Note: See TracBrowser for help on using the repository browser.