com.kolich.aws.services.AbstractAwsService.java Source code

Java tutorial

Introduction

Here is the source code for com.kolich.aws.services.AbstractAwsService.java

Source

/**
 * Copyright (c) 2014 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.aws.services;

import com.kolich.aws.KolichAwsException;
import com.kolich.aws.transport.AwsHttpRequest;
import com.kolich.http.helpers.definitions.OrHttpFailureClosure;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.protocol.HttpContext;

import java.net.URI;

import static com.google.common.base.Preconditions.checkNotNull;

public abstract class AbstractAwsService {

    protected static final String HTTPS = "https://";

    protected static final String SLASH_STRING = "/";
    protected static final String EMPTY_STRING = "";
    protected static final String QUERY_STRING = "?";
    protected static final String DOT_STRING = ".";

    private final AbstractAwsSigner signer_;
    private final URI apiEndpoint_;

    protected abstract class AwsBaseHttpClosure<S> extends OrHttpFailureClosure<S> {
        private final int expectStatus_;

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

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

    public AbstractAwsService(final AbstractAwsSigner signer, final String apiEndpoint) {
        signer_ = checkNotNull(signer, "The signer cannot be null!");
        apiEndpoint_ = getApiEndpoint(
                checkNotNull(apiEndpoint, "The service " + "client API endpoint cannot be null!"));
    }

    private final URI getApiEndpoint(String apiEndPoint) {
        // If the communication endpoint does not start with https://
        // then we assume that we need to add it.
        if (!apiEndPoint.startsWith(HTTPS)) {
            apiEndPoint = HTTPS + apiEndPoint;
        } else {
            // Our communication endpoint can only be HTTPS.
            throw new KolichAwsException("Oops! AWS endpoints must start " + "with " + HTTPS
                    + " but you gave me something else: " + apiEndPoint);
        }
        // Create our endpoint URI.
        return URI.create(apiEndPoint);
    }

    protected final void signRequest(final AwsHttpRequest request) throws Exception {
        checkNotNull(request, "Request cannot be null!");
        // Compute the final endpoint for the request and set it.
        request.setURI(getFinalEndpoint(request));
        // Sign the request using an appropriate request signer.
        signer_.signHttpRequest(request);
    }

    private URI getFinalEndpoint(final AwsHttpRequest request) {
        URI endPointURI = request.getURI();
        // If the request URI already starts with https:// then we don't
        // have to build a full endpoint URL anymore since it's already
        // been provided.  This assumes the caller knows what they are
        // doing and have built a complete and proper URL for AWS.
        if (!isComplete(endPointURI)) {
            endPointURI = URI.create(
                    // AWS endpoint URL's always start with https://   
                    HTTPS +
                    // The sub-domain is most often how we refer to S3 buckets.
                    // For example, on a bucket named "foo" the subdomain would
                    // be "foo" and the resulting endpoint we actually send
                    // the request to is "foo.s3.amazonaws.com".
                            ((request.getResource() != null) ?
                            // If there is a "resource" (usually a bucket name to
                            // represent a sub-domain, then append it followed by
                            // a single dot.
                                    request.getResource() + DOT_STRING :
                                    // If the request has no resource, usually for all AWS
                                    // services other than S3, then just append an empty
                                    // String.
                                    EMPTY_STRING)
                            +
                            // Returns the decoded authority component of this endpoint URI.
                            // The authority of a URI is basically the hostname, otherwise
                            // called the endpoint here.
                            apiEndpoint_.getAuthority() +
                            // Returns the decoded path component of the request URI.
                            // The path of a URI is the piece of the URI after the hostname,
                            // not including the query parameters.
                            getPath(endPointURI) +
                            // Returns the decoded query component of this URI.
                            // The query parameters, if any.
                            getQuery(endPointURI));
        }
        return endPointURI;
    }

    /**
     * Given a {@link URI} returns the path component of that
     * {@link URI}.  The path of a URI is the piece of the URI after
     * the hostname, not including the query parameters.  If the URI
     * is null, a single "/" is returned.  If the URI is not null, but
     * the path is empty, an "" empty string is returned.
     * @param uri the URI to extract the path from
     * @return
     */
    private static final String getPath(final URI uri) {
        if (uri == null) {
            return SLASH_STRING;
        } else {
            final String path = uri.getRawPath();
            return (path == null) ? EMPTY_STRING : path;
        }
    }

    /**
     * Given a {@link URI} returns the query string component of that
     * {@link URI}.  The query of a URI is the piece after the "?". If the
     * URI is null, an "" empty string is returned.  If the URI is not null,
     * but the query is empty, an "" empty string is returned.
     * @param uri the URI to extract the query from
     * @return
     */
    private static final String getQuery(final URI uri) {
        if (uri == null) {
            return EMPTY_STRING;
        } else {
            final String query = uri.getRawQuery();
            return (query == null) ? EMPTY_STRING : QUERY_STRING + query;
        }
    }

    /**
     * Checks if the given URI is non-null, and if it's a complete endpoint
     * URI that already starts with "https://".
     * @param uri
     * @return
     */
    private static final boolean isComplete(final URI uri) {
        return uri != null && uri.toString().startsWith(HTTPS);
    }

}