azkaban.utils.RestfulApiClient.java Source code

Java tutorial

Introduction

Here is the source code for azkaban.utils.RestfulApiClient.java

Source

/*
 * Copyright 2015 LinkedIn Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package azkaban.utils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpMessage;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.log4j.Logger;

/** class handles the communication between the application and
 *  a Restful API based web server.
 *  @param T : type of the returning response object.
 *  Note: the idea of this abstract class is to provide a wrapper for the logic around HTTP layer communication so
 *        development work can take this as a black box and focus on processing the result.
 *        With that said the abstract class will be provided as a template, which ideally can support different types
 *        of returning object (Dictionary, xmlDoc , text etc.)
 * */
public abstract class RestfulApiClient<T> {
    protected static Logger logger = Logger.getLogger(RestfulApiClient.class);

    /** Method to transform the response returned by the httpClient into the
     *  type specified.
     *  Note: Method need to handle case such as failed request.
     *        Also method is not supposed to pass the response object out
     *        via the returning value as the response will be closed after the
     *        execution steps out of the method context.
     * @throws HttpResponseException
     * @throws IOException
     * @throws ParseException
     * **/
    protected abstract T parseResponse(HttpResponse response) throws HttpResponseException, IOException;

    /** function to perform a Get http request.
     * @param uri   the URI of the request.
     * @param headerEntries   extra entries to be added to request header.
     * @return the response object type of which is specified by user.
     * @throws IOException */
    public T httpGet(URI uri, List<NameValuePair> headerEntries) throws IOException {
        // shortcut if the passed url is invalid.
        if (null == uri) {
            logger.error(" unable to perform httpGet as the passed uri is null");
            return null;
        }

        HttpGet get = new HttpGet(uri);
        return this.sendAndReturn((HttpGet) completeRequest(get, headerEntries));
    }

    /** function to perform a Post http request.
     * @param uri   the URI of the request.
     * @param headerEntries   extra entries to be added to request header.
     * @param postingBody  the content to be posted , optional.
     * @return the response object type of which is specified by user.
     * @throws UnsupportedEncodingException, IOException */
    public T httpPost(URI uri, List<NameValuePair> headerEntries, String postingBody)
            throws UnsupportedEncodingException, IOException {
        // shortcut if the passed url is invalid.
        if (null == uri) {
            logger.error(" unable to perform httpPost as the passed uri is null.");
            return null;
        }

        HttpPost post = new HttpPost(uri);
        return this.sendAndReturn(completeRequest(post, headerEntries, postingBody));
    }

    /** function to perform a Delete http request.
     * @param uri   the URI of the request.
     * @param headerEntries   extra entries to be added to request header.
     * @return the response object type of which is specified by user.
     * @throws IOException */
    public T httpDelete(URI uri, List<NameValuePair> headerEntries) throws IOException {
        // shortcut if the passed url is invalid.
        if (null == uri) {
            logger.error(" unable to perform httpDelete as the passed uri is null.");
            return null;
        }

        HttpDelete delete = new HttpDelete(uri);
        return this.sendAndReturn((HttpDelete) completeRequest(delete, headerEntries));
    }

    /** function to perform a Put http request.
     * @param uri   the URI of the request.
     * @param headerEntries   extra entries to be added to request header.
     * @param postingBody  the content to be posted , optional.
     * @return the response object type of which is specified by user.
     * @throws UnsupportedEncodingException, IOException */
    public T httpPut(URI uri, List<NameValuePair> headerEntries, String postingBody)
            throws UnsupportedEncodingException, IOException {
        // shortcut if the passed url is invalid.
        if (null == uri) {
            logger.error(" unable to perform httpPut as the passed url is null or empty.");
            return null;
        }

        HttpPut put = new HttpPut(uri);
        return this.sendAndReturn(completeRequest(put, headerEntries, postingBody));
    }

    /** function to dispatch the request and pass back the response.
     * */
    protected T sendAndReturn(HttpUriRequest request) throws IOException {
        CloseableHttpClient client = HttpClients.createDefault();
        try {
            return this.parseResponse(client.execute(request));
        } finally {
            client.close();
        }
    }

    /** helper function to build a valid URI.
     *  @param host   host name.
     *  @param port   host port.
     *  @param path   extra path after host.
     *  @param isHttp indicates if whether Http or HTTPS should be used.
     *  @param params extra query parameters.
     *  @return the URI built from the inputs.
     *  @throws IOException
     * */
    public static URI buildUri(String host, int port, String path, boolean isHttp, Pair<String, String>... params)
            throws IOException {
        URIBuilder builder = new URIBuilder();
        builder.setScheme(isHttp ? "http" : "https").setHost(host).setPort(port);

        if (null != path && path.length() > 0) {
            builder.setPath(path);
        }

        if (params != null) {
            for (Pair<String, String> pair : params) {
                builder.setParameter(pair.getFirst(), pair.getSecond());
            }
        }

        URI uri = null;
        try {
            uri = builder.build();
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }

        return uri;
    }

    /** helper function to build a valid URI.
     *  @param uri    the URI to start with.
     *  @param params extra query parameters to append.
     *  @return the URI built from the inputs.
     *  @throws IOException
     * */
    public static URI BuildUri(URI uri, Pair<String, String>... params) throws IOException {
        URIBuilder builder = new URIBuilder(uri);

        if (params != null) {
            for (Pair<String, String> pair : params) {
                builder.setParameter(pair.getFirst(), pair.getSecond());
            }
        }

        URI returningUri = null;
        try {
            returningUri = builder.build();
        } catch (URISyntaxException e) {
            throw new IOException(e);
        }

        return returningUri;
    }

    /** helper function to fill  the request with header entries .
     * */
    private static HttpMessage completeRequest(HttpMessage request, List<NameValuePair> headerEntries) {
        if (null == request) {
            logger.error("unable to complete request as the passed request object is null");
            return request;
        }

        // dump all the header entries to the request.
        if (null != headerEntries && headerEntries.size() > 0) {
            for (NameValuePair pair : headerEntries) {
                request.addHeader(pair.getName(), pair.getValue());
            }
        }
        return request;
    }

    /** helper function to fill  the request with header entries and posting body .
     * */
    private static HttpEntityEnclosingRequestBase completeRequest(HttpEntityEnclosingRequestBase request,
            List<NameValuePair> headerEntries, String postingBody) throws UnsupportedEncodingException {
        if (null != completeRequest(request, headerEntries)) {
            // dump the post body UTF-8 will be used as the default encoding type.
            if (null != postingBody && postingBody.length() > 0) {
                HttpEntity entity = new ByteArrayEntity(postingBody.getBytes("UTF-8"));
                request.setHeader("Content-Length", Long.toString(entity.getContentLength()));
                request.setEntity(entity);
            }
        }
        return request;
    }
}