ddf.catalog.resource.download.ReliableResourceDownloadManager.java Source code

Java tutorial

Introduction

Here is the source code for ddf.catalog.resource.download.ReliableResourceDownloadManager.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p>
 * 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
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.catalog.resource.download;

import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Stopwatch;

import ddf.catalog.cache.impl.ResourceCache;
import ddf.catalog.data.Metacard;
import ddf.catalog.event.retrievestatus.DownloadStatusInfo;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventListener;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventPublisher;
import ddf.catalog.event.retrievestatus.DownloadsStatusEventPublisher.ProductRetrievalStatus;
import ddf.catalog.operation.ResourceRequest;
import ddf.catalog.operation.ResourceResponse;
import ddf.catalog.operation.impl.ResourceResponseImpl;
import ddf.catalog.resource.ResourceNotFoundException;
import ddf.catalog.resource.ResourceNotSupportedException;
import ddf.catalog.resourceretriever.ResourceRetriever;

/**
 * The manager for downloading a resource, including retrying the download if problems are
 * encountered, and optionally caching the resource as it is streamed to the client.
 */
public class ReliableResourceDownloadManager {

    static final int ONE_SECOND_IN_MS = 1000;

    private static final Logger LOGGER = LoggerFactory.getLogger(ReliableResourceDownloadManager.class);

    private DownloadsStatusEventPublisher eventPublisher;

    private ResourceResponse resourceResponse;

    private String downloadIdentifier;

    private DownloadStatusInfo downloadStatusInfo;

    private ExecutorService executor;

    private ReliableResourceDownloaderConfig downloaderConfig = new ReliableResourceDownloaderConfig();

    /**
     * @param resourceCache
     *            reference to the @ResourceCache to cache the resource in
     * @param eventPublisher
     *            reference to the publisher of status events as the download progresses
     * @param eventListener
     *            reference to the {@link DownloadsStatusEventListener}
     * @param downloadStatusInfo
     *            reference to the {@link DownloadStatusInfo}
     */
    public ReliableResourceDownloadManager(ResourceCache resourceCache,
            DownloadsStatusEventPublisher eventPublisher, DownloadsStatusEventListener eventListener,
            DownloadStatusInfo downloadStatusInfo, ExecutorService executor) {
        this.downloaderConfig.setResourceCache(resourceCache);
        this.eventPublisher = eventPublisher;
        this.downloaderConfig.setEventPublisher(this.eventPublisher);
        this.downloaderConfig.setEventListener(eventListener);
        this.downloadStatusInfo = downloadStatusInfo;
        this.executor = executor;
        this.downloadIdentifier = UUID.randomUUID().toString();
    }

    public void init() {

    }

    public void cleanUp() {
        executor.shutdown();
        try {
            executor.awaitTermination(ONE_SECOND_IN_MS, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }
    }

    /**
     * @param resourceRequest
     *            the original @ResourceRequest to retrieve the resource
     * @param metacard
     *            the @Metacard associated with the resource being downloaded
     * @param retriever
     *            the @ResourceRetriever to be used to get the resource
     * @return the modified @ResourceResponse with the @ReliableResourceInputStream that the client
     *         should read from
     * @throws DownloadException
     */
    public ResourceResponse download(ResourceRequest resourceRequest, Metacard metacard,
            ResourceRetriever retriever) throws DownloadException {

        if (metacard == null) {
            throw new DownloadException("Cannot download resource if metacard is null");
        } else if (StringUtils.isBlank(metacard.getId())) {
            throw new DownloadException("Metacard must have unique id.");
        } else if (retriever == null) {
            throw new DownloadException("Cannot download resource if retriever is null");
        } else if (resourceRequest == null) {
            throw new DownloadException("Cannot download resource if request is null");
        }

        try {
            resourceResponse = retriever.retrieveResource();
        } catch (ResourceNotFoundException | ResourceNotSupportedException | IOException e) {
            throw new DownloadException("Cannot download resource", e);
        }

        resourceResponse.getProperties().put(Metacard.ID, metacard.getId());
        // Sources do not create ResourceResponses with the original ResourceRequest, hence
        // it is added here because it will be needed for caching
        resourceResponse = new ResourceResponseImpl(resourceRequest, resourceResponse.getProperties(),
                resourceResponse.getResource());

        // TODO - this should be before retrieveResource() but eventPublisher requires a
        // resourceResponse and that resource response must have a resource request in it (to get
        // USER property)
        eventPublisher.postRetrievalStatus(resourceResponse, ProductRetrievalStatus.STARTED, metacard, null, 0L,
                downloadIdentifier);

        AtomicBoolean downloadStarted = new AtomicBoolean(Boolean.FALSE);
        ReliableResourceDownloader downloader = new ReliableResourceDownloader(downloaderConfig, downloadStarted,
                downloadIdentifier, resourceResponse, retriever);
        resourceResponse = downloader.setupDownload(metacard, downloadStatusInfo);

        // Start download in separate thread so can return ResourceResponse with
        // ReliableResourceInputStream available for client to start reading from
        executor.submit(downloader);

        // Wait for download to get started before returning control to client
        Stopwatch stopwatch = Stopwatch.createStarted();
        while (!downloadStarted.get()) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
            long elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
            if (elapsedTime > ONE_SECOND_IN_MS) {
                LOGGER.debug("downloadStarted still FALSE - elapsedTime = {}", elapsedTime);
                break;
            }
        }
        LOGGER.debug("elapsedTime = {}", stopwatch.elapsed(TimeUnit.MILLISECONDS));
        stopwatch.stop();

        return resourceResponse;
    }

    public void setMaxRetryAttempts(int maxRetryAttempts) {
        downloaderConfig.setMaxRetryAttempts(maxRetryAttempts);
    }

    public void setDelayBetweenAttempts(int delayBetweenAttempts) {
        downloaderConfig.setDelayBetweenAttemptsMS(delayBetweenAttempts * ONE_SECOND_IN_MS);
    }

    public void setMonitorPeriod(long monitorPeriod) {
        downloaderConfig.setMonitorPeriodMS(monitorPeriod * ONE_SECOND_IN_MS);
    }

    public void setMonitorInitialDelay(int monitorInitialDelay) {
        this.downloaderConfig.setMonitorInitialDelayMS(monitorInitialDelay * ONE_SECOND_IN_MS);
    }

    public void setCacheEnabled(boolean cacheEnabled) {
        downloaderConfig.setCacheEnabled(cacheEnabled);
    }

    public void setCacheWhenCanceled(boolean cacheWhenCanceled) {
        downloaderConfig.setCacheWhenCanceled(cacheWhenCanceled);
    }

    public void setChunkSize(int chunkSize) {
        downloaderConfig.setChunkSize(chunkSize);
    }
}