view src/main/java/de/intevation/artifacts/httpclient/http/HttpClientImpl.java @ 17:5cb8513b9e78

Improved the stream handling. http-client/trunk@1947 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Thu, 19 May 2011 09:01:35 +0000
parents 5b62267a1c3c
children 79a5a2455d6d
line wrap: on
line source
/*
 * Copyright (c) 2010 by Intevation GmbH
 *
 * This program is free software under the LGPL (>=v2.1)
 * Read the file LGPL.txt coming with the software for details
 * or visit http://www.gnu.org/licenses/ if it does not exist.
 */
package de.intevation.artifacts.httpclient.http;

import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import org.restlet.Client;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ClientInfo;
import org.restlet.data.Language;
import org.restlet.data.MediaType;
import org.restlet.data.Method;
import org.restlet.data.Preference;
import org.restlet.data.Protocol;
import org.restlet.data.Status;
import org.restlet.ext.xml.DomRepresentation;
import org.restlet.representation.Representation;

import org.w3c.dom.Document;

import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
import de.intevation.artifacts.httpclient.http.response.ResponseHandler;
import de.intevation.artifacts.httpclient.http.response.StreamResponseHandler;
import de.intevation.artifacts.httpclient.objects.Artifact;
import de.intevation.artifacts.httpclient.objects.ArtifactFactory;
import de.intevation.artifacts.httpclient.utils.ArtifactProtocolUtils;
import de.intevation.artifacts.httpclient.utils.ArtifactCreator;


