All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.bmc.http.client.jersey.JerseyHttpClientBuilder Maven / Gradle / Ivy

/**
 * Copyright (c) 2016, 2022, Oracle and/or its affiliates.  All rights reserved.
 * This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
 */
package com.oracle.bmc.http.client.jersey;

import com.oracle.bmc.http.client.Serialization;
import com.oracle.bmc.http.client.ClientProperty;
import com.oracle.bmc.http.client.HttpClient;
import com.oracle.bmc.http.client.HttpClientBuilder;
import com.oracle.bmc.http.client.KeyStoreWithPassword;
import com.oracle.bmc.http.client.ProxyConfiguration;
import com.oracle.bmc.http.client.RequestInterceptor;
import com.oracle.bmc.http.client.StandardClientProperties;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.NoConnectionReuseStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.RequestContent;
import org.glassfish.jersey.apache.connector.ApacheClientProperties;
import org.glassfish.jersey.apache.connector.ApacheConnectionClosingStrategy;
import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
import org.glassfish.jersey.apache.connector.ApacheHttpClientBuilderConfigurator;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.RequestEntityProcessing;
import org.glassfish.jersey.internal.InternalProperties;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJaxbJsonProvider;
import org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyStore;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

final class JerseyHttpClientBuilder implements HttpClientBuilder {
    private static final Logger LOG = LoggerFactory.getLogger(JerseyHttpClientBuilder.class);

    public static ConnectionReuseStrategy DEFAULT_CONNECTION_REUSE_STRATEGY =
            new NoConnectionReuseStrategy();
    public static HttpRequestRetryHandler DEFAULT_REQUEST_RETRY_HANDLER =
            new DefaultHttpRequestRetryHandler(0, false);

    public static final JacksonJsonProvider JACKSON_JSON_PROVIDER =
            new JacksonJaxbJsonProvider(
                    Serialization.getObjectMapper(), JacksonJaxbJsonProvider.DEFAULT_ANNOTATIONS);

    private final Collection> requestInterceptors =
            new ArrayList<>();
    private final Map properties = new HashMap<>();
    private URI baseUri;
    private boolean isApacheNonBufferingClient = false;
    private KeyStoreWithPassword keyStore;
    private KeyStore trustStore;
    private HostnameVerifier hostnameVerifier;
    private SSLContext sslContext;
    private boolean useApacheConnector = true;

    JerseyHttpClientBuilder() {
        // buffer by default, for signing and better error messages.
        properties.put(
                ClientProperties.REQUEST_ENTITY_PROCESSING, RequestEntityProcessing.BUFFERED);
        if (JerseyHttpProvider.isApacheDependencyPresent) {
            properties.put(
                    ApacheClientProperties.REQUEST_CONFIG,
                    RequestConfig.custom()
                            .setContentCompressionEnabled(false)
                            .setExpectContinueEnabled(false)
                            .build());
            properties.put(
                    ApacheClientProperties.CONNECTION_CLOSING_STRATEGY,
                    new ApacheConnectionClosingStrategy.ImmediateClosingStrategy());
            PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
            cm.setMaxTotal(50);
            cm.setDefaultMaxPerRoute(50);
            properties.put(ApacheClientProperties.CONNECTION_MANAGER, cm);
            property(
                    com.oracle.bmc.http.client.jersey.ApacheClientProperties
                            .CONNECTION_MANAGER_SHARED,
                    false);
            property(
                    com.oracle.bmc.http.client.jersey.ApacheClientProperties.RETRY_HANDLER,
                    DEFAULT_REQUEST_RETRY_HANDLER);
            property(
                    com.oracle.bmc.http.client.jersey.ApacheClientProperties.REUSE_STRATEGY,
                    DEFAULT_CONNECTION_REUSE_STRATEGY);
        }
    }

    @Override
    public HttpClientBuilder baseUri(URI uri) {
        this.baseUri = uri;
        return this;
    }

