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