com.gsma.mobileconnect.utils.RestClient.java Source code

Java tutorial

Introduction

Here is the source code for com.gsma.mobileconnect.utils.RestClient.java

Source

/*
 *                                   SOFTWARE USE PERMISSION
 *
 *  By downloading and accessing this software and associated documentation files ("Software") you are granted the
 *  unrestricted right to deal in the Software, including, without limitation the right to use, copy, modify, publish,
 *  sublicense and grant such rights to third parties, subject to the following conditions:
 *
 *  The following copyright notice and this permission notice shall be included in all copies, modifications or
 *  substantial portions of this Software: Copyright  2016 GSM Association.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND, 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. YOU
 *  AGREE TO INDEMNIFY AND HOLD HARMLESS THE AUTHORS AND COPYRIGHT HOLDERS FROM AND AGAINST ANY SUCH LIABILITY.
 */

package com.gsma.mobileconnect.utils;

import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.*;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Class to make Rest requests.
 */
public class RestClient {
    /**
     * Build a HttpClientContext that will preemptively authenticate using Basic Authentication
     *
     * @param username Username for credentials
     * @param password Password for credentials
     * @param uriForRealm uri used to determine the authentication realm
     * @return A HttpClientContext that will preemptively authenticate using Basic Authentication
     */
    public HttpClientContext getHttpClientContext(String username, String password, URI uriForRealm) {
        String host = URIUtils.extractHost(uriForRealm).getHostName();
        int port = uriForRealm.getPort();
        if (port == -1) {
            if (Constants.HTTP_SCHEME.equalsIgnoreCase(uriForRealm.getScheme())) {
                port = Constants.HTTP_PORT;
            } else if (Constants.HTTPS_SCHEME.equalsIgnoreCase(uriForRealm.getScheme())) {
                port = Constants.HTTPS_PORT;
            }
        }

        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(new AuthScope(host, port),
                new UsernamePasswordCredentials(username, password));

        AuthCache authCache = new BasicAuthCache();
        authCache.put(new HttpHost(host, port, uriForRealm.getScheme()), new BasicScheme());

        HttpClientContext context = HttpClientContext.create();
        context.setCredentialsProvider(credentialsProvider);
        context.setAuthCache(authCache);

        return context;
    }

    /**
     * Make the specified request with the specified client context.
     * <p>
     * The specified cookies will be added to the request. A request will be aborted if it exceeds the specified timeout.
     * Non Json responses are converted and thrown as RestExceptions.
     * <p>
     * Ensures that all closable resources are closed.
     *
     * @param httpRequest The request to execute.
     * @param context The context to use when executing the request.
     * @param timeout The maximum time in milliseconds the request is allowed to take.
     * @param cookiesToProxy The cookies to add to the request.
     * @return The Rest response.
     * @throws RestException Thrown if the request exceeds the specified timeout, or a non Json response is received.
     * @throws IOException
     */
    public RestResponse callRestEndPoint(HttpRequestBase httpRequest, HttpClientContext context, int timeout,
            List<KeyValuePair> cookiesToProxy) throws RestException, IOException {
        CookieStore cookieStore = buildCookieStore(httpRequest.getURI().getHost(), cookiesToProxy);
        CloseableHttpClient closeableHttpClient = getHttpClient(cookieStore);
        try {
            CloseableHttpResponse closeableHttpResponse = executeRequest(closeableHttpClient, httpRequest, context,
                    timeout);
            try {
                RestResponse restResponse = buildRestResponse(httpRequest, closeableHttpResponse);
                checkRestResponse(restResponse);
                return restResponse;
            } finally {
                closeableHttpResponse.close();
            }
        } finally {
            closeableHttpClient.close();
        }
    }

    /**
     * Build a cookie store that contains the specified cookies.
     * <p>
     * The domain of the cookies is set to the specified host.
     *
     * @param host The domain of the cookies.
     * @param cookiesToProxy The cookies to add to the store.
     * @return The cookie store.
     */
    private CookieStore buildCookieStore(String host, List<KeyValuePair> cookiesToProxy) {
        CookieStore cookieStore = new BasicCookieStore();

        if (null == cookiesToProxy) {
            return cookieStore;
        }

        for (KeyValuePair cookieToProxy : cookiesToProxy) {
            BasicClientCookie cookie = new BasicClientCookie(cookieToProxy.getKey(), cookieToProxy.getValue());
            cookie.setDomain(host);
            cookieStore.addCookie(cookie);
        }

        return cookieStore;
    }

    /**
     * Build a HttpClient that follows redirects and has the specified cookie store.
     *
     * @param cookieStore The cookie store to associate with the client.
     * @return A HttpClient
     */
    private CloseableHttpClient getHttpClient(CookieStore cookieStore) {
        CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
        return httpClient;
    }

    /**
     * Execute the given request.
     * <p>
     * Abort the request if it exceeds the specified timeout.
     *
     * @param httpClient The client to use.
     * @param request The request to make.
     * @param context The context to use.
     * @param timeout The timeout to use.
     * @return A Http Response.
     * @throws RestException Thrown if the request fails or times out.
     */
    private CloseableHttpResponse executeRequest(CloseableHttpClient httpClient, final HttpRequestBase request,
            HttpClientContext context, int timeout) throws RestException {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                request.abort();
            }
        }, timeout);

        RequestConfig localConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout)
                .setConnectTimeout(timeout).setSocketTimeout(timeout).setCookieSpec(CookieSpecs.STANDARD).build();
        request.setConfig(localConfig);
        try {
            return httpClient.execute(request, context);
        } catch (IOException ex) {
            String requestUri = request.getURI().toString();
            if (request.isAborted()) {
                throw new RestException("Rest end point did not respond", requestUri);
            }
            throw new RestException("Rest call failed", requestUri, ex);
        }
    }

    /**
     * Builds a RestResponse from the given HttpResponse
     *
     * @param request The original request.
     * @param closeableHttpResponse The HttpResponse to build the RestResponse for.
     * @return The RestResponse of the HttpResponse
     * @throws IOException
     */
    private RestResponse buildRestResponse(HttpRequestBase request, CloseableHttpResponse closeableHttpResponse)
            throws IOException {
        String requestUri = request.getURI().toString();
        int statusCode = closeableHttpResponse.getStatusLine().getStatusCode();

        HeaderIterator headers = closeableHttpResponse.headerIterator();
        List<KeyValuePair> headerList = new ArrayList<KeyValuePair>(3);
        while (headers.hasNext()) {
            Header header = headers.nextHeader();
            headerList.add(new KeyValuePair(header.getName(), header.getValue()));
        }

        HttpEntity httpEntity = closeableHttpResponse.getEntity();
        String responseData = EntityUtils.toString(httpEntity);

        return new RestResponse(requestUri, statusCode, headerList, responseData);
    }

    /**
     * Checks the Rest Response converting it to an exception if necessary.
     * <p>
     * Non Json responses are converted to exceptions.
     *
     * @param response The Response to check.
     * @throws RestException
     */
    private static void checkRestResponse(RestResponse response) throws RestException {
        if (!response.isJsonContent()) {
            throw new RestException("Invalid response", response);
        }
    }
}