com.axibase.tsd.client.HttpClient.java Source code

Java tutorial

Introduction

Here is the source code for com.axibase.tsd.client.HttpClient.java

Source

/*
* Copyright 2015 Axibase Corporation or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* https://www.axibase.com/atsd/axibase-apache-2.0.pdf
*
* or in the "license" file accompanying this file. This file 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 com.axibase.tsd.client;

import com.axibase.tsd.model.system.ClientConfiguration;
import com.axibase.tsd.model.system.ServerError;
import com.axibase.tsd.query.QueryPart;
import com.fasterxml.jackson.jaxrs.base.JsonMappingExceptionMapper;
import com.fasterxml.jackson.jaxrs.base.JsonParseExceptionMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.apache.commons.io.IOUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.StrictHostnameVerifier;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.glassfish.jersey.SslConfigurator;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.filter.LoggingFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.KeyManagementException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.List;

import static com.axibase.tsd.util.AtsdUtil.JSON;

/**
 * @author Nikolay Malevanny.
 */
class HttpClient {
    private static final Logger log = LoggerFactory.getLogger(HttpClient.class);
    private final static java.util.logging.Logger legacyLogger = java.util.logging.Logger
            .getLogger(HttpClient.class.getName());
    public static final int HTTP_STATUS_OK = 200;
    public static final int HTTP_STATUS_FAIL = 400;
    public static final int HTTP_STATUS_NOT_FOUND = 404;

    private ClientConfiguration clientConfiguration;
    private final Client client;

    HttpClient(ClientConfiguration clientConfiguration) {
        Client builtClient = buildClient(clientConfiguration);
        client = builtClient;

        this.clientConfiguration = clientConfiguration;
    }

    private static Client buildClient(ClientConfiguration clientConfiguration) {
        ClientConfig clientConfig = new ClientConfig();
        clientConfig.register(JsonMappingExceptionMapper.class).register(JsonParseExceptionMapper.class)
                .register(JacksonJaxbJsonProvider.class,
                        new Class[] { MessageBodyReader.class, MessageBodyWriter.class })
                .register(RequestBodyLogger.class).register(HttpAuthenticationFeature
                        .basic(clientConfiguration.getUsername(), clientConfiguration.getPassword()));

        if (log.isDebugEnabled()) {
            clientConfig.register(new LoggingFilter(legacyLogger, true));
        }

        configureHttps(clientConfiguration, clientConfig);

        ConnectorProvider connectorProvider = new ApacheConnectorProvider();
        clientConfig.connectorProvider(connectorProvider);

        Client builtClient = ClientBuilder.newBuilder().withConfig(clientConfig).build();
        builtClient.property(ClientProperties.CONNECT_TIMEOUT, clientConfiguration.getConnectTimeoutMillis());
        builtClient.property(ClientProperties.READ_TIMEOUT, clientConfiguration.getReadTimeoutMillis());
        return builtClient;
    }

    private static void configureHttps(ClientConfiguration clientConfiguration, ClientConfig clientConfig) {
        SslConfigurator sslConfig = SslConfigurator.newInstance().securityProtocol("SSL");
        PoolingHttpClientConnectionManager connectionManager = createConnectionManager(clientConfiguration,
                sslConfig);
        clientConfig.property(ApacheClientProperties.CONNECTION_MANAGER, connectionManager);
        clientConfig.property(ApacheClientProperties.SSL_CONFIG, sslConfig);
    }

    static PoolingHttpClientConnectionManager createConnectionManager(ClientConfiguration clientConfiguration,
            SslConfigurator sslConfig) {
        SSLContext sslContext = sslConfig.createSSLContext();
        X509HostnameVerifier hostnameVerifier;
        if (clientConfiguration.isIgnoreSSLErrors()) {
            ignoreSslCertificateErrorInit(sslContext);
            hostnameVerifier = new AllowAllHostnameVerifier();
        } else {
            hostnameVerifier = new StrictHostnameVerifier();
        }

        LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
                hostnameVerifier);

