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.BufferedReader; 007import java.io.IOException; 008import java.io.InputStream; 009import java.io.InputStreamReader; 010import java.net.HttpURLConnection; 011import java.net.MalformedURLException; 012import java.net.URL; 013import java.util.zip.GZIPInputStream; 014import java.util.zip.Inflater; 015import java.util.zip.InflaterInputStream; 016 017import org.openstreetmap.josm.Main; 018import org.openstreetmap.josm.data.gpx.GpxData; 019import org.openstreetmap.josm.data.osm.DataSet; 020import org.openstreetmap.josm.gui.progress.ProgressMonitor; 021import org.openstreetmap.josm.tools.Utils; 022 023/** 024 * This DataReader reads directly from the REST API of the osm server. 025 * 026 * It supports plain text transfer as well as gzip or deflate encoded transfers; 027 * if compressed transfers are unwanted, set property osm-server.use-compression 028 * to false. 029 * 030 * @author imi 031 */ 032public abstract class OsmServerReader extends OsmConnection { 033 private OsmApi api = OsmApi.getOsmApi(); 034 private boolean doAuthenticate = false; 035 protected boolean gpxParsedProperly; 036 037 protected enum Compression { 038 NONE, 039 BZIP2, 040 GZIP 041 } 042 043 /** 044 * Open a connection to the given url and return a reader on the input stream 045 * from that connection. In case of user cancel, return <code>null</code>. 046 * Relative URL's are directed to API base URL. 047 * @param urlStr The url to connect to. 048 * @param progressMonitor progress monitoring and abort handler 049 * @return An reader reading the input stream (servers answer) or <code>null</code>. 050 * @throws OsmTransferException thrown if data transfer errors occur 051 */ 052 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException { 053 try { 054 api.initialize(progressMonitor); 055 urlStr = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr); 056 return getInputStreamRaw(urlStr, progressMonitor); 057 } finally { 058 progressMonitor.invalidate(); 059 } 060 } 061 062 /** 063 * Retrun the base URL for relative URL requests 064 * @return base url of API 065 */ 066 protected String getBaseUrl() { 067 return api.getBaseUrl(); 068 } 069 070 /** 071 * Open a connection to the given url and return a reader on the input stream 072 * from that connection. In case of user cancel, return <code>null</code>. 073 * @param urlStr The exact url to connect to. 074 * @param progressMonitor progress monitoring and abort handler 075 * @return An reader reading the input stream (servers answer) or <code>null</code>. 076 * @throws OsmTransferException thrown if data transfer errors occur 077 */ 078 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException { 079 try { 080 URL url = null; 081 try { 082 url = new URL(urlStr.replace(" ", "%20")); 083 } catch(MalformedURLException e) { 084 throw new OsmTransferException(e); 085 } 086 try { 087 // fix #7640, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive 088 activeConnection = Utils.openHttpConnection(url, false); 089 } catch(Exception e) { 090 throw new OsmTransferException(tr("Failed to open connection to API {0}.", url.toExternalForm()), e); 091 } 092 if (cancel) { 093 activeConnection.disconnect(); 094 return null; 095 } 096 097 if (doAuthenticate) { 098 addAuth(activeConnection); 099 } 100 if (cancel) 101 throw new OsmTransferCanceledException(); 102 if (Main.pref.getBoolean("osm-server.use-compression", true)) { 103 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate"); 104 } 105 106 activeConnection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000); 107 108 try { 109 Main.info("GET " + url); 110 activeConnection.connect(); 111 } catch (Exception e) { 112 e.printStackTrace(); 113 OsmTransferException ote = new OsmTransferException(tr("Could not connect to the OSM server. Please check your internet connection."), e); 114 ote.setUrl(url.toString()); 115 throw ote; 116 } 117 try { 118 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED) 119 throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null); 120 121 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH) 122 throw new OsmTransferCanceledException(); 123 124 String encoding = activeConnection.getContentEncoding(); 125 if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) { 126 String errorHeader = activeConnection.getHeaderField("Error"); 127 StringBuilder errorBody = new StringBuilder(); 128 try 129 { 130 InputStream i = FixEncoding(activeConnection.getErrorStream(), encoding); 131 if (i != null) { 132 BufferedReader in = new BufferedReader(new InputStreamReader(i)); 133 String s; 134 while((s = in.readLine()) != null) { 135 errorBody.append(s); 136 errorBody.append("\n"); 137 } 138 } 139 } 140 catch(Exception e) { 141 errorBody.append(tr("Reading error text failed.")); 142 } 143 144 throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString(), url.toString()); 145 } 146 147 return FixEncoding(new ProgressInputStream(activeConnection, progressMonitor), encoding); 148 } catch (OsmTransferException e) { 149 throw e; 150 } catch (Exception e) { 151 throw new OsmTransferException(e); 152 } 153 } finally { 154 progressMonitor.invalidate(); 155 } 156 } 157 158 private InputStream FixEncoding(InputStream stream, String encoding) throws IOException 159 { 160 if ("gzip".equalsIgnoreCase(encoding)) { 161 stream = new GZIPInputStream(stream); 162 } 163 else if ("deflate".equalsIgnoreCase(encoding)) { 164 stream = new InflaterInputStream(stream, new Inflater(true)); 165 } 166 return stream; 167 } 168 169 /** 170 * Download OSM files from somewhere 171 * @param progressMonitor The progress monitor 172 * @return The corresponding dataset 173 * @throws OsmTransferException if any error occurs 174 */ 175 public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException; 176 177 /** 178 * Download OSM Change files from somewhere 179 * @param progressMonitor The progress monitor 180 * @return The corresponding dataset 181 * @throws OsmTransferException if any error occurs 182 */ 183 public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException { 184 return null; 185 } 186 187 /** 188 * Download BZip2-compressed OSM Change files from somewhere 189 * @param progressMonitor The progress monitor 190 * @return The corresponding dataset 191 * @throws OsmTransferException if any error occurs 192 */ 193 public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 194 return null; 195 } 196 197 /** 198 * Download GZip-compressed OSM Change files from somewhere 199 * @param progressMonitor The progress monitor 200 * @return The corresponding dataset 201 * @throws OsmTransferException if any error occurs 202 */ 203 public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException { 204 return null; 205 } 206 207 /** 208 * Retrieve raw gps waypoints from the server API. 209 * @param progressMonitor The progress monitor 210 * @return The corresponding GPX tracks 211 * @throws OsmTransferException if any error occurs 212 */ 213 public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException { 214 return null; 215 } 216 217 /** 218 * Retrieve BZip2-compressed GPX files from somewhere. 219 * @param progressMonitor The progress monitor 220 * @return The corresponding GPX tracks 221 * @throws OsmTransferException if any error occurs 222 * @since 6244 223 */ 224 public GpxData parseRawGpsBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 225 return null; 226 } 227 228 /** 229 * Download BZip2-compressed OSM files from somewhere 230 * @param progressMonitor The progress monitor 231 * @return The corresponding dataset 232 * @throws OsmTransferException if any error occurs 233 */ 234 public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException { 235 return null; 236 } 237 238 /** 239 * Download GZip-compressed OSM files from somewhere 240 * @param progressMonitor The progress monitor 241 * @return The corresponding dataset 242 * @throws OsmTransferException if any error occurs 243 */ 244 public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException { 245 return null; 246 } 247 248 /** 249 * Returns true if this reader is adding authentication credentials to the read 250 * request sent to the server. 251 * 252 * @return true if this reader is adding authentication credentials to the read 253 * request sent to the server 254 */ 255 public boolean isDoAuthenticate() { 256 return doAuthenticate; 257 } 258 259 /** 260 * Sets whether this reader adds authentication credentials to the read 261 * request sent to the server. 262 * 263 * @param doAuthenticate true if this reader adds authentication credentials to the read 264 * request sent to the server 265 */ 266 public void setDoAuthenticate(boolean doAuthenticate) { 267 this.doAuthenticate = doAuthenticate; 268 } 269 270 /** 271 * Determines if the GPX data has been parsed properly. 272 * @return true if the GPX data has been parsed properly, false otherwise 273 * @see GpxReader#parse 274 */ 275 public final boolean isGpxParsedProperly() { 276 return gpxParsedProperly; 277 } 278}