org.bitrepository.protocol.http.HttpFileExchange.java Source code

Java tutorial

Introduction

Here is the source code for org.bitrepository.protocol.http.HttpFileExchange.java

Source

/*
 * #%L
 * Bitmagasin Protocol
 * 
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2010 The State and University Library, The Royal Library and The State Archives, Denmark
 * %%
 * This program 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 2.1 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 Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
 * #L%
 */
package org.bitrepository.protocol.http;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import org.bitrepository.common.ArgumentValidator;
import org.bitrepository.common.settings.Settings;
import org.bitrepository.common.utils.StreamUtils;
import org.bitrepository.protocol.CoordinationLayerException;
import org.bitrepository.protocol.FileExchange;
import org.bitrepository.settings.referencesettings.FileExchangeSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Simple interface for data transfer between an application and a HTTP server.
 */
public class HttpFileExchange implements FileExchange {
    /** The log. */
    private final Logger log = LoggerFactory.getLogger(getClass());

    /** The lower boundary for the error codes of the HTTP codes.*/
    private static final int HTTP_ERROR_CODE_BARRIER = 300;
    /** The settings for the file exchange.*/
    protected final Settings settings;

    /**
     * Initialise HTTP file exchange.
     * @param settings The settings regarding the file exchange through HTTP.
     */
    public HttpFileExchange(Settings settings) {
        this.settings = settings;
    }

    @Override
    public void uploadToServer(InputStream in, URL url) throws IOException {
        performUpload(in, url);
    }

    @Override
    public InputStream downloadFromServer(URL url) throws IOException {
        return retrieveStream(url);
    }

    @Override
    public URL uploadToServer(InputStream in, String filename) throws IOException {
        if (in == null || filename == null || filename.isEmpty()) {
            throw new IllegalArgumentException("InputStream in: " + in + ", String filename: " + filename);
        }
        URL url = getURL(filename);
        performUpload(in, url);
        return url;
    }

    @Override
    public URL uploadToServer(File dataFile) {
        if (dataFile == null) {
            throw new IllegalArgumentException("The datafile may not be null.");
        }
        if (!dataFile.isFile()) {
            throw new IllegalArgumentException("The datafile '" + dataFile.getPath() + "' is not a proper file.");
        }

        try {
            // generate the URL for the file.
            URL url = getURL(dataFile.getName());

            FileInputStream fis = null;
            try {
                fis = new FileInputStream(dataFile);
                performUpload(fis, url);
            } finally {
                if (fis != null) {
                    fis.close();
                }
            }
            return url;
        } catch (IOException e) {
            throw new CoordinationLayerException(
                    "Could not upload the file '" + dataFile.getAbsolutePath() + "' to the server.", e);
        }
    }

    @Override
    public void downloadFromServer(OutputStream out, URL url) throws IOException {
        performDownload(out, url);
    }

    @Override
    public void downloadFromServer(File outputFile, String fileAddress) {
        try {
            // retrieve the url and the outputstream for the file.
            URL url = new URL(fileAddress);
            FileOutputStream fos = new FileOutputStream(outputFile);

            // download the file.
            performDownload(fos, url);
        } catch (IOException e) {
            throw new CoordinationLayerException("Could not download data " + "from '" + fileAddress
                    + "' to the file '" + outputFile.getAbsolutePath() + "'.", e);
        }
    }

    /**
     * Retrieves the data from a given url and puts it onto a given 
     * outputstream. It has to be a 'HTTP' url, since the data is retrieved 
     * through a HTTP-request.
     * 
     * @param out The output stream to put the data.
     * @param url The url for where the data should be retrieved.
     * @throws IOException If any problems occurs during the retrieval of the 
     * data.
     */
    protected void performDownload(OutputStream out, URL url) throws IOException {
        if (out == null || url == null) {
            throw new IllegalArgumentException("OutputStream out: '" + out + "', URL: '" + url + "'");
        }
        InputStream is = retrieveStream(url);
        StreamUtils.copyInputStreamToOutputStream(is, out);
    }