        final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory).build();
        return new PoolingHttpClientConnectionManager(registry);
    }

    private static void ignoreSslCertificateErrorInit(SSLContext sslContext) {
        try {
            sslContext.init(null, new TrustManager[] { new X509TrustManager() {
                @Override
                public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
                        throws CertificateException {
                }

                @Override
                public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
                        throws CertificateException {
                }

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            } }, new SecureRandom());
        } catch (KeyManagementException e) {
            log.warn("SSL context initialization error: ", e);
        }
    }

    <T> List<T> requestMetaDataList(Class<T> clazz, QueryPart<T> query) {
        return requestList(clientConfiguration.getMetadataUrl(), clazz, query, null);
    }

    public <T> T requestMetaDataObject(Class<T> clazz, QueryPart<T> query) {
        return requestObject(clientConfiguration.getMetadataUrl(), clazz, query, null);
    }

    public <E> boolean updateMetaData(QueryPart query, RequestProcessor<E> requestProcessor) {
        return update(clientConfiguration.getMetadataUrl(), query, requestProcessor);
    }

    public <E> boolean updateData(QueryPart query, RequestProcessor<E> requestProcessor) {
        return update(clientConfiguration.getDataUrl(), query, requestProcessor);
    }

    public boolean updateData(QueryPart query, String data) {
        return update(clientConfiguration.getDataUrl(), query, RequestProcessor.post(data), MediaType.TEXT_PLAIN);
    }

    public <T, E> List<T> requestDataList(Class<T> clazz, QueryPart<T> query,
            RequestProcessor<E> requestProcessor) {
        String url = clientConfiguration.getDataUrl();
        return requestList(url, clazz, query, requestProcessor);
    }

    public <T, E> T requestData(Class<T> clazz, QueryPart<T> query, RequestProcessor<E> requestProcessor) {
        String url = clientConfiguration.getDataUrl();
        return requestObject(url, clazz, query, requestProcessor);
    }

    private <T, E> List<T> requestList(String url, Class<T> resultClass, QueryPart<T> query,
            RequestProcessor<E> requestProcessor) {
        Response response = doRequest(url, query, requestProcessor);
        if (response.getStatus() == HTTP_STATUS_OK) {
            return response.readEntity(listType(resultClass));
        } else if (response.getStatus() == HTTP_STATUS_NOT_FOUND) {
            return Collections.emptyList();
        } else {
            throw buildException(response);
        }
    }

    private <T, E> T requestObject(String url, Class<T> resultClass, QueryPart<T> query,
            RequestProcessor<E> requestProcessor) {
        Response response = doRequest(url, query, requestProcessor);
        if (response.getStatus() == HTTP_STATUS_OK) {
            return response.readEntity(resultClass);
        } else if (response.getStatus() == HTTP_STATUS_NOT_FOUND) {
            buildAndLogServerError(response);
            return null;
        } else {
            throw buildException(response);
        }
    }

    private <E> boolean update(String url, QueryPart query, RequestProcessor<E> requestProcessor) {
        Response response = doRequest(url, query, requestProcessor);
        fixApacheHttpClientBlocking(response);
        if (response.getStatus() == HTTP_STATUS_OK) {
            return true;
        } else if (response.getStatus() == HTTP_STATUS_FAIL) {
            return false;
        } else {
            throw buildException(response);
        }
    }

    private <E> boolean update(String url, QueryPart query, RequestProcessor<E> requestProcessor,
            String mediaType) {
        Response response = doRequest(url, query, requestProcessor, mediaType);
        fixApacheHttpClientBlocking(response);
        if (response.getStatus() == HTTP_STATUS_OK) {
            return true;
        } else if (response.getStatus() == HTTP_STATUS_FAIL) {
            return false;
        } else {
            throw buildException(response);
        }
    }

    private AtsdServerException buildException(Response response) {
        ServerError serverError = buildAndLogServerError(response);
        return new AtsdServerException(response.getStatusInfo().getReasonPhrase() + " (" + response.getStatus()
                + ")" + ((serverError == null) ? "" : (", " + serverError.getMessage())));
    }

    private ServerError buildAndLogServerError(Response response) {
        ServerError serverError = null;
        try {
            serverError = response.readEntity(ServerError.class);
            log.warn("Server error: {}", serverError);
        } catch (Throwable e) {
            log.warn("Couldn't read error message", e);
        }
        return serverError;
    }

    private <T, E> Response doRequest(String url, QueryPart<T> query, RequestProcessor<E> requestProcessor) {
        return doRequest(url, query, requestProcessor, JSON);
    }

    private <T, E> Response doRequest(String url, QueryPart<T> query, RequestProcessor<E> requestProcessor,
            String mediaType) {
        WebTarget target = client.target(url);
        target = query.fill(target);
        log.debug("url = {}", target.getUri());
        Invocation.Builder request = target.request(mediaType);

        Response response = null;
        try {
            if (requestProcessor == null) {
                response = request.get();
            } else {
                response = requestProcessor.process(request, mediaType);
            }
        } catch (Throwable e) {
            throw new AtsdClientException("Error while processing the request", e);
        }
        return response;
    }

    private <T> GenericType<List<T>> listType(final Class<T> clazz) {
        ParameterizedType genericType = new ParameterizedType() {
            public Type[] getActualTypeArguments() {
                return new Type[] { clazz };
            }

            public Type getRawType() {
                return List.class;
            }

            public Type getOwnerType() {
                return List.class;
            }
        };
        return new GenericType<List<T>>(genericType) {
        };
    }

    void close() {
        if (client != null) {
            client.close();
        }
    }

    private static void fixApacheHttpClientBlocking(Response response) {
        Object entity = response.getEntity();
        if (entity instanceof InputStream) {
            IOUtils.closeQuietly((InputStream) entity);
        }
    }
}