All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.azure.sdk.iot.device.fileupload.FileUpload Maven / Gradle / Ivy

There is a newer version: 2.5.0
Show newest version
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

package com.microsoft.azure.sdk.iot.device.fileupload;

import com.microsoft.azure.sdk.iot.device.CustomLogger;
import com.microsoft.azure.sdk.iot.device.DeviceClientConfig;
import com.microsoft.azure.sdk.iot.device.IotHubEventCallback;
import com.microsoft.azure.sdk.iot.device.IotHubStatusCode;
import com.microsoft.azure.sdk.iot.device.transport.https.HttpsTransportManager;

import java.io.IOException;
import java.io.InputStream;
import java.util.Queue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ScheduledExecutorService;

/**
 * Provide means to upload file in the Azure Storage using the IoTHub.
 */
public final class FileUpload
{
    private static final int MAX_UPLOAD_PARALLEL = 10;

    private HttpsTransportManager httpsTransportManager;
    private static CustomLogger logger;
    private ScheduledExecutorService taskScheduler;
    private FileUploadStatusCallBack fileUploadStatusCallBack;
    private static Queue fileUploadInProgressesSet;

    /**
     * CONSTRUCTOR
     *
     * @param config is the set of device client configurations.
     * @throws IllegalArgumentException if one of the parameters is null.
     * @throws IOException if cannot create the artifacts to control the file upload.
     */
    public FileUpload(DeviceClientConfig config) throws IllegalArgumentException, IOException
    {
        /* Codes_SRS_FILEUPLOAD_21_001: [If the provided `config` is null, the constructor shall throw IllegalArgumentException.] */
        if(config == null)
        {
            throw new IllegalArgumentException("config is null");
        }

        // File upload will directly use the HttpsTransportManager, avoiding
        //  all extra async controls.
        // We can do that because File upload have its own async mechanism.
        /* Codes_SRS_FILEUPLOAD_21_002: [The constructor shall create a new instance of `HttpsTransportManager` with the provided `config`.] */
        /* Codes_SRS_FILEUPLOAD_21_003: [If the constructor fail to create the new instance of the `HttpsTransportManager`, it shall throw IllegalArgumentException, threw by the HttpsTransportManager constructor.] */
        this.httpsTransportManager = new HttpsTransportManager(config);

        try
        {
            /* Codes_SRS_FILEUPLOAD_21_012: [The constructor shall create an pool of 10 threads to execute the uploads in parallel.] */
            taskScheduler = Executors.newScheduledThreadPool(MAX_UPLOAD_PARALLEL);
        }
        catch (IllegalArgumentException | NullPointerException e)
        {
            /* Codes_SRS_FILEUPLOAD_21_015: [If create the executor failed, the constructor shall throws IOException.] */
            throw new IOException("Cannot create a pool of threads to manager uploads: " + e);
        }
        /* Codes_SRS_FILEUPLOAD_21_013: [The constructor shall create a list `fileUploadInProgressesSet` to control the pending uploads.] */
        fileUploadInProgressesSet = new LinkedBlockingDeque();
        /* Codes_SRS_FILEUPLOAD_21_014: [The constructor shall create an Event callback `fileUploadStatusCallBack` to receive the upload status.] */
        fileUploadStatusCallBack = new FileUploadStatusCallBack();

        logger = new CustomLogger(this.getClass());
        logger.LogInfo("FileUpload object is created successfully, method name is %s ", logger.getMethodName());
    }

