net.paissad.waqtsalat.utils.DownloadHelper.java Source code

Java tutorial

Introduction

Here is the source code for net.paissad.waqtsalat.utils.DownloadHelper.java

Source

/*
 * WaqtSalat, for indicating the muslim prayers times in most cities. Copyright
 * (C) 2011 Papa Issa DIAKHATE (paissad).
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * PLEASE DO NOT REMOVE THIS COPYRIGHT BLOCK.
 */

package net.paissad.waqtsalat.utils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Observable;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.paissad.waqtsalat.WSConstants;

/**
 * @author Papa Issa DIAKHATE (paissad)
 */
public class DownloadHelper extends Observable {

    private static Logger logger = LoggerFactory.getLogger(DownloadHelper.class);

    private static final int BUFFER_SIZE = 4096;

    private DownloadState state;
    private int totalBytes;
    private int bytesDownloaded;
    private boolean isCancelled;

    // _________________________________________________________________________
    // Constructors ...

    public DownloadHelper() {
        this.totalBytes = 0;
        this.bytesDownloaded = 0;
        this.state = DownloadState.UNKNOWN;
        this.isCancelled = false;
    }

    // _________________________________________________________________________

    public enum DownloadState {
        IN_PROGRESS, CANCELED, FINISHED, ERROR, UNKNOWN,
    };

    // _________________________________________________________________________

    /**
     * <p>
     * Download a resource from the specified url and save it to the specified
     * file.
     * </p>
     * <b>Note</b>: If you plan to cancel the download at any time, then this
     * method should be embed into it's own thread.
     * <p>
     * <b>Example</b>:
     * 
     * <pre>
     *  final DownloadHelper downloader = new DownloadHelper();
     *  Thread t = new Thread() {
     *      public void run() {
     *          try {
     *              downloader.download("http://domain.com/file.zip", new File("/tmp/output.zip"));
     *          } catch (Exception e) {
     *              ...
     *          }
     *      }
     *  };
     *  t.start();
     *  // Waits 3 seconds and then cancels the download.
     *  Thread.sleep(3 * 1000L);
     *  downloader.cancel();
     *  ...
     * </pre>
     * 
     * </p>
     * 
     * @param url
     *            - The url of the file to download.
     * @param file
     *            - The downloaded file (where it will be stored).
     * @throws IOException
     * @throws HttpException
     */
    public void download(final String url, final File file) throws HttpException, IOException {

        GetMethod method = null;
        InputStream responseBody = null;
        OutputStream out = null;

        try {
            final boolean fileExisted = file.exists();

            HttpClient client = new HttpClient();
            method = new GetMethod(url);
            method.setFollowRedirects(true);
            method.setRequestHeader("User-Agent", WSConstants.WS_USER_AGENT);
            method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
                    new DefaultHttpMethodRetryHandler(3, false));
            method.getParams().setParameter(HttpMethodParams.WARN_EXTRA_INPUT, Boolean.TRUE);

            // Execute the method.
            int responseCode = client.executeMethod(method);
            if (responseCode != HttpStatus.SC_OK) {
                logger.error("Http method failed : {}.", method.getStatusLine().toString());
            }

            // Read the response body.
            responseBody = method.getResponseBodyAsStream();

            // Let's try to compute the amount of total bytes of the file to
            // download.
            URL u = new URL(url);
            URLConnection urlc = u.openConnection();
            this.totalBytes = urlc.getContentLength();
            long lastModified = urlc.getLastModified();

            // The OutputStream for the 'downloaded' file.
            out = new BufferedOutputStream(new FileOutputStream(file));

            this.updateState(DownloadState.IN_PROGRESS);

            byte[] data = new byte[BUFFER_SIZE];
            int length;
            while ((length = responseBody.read(data, 0, BUFFER_SIZE)) != -1 && !isCancelled) {
                out.write(data, 0, length);
                this.bytesDownloaded += length;
                setChanged();
                notifyObservers(getBytesDownloaded());
            }

            if (isCancelled) {
                this.updateState(DownloadState.CANCELED);
                logger.info("The download has been cancelled.");

            } else {
                // The download was not cancelled.
                out.flush();
                if (lastModified > 0) {
                    file.setLastModified(lastModified);
                }

                if (bytesDownloaded != totalBytes) {
                    logger.warn("The expected bytes to download is {}, but got {}.", getTotalBytes(),
                            getBytesDownloaded());
                    this.updateState(DownloadState.ERROR);
                }

                this.updateState(DownloadState.FINISHED);
                logger.info("Download of '{}' finished successfully.", url);
            }

            // If the file did not exist before the download but does exit now,
            // we must remove it if an error occurred or if the download was
            // cancelled.
            if (getState() == DownloadState.CANCELED || getState() == DownloadState.ERROR) {
                if (!fileExisted && file.exists()) {
                    FileUtils.forceDelete(file);
                }
            }

        } catch (HttpException he) {
            this.updateState(DownloadState.ERROR);
            logger.error("Fatal protocol violation : " + he);
            throw new HttpException("Error while downloading from the url '" + url + "'", he);

        } catch (IOException ioe) {
            this.updateState(DownloadState.ERROR);
            logger.error("Fatal transport error : " + ioe);
            throw new IOException(ioe);

        } finally {
            if (method != null)
                method.releaseConnection();
            if (responseBody != null)
                responseBody.close();
            if (out != null)
                out.close();
        }
    }

    // _________________________________________________________________________

    private void updateState(DownloadState newState) {
        this.state = newState;
        setChanged();
        notifyObservers(getBytesDownloaded());
    }

    /**
     * Cancels the download.
     */
    public void cancel() {
        this.isCancelled = true;
    }

    // _________________________________________________________________________
    // Getters / Setters ...

    public DownloadState getState() {
        return state;
    }

    public int getTotalBytes() {
        return totalBytes;
    }

    public int getBytesDownloaded() {
        return bytesDownloaded;
    }

    // _________________________________________________________________________

    /*
     * For testing purpose only ! XXX
     */
    @Deprecated
    public static void main(String[] args) throws HttpException, IOException, InterruptedException {

        final String url = "http://sourceforge.net/projects/mod-security/files/modsecurity-apache/2.6.1/modsecurity-apache_2.6.1.tar.gz/download";
        final File file = new File("test.tar.gz");

        final DownloadHelper downloader = new DownloadHelper();
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    downloader.download(url, file);
                } catch (Exception e) {
                    logger.error("Download failed ... ", e);
                }
            }
        };
        t.start();
        // Waits 3 seconds and then cancels the download.
        Thread.sleep(3 * 1000L);
        downloader.cancel();
    }
}