001 // License: GPL. Copyright 2007 by Immanuel Scholz and others
002 package org.openstreetmap.josm.io;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.io.BufferedReader;
007 import java.io.IOException;
008 import java.io.InputStream;
009 import java.io.InputStreamReader;
010 import java.net.HttpURLConnection;
011 import java.net.MalformedURLException;
012 import java.net.URL;
013 import java.util.zip.GZIPInputStream;
014 import java.util.zip.Inflater;
015 import java.util.zip.InflaterInputStream;
016
017 import org.openstreetmap.josm.Main;
018 import org.openstreetmap.josm.data.gpx.GpxData;
019 import org.openstreetmap.josm.data.osm.DataSet;
020 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
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 */
031 public abstract class OsmServerReader extends OsmConnection {
032 private OsmApi api = OsmApi.getOsmApi();
033 private boolean doAuthenticate = false;
034 protected boolean gpxParsedProperly;
035
036 /**
037 * Open a connection to the given url and return a reader on the input stream
038 * from that connection. In case of user cancel, return <code>null</code>.
039 * @param urlStr The exact url to connect to.
040 * @param pleaseWaitDlg
041 * @return An reader reading the input stream (servers answer) or <code>null</code>.
042 */
043 protected InputStream getInputStream(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
044 try {
045 api.initialize(progressMonitor);
046 urlStr = urlStr.startsWith("http") ? urlStr : (getBaseUrl() + urlStr);
047 return getInputStreamRaw(urlStr, progressMonitor);
048 } finally {
049 progressMonitor.invalidate();
050 }
051 }
052
053 protected String getBaseUrl() {
054 return api.getBaseUrl();
055 }
056
057 protected InputStream getInputStreamRaw(String urlStr, ProgressMonitor progressMonitor) throws OsmTransferException {
058 try {
059 URL url = null;
060 try {
061 url = new URL(urlStr.replace(" ", "%20"));
062 } catch(MalformedURLException e) {
063 throw new OsmTransferException(e);
064 }
065 try {
066 activeConnection = (HttpURLConnection)url.openConnection();
067 // fix #7640, see http://www.tikalk.com/java/forums/httpurlconnection-disable-keep-alive
068 activeConnection.setRequestProperty("Connection", "close");
069 } catch(Exception e) {
070 throw new OsmTransferException(tr("Failed to open connection to API {0}.", url.toExternalForm()), e);
071 }
072 if (cancel) {
073 activeConnection.disconnect();
074 return null;
075 }
076
077 if (doAuthenticate) {
078 addAuth(activeConnection);
079 }
080 if (cancel)
081 throw new OsmTransferCanceledException();
082 if (Main.pref.getBoolean("osm-server.use-compression", true)) {
083 activeConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
084 }
085
086 activeConnection.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
087
088 try {
089 System.out.println("GET " + url);
090 activeConnection.connect();
091 } catch (Exception e) {
092 e.printStackTrace();
093 OsmTransferException ote = new OsmTransferException(tr("Could not connect to the OSM server. Please check your internet connection."), e);
094 ote.setUrl(url.toString());
095 throw ote;
096 }
097 try {
098 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
099 throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null);
100
101 if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH)
102 throw new OsmTransferCanceledException();
103
104 String encoding = activeConnection.getContentEncoding();
105 if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
106 String errorHeader = activeConnection.getHeaderField("Error");
107 StringBuilder errorBody = new StringBuilder();
108 try
109 {
110 InputStream i = FixEncoding(activeConnection.getErrorStream(), encoding);
111 if (i != null) {
112 BufferedReader in = new BufferedReader(new InputStreamReader(i));
113 String s;
114 while((s = in.readLine()) != null) {
115 errorBody.append(s);
116 errorBody.append("\n");
117 }
118 }
119 }
120 catch(Exception e) {
121 errorBody.append(tr("Reading error text failed."));
122 }
123
124 throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString());
125 }
126
127 return FixEncoding(new ProgressInputStream(activeConnection, progressMonitor), encoding);
128 } catch(Exception e) {
129 if (e instanceof OsmTransferException)
130 throw (OsmTransferException)e;
131 else
132 throw new OsmTransferException(e);
133
134 }
135 } finally {
136 progressMonitor.invalidate();
137 }
138 }
139
140 private InputStream FixEncoding(InputStream stream, String encoding) throws IOException
141 {
142 if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
143 stream = new GZIPInputStream(stream);
144 }
145 else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
146 stream = new InflaterInputStream(stream, new Inflater(true));
147 }
148 return stream;
149 }
150
151 public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
152
153 public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
154 return null;
155 }
156
157 public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
158 return null;
159 }
160
161 public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
162 return null;
163 }
164
165 public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
166 return null;
167 }
168
169 public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
170 return null;
171 }
172
173 public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
174 return null;
175 }
176
177 /**
178 * Returns true if this reader is adding authentication credentials to the read
179 * request sent to the server.
180 *
181 * @return true if this reader is adding authentication credentials to the read
182 * request sent to the server
183 */
184 public boolean isDoAuthenticate() {
185 return doAuthenticate;
186 }
187
188 /**
189 * Sets whether this reader adds authentication credentials to the read
190 * request sent to the server.
191 *
192 * @param doAuthenticate true if this reader adds authentication credentials to the read
193 * request sent to the server
194 */
195 public void setDoAuthenticate(boolean doAuthenticate) {
196 this.doAuthenticate = doAuthenticate;
197 }
198
199 /**
200 * Determines if the GPX data has been parsed properly.
201 * @return true if the GPX data has been parsed properly, false otherwise
202 * @see GpxReader#parse
203 */
204 public final boolean isGpxParsedProperly() {
205 return gpxParsedProperly;
206 }
207 }