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    }