001    package org.openstreetmap.josm.gui.io;
002    
003    
004    
005    // License: GPL. For details, see LICENSE file.
006    
007    import static org.openstreetmap.josm.tools.I18n.tr;
008    
009    import java.awt.Component;
010    import java.io.BufferedOutputStream;
011    import java.io.File;
012    import java.io.FileOutputStream;
013    import java.io.IOException;
014    import java.io.InputStream;
015    import java.io.OutputStream;
016    import java.net.HttpURLConnection;
017    import java.net.MalformedURLException;
018    import java.net.URL;
019    
020    import java.net.URLConnection;
021    import java.util.Enumeration;
022    
023    import java.util.zip.ZipEntry;
024    import java.util.zip.ZipFile;
025    import org.openstreetmap.josm.data.Version;
026    import org.openstreetmap.josm.gui.PleaseWaitDialog;
027    import org.openstreetmap.josm.gui.PleaseWaitRunnable;
028    import org.openstreetmap.josm.tools.Utils;
029    import org.xml.sax.SAXException;
030    
031    
032    /**
033     * Asynchronous task for downloading andnd unpacking arbitrary file lists
034     * Shows progress bar when donloading
035     */
036    public class DownloadFileTask extends PleaseWaitRunnable{
037        private final String address;
038        private final File file;
039        private final boolean mkdir;
040        private final boolean unpack;
041    
042        /**
043         * Creates the download task
044         *
045         * @param parent the parent component relative to which the {@link PleaseWaitDialog} is displayed
046         * @param title the title to display in the {@link PleaseWaitDialog}
047         * @throws IllegalArgumentException thrown if toUpdate is null
048         */
049        public DownloadFileTask(Component parent, String address, File file, boolean mkdir, boolean unpack) {
050            super(parent, tr("Downloading file"), false);
051            this.address = address;
052            this.file = file;
053            this.mkdir = mkdir;
054            this.unpack = unpack;
055                    
056        }    
057        
058        private static class DownloadException extends Exception {
059            public DownloadException(String msg) {
060                super(msg);
061            }
062        }
063    
064        private boolean canceled;
065        private URLConnection downloadConnection;
066    
067        private synchronized void closeConnectionIfNeeded() {
068            if (downloadConnection != null && downloadConnection instanceof HttpURLConnection) {
069                HttpURLConnection conn = ((HttpURLConnection) downloadConnection);
070                conn.disconnect();
071            }
072            downloadConnection = null;
073        }
074    
075    
076        @Override 
077        protected void cancel() {
078            this.canceled = true;
079            closeConnectionIfNeeded();
080        }
081    
082        @Override 
083        protected void finish() {}
084    
085        public void download() throws DownloadException {
086            OutputStream out = null;
087            InputStream in = null;
088            try {
089                if (mkdir) {
090                    File newDir = file.getParentFile();
091                    if (!newDir.exists()) {
092                        newDir.mkdirs();
093                    }
094                }
095                
096                URL url = new URL(address);
097                int size;
098                synchronized(this) {
099                    downloadConnection = url.openConnection();
100                    downloadConnection.setRequestProperty("Cache-Control", "no-cache");
101                    downloadConnection.setRequestProperty("User-Agent",Version.getInstance().getAgentString());
102                    downloadConnection.setRequestProperty("Host", url.getHost());
103                    downloadConnection.connect();
104                    size = downloadConnection.getContentLength();
105                }
106                
107                progressMonitor.setTicksCount(100);
108                progressMonitor.subTask(tr("Downloading File {0}: {1} bytes...", file.getName(),size));
109                
110                in = downloadConnection.getInputStream();
111                out = new FileOutputStream(file);
112                byte[] buffer = new byte[32768];
113                int count=0;
114                int p1=0, p2=0;
115                for (int read = in.read(buffer); read != -1; read = in.read(buffer)) {
116                    out.write(buffer, 0, read);
117                    count+=read;
118                    if (canceled) return;                            
119                    p2 = 100 * count / size;
120                    if (p2!=p1) {
121                        progressMonitor.setTicks(p2);
122                        p1=p2;
123                    }
124                }
125                out.close();
126                System.out.println(tr("Download finished"));
127                if (unpack) {
128                    System.out.println(tr("Unpacking {0} into {1}", file.getAbsolutePath(), file.getParent()));
129                    unzipFileRecursively(file, file.getParent());
130                    file.delete();
131                }
132            } catch(MalformedURLException e) {
133                String msg = tr("Warning: Cannot download file ''{0}''. Its download link ''{1}'' is not a valid URL. Skipping download.", file.getName(), address);
134                System.err.println(msg);
135                throw new DownloadException(msg);
136            } catch (IOException e) {
137                if (canceled)
138                    return;
139                throw new DownloadException(e.getMessage());
140            } finally {
141                closeConnectionIfNeeded();
142                Utils.close(out);
143            }
144        }
145    
146        @Override 
147        protected void realRun() throws SAXException, IOException {
148            if (canceled) return;
149            try {
150                download();
151            } catch(DownloadException e) {
152                e.printStackTrace();
153            }
154        }
155    
156        /**
157         * Replies true if the task was canceled by the user
158         *
159         * @return
160         */
161        public boolean isCanceled() {
162            return canceled;
163        }
164        
165        /**
166         * Recursive unzipping function
167         * TODO: May be placed somewhere else - Tools.Utils?
168         * @param file
169         * @param dir
170         * @throws IOException 
171         */
172        public static void unzipFileRecursively(File file, String dir) throws IOException {
173            OutputStream os = null;
174            InputStream is = null;
175            ZipFile zf = null;
176            try {
177                zf = new ZipFile(file);
178                Enumeration es = zf.entries();
179                ZipEntry ze;
180                while (es.hasMoreElements()) {
181                    ze = (ZipEntry) es.nextElement();
182                    File newFile = new File(dir, ze.getName());
183                    if (ze.isDirectory()) {
184                        newFile.mkdirs();
185                    } else {
186                        is = zf.getInputStream(ze);
187                        os = new BufferedOutputStream(new FileOutputStream(newFile));
188                        byte[] buffer = new byte[8192];
189                        int read;
190                        while ((read = is.read(buffer)) != -1) {
191                            os.write(buffer, 0, read);
192                        }
193                        os.close();
194                        is.close();
195                    }
196                }
197                zf.close();
198            } finally {
199                if (zf!=null) zf.close();
200            }
201        }
202    }
203