com.amazonaws.mobileconnectors.s3.transferutility.DownloadTask.java Source code

Java tutorial

Introduction

Here is the source code for com.amazonaws.mobileconnectors.s3.transferutility.DownloadTask.java

Source

/**
 * Copyright 2015-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

package com.amazonaws.mobileconnectors.s3.transferutility;

import com.amazonaws.AmazonClientException;
import com.amazonaws.mobileconnectors.s3.transferutility.TransferService.NetworkInfoReceiver;
import com.amazonaws.retry.RetryUtils;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.S3Object;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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.SocketTimeoutException;
import java.util.concurrent.Callable;

/**
 * Performs download operation and returns a Boolean value indicating whether
 * the file has been downloaded successfully.
 */
class DownloadTask implements Callable<Boolean> {

    private static final Log LOGGER = LogFactory.getLog(DownloadTask.class);
    private static final int SIXTEEN_KB = 1024 * 16;

    private final AmazonS3 s3;
    private final TransferRecord download;
    private final TransferStatusUpdater updater;
    private final NetworkInfoReceiver networkInfo;

    /**
     * Constructs a DownloadTask with the given download info and S3 client.
     *
     * @param download A TransferRecord object storing all the information of
     *            the download
     * @param s3 Low-level S3 client
     * @param updater status updater
     * @param networkInfo network info
     */
    public DownloadTask(TransferRecord download, AmazonS3 s3, TransferStatusUpdater updater,
            NetworkInfoReceiver networkInfo) {
        this.download = download;
        this.s3 = s3;
        this.updater = updater;
        this.networkInfo = networkInfo;
    }

    /**
     * Runs download task and returns whether successfully downloaded.
     */
    @Override
    public Boolean call() throws Exception {
        if (!networkInfo.isNetworkConnected()) {
            updater.updateState(download.id, TransferState.WAITING_FOR_NETWORK);
            return false;
        }
        updater.updateState(download.id, TransferState.IN_PROGRESS);

        final GetObjectRequest getObjectRequest = new GetObjectRequest(download.bucketName, download.key);
        TransferUtility.appendTransferServiceUserAgentString(getObjectRequest);
        final File file = new File(download.file);
        final long bytesCurrent = file.length();
        if (bytesCurrent > 0) {
            LOGGER.debug(String.format("Resume transfer %d from %d bytes", download.id, bytesCurrent));
            /*
             * Setting the last byte position to ?1 means downloading the object
             * from bytesCurrent to the end.
             */
            getObjectRequest.setRange(bytesCurrent, -1);
        }
        getObjectRequest.setGeneralProgressListener(updater.newProgressListener(download.id));

        try {
            final S3Object object = s3.getObject(getObjectRequest);
            if (object == null) {
                updater.throwError(download.id, new IllegalStateException("AmazonS3.getObject returns null"));
                updater.updateState(download.id, TransferState.FAILED);
                return false;
            }

            final long bytesTotal = object.getObjectMetadata().getInstanceLength();
            updater.updateProgress(download.id, bytesCurrent, bytesTotal);
            saveToFile(object.getObjectContent(), file);
            updater.updateProgress(download.id, bytesTotal, bytesTotal);
            updater.updateState(download.id, TransferState.COMPLETED);
            return true;
        } catch (final Exception e) {
            if (RetryUtils.isInterrupted(e)) {
                /*
                 * thread is interrupted by user. don't update the state as it's
                 * set by caller who interrupted
                 */
                LOGGER.debug("Transfer " + download.id + " is interrupted by user");
            } else if (e.getCause() != null
                    && (e.getCause() instanceof IOException || e.getCause() instanceof AmazonClientException)
                    && !networkInfo.isNetworkConnected()) {
                LOGGER.debug("Transfer " + download.id + " waits for network");
                updater.updateState(download.id, TransferState.WAITING_FOR_NETWORK);
            } else {
                LOGGER.debug("Failed to download: " + download.id + " due to " + e.getMessage());
                updater.throwError(download.id, e);
                updater.updateState(download.id, TransferState.FAILED);
            }
        }
        return false;
    }

    /**
     * Writes stream data into a file.
     *
     * @param is input stream
     * @param file file to be written
     */
    private void saveToFile(InputStream is, File file) {
        // attempt to create the parent if it doesn't exist
        final File parentDirectory = file.getParentFile();
        if (parentDirectory != null && !parentDirectory.exists()) {
            parentDirectory.mkdirs();
        }

        final boolean append = file.length() > 0;
        OutputStream os = null;
        try {
            os = new BufferedOutputStream(new FileOutputStream(file, append));
            final byte[] buffer = new byte[SIXTEEN_KB];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (final SocketTimeoutException socketTimeoutException) {
            String errorString = "SocketTimeoutException: Unable to retrieve contents over network: "
                    + socketTimeoutException.getMessage();
            LOGGER.error(errorString);
            throw new AmazonClientException(errorString, socketTimeoutException);
        } catch (final IOException e) {
            throw new AmazonClientException("Unable to store object contents to disk: " + e.getMessage(), e);
        } finally {
            try {
                if (os != null) {
                    os.close();
                }
            } catch (final IOException ioe) {
                LOGGER.warn("got exception", ioe);
            }
            try {
                is.close();
            } catch (final IOException ioe) {
                LOGGER.warn("got exception", ioe);
            }
        }
    }
}