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                    throw new OsmTransferException(tr("Could not connect to the OSM server. Please check your internet connection."), e);
094                }
095                try {
096                    if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED)
097                        throw new OsmApiException(HttpURLConnection.HTTP_UNAUTHORIZED,null,null);
098    
099                    if (activeConnection.getResponseCode() == HttpURLConnection.HTTP_PROXY_AUTH)
100                        throw new OsmTransferCanceledException();
101    
102                    String encoding = activeConnection.getContentEncoding();
103                    if (activeConnection.getResponseCode() != HttpURLConnection.HTTP_OK) {
104                        String errorHeader = activeConnection.getHeaderField("Error");
105                        StringBuilder errorBody = new StringBuilder();
106                        try
107                        {
108                            InputStream i = FixEncoding(activeConnection.getErrorStream(), encoding);
109                            if (i != null) {
110                                BufferedReader in = new BufferedReader(new InputStreamReader(i));
111                                String s;
112                                while((s = in.readLine()) != null) {
113                                    errorBody.append(s);
114                                    errorBody.append("\n");
115                                }
116                            }
117                        }
118                        catch(Exception e) {
119                            errorBody.append(tr("Reading error text failed."));
120                        }
121    
122                        throw new OsmApiException(activeConnection.getResponseCode(), errorHeader, errorBody.toString());
123                    }
124    
125                    return FixEncoding(new ProgressInputStream(activeConnection, progressMonitor), encoding);
126                } catch(Exception e) {
127                    if (e instanceof OsmTransferException)
128                        throw (OsmTransferException)e;
129                    else
130                        throw new OsmTransferException(e);
131    
132                }
133            } finally {
134                progressMonitor.invalidate();
135            }
136        }
137    
138        private InputStream FixEncoding(InputStream stream, String encoding) throws IOException
139        {
140            if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
141                stream = new GZIPInputStream(stream);
142            }
143            else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
144                stream = new InflaterInputStream(stream, new Inflater(true));
145            }
146            return stream;
147        }
148    
149        public abstract DataSet parseOsm(final ProgressMonitor progressMonitor) throws OsmTransferException;
150    
151        public DataSet parseOsmChange(final ProgressMonitor progressMonitor) throws OsmTransferException {
152            return null;
153        }
154    
155        public DataSet parseOsmChangeBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
156            return null;
157        }
158    
159        public DataSet parseOsmChangeGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
160            return null;
161        }
162    
163        public GpxData parseRawGps(final ProgressMonitor progressMonitor) throws OsmTransferException {
164            return null;
165        }
166    
167        public DataSet parseOsmBzip2(final ProgressMonitor progressMonitor) throws OsmTransferException {
168            return null;
169        }
170    
171        public DataSet parseOsmGzip(final ProgressMonitor progressMonitor) throws OsmTransferException {
172            return null;
173        }
174    
175        /**
176         * Returns true if this reader is adding authentication credentials to the read
177         * request sent to the server.
178         *
179         * @return true if this reader is adding authentication credentials to the read
180         * request sent to the server
181         */
182        public boolean isDoAuthenticate() {
183            return doAuthenticate;
184        }
185    
186        /**
187         * Sets whether this reader adds authentication credentials to the read
188         * request sent to the server.
189         *
190         * @param doAuthenticate  true if  this reader adds authentication credentials to the read
191         * request sent to the server
192         */
193        public void setDoAuthenticate(boolean doAuthenticate) {
194            this.doAuthenticate = doAuthenticate;
195        }
196        
197        /**
198         * Determines if the GPX data has been parsed properly.
199         * @return true if the GPX data has been parsed properly, false otherwise
200         * @see GpxReader#parse
201         */
202        public final boolean isGpxParsedProperly() {
203            return gpxParsedProperly;
204        }
205    }