de.fhg.iais.asc.xslt.binaries.download.Downloader.java Source code

Java tutorial

Introduction

Here is the source code for de.fhg.iais.asc.xslt.binaries.download.Downloader.java

Source

package de.fhg.iais.asc.xslt.binaries.download;

/******************************************************************************
 * Copyright 2011 (c) Fraunhofer IAIS Netmedia  http://www.iais.fraunhofer.de *
 * ************************************************************************** *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
 * not use this file except in compliance with the License.                   *
 * You may obtain a copy of the License at                                    *
 * http://www.apache.org/licenses/LICENSE-2.0                                 *
 * Unless required by applicable law or agreed to in writing,                 *
 * software distributed under the License 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.                                             *
 ******************************************************************************/

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Charsets;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;

import de.fhg.iais.asc.commons.AscConfiguration;
import de.fhg.iais.asc.commons.ParentDirectoryUtils;
import de.fhg.iais.asc.commons.exceptions.AscTechnicalErrorException;
import de.fhg.iais.asc.transformer.TransformationContext;
import de.fhg.iais.commons.stream.AtomicFileOutputStream;

public class Downloader {
    private static final Logger LOG = LoggerFactory.getLogger(Downloader.class);

    private static final List<String> ALLOWED_SCHEMES = Arrays.asList("http", "https");
    private static final Pattern START_SLASHES_PATTERN = Pattern.compile("^\\/+");

    private static final long BROKEN_IMAGE_THRESHOLD = 5; // bytes
    private static int MAX_DOWNLOAD_RETRIES;
    private static int DOWNLOAD_BINARY_TIMEOUT;
    private static AscConfiguration config;

    private static Client client;

    public static Downloader forContext(TransformationContext transContext) {
        config = transContext.getConfig();
        MAX_DOWNLOAD_RETRIES = Downloader.config.get(AscConfiguration.DOWNLOAD_BINARIES_RETRIES, 5);
        DOWNLOAD_BINARY_TIMEOUT = Downloader.config.get(AscConfiguration.DOWNLOAD_BINARIES_TIMEOUT, 60000);
        Downloader result;

        synchronized (transContext) {
            result = transContext.getObject(Downloader.class);
            if (result == null) {
                result = new Downloader();
                transContext.putObject(Downloader.class, result);
            }
        }

        return result;
    }

    public static void init(Client httpClient) {
        client = httpClient;
    }

    private Downloader() {
    }

    public static URI createDownloadableURI(String path) {
        URI uri;
        try {
            try {
                uri = new URI(path);
            } catch (URISyntaxException e) {
                String decoded = URLDecoder.decode(path, Charsets.UTF_8.name());
                URL url = new URL(decoded);
                uri = createURI(url);
            }
        } catch (Exception e) {
            // give up
            return null;
        }

        return isDownloadableURI(uri) ? uri : null;
    }

    private static URI createURI(URL url) throws URISyntaxException {
        return new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(),
                url.getQuery(), url.getRef());
    }

    private static boolean isDownloadableURI(URI uri) {
        String scheme = uri.getScheme();
        return ALLOWED_SCHEMES.contains(scheme);
    }

    public static String createPathFromURI(final URI uri) {
        String rawPath = uri.getRawPath();
        return START_SLASHES_PATTERN.matcher(rawPath).replaceFirst("");
    }

    public void downloadBinary(URI uri, File target) {
        InputStream contentStream = null;
        try {
            ClientResponse response = Downloader.client.resource(uri).get(ClientResponse.class);
            int retries = 0;
            while (!checkStatusCode(response) && retries < MAX_DOWNLOAD_RETRIES) {
                ++retries;
                try {
                    Thread.sleep(DOWNLOAD_BINARY_TIMEOUT);
                } catch (InterruptedException e) {
                    LOG.debug("Sleeping thread interrupted!");
                }

                response = Downloader.client.resource(uri).get(ClientResponse.class);
            }

            if (!checkStatusCode(response)) {
                String msg = "Downloading of \"" + uri.toASCIIString() + "\" failed with status "
                        + response.getStatus();
                throw new AscTechnicalErrorException(msg);
            }

            contentStream = response.getEntityInputStream();

            if (contentStream == null) {
                throw new IOException("No response entity");
            }

            writeStreamToFile(contentStream, target);

            if (!checkContentLength(target)) {
                String msg = "Content of response \"" + uri.toASCIIString() + "\" has a length of zero!";
                throw new AscTechnicalErrorException(msg);
            }
        } catch (Exception e) {
            if (target.exists() && !target.delete()) {
                LOG.warn(String.format("Could not delete invalid file \"%s\"", target.getAbsolutePath()));
            }

            String msg = "Exception downloading \"" + uri.toASCIIString() + "\"";
            LOG.error(msg, e);
            throw AscTechnicalErrorException.wrap(msg, e);
        } finally {
            IOUtils.closeQuietly(contentStream);
        }
    }

    private boolean checkContentLength(File target) {
        boolean hasValidContent = false;

        try {
            if (target.length() > BROKEN_IMAGE_THRESHOLD) {
                hasValidContent = true;
            }
        } catch (IllegalStateException e) {
            ;
        } catch (NullPointerException e) {
            ;
        }

        return hasValidContent;
    }

    private boolean checkStatusCode(final ClientResponse response) {
        int statusCode = response.getStatus();
        return (statusCode / 100) == 2;
    }

    private static void writeStreamToFile(InputStream contentStream, File target) throws IOException {
        ParentDirectoryUtils.forceCreateParentDirectoryOf(target);

        AtomicFileOutputStream aos = null;
        try {
            aos = new AtomicFileOutputStream(target);

            IOUtils.copyLarge(contentStream, aos);
            aos.closeAsCompleted();
        } finally {
            IOUtils.closeQuietly(aos);
        }
    }

    public boolean existsBinary(URI uri) throws IOException {
        final ClientResponse response = Downloader.client.resource(uri).head();
        return checkStatusCode(response);
    }
}