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