source: josm/trunk/src/org/openstreetmap/josm/io/OsmServerReader.java@ 6830

Last change on this file since 6830 was 6803, checked in by simon04, 10 years ago

fix #9660 - Allow to download compressed GPX tracks from osm.org/trace/ using "Download location"

This works by inspecting the filename in the Content-Disposition HTTP header.

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