    /**
     * Retrieves the Input stream for a given URL.
     * @param url The URL to retrieve.
     * @return The InputStream to the given URL.
     * @throws IOException If any problems occurs during the retrieval.
     */
    protected InputStream retrieveStream(URL url) throws IOException {
        HttpURLConnection conn = getConnection(url);
        conn.setDoInput(true);
        conn.setRequestMethod("GET");
        return conn.getInputStream();
    }

    /**
     * Method for putting data on the HTTP-server of a given url.
     * 
     * TODO perhaps make it synchronized around the URL, to prevent data from 
     * trying to uploaded several times to the same location simultaneously. 
     * 
     * @param in The data to put into the url.
     * @param url The place to put the data.
     * @throws IOException If a problem with the connection occurs during the 
     * transaction. Also if the response code is 300 or above, which indicates
     * that the transaction has not been successful.
     */
    private void performUpload(InputStream in, URL url) throws IOException {
        HttpClient httpClient = null;
        try {
            httpClient = getHttpClient();
            HttpPut httpPut = new HttpPut(url.toExternalForm());
            InputStreamEntity reqEntity = new InputStreamEntity(in, -1);
            reqEntity.setChunked(true);
            httpPut.setEntity(reqEntity);
            HttpResponse response = httpClient.execute(httpPut);

            // HTTP code >= 300 means error!
            if (response.getStatusLine().getStatusCode() >= HTTP_ERROR_CODE_BARRIER) {
                throw new IOException("Could not upload file to URL '" + url.toExternalForm()
                        + "'. got status code '" + response.getStatusLine() + "'");
            }
            log.debug("Uploaded datastream to url '" + url.toString() + "' and " + "received the response line '"
                    + response.getStatusLine() + "'.");
        } finally {
            if (httpClient != null) {
                httpClient.getConnectionManager().shutdown();
            }
        }
    }

    @Override
    public URL getURL(String filename) throws MalformedURLException {
        ArgumentValidator.checkNotNullOrEmpty(filename, "String fileName");
        ArgumentValidator.checkNotNull(settings.getReferenceSettings().getFileExchangeSettings(),
                "The ReferenceSettings are missing the settings for the file exchange.");
        FileExchangeSettings feSettings = settings.getReferenceSettings().getFileExchangeSettings();
        try {
            String urlEncodedFilename = URLEncoder.encode(filename, "UTF-8");

            // create the URL based on hardcoded values (change to using settings!)
            URL res = new URL(feSettings.getProtocolType().value(), feSettings.getServerName(),
                    feSettings.getPort().intValue(), feSettings.getPath() + "/" + urlEncodedFilename);
            return res;
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException("Cannot create the URL.", e);
        }
    }

    /**
     * Method for opening a HTTP connection to the given URL.
     * 
     * @param url The URL to open the connection to.
     * @return The HTTP connection to the given URL.
     */
    protected HttpURLConnection getConnection(URL url) {
        try {
            return (HttpURLConnection) url.openConnection();
        } catch (IOException e) {
            throw new CoordinationLayerException("Could not open the connection to the url '" + url + "'", e);
        }
    }

    /**
     * Retrieves the HttpClient with the correct setup.
     * For HTTPS this should be overridden with SSL context.
     * @return The HttpClient for this FileExchange.
     */
    protected HttpClient getHttpClient() {
        return new DefaultHttpClient();
    }

    @Override
    public void deleteFromServer(URL url) throws IOException, URISyntaxException {
        HttpClient httpClient = null;
        try {
            httpClient = getHttpClient();
            HttpDelete deleteOperation = new HttpDelete(url.toURI());
            httpClient.execute(deleteOperation);
        } finally {
            if (httpClient != null) {
                httpClient.getConnectionManager().shutdown();
            }
        }
    }
}