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

Last change on this file since 2828 was 2748, checked in by Gubaer, 14 years ago

new: JOSM now supports OAuth

See also online help for server preferences and new OAuth Authorisation Wizard

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