    /**
     * Upload the file to container, which was associated to the iothub.
     * This function will start the upload process, and back the execution
     * to the caller. The upload process will be executed in background.
     * When it is completed, the background thread will trigger the
     * callback with the upload status.
     *
     * @param blobName is the name of the file in the container.
     * @param inputStream is the input stream.
     * @param streamLength is the stream length.
     * @param statusCallback is the callback to notify that the upload is completed (with status).
     * @param statusCallbackContext is the context of the callback, allowing multiple uploads in parallel.
     * @throws IllegalArgumentException if one of the parameters is invalid.
     *              blobName is {@code null} or empty,
     *              inputStream is {@code null} or not available,
     *              streamLength is negative,
     *              statusCallback is {@code null}
     * @throws IOException if an I/O error occurs in the inputStream.
     */
    public synchronized void uploadToBlobAsync(
            String blobName, InputStream inputStream, long streamLength,
            IotHubEventCallback statusCallback, Object statusCallbackContext)
            throws IllegalArgumentException, IOException
    {
        /* Codes_SRS_FILEUPLOAD_21_005: [If the `blobName` is null or empty, the uploadToBlobAsync shall throw IllegalArgumentException.] */
        if((blobName == null) || blobName.isEmpty())
        {
            throw new IllegalArgumentException("blobName is null or empty");
        }

        /* Codes_SRS_FILEUPLOAD_21_006: [If the `inputStream` is null or not available, the uploadToBlobAsync shall throw IllegalArgumentException.] */
        /* Codes_SRS_FILEUPLOAD_21_011: [If the `inputStream` failed to do I/O, the uploadToBlobAsync shall throw IOException, threw by the InputStream class.] */
        if((inputStream == null))
        {
            throw new IllegalArgumentException("inputStream is null");
        }

        /* Codes_SRS_FILEUPLOAD_21_007: [If the `streamLength` is negative, the uploadToBlobAsync shall throw IllegalArgumentException.] */
        if(streamLength < 0)
        {
            throw new IllegalArgumentException("streamLength is negative");
        }

        /* Codes_SRS_FILEUPLOAD_21_008: [If the `userCallback` is null, the uploadToBlobAsync shall throw IllegalArgumentException.] */
        if(statusCallback == null)
        {
            throw new IllegalArgumentException("statusCallback is null");
        }

        /* Codes_SRS_FILEUPLOAD_21_016: [The uploadToBlobAsync shall create a `FileUploadInProgress` to store the fileUpload context.] */
        FileUploadInProgress newUpload = new FileUploadInProgress(statusCallback, statusCallbackContext);
        fileUploadInProgressesSet.add(newUpload);

        /* Codes_SRS_FILEUPLOAD_21_004: [The uploadToBlobAsync shall asynchronously upload the InputStream `inputStream` to the blob in `blobName`.] */
        /* Codes_SRS_FILEUPLOAD_21_009: [The uploadToBlobAsync shall create a `FileUploadTask` to control this file upload.] */
        FileUploadTask fileUploadTask = new FileUploadTask(blobName, inputStream, streamLength, httpsTransportManager, fileUploadStatusCallBack, newUpload);

        /* Codes_SRS_FILEUPLOAD_21_010: [The uploadToBlobAsync shall schedule the task `FileUploadTask` to immediately start.] */
        newUpload.setTask(taskScheduler.submit(fileUploadTask));
    }

    private final class FileUploadStatusCallBack implements IotHubEventCallback
    {
        @Override
        public synchronized void execute(IotHubStatusCode status, Object context)
        {
            /* Codes_SRS_FILEUPLOAD_21_019: [The FileUploadStatusCallBack shall implements the `IotHubEventCallback` as result of the FileUploadTask.] */
            if(context instanceof FileUploadInProgress)
            {
                FileUploadInProgress uploadInProgress = (FileUploadInProgress) context;
                /* Codes_SRS_FILEUPLOAD_21_020: [The FileUploadStatusCallBack shall call the `statusCallback` reporting the received status.] */
                uploadInProgress.triggerCallback(status);
                /* Codes_SRS_FILEUPLOAD_21_021: [The FileUploadStatusCallBack shall delete the `FileUploadInProgress` that store this file upload context.] */
                try
                {
                    fileUploadInProgressesSet.remove(context);
                }
                catch (ClassCastException | NullPointerException | UnsupportedOperationException e)
                {
                    /* Codes_SRS_FILEUPLOAD_21_023: [If the FileUploadStatusCallBack failed to delete the `FileUploadInProgress`, it shall log a error.] */
                    logger.LogError("FileUploadStatusCallBack received callback for unknown FileUpload");
                }
            }
            else
            {
                /* Codes_SRS_FILEUPLOAD_21_022: [If the received context is not type of `FileUploadInProgress`, the FileUploadStatusCallBack shall log a error and ignore the message.] */
                logger.LogError("FileUploadStatusCallBack received callback for unknown FileUpload");
            }
        }
    }

    /**
     * Close the file upload cancelling all existing uploads and shutting down the thread pool.
     *
     * @throws IOException if an I/O error occurs in the inputStream.
     */
    public void closeNow() throws IOException
    {
        /* Codes_SRS_FILEUPLOAD_21_017: [The closeNow shall shutdown the thread pool by calling `shutdownNow`.] */
        taskScheduler.shutdownNow();

        /* Codes_SRS_FILEUPLOAD_21_018: [If there is pending file uploads, the closeNow shall cancel the upload, and call the `statusCallback` reporting ERROR.] */
        for (FileUploadInProgress uploadInProgress : fileUploadInProgressesSet)
        {
            if(uploadInProgress.isCancelled())
            {
                uploadInProgress.triggerCallback(IotHubStatusCode.ERROR);
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy