cc.arduino.utils.network.FileDownloader.java Source code

Java tutorial

Introduction

Here is the source code for cc.arduino.utils.network.FileDownloader.java

Source

/*
 * This file is part of Arduino.
 *
 * Copyright 2014 Arduino LLC (http://www.arduino.cc/)
 *
 * Arduino 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 2 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * As a special exception, you may use this file as part of a free software
 * library without restriction.  Specifically, if other files instantiate
 * templates or use macros or inline functions from this file, or you compile
 * this file and link it with other files to produce an executable, this
 * file does not by itself cause the resulting executable to be covered by
 * the GNU General Public License.  This exception does not however
 * invalidate any other reasons why the executable file might be covered by
 * the GNU General Public License.
 */

package cc.arduino.utils.network;

import cc.arduino.net.CustomProxySelector;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.compress.utils.IOUtils;

import processing.app.BaseNoGui;
import processing.app.PreferencesData;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Observable;

public class FileDownloader extends Observable {

    public enum Status {
        CONNECTING, //
        CONNECTION_TIMEOUT_ERROR, //
        DOWNLOADING, //
        COMPLETE, //
        CANCELLED, //
        ERROR, //
    }

    private Status status;
    private long initialSize;
    private Long downloadSize = null;
    private long downloaded;
    private final URL downloadUrl;

    private final File outputFile;
    private InputStream stream = null;
    private Exception error;
    private String userAgent;

    public FileDownloader(URL url, File file) {
        downloadUrl = url;
        outputFile = file;
        downloaded = 0;
        initialSize = 0;
        userAgent = "ArduinoIDE/" + BaseNoGui.VERSION_NAME + " Java/" + System.getProperty("java.version");
    }

    public long getInitialSize() {
        return initialSize;
    }

    public Long getDownloadSize() {
        return downloadSize;
    }

    public void setDownloadSize(Long downloadSize) {
        this.downloadSize = downloadSize;
        setChanged();
        notifyObservers();
    }

    public long getDownloaded() {
        return downloaded;
    }

    private void setDownloaded(long downloaded) {
        this.downloaded = downloaded;
        setChanged();
        notifyObservers();
    }

    public float getProgress() {
        if (downloadSize == null)
            return 0;
        if (downloadSize == 0)
            return 100;
        return ((float) downloaded / downloadSize) * 100;
    }

    public Status getStatus() {
        return status;
    }

    public void setStatus(Status status) {
        this.status = status;
        setChanged();
        notifyObservers();
    }

    public void download() throws InterruptedException {
        download(false);
    }

    public void download(boolean noResume) throws InterruptedException {
        if ("file".equals(downloadUrl.getProtocol())) {
            saveLocalFile();
        } else {
            downloadFile(noResume);
        }
    }

    private void saveLocalFile() {
        try {
            Files.write(outputFile.toPath(), Files.readAllBytes(Paths.get(downloadUrl.getPath())));
            setStatus(Status.COMPLETE);
        } catch (Exception e) {
            setStatus(Status.ERROR);
            setError(e);
        }
    }

    private void downloadFile(boolean noResume) throws InterruptedException {
        RandomAccessFile file = null;

        try {
            // Open file and seek to the end of it
            file = new RandomAccessFile(outputFile, "rw");
            initialSize = file.length();

            if (noResume && initialSize > 0) {
                // delete file and restart downloading
                Files.delete(outputFile.toPath());
                initialSize = 0;
            }

            file.seek(initialSize);

            setStatus(Status.CONNECTING);

            Proxy proxy = new CustomProxySelector(PreferencesData.getMap()).getProxyFor(downloadUrl.toURI());
            if ("true".equals(System.getProperty("DEBUG"))) {
                System.err.println("Using proxy " + proxy);
            }

            HttpURLConnection connection = (HttpURLConnection) downloadUrl.openConnection(proxy);
            connection.setRequestProperty("User-agent", userAgent);
            if (downloadUrl.getUserInfo() != null) {
                String auth = "Basic " + new String(new Base64().encode(downloadUrl.getUserInfo().getBytes()));
                connection.setRequestProperty("Authorization", auth);
            }

            connection.setRequestProperty("Range", "bytes=" + initialSize + "-");
            connection.setConnectTimeout(5000);
            setDownloaded(0);

            // Connect
            connection.connect();
            int resp = connection.getResponseCode();

            if (resp == HttpURLConnection.HTTP_MOVED_PERM || resp == HttpURLConnection.HTTP_MOVED_TEMP) {
                URL newUrl = new URL(connection.getHeaderField("Location"));

                proxy = new CustomProxySelector(PreferencesData.getMap()).getProxyFor(newUrl.toURI());

                // open the new connnection again
                connection = (HttpURLConnection) newUrl.openConnection(proxy);
                connection.setRequestProperty("User-agent", userAgent);
                if (downloadUrl.getUserInfo() != null) {
                    String auth = "Basic " + new String(new Base64().encode(downloadUrl.getUserInfo().getBytes()));
                    connection.setRequestProperty("Authorization", auth);
                }

                connection.setRequestProperty("Range", "bytes=" + initialSize + "-");
                connection.setConnectTimeout(5000);

                connection.connect();
                resp = connection.getResponseCode();
            }

            if (resp < 200 || resp >= 300) {
                throw new IOException("Received invalid http status code from server: " + resp);
            }

            // Check for valid content length.
            long len = connection.getContentLength();
            if (len >= 0) {
                setDownloadSize(len);
            }
            setStatus(Status.DOWNLOADING);

            synchronized (this) {
                stream = connection.getInputStream();
            }
            byte buffer[] = new byte[10240];
            while (status == Status.DOWNLOADING) {
                int read = stream.read(buffer);
                if (read == -1)
                    break;

                file.write(buffer, 0, read);
                setDownloaded(getDownloaded() + read);

                if (Thread.interrupted()) {
                    file.close();
                    throw new InterruptedException();
                }
            }

            if (getDownloadSize() != null) {
                if (getDownloaded() < getDownloadSize())
                    throw new Exception("Incomplete download");
            }
            setStatus(Status.COMPLETE);
        } catch (InterruptedException e) {
            setStatus(Status.CANCELLED);
            // lets InterruptedException go up to the caller
            throw e;

        } catch (SocketTimeoutException e) {
            setStatus(Status.CONNECTION_TIMEOUT_ERROR);
            setError(e);

        } catch (Exception e) {
            setStatus(Status.ERROR);
            setError(e);

        } finally {
            IOUtils.closeQuietly(file);

            synchronized (this) {
                IOUtils.closeQuietly(stream);
            }
        }
    }

    private void setError(Exception e) {
        error = e;
    }

    public Exception getError() {
        return error;
    }

    public boolean isCompleted() {
        return status == Status.COMPLETE;
    }
}