001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.io.IOException; 007import java.io.InputStream; 008import java.net.HttpURLConnection; 009import java.net.MalformedURLException; 010import java.net.URL; 011import java.util.List; 012 013import org.openstreetmap.josm.Main; 014import org.openstreetmap.josm.data.gpx.GpxData; 015import org.openstreetmap.josm.data.notes.Note; 016import org.openstreetmap.josm.data.osm.DataSet; 017import org.openstreetmap.josm.gui.progress.ProgressMonitor; 018import org.openstreetmap.josm.io.auth.CredentialsAgentException; 019import org.openstreetmap.josm.io.auth.CredentialsManager; 020import org.openstreetmap.josm.tools.HttpClient; 021 022/** 023 * This DataReader reads directly from the REST API of the osm server. 024 * 025 * It supports plain text transfer as well as gzip or deflate encoded transfers; 026 * if compressed transfers are unwanted, set property osm-server.use-compression 027 * to false. 028 * 029 * @author imi 030 */ 031public abstract class OsmServerReader extends OsmConnection { 032 private final OsmApi api = OsmApi.getOsmApi(); 033 private boolean doAuthenticate; 034 protected boolean gpxParsedProperly; 035 036 /** 037 * Constructs a new {@code OsmServerReader}. 038 */ 039 public OsmServerReader() { 040 try { 041 doAuthenticate = OsmApi.isUsingOAuth() && CredentialsManager.getInstance().lookupOAuthAccessToken() != null; 042 } catch (CredentialsAgentException e) { 043 Main.warn(e); 044 } 045 } 046 047 /** 048 * Open a connection to the given url and return a reader on the input stream 049 * from that connection. In case of user cancel, return <code>null</code>. 050 * Relative URL's are directed to API base URL. 051 * @param urlStr The url to connect to. 052 * @param progressMonitor progress monitoring and abort handler 053 * @return A reader reading the input stream (servers answer) or <code>null</code>. 054 * @throws OsmTransferException if data transfer errors occur 055 */ 056 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException { 057 return getInputStream(urlStr, progressMonitor, null); 058 } 059 060 /** 061 * Open a connection to the given url and return a reader on the input stream 062 * from that connection. In case of user cancel, return <code>null</code>. 063 * Relative URL's are directed to API base URL. 064 * @param urlStr The url to connect to. 065 * @param progressMonitor progress monitoring and abort handler 066 * @param reason The reason to show on console. Can be {@code null} if no reason is given 067 * @return A reader reading the input stream (servers answer) or <code>null</code>. 068 * @throws OsmTransferException if data transfer errors occur 069 */ 070 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException { 071 try { 072 api.initialize(progressMonitor); 073 String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr); 074 return getInputStreamRaw(url, progressMonitor, reason); 075 } finally { 076 progressMonitor.invalidate(); 077 } 078 } 079 080 /** 081 * Return the base URL for relative URL requests 082 * @return base url of API 083 */ 084 protected String getBaseUrl() { 085 return api.getBaseUrl(); 086 } 087 088 /** 089 * Open a connection to the given url and return a reader on the input stream 090 * from that connection. In case of user cancel, return <code>null</code>. 091 * @param urlStr The exact url to connect to. 092 * @param progressMonitor progress monitoring and abort handler 093 * @return An reader reading the input stream (servers answer) or <code>null</code>. 094 * @throws OsmTransferException if data transfer errors occur 095 */ 096 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException { 097 return getInputStreamRaw(urlStr, progressMonitor, null); 098 } 099 100 /** 101 * Open a connection to the given url and return a reader on the input stream 102 * from that connection. In case of user cancel, return <code>null</code>. 103 * @param urlStr The exact url to connect to. 104 * @param progressMonitor progress monitoring and abort handler 105 * @param reason The reason to show on console. Can be {@code null} if no reason is given 106 * @return An reader reading the input stream (servers answer) or <code>null</code>. 107 * @throws OsmTransferException if data transfer errors occur 108 */ 109 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException { 110 return getInputStreamRaw(urlStr, progressMonitor, reason, false); 111 } 112 113 /** 114 * Open a connection to the given url and return a reader on the input stream 115 * from that connection. In case of user cancel, return <code>null</code>. 116 * @param urlStr The exact url to connect to. 117 * @param progressMonitor progress monitoring and abort handler 118 * @param reason The reason to show on console. Can be {@code null} if no reason is given 119 * @param uncompressAccordingToContentDisposition Whether to inspect the HTTP header {@code Content-Disposition} 120 * for {@code filename} and uncompress a gzip/bzip2 stream. 121 * @return An reader reading the input stream (servers answer) or <code>null</code>. 122 * @throws OsmTransferException if data transfer errors occur 123 */ 124 @SuppressWarnings("resource") 125 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason, 126 boolean uncompressAccordingToContentDisposition) throws OsmTransferException { 127 try { 128 OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Main.getJOSMWebsite()); 129 OnlineResource.OSM_API.checkOfflineAccess(urlStr, OsmApi.getOsmApi().getServerUrl()); 130 131 URL url = null; 132 try { 133 url = new URL(urlStr.replace(" ", "%20")); 134 } catch (MalformedURLException e) { 135 throw new OsmTransferException(e); 136 } 137 138 if ("file".equals(url.getProtocol())) { 139 try { 140 return url.openStream(); 141 } catch (IOException e) { 142 throw new OsmTransferException(e); 143 } 144 } 145 146 final HttpClient client = HttpClient.create(url); 147 activeConnection = client; 148 client.setReasonForRequest(reason); 149 adaptRequest(client); 150 if (doAuthenticate) { 151 addAuth(client); 152 } 153 if (cancel) 154 throw new OsmTransferCanceledException("Operation canceled"); 155 156 final HttpClient.Response response; 157 try { 158 response = client.connect(progressMonitor); 159 } catch (IOException e) { 160 Main.error(e); 161 OsmTransferException ote = new OsmTransferException( 162 tr("Could not connect to the OSM server. Please check your internet connection."), e); 163 ote.setUrl(url.toString()); 164 throw ote; 165 } 166 try { 167 if (response.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) 168 throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED, null, null); 169 170 if (response.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH) 171 throw new OsmTransferCanceledException("Proxy Authentication Required"); 172 173 if (response.getResponseCode() != HttpURLConnection.HTTP_OK) { 174 String errorHeader = response.getHeaderField("Error"); 175 String errorBody = fetchResponseText(response); 176 throw new OsmApiException(response.getResponseCode(), errorHeader, errorBody, url.toString()); 177 } 178 179 response.uncompressAccordingToContentDisposition(uncompressAccordingToContentDisposition); 180 return response.getContent(); 181 } catch (OsmTransferException e) { 182 throw e; 183 } catch (IOException e) { 184 throw new OsmTransferException(e); 185 } 186 } finally { 187 progressMonitor.invalidate(); 188 } 189 } 190 191 private static String fetchResponseText(final HttpClient.Response response) { 192 try { 193 return response.fetchContent(); 194 } catch (IOException e) { 195 Main.error(e); 196 return tr("Reading error text failed."); 197 } 198 } 199 200 /** 201 * Allows subclasses to modify the request. 202 * @param request the prepared request 203 * @since 9308 204 */ 205 protected void adaptRequest(HttpClient request) { 206 } 207 208 /** 209 * Download OSM files from somewhere 210 * @param progressMonitor The progress monitor 211 * @return The corresponding dataset 212 * @throws OsmTransferException if any error occurs 213 */ 214 public abstract DataSet parseOsm(ProgressMonitor progressMonitor) throws OsmTransferException; 215 216 /** 217 * Download OSM Change files from somewhere 218 * @param progressMonitor The progress monitor 219 * @return The corresponding dataset 220 * @throws OsmTransferException if any error occurs 221 */ 222 public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException { 223 return null; 224 } 225 226 /** 227 * Download BZip2-compressed OSM Change files from somewhere 228 * @param progressMonitor The progress monitor 229 * @return The corresponding dataset 230 * @throws OsmTransferException if any error occurs 231 */ 232 public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 233 return null; 234 } 235 236 /** 237 * Download GZip-compressed OSM Change files from somewhere 238 * @param progressMonitor The progress monitor 239 * @return The corresponding dataset 240 * @throws OsmTransferException if any error occurs 241 */ 242 public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException { 243 return null; 244 } 245 246 /** 247 * Retrieve raw gps waypoints from the server API. 248 * @param progressMonitor The progress monitor 249 * @return The corresponding GPX tracks 250 * @throws OsmTransferException if any error occurs 251 */ 252 public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException { 253 return null; 254 } 255 256 /** 257 * Retrieve BZip2-compressed GPX files from somewhere. 258 * @param progressMonitor The progress monitor 259 * @return The corresponding GPX tracks 260 * @throws OsmTransferException if any error occurs 261 * @since 6244 262 */ 263 public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 264 return null; 265 } 266 267 /** 268 * Download BZip2-compressed OSM files from somewhere 269 * @param progressMonitor The progress monitor 270 * @return The corresponding dataset 271 * @throws OsmTransferException if any error occurs 272 */ 273 public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 274 return null; 275 } 276 277 /** 278 * Download GZip-compressed OSM files from somewhere 279 * @param progressMonitor The progress monitor 280 * @return The corresponding dataset 281 * @throws OsmTransferException if any error occurs 282 */ 283 public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException { 284 return null; 285 } 286 287 /** 288 * Download Zip-compressed OSM files from somewhere 289 * @param progressMonitor The progress monitor 290 * @return The corresponding dataset 291 * @throws OsmTransferException if any error occurs 292 * @since 6882 293 */ 294 public DataSet parseOsmZip(final ProgressMonitor progressMonitor) throws OsmTransferException { 295 return null; 296 } 297 298 /** 299 * Returns true if this reader is adding authentication credentials to the read 300 * request sent to the server. 301 * 302 * @return true if this reader is adding authentication credentials to the read 303 * request sent to the server 304 */ 305 public boolean isDoAuthenticate() { 306 return doAuthenticate; 307 } 308 309 /** 310 * Sets whether this reader adds authentication credentials to the read 311 * request sent to the server. 312 * 313 * @param doAuthenticate true if this reader adds authentication credentials to the read 314 * request sent to the server 315 */ 316 public void setDoAuthenticate(boolean doAuthenticate) { 317 this.doAuthenticate = doAuthenticate; 318 } 319 320 /** 321 * Determines if the GPX data has been parsed properly. 322 * @return true if the GPX data has been parsed properly, false otherwise 323 * @see GpxReader#parse 324 */ 325 public final boolean isGpxParsedProperly() { 326 return gpxParsedProperly; 327 } 328 329 /** 330 * Downloads notes from the API, given API limit parameters 331 * 332 * @param noteLimit How many notes to download. 333 * @param daysClosed Return notes closed this many days in the past. -1 means all notes, ever. 0 means only unresolved notes. 334 * @param progressMonitor Progress monitor for user feedback 335 * @return List of notes returned by the API 336 * @throws OsmTransferException if any errors happen 337 */ 338 public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException { 339 return null; 340 } 341 342 /** 343 * Downloads notes from a given raw URL. The URL is assumed to be complete and no API limits are added 344 * 345 * @param progressMonitor progress monitor 346 * @return A list of notes parsed from the URL 347 * @throws OsmTransferException if any error occurs during dialog with OSM API 348 */ 349 public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException { 350 return null; 351 } 352 353 /** 354 * Download notes from a URL that contains a bzip2 compressed notes dump file 355 * @param progressMonitor progress monitor 356 * @return A list of notes parsed from the URL 357 * @throws OsmTransferException if any error occurs during dialog with OSM API 358 */ 359 public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 360 return null; 361 } 362}