001/**
002 * BioJava development code
003 *
004 * This code may be freely distributed and modified under the terms of the GNU
005 * Lesser General Public Licence. This should be distributed with the code. If
006 * you do not have a copy, see:
007 *
008 * http://www.gnu.org/copyleft/lesser.html
009 *
010 * Copyright for this code is held jointly by the individual authors. These
011 * should be listed in @author doc comments.
012 *
013 * For more information on the BioJava project and its aims, or to join the
014 * biojava-l mailing list, visit the home page at:
015 *
016 * http://www.biojava.org/
017 *
018 * Created on Feb 23, 2012 Created by Andreas Prlic
019 *
020 * @since 3.0.2
021 */
022package org.biojava.nbio.core.util;
023
024import java.io.File;
025import java.io.FileInputStream;
026import java.io.FileOutputStream;
027import java.io.IOException;
028import java.io.InputStream;
029import java.net.HttpURLConnection;
030import java.net.SocketTimeoutException;
031import java.net.URL;
032import java.net.URLConnection;
033import java.nio.channels.Channels;
034import java.nio.channels.FileChannel;
035import java.nio.channels.ReadableByteChannel;
036import java.nio.file.FileVisitResult;
037import java.nio.file.Files;
038import java.nio.file.Path;
039import java.nio.file.Paths;
040import java.nio.file.SimpleFileVisitor;
041import java.nio.file.attribute.BasicFileAttributes;
042
043import org.slf4j.Logger;
044import org.slf4j.LoggerFactory;
045
046public class FileDownloadUtils {
047
048        private static final Logger logger = LoggerFactory.getLogger(FileDownloadUtils.class);
049
050        /**
051         * Copy the content of file src to dst TODO since java 1.7 this is provided
052         * in java.nio.file.Files
053         *
054         * @param src
055         * @param dst
056         * @throws IOException
057         */
058        @SuppressWarnings("resource")
059        public static void copy(File src, File dst) throws IOException {
060
061                // Took following recipe from
062                // http://stackoverflow.com/questions/106770/standard-concise-way-to-copy-a-file-in-java
063                // The nio package seems to be the most efficient way to copy a file
064                FileChannel source = null;
065                FileChannel destination = null;
066
067                try {
068                        // we need the supress warnings here (the warning that the stream is not closed is harmless)
069                        // see http://stackoverflow.com/questions/12970407/does-filechannel-close-close-the-underlying-stream
070                        source = new FileInputStream(src).getChannel();
071                        destination = new FileOutputStream(dst).getChannel();
072                        destination.transferFrom(source, 0, source.size());
073                } finally {
074                        if (source != null) {
075                                source.close();
076                        }
077                        if (destination != null) {
078                                destination.close();
079                        }
080                }
081        }
082
083        public static String getFileExtension(File f) {
084                String fileName = f.getName();
085                String ext = "";
086                int mid = fileName.lastIndexOf(".");
087                ext = fileName.substring(mid + 1, fileName.length());
088                return ext;
089        }
090
091        public static String getFilePrefix(File f) {
092                String fileName = f.getName();
093                String fname = "";
094
095                int mid = fileName.indexOf(".");
096                fname = fileName.substring(0, mid);
097
098                return fname;
099        }
100
101        /**
102         * Download the content provided at URL url and store the result to a local
103         * file, using a temp file to cache the content in case something goes wrong
104         * in download
105         *
106         * @param url
107         * @param destination
108         * @throws IOException
109         */
110        public static void downloadFile(URL url, File destination) throws IOException {
111                int count = 0;
112                int maxTries = 10;
113                int timeout = 60000; //60 sec
114
115                File tempFile = File.createTempFile(getFilePrefix(destination), "." + getFileExtension(destination));
116
117                // Took following recipe from stackoverflow:
118                // http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java
119                // It seems to be the most efficient way to transfer a file
120                // See: http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileChannel.html
121                ReadableByteChannel rbc = null;
122                FileOutputStream fos = null;
123                while (true) {
124                        try {
125                                URLConnection connection = prepareURLConnection(url.toString(), timeout);
126                                connection.connect();
127                                InputStream inputStream = connection.getInputStream();
128
129                                rbc = Channels.newChannel(inputStream);
130                                fos = new FileOutputStream(tempFile);
131                                fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
132                                break;
133                        } catch (SocketTimeoutException e) {
134                                if (++count == maxTries) throw e;
135                        } finally {
136                                if (rbc != null) {
137                                        rbc.close();
138                                }
139                                if (fos != null) {
140                                        fos.close();
141                                }
142                        }
143                }
144
145                logger.debug("Copying temp file {} to final location {}", tempFile, destination);
146                copy(tempFile, destination);
147
148                // delete the tmp file
149                tempFile.delete();
150
151        }
152
153        /**
154         * Converts path to Unix convention and adds a terminating slash if it was
155         * omitted
156         *
157         * @param path original platform dependent path
158         * @return path in Unix convention
159         * @author Peter Rose
160         * @since 3.2
161         */
162        public static String toUnixPath(String path) {
163                String uPath = path;
164                if (uPath.contains("\\")) {
165                        uPath = uPath.replaceAll("\\\\", "/");
166                }
167                // this should be removed, it's need since "\" is added AtomCache code
168                if (uPath.endsWith("//")) {
169                        uPath = uPath.substring(0, uPath.length() - 1);
170                }
171                if (!uPath.endsWith("/")) {
172                        uPath = uPath + "/";
173                }
174                return uPath;
175        }
176
177        /**
178         * Expands ~ in paths to the user's home directory.
179         *
180         * <p>
181         * This does not work for some special cases for paths: Other users' homes
182         * (~user/...), and Tilde expansion within the path (/.../~/...)
183         *
184         * @param file
185         * @return
186         */
187        public static String expandUserHome(String file) {
188                if (file.startsWith("~" + File.separator)) {
189                        file = System.getProperty("user.home") + file.substring(1);
190                }
191                return file;
192        }
193
194        /**
195         * Pings a HTTP URL. This effectively sends a HEAD request and returns
196         * <code>true</code> if the response code is in the 200-399 range.
197         *
198         * @param url The HTTP URL to be pinged.
199         * @param timeout The timeout in millis for both the connection timeout and
200         * the response read timeout. Note that the total timeout is effectively two
201         * times the given timeout.
202         * @return <code>true</code> if the given HTTP URL has returned response
203         * code 200-399 on a HEAD request within the given timeout, otherwise
204         * <code>false</code>.
205         * @author BalusC,
206         * http://stackoverflow.com/questions/3584210/preferred-java-way-to-ping-a-http-url-for-availability
207         */
208        public static boolean ping(String url, int timeout) {
209                //url = url.replaceFirst("https", "http"); // Otherwise an exception may be thrown on invalid SSL certificates.
210
211                try {
212                        HttpURLConnection connection = (HttpURLConnection) prepareURLConnection(url, timeout);
213                        connection.setRequestMethod("HEAD");
214                        int responseCode = connection.getResponseCode();
215                        return (200 <= responseCode && responseCode <= 399);
216                } catch (IOException exception) {
217                        return false;
218                }
219        }
220
221        /**
222         * Prepare {@link URLConnection} with customised timeouts.
223         *
224         * @param url The URL
225         * @param timeout The timeout in millis for both the connection timeout and
226         * the response read timeout. Note that the total timeout is effectively two
227         * times the given timeout.
228         *
229         * <p>
230         * Example of code.      <code>
231                 * UrlConnection conn = prepareURLConnection("http://www.google.com/", 20000);
232         * conn.connect();
233         * conn.getInputStream();
234         * </code>
235         * <p>
236         *
237         * <bold>NB. User should execute connect() method before getting input
238         * stream.</bold>
239         * @return
240         * @throws IOException
241         * @author Jacek Grzebyta
242         */
243        public static URLConnection prepareURLConnection(String url, int timeout) throws IOException {
244                URLConnection connection = new URL(url).openConnection();
245                connection.setReadTimeout(timeout);
246                connection.setConnectTimeout(timeout);
247                return connection;
248        }
249
250        /**
251         * Recursively delete a folder & contents
252         *
253         * @param dir directory to delete
254         */
255        public static void deleteDirectory(Path dir) throws IOException {
256                if(dir == null || !Files.exists(dir))
257                        return;
258                Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
259                @Override
260                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
261                    Files.delete(file);
262                    return FileVisitResult.CONTINUE;
263                }
264
265                @Override
266                public FileVisitResult postVisitDirectory(Path dir, IOException e) throws IOException {
267                    if (e != null) {
268                        throw e;
269                    }
270                    Files.delete(dir);
271                    return FileVisitResult.CONTINUE;
272                }
273            });
274        }
275        /**
276         * Recursively delete a folder & contents
277         *
278         * @param dir directory to delete
279         */
280        public static void deleteDirectory(String dir) throws IOException {
281                deleteDirectory(Paths.get(dir));
282        }
283
284
285        public static void main(String[] args) {
286                String url;
287                url = "http://scop.mrc-lmb.cam.ac.uk/scop/parse/";
288                System.out.format("%s\t%s%n", ping(url, 200), url);
289                url = "http://scop.mrc-lmb.cam.ac.uk/scop/parse/foo";
290                System.out.format("%s\t%s%n", ping(url, 200), url);
291                url = "http://scopzzz.mrc-lmb.cam.ac.uk/scop/parse/";
292                System.out.format("%s\t%s%n", ping(url, 200), url);
293                url = "scop.mrc-lmb.cam.ac.uk";
294                System.out.format("%s\t%s%n", ping(url, 200), url);
295                url = "http://scop.mrc-lmb.cam.ac.uk";
296                System.out.format("%s\t%s%n", ping(url, 200), url);
297        }
298
299}