com.kolich.havalo.client.service.HavaloClient.java Source code

Java tutorial

Introduction

Here is the source code for com.kolich.havalo.client.service.HavaloClient.java

Source

/**
 * Copyright (c) 2012 Mark S. Kolich
 * http://mark.koli.ch
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

package com.kolich.havalo.client.service;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.kolich.common.functional.either.Either;
import com.kolich.havalo.client.entities.FileObject;
import com.kolich.havalo.client.entities.KeyPair;
import com.kolich.havalo.client.entities.ObjectList;
import com.kolich.havalo.client.signing.HavaloAbstractSigner;
import com.kolich.http.HttpClient4Closure;
import com.kolich.http.helpers.EntityConverterClosures.CustomEntityConverterClosure;
import com.kolich.http.helpers.GsonClosures.GsonOrHttpFailureClosure;
import com.kolich.http.helpers.StatusCodeAndHeaderClosures.StatusCodeOrHttpFailureClosure;
import com.kolich.http.helpers.definitions.CustomEntityConverter;
import com.kolich.http.helpers.definitions.CustomFailureEntityConverter;
import com.kolich.http.helpers.definitions.CustomSuccessEntityConverter;
import com.kolich.http.common.response.HttpFailure;
import com.kolich.http.common.response.HttpSuccess;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.protocol.HttpContext;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

import static com.kolich.common.entities.KolichCommonEntity.getDefaultGsonBuilder;
import static com.kolich.common.util.URLEncodingUtils.urlEncode;
import static com.kolich.http.KolichDefaultHttpClient.KolichHttpClientFactory.getNewInstanceWithProxySelector;
import static org.apache.commons.io.IOUtils.copyLarge;
import static org.apache.http.HttpStatus.SC_NO_CONTENT;
import static org.apache.http.HttpStatus.SC_OK;

public final class HavaloClient extends HavaloAbstractService {

    private static final String API_ACTION_AUTHENTICATE = "authenticate";
    private static final String API_ACTION_REPOSITORY = "repository";
    private static final String API_ACTION_OBJECT = "object";

    private static final String API_PARAM_STARTSWITH = "startsWith";

    private final HttpClient client_;
    private final GsonBuilder gson_;

    public HavaloClient(final HttpClient client, final HavaloAbstractSigner signer, final GsonBuilder gson,
            final String apiEndpoint) {
        super(signer, apiEndpoint);
        client_ = client;
        gson_ = gson;
    }

    public HavaloClient(final HttpClient client, final HavaloAbstractSigner signer, final String apiEndpoint) {
        this(client, signer, getDefaultGsonBuilder(), apiEndpoint);
    }

    public HavaloClient(final HavaloAbstractSigner signer, final String apiEndpoint) {
        this(getNewInstanceWithProxySelector(), signer, apiEndpoint);
    }

    public HavaloClient(final HttpClient client, final HavaloClientCredentials credentials,
            final String apiEndpoint) {
        this(client, new HavaloClientSigner(credentials), apiEndpoint);
    }

    public HavaloClient(final HavaloClientCredentials credentials, final String apiEndpoint) {
        this(new HavaloClientSigner(credentials), apiEndpoint);
    }

    public HavaloClient(final HttpClient client, final UUID key, final String secret, final String apiEndpoint) {
        this(client, new HavaloClientCredentials(key, secret), apiEndpoint);
    }

    public HavaloClient(final UUID key, final String secret, final String apiEndpoint) {
        this(new HavaloClientCredentials(key, secret), apiEndpoint);
    }

    public HavaloClient(final HttpClient client, final String key, final String secret, final String apiEndpoint) {
        this(client, UUID.fromString(key), secret, apiEndpoint);
    }

    public HavaloClient(final String key, final String secret, final String apiEndpoint) {
        this(UUID.fromString(key), secret, apiEndpoint);
    }

    private abstract class HavaloBaseClosure<T> extends HttpClient4Closure<HttpFailure, T> {
        private final int expectStatus_;

        public HavaloBaseClosure(final HttpClient client, final int expectStatus) {
            super(client);
            expectStatus_ = expectStatus;
        }

        @Override
        public void before(final HttpRequestBase request) throws Exception {
            signRequest(request);
        }

        @Override
        public boolean check(final HttpResponse response, final HttpContext context) {
            return expectStatus_ == response.getStatusLine().getStatusCode();
        }

        public final Either<HttpFailure, T> head(final String action, final String... path) {
            return super.head(buildPath(action, path));
        }
    }

    private abstract class HavaloGsonClosure<T> extends GsonOrHttpFailureClosure<T> {
        private final int expectStatus_;

        public HavaloGsonClosure(final HttpClient client, final Gson gson, final Class<T> clazz,
                final int expectStatus) {
            super(client, gson, clazz);
            expectStatus_ = expectStatus;
        }

        @Override
        public void before(final HttpRequestBase request) throws Exception {
            signRequest(request);
        }

        @Override
        public boolean check(final HttpResponse response, final HttpContext context) {
            return expectStatus_ == response.getStatusLine().getStatusCode();
        }

        @Override
        public final Either<HttpFailure, T> get(final String action) {
            return super.get(buildPath(action));
        }

        @Override
        public final Either<HttpFailure, T> post(final String action) {
            return super.post(buildPath(action));
        }

        public final Either<HttpFailure, T> put(final String action, final String... path) {
            return super.put(buildPath(action, path));
        }
    }

    private abstract class HavaloStatusCodeClosure extends StatusCodeOrHttpFailureClosure {
        private final int expectStatus_;

        public HavaloStatusCodeClosure(final HttpClient client, final int expectStatus) {
            super(client);
            expectStatus_ = expectStatus;
        }

        @Override
        public void before(final HttpRequestBase request) throws Exception {
            signRequest(request);
        }

        @Override
        public boolean check(final HttpResponse response, final HttpContext context) {
            return expectStatus_ == response.getStatusLine().getStatusCode();
        }

        public final Either<HttpFailure, Integer> delete(final String action, final String... path) {
            return super.delete(buildPath(action, path));
        }
    }

    private abstract class HavaloEntityConverterClosure<F, S> extends CustomEntityConverterClosure<F, S> {
        private final int expectStatus_;

        public HavaloEntityConverterClosure(final HttpClient client, final CustomEntityConverter<F, S> converter,
                final int expectStatus) {
            super(client, converter);
            expectStatus_ = expectStatus;
        }

        @Override
        public void before(final HttpRequestBase request) throws Exception {
            signRequest(request);
        }

        @Override
        public boolean check(final HttpResponse response, final HttpContext context) {
            return expectStatus_ == response.getStatusLine().getStatusCode();
        }

        public final Either<F, S> get(final String action, final String... path) {
            return super.get(buildPath(action, path));
        }
    }

    public Either<HttpFailure, KeyPair> authenticate() {
        // The POST of auth credentials is only successful when the
        // resulting status code is a 200 OK.  Any other status
        // code on the response is failure.
        return new HavaloGsonClosure<KeyPair>(client_, gson_.create(), KeyPair.class, SC_OK) {
        }.post(API_ACTION_AUTHENTICATE);
    }

    public Either<HttpFailure, KeyPair> createRepository() {
        // The POST of a repository is only successful when the
        // resulting status code is a 201 Created.  Any other status
        // code on the response is failure.
        return new HavaloGsonClosure<KeyPair>(client_, gson_.create(), KeyPair.class, SC_OK) {
        }.post(API_ACTION_REPOSITORY);
    }

    public Either<HttpFailure, Integer> deleteRepository(final UUID repoId) {
        // The DELETE of a repository is only successful when the
        // resulting status code is a 204 No Content.  Any other
        // status code on the response is failure.
        return new HavaloStatusCodeClosure(client_, SC_NO_CONTENT) {
        }.delete(API_ACTION_REPOSITORY, repoId.toString());
    }

    public Either<HttpFailure, ObjectList> listObjects(final String... path) {
        // The listing of objects is only successful when the
        // resulting status code is a 200 OK.  Any other status
        // code on the response is failure.
        return new HavaloGsonClosure<ObjectList>(client_, gson_.create(), ObjectList.class, SC_OK) {
            @Override
            public void before(final HttpRequestBase request) throws Exception {
                final URIBuilder builder = new URIBuilder(request.getURI());
                if (path != null && path.length > 0) {
                    builder.addParameter(API_PARAM_STARTSWITH, varargsToPrefixString(path));
                }
                request.setURI(builder.build());
                super.before(request);
            }
        }.get(API_ACTION_REPOSITORY);
    }

    public Either<HttpFailure, ObjectList> listObjects() {
        return listObjects((String[]) null);
    }

    public Either<HttpFailure, List<Header>> getObject(final OutputStream destination, final String... path) {
        return getObject(new CustomEntityConverter<HttpFailure, List<Header>>() {
            @Override
            public List<Header> success(final HttpSuccess success) throws Exception {
                // Copy the object.
                copyLarge(success.getContent(), destination);
                // Get and return the headers on the HTTP response.
                // This is where stuff like "Content-Type" and
                // "Content-Length" live.
                return Arrays.asList(success.getResponse().getAllHeaders());
            }

            @Override
            public HttpFailure failure(final HttpFailure failure) {
                return failure;
            }
        }, path);
    }

    public <F, S> Either<F, S> getObject(final CustomSuccessEntityConverter<S> success,
            final CustomFailureEntityConverter<F> failure, final String... path) {
        // Create a new custom entity converter using the provided
        // success and failure handlers.  This acts as a convenience
        // "interface" between the entity converters and units of work
        // that represent separate success and failure handlers.
        return getObject(new CustomEntityConverter<F, S>() {
            @Override
            public S success(final HttpSuccess hSuccess) throws Exception {
                return success.success(hSuccess);
            }

            @Override
            public F failure(final HttpFailure hFailure) {
                return failure.failure(hFailure);
            }
        }, path);
    }

    public <F, S> Either<F, S> getObject(final CustomEntityConverter<F, S> converter, final String... path) {
        // The GET of an object is only successful when the
        // resulting status code is a 200 OK.  Any other status
        // code on the response is failure.
        return new HavaloEntityConverterClosure<F, S>(client_, converter, SC_OK) {
        }.get(API_ACTION_OBJECT, path);
    }

    public Either<HttpFailure, List<Header>> getObjectMetaData(final String... path) {
        // The HEAD of an object is only successful when the
        // resulting status code is a 200 OK.  Any other status
        // code on the response is failure.
        return new HavaloBaseClosure<List<Header>>(client_, SC_OK) {
            @Override
            public List<Header> success(final HttpSuccess success) {
                return Arrays.asList(success.getResponse().getAllHeaders());
            }
        }.head(API_ACTION_OBJECT, path);
    }

    public Either<HttpFailure, FileObject> putObject(final InputStream input, final long contentLength,
            final Header[] headers, final String... path) {
        // The upload of an object is only successful when the
        // resulting status code is a 200 OK.  Any other status
        // code on the response is failure.
        return new HavaloGsonClosure<FileObject>(client_, gson_.create(), FileObject.class, SC_OK) {
            @Override
            public void before(final HttpRequestBase request) throws Exception {
                if (headers != null) {
                    request.setHeaders(headers);
                }
                ((HttpPut) request).setEntity(new InputStreamEntity(input, contentLength));
                super.before(request);
            }
        }.put(API_ACTION_OBJECT, path);
    }

    public Either<HttpFailure, FileObject> putObject(final byte[] input, final Header[] headers,
            final String... path) {
        final InputStream is = new ByteArrayInputStream(input);
        return putObject(is, (long) input.length, headers, path);
    }

    public Either<HttpFailure, FileObject> putObject(final byte[] input, final String... path) {
        return putObject(input, null, path);
    }

    public Either<HttpFailure, Integer> deleteObject(final Header[] headers, final String... path) {
        // The deletion of an object is only successful when the
        // resulting status code is a 204 No Content.  Any other status
        // code on the response is failure.
        return new HavaloStatusCodeClosure(client_, SC_NO_CONTENT) {
            @Override
            public void before(final HttpRequestBase request) throws Exception {
                if (headers != null) {
                    request.setHeaders(headers);
                }
                super.before(request);
            }
        }.delete(API_ACTION_OBJECT, path);
    }

    public Either<HttpFailure, Integer> deleteObject(final String... path) {
        return deleteObject(null, path);
    }

    private static final String buildPath(final String action, final String... path) {
        final StringBuilder sb = new StringBuilder(SLASH_STRING);
        sb.append(action);
        if (path != null) {
            sb.append(SLASH_STRING).append(urlEncode(varargsToPrefixString(path)));
        }
        return sb.toString();
    }

    private static final String buildPath(final String action) {
        return buildPath(action, (String[]) null);
    }

}