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

io.quarkiverse.opensearch.client.runtime.OpenSearchTransportHelper Maven / Gradle / Ivy

The newest version!
package io.quarkiverse.opensearch.client.runtime;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;

import jakarta.enterprise.inject.Instance;

import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.util.Timeout;
import org.jboss.logging.Logger;
import org.opensearch.client.json.jackson.JacksonJsonpMapper;
import org.opensearch.client.transport.aws.AwsSdk2Transport;
import org.opensearch.client.transport.aws.AwsSdk2TransportOptions;
import org.opensearch.client.transport.httpclient5.ApacheHttpClient5Transport;
import org.opensearch.client.transport.httpclient5.ApacheHttpClient5TransportBuilder;

import com.fasterxml.jackson.databind.ObjectMapper;

import io.quarkiverse.opensearch.OpenSearchConfig;
import io.quarkiverse.opensearch.SSLContextHelper;
import io.quarkiverse.opensearch.client.AwsSdk2TransportOptionsCallback;
import io.quarkiverse.opensearch.client.OpenSearchTransportConfig;
import io.quarkus.arc.Arc;
import io.quarkus.arc.InstanceHandle;
import io.vertx.core.spi.VertxThreadFactory;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;

public final class OpenSearchTransportHelper {

    private static final VertxThreadFactory VERTX_THREAD_FACTORY = VertxThreadFactory.INSTANCE;

    private OpenSearchTransportHelper() {
        // private constructor
    }

    private static final Logger LOG = Logger.getLogger(OpenSearchTransportHelper.class);

    public static ApacheHttpClient5Transport createApacheHttpClient5Transport(final OpenSearchConfig config,
            final Instance objectMappers)
            throws NoSuchAlgorithmException, KeyManagementException, CertificateException, KeyStoreException, IOException {
        List list = new ArrayList<>();
        for (String s : config.hosts()
                .orElse(List.of("127.0.0.1:9200"))) {
            String[] h = s.split(":");
            HttpHost apply = new HttpHost(config.protocol(), h[0], Integer.valueOf(h[1]));
            list.add(apply);
        }

        final HttpHost[] hosts = list.toArray(new HttpHost[0]);
        final ApacheHttpClient5TransportBuilder builder = ApacheHttpClient5TransportBuilder
                .builder(hosts);

        // use existing ObjectMapper or create new ObjectMapper and register all modules
        final ObjectMapper objectMapper = objectMappers.stream().findFirst()
                .orElse(new ObjectMapper().findAndRegisterModules());
        builder.setMapper(new JacksonJsonpMapper(objectMapper));

        final SSLContext sslContext = SSLContextHelper.createSSLContext(config);

        builder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {

            final ClientTlsStrategyBuilder tlsStrategyBuilder = ClientTlsStrategyBuilder.create()
                    .setSslContext(sslContext);

            // disable hostname verification from config
            if (!config.sslVerifyHostname() || !config.sslVerify()) {
                tlsStrategyBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
            }

            final TlsStrategy tlsStrategy = tlsStrategyBuilder.build();
            final ConnectionConfig connectionConfig = ConnectionConfig.custom()
                    .setConnectTimeout(Timeout.of(config.connectionTimeout()))
                    .setSocketTimeout(Timeout.of(config.socketTimeout()))
                    .build();

            final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder
                    .create()
                    .setTlsStrategy(tlsStrategy)
                    .setMaxConnPerRoute(config.maxConnectionsPerRoute())
                    .setMaxConnTotal(config.maxConnections())
                    .setDefaultConnectionConfig(connectionConfig)
                    .build();

            if (config.username().isPresent() && config.password().isPresent()) {
                if (!"https".equalsIgnoreCase(config.protocol())) {
                    LOG.warn("Using Basic authentication in HTTP implies sending plain text passwords over the wire, " +
                            "use the HTTPS protocol instead.");
                }
                BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
                credentialsProvider.setCredentials(new AuthScope(null, -1),
                        new UsernamePasswordCredentials(config.username().get(), config.password().get().toCharArray()));
                httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
            }

            // Apply configuration from ApacheHttpClient5TransportBuilder.HttpClientConfigCallback implementations annotated with OpenSearchTransportConfig
            HttpAsyncClientBuilder result = httpAsyncClientBuilder.setConnectionManager(connectionManager);
            if (VERTX_THREAD_FACTORY != null) {
                result.setThreadFactory(new ThreadFactory() {

                    @Override
                    public Thread newThread(Runnable r) {
                        return VERTX_THREAD_FACTORY.newVertxThread(r, "HttpClient5OpenSearchTransport", true,
                                config.threadTimeout().getSeconds(), TimeUnit.SECONDS);
                    }
                });
            }
            final Iterable> httpClientConfigCallbackHandles = Arc
                    .container()
                    .select(ApacheHttpClient5TransportBuilder.HttpClientConfigCallback.class,
                            new OpenSearchTransportConfig.Literal())
                    .handles();
            for (InstanceHandle handle : httpClientConfigCallbackHandles) {
                result = handle.get().customizeHttpClient(result);
                handle.close();
            }
            return result;
        });

        // Apply configuration from ApacheHttpClient5TransportBuilder.RequestConfigCallback implementations annotated with OpenSearchTransportConfig
        builder.setRequestConfigCallback(requestConfigBuilder -> {
            RequestConfig.Builder result = requestConfigBuilder;
            final Iterable> requestConfigBuilderHandles = Arc
                    .container()
                    .select(ApacheHttpClient5TransportBuilder.RequestConfigCallback.class,
                            new OpenSearchTransportConfig.Literal())
                    .handles();
            for (InstanceHandle handle : requestConfigBuilderHandles) {
                result = handle.get().customizeRequestConfig(result);
                handle.close();
            }
            return result;
        });
        return builder.build();
    }

    public static AwsSdk2Transport createAwsSdk2Transport(final OpenSearchConfig config,
            final Instance objectMappers) {
        final SdkAsyncHttpClient nettyHttpClient = NettyNioAsyncHttpClient.create();
        AwsSdk2TransportOptions.Builder options = AwsSdk2TransportOptions.builder();

        // use existing ObjectMapper or create new ObjectMapper and register all modules
        final ObjectMapper objectMapper = objectMappers.stream().findFirst()
                .orElse(new ObjectMapper().findAndRegisterModules());
        options.setMapper(new JacksonJsonpMapper(objectMapper));

        if (config.accessKeyId().isPresent() && config.secretAccessKey().isPresent()) {
            options.setCredentials(AwsCredentialsProviderChain.of(
                    StaticCredentialsProvider
                            .create(AwsBasicCredentials.create(config.accessKeyId().get(), config.secretAccessKey().get())),
                    DefaultCredentialsProvider.create()));
        } else {
            options.setCredentials(DefaultCredentialsProvider.create());
        }

        // Apply configuration from AwsSdk2TransportOptionsCallback implementations
        final Iterable> handles = Arc.container()
                .select(AwsSdk2TransportOptionsCallback.class, new OpenSearchTransportConfig.Literal())
                .handles();
        for (InstanceHandle handle : handles) {
            options = handle.get().customize(options);
            handle.close();
        }
        return new AwsSdk2Transport(
                nettyHttpClient,
                config.hosts().orElse(List.of("")).get(0),
                config.awsService().get(),
                Region.of(config.awsRegion()),
                options.build());
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy