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

Java tutorial

Introduction

Here is the source code for com.kolich.havalo.client.service.HavaloAbstractService.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 static com.google.common.base.Preconditions.checkNotNull;
import static com.kolich.common.DefaultCharacterEncoding.UTF_8;
import static java.util.regex.Pattern.quote;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.http.client.methods.HttpRequestBase;

import com.kolich.havalo.client.HavaloClientException;
import com.kolich.havalo.client.signing.HavaloAbstractSigner;

public abstract class HavaloAbstractService {

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

    protected static final String HTTP_SCHEME_SLASHES = "://";

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

    protected final HavaloAbstractSigner signer_;

    /**
     * The Havalo API endpoint that this service communicates with.
     */
    protected final URI apiEndpoint_;

    public HavaloAbstractService(HavaloAbstractSigner signer, String apiEndpoint) {
        checkNotNull(signer, "The signer cannot be null!");
        checkNotNull(apiEndpoint, "The service client API endpoint cannot " + "be null!");
        signer_ = signer;
        apiEndpoint_ = URI.create(apiEndpoint);
    }

    /**
     * Prepares and signs the request.
     * @param request the request object
     * @return the {@link HavaloHttpResponse} response assuming the
     * request was successful. If the request was not successful, then
     * an exception will be thrown.
     */
    protected final void signRequest(final HttpRequestBase request) {
        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 final URI getFinalEndpoint(final HttpRequestBase 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 its already
        // been provided.  This assumes the caller knows what they are
        // doing and have built a complete and proper URL to the API.
        if (!isComplete(endPointURI)) {
            endPointURI = URI.create(
                    // Havalo API endpoints usually start with https://   
                    apiEndpoint_.getScheme() + HTTP_SCHEME_SLASHES +
                    // 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() + apiEndpoint_.getPath() +
                            // 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://" or "http://".
     * @param uri
     * @return
     */
    private static final boolean isComplete(final URI uri) {
        return uri != null && (uri.toString().startsWith(HTTPS) || uri.toString().startsWith(HTTP));
    }

    /**
     * Given a variable list of arguments, prepare a fully qualified
     * path to a key in a repository.  Each prefix in the list is
     * separated by an appropriate path separator.  The resulting string
     * is NOT URL-encoded, but each prefix component is URL-encoded before
     * concatenated to the resulting path -- slashes and other special
     * characters in a prefix component that may be interpreted wrong when
     * used in a path are URL-encoded so we won't have any conflicts.
     * Note that empty strings in the varargs prefix list will NOT be appended
     * to the resulting prefix string.
     * Example:
     * <code>
     * new String[]{"accounts", "", "silly/path+dog"}
     * </code>
     * is returned as
     * <code>
     * "accounts/silly%2Fpath%2Bdog"
     * </code>
     * @param prefixes
     * @return
     */
    public static final String varargsToPrefixString(final String... prefixes) {
        checkNotNull(prefixes, "The prefix list cannot be null!");
        try {
            final StringBuilder sb = new StringBuilder();
            for (int i = 0, l = prefixes.length; i < l; i++) {
                if (!EMPTY_STRING.equals(prefixes[i])) {
                    sb.append(URLEncoder.encode(prefixes[i], UTF_8));
                    // Don't append a "/" if this element is the last in the list.
                    sb.append((i < l - 1) ? SLASH_STRING : EMPTY_STRING);
                }
            }
            return sb.toString();
        } catch (UnsupportedEncodingException e) {
            throw new HavaloClientException(e);
        }
    }

    /**
     * Given a prefix string, generated by
     * {@link HavaloAbstractService#varargsToPrefixString(String...)}, returns
     * a variable arguments compatible String[] array containing each prefix
     * component.  Each component in the resulting prefix String[] array will be
     * fully URL-decoded.  Note that any empty strings, once the prefix string
     * is split around a path separator, are NOT added to the resulting
     * varargs list.
     * @param prefix
     * @return
     */
    public static final String[] prefixStringToVarargs(final String prefix) {
        checkNotNull(prefix, "The prefix string cannot be null!");
        try {
            final List<String> prl = new ArrayList<String>();
            for (final String p : prefix.split(quote(SLASH_STRING))) {
                if (!EMPTY_STRING.equals(p)) {
                    prl.add(URLEncoder.encode(p, UTF_8));
                }
            }
            return prl.toArray(new String[] {});
        } catch (UnsupportedEncodingException e) {
            throw new HavaloClientException(e);
        }
    }

    /**
     * Appends the given key to the end of the prefix list, then returns a
     * a new String[] array representing that list.
     * @param key
     * @param prefixes
     * @return
     */
    public static final String[] appendKeyToPrefixes(final String key, final String... prefixes) {
        checkNotNull(key, "The key to append cannot be null!");
        checkNotNull(prefixes, "The prefix list cannot be null!");
        final List<String> prl = new ArrayList<String>(Arrays.asList(prefixes));
        // The "key" becomes the last element in the prefix list.
        prl.add(key);
        return prl.toArray(new String[] {});
    }

}