/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class HttpClientImpl implements HttpClient {

    private static final Logger logger = Logger.getLogger(HttpClient.class);

    /** The URL part of the resource to list the existing users of the server.*/
    public static final String PATH_LIST_USERS = "/list-users";

    /** The URL part of the resource to list the Collections owned by a specific
     * user.*/
    public static final String PATH_USER_COLLECTIONS = "/list-collections";

    /** The URL part of the resource to call a specific service.*/
    public static final String PATH_SERVICE = "/service";

    /** The URL path of the resource to create new artifact collections.*/
    public static final String PATH_CREATE_COLLECTION = "/create-collection";

    /** The URL path of the resource to work with an artifact collections.*/
    public static final String PATH_ACTION_COLLECTION = "/collection";

    /** The URL path of the resource to work with an artifact collections.*/
    public static final String PATH_OUT_COLLECTION = "/collection";

    private String serverUrl;

    private String localeString;


    public HttpClientImpl(String serverUrl) {
        this.serverUrl = serverUrl;
    }


    /**
     * This constructor might be used to modify the request's locale manually.
     * E.g. the localization should not be based on the configured browser
     * locale, but site specific configuration - than you are able to set the
     * locale in this constructor.
     *
     * @param serverUrl The url that is used for the request.
     * @param localeString The string representation of the desired locale.
     */
    public HttpClientImpl(String serverUrl, String localeString) {
        this(serverUrl);

        this.localeString = localeString;
    }


    @Override
    public ArtifactFactory[] getArtifactFactories()
    throws ConnectionException
    {
        ResponseHandler handler = new DocumentResponseHandler();

        try {
            String    url   = serverUrl + "/factories";
            Document result = (Document) handler.handle(doGet(url));

            return ArtifactProtocolUtils.extractArtifactFactories(result);
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed. No Factories recieved.");
        }
    }


    /**
     * This method creates a new artifact in the artifact server and returns
     * this artifact. The new artifact is created using <i>creator</i>. If no
     * {@link ArtifactCreator} is given (null), an {@link Artifact} is returned.
     *
     * @param doc The CREATE document.
     * @param creator The {@link ArtifactCreator} that is used to extract the
     * new artifact from response document of the server.
     *
     * @return the new artifact.
     */
    @Override
    public Object create(Document doc, ArtifactCreator creator)
    throws ConnectionException
    {
        ResponseHandler handler = new DocumentResponseHandler();

        try {
            String   url    = serverUrl + "/create";
            Document result = (Document) handler.handle(doPost(url, doc));

            return creator == null
                ? ArtifactProtocolUtils.extractArtifact(result)
                : creator.create(result);
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed. No Artifact created.");
        }
    }


    @Override
    public Object describe(
        Artifact        artifact,
        Document        doc,
        ResponseHandler handler)
    throws ConnectionException
    {
        try {
            String   url    = serverUrl + "/artifact/" + artifact.getUuid();
            return handler.handle(doPost(url, doc));
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed: " + ioe.getMessage());
        }
    }


    @Override
    public Object feed(Artifact artifact, Document doc, ResponseHandler handler)
    throws ConnectionException
    {
        try {
            String   url    = serverUrl + "/artifact/" + artifact.getUuid();
            Document result = (Document) handler.handle(doPost(url, doc));

            return result;
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed: " + ioe.getMessage());
        }
    }


    @Override
    public Object advance(Artifact artifact, Document doc, ResponseHandler handler)
    throws ConnectionException
    {
        try {
            String   url    = serverUrl + "/artifact/" + artifact.getUuid();
            Document result = (Document) handler.handle(doPost(url, doc));

            return result;
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed: " + ioe.getMessage());
        }
    }


    @Override
    public void out(
        Artifact     artifact,
        Document     doc,
        String       target,
        OutputStream out)
    throws ConnectionException
    {
        try {
            String url =
                serverUrl
                + "/artifact/"
                + artifact.getUuid()
                + "/" + target;

            ResponseHandler handler = new StreamResponseHandler();

            InputStream stream = (InputStream) handler.handle(doPost(url, doc));
            try {
                byte[] b = new byte[4096];
                int i;
                while ((i = stream.read(b)) >= 0) {
                    out.write(b, 0, i);
                }
            }
            finally {
                stream.close();
            }
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed: " + ioe.getMessage());
        }
    }


    //==============================
    // HTTP specific methods
    //==============================

    private Response doPost(String url, Document body) throws IOException {
        logger.info("Start HTTP-POST request to: "+ url);

        Client client   = new Client(Protocol.HTTP);
        Request request = prepareRequest(Method.POST, url);

        Representation representation = new DomRepresentation(
            MediaType.APPLICATION_XML,
            body);

        request.setEntity(representation);
        Response response = client.handle(request);

        Status status = response.getStatus();
        if (status.getCode() != 200) {
            logger.error("Response status: " + status.getCode());
            throw new IOException(status.getDescription());
        }

        return response;
    }


    private Response doGet(String url) throws IOException {
        logger.info("Start HTTP-POST request to: "+ url);

        Client client   = new Client(Protocol.HTTP);
        Request request = prepareRequest(Method.GET, url);

        Response response = client.handle(request);

        Status status = response.getStatus();
        if (status.getCode() != 200) {
            logger.error("Response status: " + status.getCode());
            throw new IOException(status.getDescription());
        }

        return response;
    }


    /**
     * This method prepares the request object.
     *
     * @param method The HTTP method (GET,POST).
     * @param url The URL used for the request.
     *
     * @return the request object.
     */
    private Request prepareRequest(Method method, String url) {
        Request request = new Request(method, url);

        ClientInfo info = request.getClientInfo();

        setLocale(info);

        request.setClientInfo(info);

        return request;
    }


    /**
     * This method is called to set the request's locale.
     *
     * @param info The ClientInfo that is used to provide request information.
     */
    private void setLocale(ClientInfo info) {
        if (localeString == null) {
            return;
        }

        List<Preference<Language>> accepted =
            new ArrayList<Preference<Language>>();

        Language lang = Language.valueOf(localeString);

        if (lang != null) {
            logger.info("Set locale of the request object: " + lang.toString());

            Preference<Language> pref = new Preference<Language>();
            pref.setMetadata(lang);
            accepted.add(pref);

            info.setAcceptedLanguages(accepted);
        }
    }


    //==============================
    // Collection API
    //==============================

    /**
     * This method triggers the artifact servers resource to create a new
     * artifact collection.
     *
     * @param create The CREATE document for the collection.
     * @param ownerId The uuid of the creator.
     * @param handler The handler that is used to create the result object.
     *
     * @return a result object created by <i>handler</i>.
     */
    public Object createCollection(
        Document        create,
        String          ownerId,
        ResponseHandler handler)
    throws ConnectionException
    {
        String url = serverUrl + PATH_CREATE_COLLECTION + "/" + ownerId;

        try {
            return handler.handle(doPost(url, create));
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }


    /**
     * This method might be used to trigger a collection specific action. The
     * action that is executed depends on the document <i>actionDoc</i>.
     *
     * @param actionDoc The document that describes the action to be executed.
     * @param uuid      The uuid of the collection.
     * @param handler   The handler that is used to create the result object.
     *
     * @return a result object created by <i>handler</i>.
     */
    public Object doCollectionAction(
        Document        actionDoc,
        String          uuid,
        ResponseHandler handler)
    throws ConnectionException
    {
        String url = serverUrl + PATH_ACTION_COLLECTION + "/" + uuid;

        try {
            return handler.handle(doPost(url, actionDoc));
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }


    /**
     * This method triggers the out() operation of a Collection. The result of
     * this operation is written to <i>out</i> directly - there is no return
     * value.
     *
     * @param doc The request document for the out() operation.
     * @param uuid The identifier of the Collection.
     * @param type The name of the output type.
     * @param out The OutputStream.
     */
    public void collectionOut(
        Document     doc,
        String       uuid,
        String       type,
        OutputStream out)
    throws ConnectionException
    {
        try {
            InputStream stream = collectionOut(doc, uuid, type);

            byte[] b = new byte[4096];
            try {
                int i;
                while ((i = stream.read(b)) >= 0) {
                    out.write(b, 0, i);
                }
            }
            finally {
                stream.close();
            }
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }


    /**
     * This method triggers the out() operation of a Collection. The result of
     * this operation is returned as an InputStream.
     *
     * @param doc The request document for the out() operation.
     * @param uuid The identifier of the Collection.
     * @param type The name of the output type.
     *
     * @return an InputStream.
     */
    public InputStream collectionOut(
        Document    doc,
        String      uuid,
        String      type)
    throws ConnectionException
    {
        String url = serverUrl + PATH_OUT_COLLECTION + "/" + uuid + "/" + type;

        ResponseHandler handler = new StreamResponseHandler();

        try {
            return (InputStream) handler.handle(doPost(url, doc));
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }


    /*******************************
     * Service API
     *******************************/

     public Document callService(String url, String service, Document input)
     throws ConnectionException
     {
        logger.info("Start service call to '" + service + "'");

        DocumentResponseHandler handler = new DocumentResponseHandler();

        try {
            String serverUrl = url + PATH_SERVICE + "/" + service;
            return (Document) handler.handle(doPost(serverUrl, input));
        }
        catch (IOException ioe) {
            throw new ConnectionException(
                "Connection to server failed: " + ioe.getMessage());
        }
     }


    /*******************************
     * Users API
     *******************************/

    public Document listUsers()
    throws ConnectionException
    {
        ResponseHandler handler = new DocumentResponseHandler();
        String    url           = serverUrl + PATH_LIST_USERS;

        try {
            return (Document) handler.handle(doGet(url));
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }


    public Document listUserCollections(String userid)
    throws ConnectionException
    {
        ResponseHandler handler = new DocumentResponseHandler();

        String url = serverUrl + PATH_USER_COLLECTIONS + "/" + userid;

        try {
            return (Document) handler.handle(doGet(url));
        }
        catch (IOException ioe) {
            throw new ConnectionException(ioe.getMessage(), ioe);
        }
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8:

http://dive4elements.wald.intevation.org