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