    @Override
    public  HttpClientBuilder property(ClientProperty key, T value) {
        if (key == StandardClientProperties.ASYNC_POOL_SIZE) {
            properties.put(ClientProperties.ASYNC_THREADPOOL_SIZE, value);
        } else if (key == StandardClientProperties.READ_TIMEOUT) {
            properties.put(ClientProperties.READ_TIMEOUT, (int) ((Duration) value).toMillis());
        } else if (key == StandardClientProperties.CONNECT_TIMEOUT) {
            properties.put(ClientProperties.CONNECT_TIMEOUT, (int) ((Duration) value).toMillis());
        } else if (key == StandardClientProperties.KEY_STORE) {
            keyStore = (KeyStoreWithPassword) value;
        } else if (key == StandardClientProperties.TRUST_STORE) {
            trustStore = (KeyStore) value;
        } else if (key == StandardClientProperties.HOSTNAME_VERIFIER) {
            hostnameVerifier = (HostnameVerifier) value;
        } else if (key == StandardClientProperties.SSL_CONTEXT) {
            sslContext = (SSLContext) value;
        } else if (key == StandardClientProperties.BUFFER_REQUEST) {
            if ((Boolean) value) {
                properties.put(
                        ClientProperties.REQUEST_ENTITY_PROCESSING,
                        RequestEntityProcessing.BUFFERED);
            } else {
                if (JerseyHttpProvider.isApacheDependencyPresent) {
                    isApacheNonBufferingClient = true;
                } else {
                    properties.put(
                            ClientProperties.REQUEST_ENTITY_PROCESSING,
                            RequestEntityProcessing.CHUNKED);
                }
            }
        } else if (key == StandardClientProperties.PROXY) {
            ProxyConfiguration proxy = (ProxyConfiguration) value;
            String scheme;
            switch (proxy.getProxy().type()) {
                case HTTP:
                    scheme = "http";
                    break;
                case SOCKS:
                    scheme = "socks";
                    break;
                default:
                    throw new IllegalArgumentException(
                            "Unsupported proxy type: " + proxy.getProxy().type());
            }
            InetSocketAddress address = (InetSocketAddress) proxy.getProxy().address();
            try {
                properties.put(
                        ClientProperties.PROXY_URI,
                        new URI(
                                scheme,
                                null,
                                address.getHostString(),
                                address.getPort(),
                                null,
                                null,
                                null));
            } catch (URISyntaxException e) {
                throw new IllegalArgumentException(e);
            }
            if (proxy.getUsername() != null) {
                properties.put(ClientProperties.PROXY_USERNAME, proxy.getUsername());
            }
            if (proxy.getPassword() != null) {
                properties.put(
                        ClientProperties.PROXY_PASSWORD, String.valueOf(proxy.getPassword()));
            }
        } else if (key == JerseyClientProperties.USE_APACHE_CONNECTOR) {
            useApacheConnector = (Boolean) value;
        } else if (key instanceof JerseyClientProperty) {
            properties.put(((JerseyClientProperty) key).jerseyProperty, value);
        } else {
            throw new IllegalArgumentException(
                    "Unknown or unsupported HTTP client property " + key);
        }
        return this;
    }

    @Override
    public HttpClientBuilder registerRequestInterceptor(
            int priority, RequestInterceptor interceptor) {
        requestInterceptors.add(new PrioritizedValue<>(priority, interceptor));
        return this;
    }

    @Override
    public HttpClient build() {
        ClientBuilder clientBuilder = ClientBuilder.newBuilder();
        ClientConfig clientConfig = new ClientConfig();
        if (JerseyHttpProvider.isApacheDependencyPresent && useApacheConnector) {
            LOG.info("Setting connector provider to ApacheConnectorProvider");
            clientConfig.connectorProvider(new ApacheConnectorProvider());
            // need to configure the client so that it doesn't fail if we provide Content-Length
            // headers etc.
            // this will overwrite them instead
            clientConfig.register(
                    new ApacheHttpClientBuilderConfigurator() {
                        @Override
                        public org.apache.http.impl.client.HttpClientBuilder configure(
                                org.apache.http.impl.client.HttpClientBuilder httpClientBuilder) {
                            return httpClientBuilder.addInterceptorLast(new RequestContent(true));
                        }
                    });
        } else {
            LOG.info("Setting connector provider to HttpUrlConnectorProvider");
            // 1) enable workaround for 'patch' requests
            HttpUrlConnectorProvider provider =
                    new HttpUrlConnectorProvider().useSetMethodWorkaround();
            clientConfig.connectorProvider(provider);
        }
        clientBuilder.withConfig(clientConfig);
        clientBuilder.register(JACKSON_JSON_PROVIDER);
        for (Map.Entry prop : properties.entrySet()) {
            clientBuilder.property(prop.getKey(), prop.getValue());
        }
        clientBuilder.property(InternalProperties.JSON_FEATURE, "JacksonFeature");
        if (keyStore != null) {
            clientBuilder.keyStore(keyStore.getKeyStore(), keyStore.getPassword());
        }
        if (sslContext == null) {
            if (trustStore != null) {
                clientBuilder.trustStore(trustStore);
            }
            if (hostnameVerifier != null) {
                clientBuilder.hostnameVerifier(hostnameVerifier);
            }
        } else {
            if (trustStore != null || hostnameVerifier != null) {
                throw new IllegalStateException(
                        "Cannot set trust store or hostname verifier when SSL context is also set");
            }
            clientBuilder.sslContext(sslContext);
        }

        Client client = clientBuilder.build();

        if (LOG.isTraceEnabled()) {
            String collectedProperties = null;
            if (client.getConfiguration() != null
                    && client.getConfiguration().getProperties() != null) {
                collectedProperties =
                        client.getConfiguration().getProperties().entrySet().stream()
                                .sorted(Comparator.comparing(e -> e.getKey()))
                                .map(e -> "['" + e.getKey() + "':'" + e.getValue() + "']")
                                .collect(Collectors.joining(", "));
            }
            LOG.trace(
                    "Creating client '{}' with 'isApacheNonBufferingClient'='{}' and properties: {}",
                    client.getClass().getName(),
                    isApacheNonBufferingClient,
                    collectedProperties);
        }

        return new JerseyHttpClient(
                client,
                client.target(baseUri),
                requestInterceptors.stream()
                        .sorted(Comparator.comparingInt(p -> p.priority))
                        .map(p -> p.value)
                        .collect(Collectors.toList()),
                isApacheNonBufferingClient);
    }

    private static class PrioritizedValue {
        final int priority;
        final T value;

        PrioritizedValue(int priority, T value) {
            this.priority = priority;
            this.value = value;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy