[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[18] | 2 | package org.openstreetmap.josm.io;
|
---|
| 3 |
|
---|
[916] | 4 | import static org.openstreetmap.josm.tools.I18n.tr;
|
---|
| 5 |
|
---|
[1670] | 6 | import java.io.BufferedReader;
|
---|
[3511] | 7 | import java.io.IOException;
|
---|
[103] | 8 | import java.io.InputStream;
|
---|
[1670] | 9 | import java.io.InputStreamReader;
|
---|
[18] | 10 | import java.net.HttpURLConnection;
|
---|
[1670] | 11 | import java.net.MalformedURLException;
|
---|
[18] | 12 | import java.net.URL;
|
---|
[7082] | 13 | import java.nio.charset.StandardCharsets;
|
---|
[6803] | 14 | import java.util.List;
|
---|
| 15 | import java.util.Map;
|
---|
[1670] | 16 | import java.util.zip.GZIPInputStream;
|
---|
[228] | 17 | import java.util.zip.Inflater;
|
---|
| 18 | import java.util.zip.InflaterInputStream;
|
---|
[18] | 19 |
|
---|
[34] | 20 | import org.openstreetmap.josm.Main;
|
---|
[4521] | 21 | import org.openstreetmap.josm.data.gpx.GpxData;
|
---|
[7531] | 22 | import org.openstreetmap.josm.data.notes.Note;
|
---|
[1146] | 23 | import org.openstreetmap.josm.data.osm.DataSet;
|
---|
[1811] | 24 | import org.openstreetmap.josm.gui.progress.ProgressMonitor;
|
---|
[5587] | 25 | import org.openstreetmap.josm.tools.Utils;
|
---|
[18] | 26 |
|
---|
| 27 | /**
|
---|
[228] | 28 | * This DataReader reads directly from the REST API of the osm server.
|
---|
[1169] | 29 | *
|
---|
[228] | 30 | * It supports plain text transfer as well as gzip or deflate encoded transfers;
|
---|
| 31 | * if compressed transfers are unwanted, set property osm-server.use-compression
|
---|
| 32 | * to false.
|
---|
[153] | 33 | *
|
---|
[18] | 34 | * @author imi
|
---|
| 35 | */
|
---|
[1146] | 36 | public abstract class OsmServerReader extends OsmConnection {
|
---|
[1664] | 37 | private OsmApi api = OsmApi.getOsmApi();
|
---|
[2124] | 38 | private boolean doAuthenticate = false;
|
---|
[5494] | 39 | protected boolean gpxParsedProperly;
|
---|
[1664] | 40 |
|
---|
[1169] | 41 | /**
|
---|
| 42 | * Open a connection to the given url and return a reader on the input stream
|
---|
| 43 | * from that connection. In case of user cancel, return <code>null</code>.
|
---|
[5863] | 44 | * Relative URL's are directed to API base URL.
|
---|
| 45 | * @param urlStr The url to connect to.
|
---|
| 46 | * @param progressMonitor progress monitoring and abort handler
|
---|
[6695] | 47 | * @return A reader reading the input stream (servers answer) or <code>null</code>.
|
---|
[8291] | 48 | * @throws OsmTransferException if data transfer errors occur
|
---|
[1169] | 49 | */
|
---|
[1811] | 50 | protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[6695] | 51 | return getInputStream(urlStr, progressMonitor, null);
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | /**
|
---|
| 55 | * Open a connection to the given url and return a reader on the input stream
|
---|
| 56 | * from that connection. In case of user cancel, return <code>null</code>.
|
---|
| 57 | * Relative URL's are directed to API base URL.
|
---|
| 58 | * @param urlStr The url to connect to.
|
---|
| 59 | * @param progressMonitor progress monitoring and abort handler
|
---|
| 60 | * @param reason The reason to show on console. Can be {@code null} if no reason is given
|
---|
| 61 | * @return A reader reading the input stream (servers answer) or <code>null</code>.
|
---|
[8291] | 62 | * @throws OsmTransferException if data transfer errors occur
|
---|
[6695] | 63 | */
|
---|
| 64 | protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException {
|
---|
[1875] | 65 | try {
|
---|
[2035] | 66 | api.initialize(progressMonitor);
|
---|
[6695] | 67 | String url = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr);
|
---|
| 68 | return getInputStreamRaw(url, progressMonitor, reason);
|
---|
[1875] | 69 | } finally {
|
---|
| 70 | progressMonitor.invalidate();
|
---|
| 71 | }
|
---|
[1670] | 72 | }
|
---|
[1664] | 73 |
|
---|
[5863] | 74 | /**
|
---|
[6695] | 75 | * Return the base URL for relative URL requests
|
---|
[5863] | 76 | * @return base url of API
|
---|
| 77 | */
|
---|
[5097] | 78 | protected String getBaseUrl() {
|
---|
| 79 | return api.getBaseUrl();
|
---|
| 80 | }
|
---|
| 81 |
|
---|
[5863] | 82 | /**
|
---|
| 83 | * Open a connection to the given url and return a reader on the input stream
|
---|
| 84 | * from that connection. In case of user cancel, return <code>null</code>.
|
---|
| 85 | * @param urlStr The exact url to connect to.
|
---|
| 86 | * @param progressMonitor progress monitoring and abort handler
|
---|
| 87 | * @return An reader reading the input stream (servers answer) or <code>null</code>.
|
---|
[8291] | 88 | * @throws OsmTransferException if data transfer errors occur
|
---|
[5863] | 89 | */
|
---|
[1811] | 90 | protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[6695] | 91 | return getInputStreamRaw(urlStr, progressMonitor, null);
|
---|
| 92 | }
|
---|
[6787] | 93 |
|
---|
[6695] | 94 | /**
|
---|
| 95 | * Open a connection to the given url and return a reader on the input stream
|
---|
| 96 | * from that connection. In case of user cancel, return <code>null</code>.
|
---|
| 97 | * @param urlStr The exact url to connect to.
|
---|
| 98 | * @param progressMonitor progress monitoring and abort handler
|
---|
| 99 | * @param reason The reason to show on console. Can be {@code null} if no reason is given
|
---|
| 100 | * @return An reader reading the input stream (servers answer) or <code>null</code>.
|
---|
[8291] | 101 | * @throws OsmTransferException if data transfer errors occur
|
---|
[6695] | 102 | */
|
---|
| 103 | protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason) throws OsmTransferException {
|
---|
[6803] | 104 | return getInputStreamRaw(urlStr, progressMonitor, reason, false);
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | /**
|
---|
| 108 | * Open a connection to the given url and return a reader on the input stream
|
---|
| 109 | * from that connection. In case of user cancel, return <code>null</code>.
|
---|
| 110 | * @param urlStr The exact url to connect to.
|
---|
| 111 | * @param progressMonitor progress monitoring and abort handler
|
---|
| 112 | * @param reason The reason to show on console. Can be {@code null} if no reason is given
|
---|
| 113 | * @param uncompressAccordingToContentDisposition Whether to inspect the HTTP header {@code Content-Disposition}
|
---|
| 114 | * for {@code filename} and uncompress a gzip/bzip2 stream.
|
---|
| 115 | * @return An reader reading the input stream (servers answer) or <code>null</code>.
|
---|
[8291] | 116 | * @throws OsmTransferException if data transfer errors occur
|
---|
[6803] | 117 | */
|
---|
[7033] | 118 | @SuppressWarnings("resource")
|
---|
[8509] | 119 | protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor, String reason,
|
---|
| 120 | boolean uncompressAccordingToContentDisposition) throws OsmTransferException {
|
---|
[1608] | 121 | try {
|
---|
[7434] | 122 | OnlineResource.JOSM_WEBSITE.checkOfflineAccess(urlStr, Main.getJOSMWebsite());
|
---|
| 123 | OnlineResource.OSM_API.checkOfflineAccess(urlStr, Main.pref.get("osm-server.url", OsmApi.DEFAULT_API_URL));
|
---|
| 124 |
|
---|
[1875] | 125 | URL url = null;
|
---|
| 126 | try {
|
---|
[3021] | 127 | url = new URL(urlStr.replace(" ", "%20"));
|
---|
[8510] | 128 | } catch (MalformedURLException e) {
|
---|
[1875] | 129 | throw new OsmTransferException(e);
|
---|
| 130 | }
|
---|
| 131 | try {
|
---|
[5324] | 132 | // fix #7640, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive
|
---|
[5587] | 133 | activeConnection = Utils.openHttpConnection(url, false);
|
---|
[8510] | 134 | } catch (Exception e) {
|
---|
[2181] | 135 | throw new OsmTransferException(tr("Failed to open connection to API {0}.", url.toExternalForm()), e);
|
---|
[1875] | 136 | }
|
---|
[8563] | 137 | Utils.setupURLConnection(activeConnection);
|
---|
[1875] | 138 | if (cancel) {
|
---|
| 139 | activeConnection.disconnect();
|
---|
| 140 | return null;
|
---|
| 141 | }
|
---|
[228] | 142 |
|
---|
[2641] | 143 | if (doAuthenticate) {
|
---|
| 144 | addAuth(activeConnection);
|
---|
[2124] | 145 | }
|
---|
[2641] | 146 | if (cancel)
|
---|
[8415] | 147 | throw new OsmTransferCanceledException("Operation canceled");
|
---|
[1875] | 148 | if (Main.pref.getBoolean("osm-server.use-compression", true)) {
|
---|
| 149 | activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
|
---|
| 150 | }
|
---|
[1169] | 151 |
|
---|
[1875] | 152 | try {
|
---|
[6695] | 153 | if (reason != null && !reason.isEmpty()) {
|
---|
| 154 | Main.info("GET " + url + " (" + reason + ")");
|
---|
| 155 | } else {
|
---|
| 156 | Main.info("GET " + url);
|
---|
| 157 | }
|
---|
[1875] | 158 | activeConnection.connect();
|
---|
| 159 | } catch (Exception e) {
|
---|
[6643] | 160 | Main.error(e);
|
---|
[8509] | 161 | OsmTransferException ote = new OsmTransferException(
|
---|
| 162 | tr("Could not connect to the OSM server. Please check your internet connection."), e);
|
---|
[5575] | 163 | ote.setUrl(url.toString());
|
---|
| 164 | throw ote;
|
---|
[1875] | 165 | }
|
---|
| 166 | try {
|
---|
[6852] | 167 | if (Main.isDebugEnabled()) {
|
---|
| 168 | Main.debug("RESPONSE: "+activeConnection.getHeaderFields());
|
---|
| 169 | }
|
---|
[2641] | 170 | if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
|
---|
[8510] | 171 | throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED, null, null);
|
---|
[1353] | 172 |
|
---|
[2641] | 173 | if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH)
|
---|
[8415] | 174 | throw new OsmTransferCanceledException("Proxy Authentication Required");
|
---|
[2641] | 175 |
|
---|
[3511] | 176 | String encoding = activeConnection.getContentEncoding();
|
---|
[1875] | 177 | if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
|
---|
| 178 | String errorHeader = activeConnection.getHeaderField("Error");
|
---|
| 179 | StringBuilder errorBody = new StringBuilder();
|
---|
[6787] | 180 | try {
|
---|
| 181 | InputStream i = fixEncoding(activeConnection.getErrorStream(), encoding);
|
---|
[3511] | 182 | if (i != null) {
|
---|
[7082] | 183 | BufferedReader in = new BufferedReader(new InputStreamReader(i, StandardCharsets.UTF_8));
|
---|
[3511] | 184 | String s;
|
---|
[8510] | 185 | while ((s = in.readLine()) != null) {
|
---|
[3511] | 186 | errorBody.append(s);
|
---|
[8390] | 187 | errorBody.append('\n');
|
---|
[3511] | 188 | }
|
---|
[1875] | 189 | }
|
---|
[8510] | 190 | } catch (Exception e) {
|
---|
[3511] | 191 | errorBody.append(tr("Reading error text failed."));
|
---|
| 192 | }
|
---|
[1875] | 193 |
|
---|
[5584] | 194 | throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString(), url.toString());
|
---|
[1670] | 195 | }
|
---|
[1169] | 196 |
|
---|
[6803] | 197 | InputStream in = new ProgressInputStream(activeConnection, progressMonitor);
|
---|
| 198 | if (uncompressAccordingToContentDisposition) {
|
---|
| 199 | in = uncompressAccordingToContentDisposition(in, activeConnection.getHeaderFields());
|
---|
| 200 | }
|
---|
| 201 | return fixEncoding(in, encoding);
|
---|
[6246] | 202 | } catch (OsmTransferException e) {
|
---|
| 203 | throw e;
|
---|
| 204 | } catch (Exception e) {
|
---|
| 205 | throw new OsmTransferException(e);
|
---|
[1670] | 206 | }
|
---|
[1875] | 207 | } finally {
|
---|
| 208 | progressMonitor.invalidate();
|
---|
[1169] | 209 | }
|
---|
| 210 | }
|
---|
| 211 |
|
---|
[6787] | 212 | private InputStream fixEncoding(InputStream stream, String encoding) throws IOException {
|
---|
[6089] | 213 | if ("gzip".equalsIgnoreCase(encoding)) {
|
---|
[3511] | 214 | stream = new GZIPInputStream(stream);
|
---|
[6787] | 215 | } else if ("deflate".equalsIgnoreCase(encoding)) {
|
---|
[3511] | 216 | stream = new InflaterInputStream(stream, new Inflater(true));
|
---|
| 217 | }
|
---|
| 218 | return stream;
|
---|
| 219 | }
|
---|
| 220 |
|
---|
[6803] | 221 | private InputStream uncompressAccordingToContentDisposition(InputStream stream, Map<String, List<String>> headerFields) throws IOException {
|
---|
[7267] | 222 | List<String> field = headerFields.get("Content-Disposition");
|
---|
| 223 | if (field != null && field.toString().contains(".gz\"")) {
|
---|
[6803] | 224 | return Compression.GZIP.getUncompressedInputStream(stream);
|
---|
[7267] | 225 | } else if (field != null && field.toString().contains(".bz2\"")) {
|
---|
[6803] | 226 | return Compression.BZIP2.getUncompressedInputStream(stream);
|
---|
| 227 | } else {
|
---|
| 228 | return stream;
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 |
|
---|
[6244] | 232 | /**
|
---|
| 233 | * Download OSM files from somewhere
|
---|
| 234 | * @param progressMonitor The progress monitor
|
---|
| 235 | * @return The corresponding dataset
|
---|
[6643] | 236 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 237 | */
|
---|
[5317] | 238 | public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
|
---|
[1169] | 239 |
|
---|
[6244] | 240 | /**
|
---|
| 241 | * Download OSM Change files from somewhere
|
---|
| 242 | * @param progressMonitor The progress monitor
|
---|
| 243 | * @return The corresponding dataset
|
---|
[6643] | 244 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 245 | */
|
---|
[5317] | 246 | public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[4530] | 247 | return null;
|
---|
| 248 | }
|
---|
[5361] | 249 |
|
---|
[6244] | 250 | /**
|
---|
| 251 | * Download BZip2-compressed OSM Change files from somewhere
|
---|
| 252 | * @param progressMonitor The progress monitor
|
---|
| 253 | * @return The corresponding dataset
|
---|
[6643] | 254 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 255 | */
|
---|
[5361] | 256 | public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 257 | return null;
|
---|
| 258 | }
|
---|
| 259 |
|
---|
[6244] | 260 | /**
|
---|
| 261 | * Download GZip-compressed OSM Change files from somewhere
|
---|
| 262 | * @param progressMonitor The progress monitor
|
---|
| 263 | * @return The corresponding dataset
|
---|
[6643] | 264 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 265 | */
|
---|
[5361] | 266 | public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 267 | return null;
|
---|
| 268 | }
|
---|
| 269 |
|
---|
[6244] | 270 | /**
|
---|
| 271 | * Retrieve raw gps waypoints from the server API.
|
---|
| 272 | * @param progressMonitor The progress monitor
|
---|
| 273 | * @return The corresponding GPX tracks
|
---|
| 274 | * @throws OsmTransferException if any error occurs
|
---|
| 275 | */
|
---|
[5317] | 276 | public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[4521] | 277 | return null;
|
---|
| 278 | }
|
---|
[5317] | 279 |
|
---|
[6244] | 280 | /**
|
---|
| 281 | * Retrieve BZip2-compressed GPX files from somewhere.
|
---|
| 282 | * @param progressMonitor The progress monitor
|
---|
| 283 | * @return The corresponding GPX tracks
|
---|
| 284 | * @throws OsmTransferException if any error occurs
|
---|
| 285 | * @since 6244
|
---|
| 286 | */
|
---|
| 287 | public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 288 | return null;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
| 291 | /**
|
---|
| 292 | * Download BZip2-compressed OSM files from somewhere
|
---|
| 293 | * @param progressMonitor The progress monitor
|
---|
| 294 | * @return The corresponding dataset
|
---|
[6643] | 295 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 296 | */
|
---|
[5317] | 297 | public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 298 | return null;
|
---|
| 299 | }
|
---|
[5361] | 300 |
|
---|
[6244] | 301 | /**
|
---|
| 302 | * Download GZip-compressed OSM files from somewhere
|
---|
| 303 | * @param progressMonitor The progress monitor
|
---|
| 304 | * @return The corresponding dataset
|
---|
[6643] | 305 | * @throws OsmTransferException if any error occurs
|
---|
[6244] | 306 | */
|
---|
[5361] | 307 | public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 308 | return null;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
[2124] | 311 | /**
|
---|
[6882] | 312 | * Download Zip-compressed OSM files from somewhere
|
---|
| 313 | * @param progressMonitor The progress monitor
|
---|
| 314 | * @return The corresponding dataset
|
---|
| 315 | * @throws OsmTransferException if any error occurs
|
---|
| 316 | * @since 6882
|
---|
| 317 | */
|
---|
| 318 | public DataSet parseOsmZip(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 319 | return null;
|
---|
| 320 | }
|
---|
| 321 |
|
---|
| 322 | /**
|
---|
[2124] | 323 | * Returns true if this reader is adding authentication credentials to the read
|
---|
| 324 | * request sent to the server.
|
---|
[2512] | 325 | *
|
---|
[2124] | 326 | * @return true if this reader is adding authentication credentials to the read
|
---|
| 327 | * request sent to the server
|
---|
| 328 | */
|
---|
| 329 | public boolean isDoAuthenticate() {
|
---|
| 330 | return doAuthenticate;
|
---|
| 331 | }
|
---|
| 332 |
|
---|
| 333 | /**
|
---|
| 334 | * Sets whether this reader adds authentication credentials to the read
|
---|
| 335 | * request sent to the server.
|
---|
[2512] | 336 | *
|
---|
[2124] | 337 | * @param doAuthenticate true if this reader adds authentication credentials to the read
|
---|
| 338 | * request sent to the server
|
---|
| 339 | */
|
---|
| 340 | public void setDoAuthenticate(boolean doAuthenticate) {
|
---|
| 341 | this.doAuthenticate = doAuthenticate;
|
---|
| 342 | }
|
---|
[6070] | 343 |
|
---|
[5494] | 344 | /**
|
---|
| 345 | * Determines if the GPX data has been parsed properly.
|
---|
| 346 | * @return true if the GPX data has been parsed properly, false otherwise
|
---|
| 347 | * @see GpxReader#parse
|
---|
| 348 | */
|
---|
| 349 | public final boolean isGpxParsedProperly() {
|
---|
| 350 | return gpxParsedProperly;
|
---|
| 351 | }
|
---|
[7531] | 352 |
|
---|
| 353 | /**
|
---|
| 354 | * Downloads notes from the API, given API limit parameters
|
---|
| 355 | *
|
---|
[8218] | 356 | * @param noteLimit How many notes to download.
|
---|
[7531] | 357 | * @param daysClosed Return notes closed this many days in the past. -1 means all notes, ever. 0 means only unresolved notes.
|
---|
| 358 | * @param progressMonitor Progress monitor for user feedback
|
---|
| 359 | * @return List of notes returned by the API
|
---|
| 360 | * @throws OsmTransferException if any errors happen
|
---|
| 361 | */
|
---|
[8218] | 362 | public List<Note> parseNotes(int noteLimit, int daysClosed, ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
[7531] | 363 | return null;
|
---|
| 364 | }
|
---|
| 365 |
|
---|
| 366 | /**
|
---|
| 367 | * Downloads notes from a given raw URL. The URL is assumed to be complete and no API limits are added
|
---|
| 368 | *
|
---|
[8470] | 369 | * @param progressMonitor progress monitor
|
---|
[7531] | 370 | * @return A list of notes parsed from the URL
|
---|
[8470] | 371 | * @throws OsmTransferException if any error occurs during dialog with OSM API
|
---|
[7531] | 372 | */
|
---|
| 373 | public List<Note> parseRawNotes(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 374 | return null;
|
---|
| 375 | }
|
---|
| 376 |
|
---|
| 377 | /**
|
---|
| 378 | * Download notes from a URL that contains a bzip2 compressed notes dump file
|
---|
[8470] | 379 | * @param progressMonitor progress monitor
|
---|
[7531] | 380 | * @return A list of notes parsed from the URL
|
---|
[8470] | 381 | * @throws OsmTransferException if any error occurs during dialog with OSM API
|
---|
[7531] | 382 | */
|
---|
| 383 | public List<Note> parseRawNotesBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
|
---|
| 384 | return null;
|
---|
| 385 | }
|
---|
[18] | 386 | }
|
---|