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

com.proofpoint.http.client.HttpClientBinder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010 Proofpoint, Inc.
 *
 * 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 com.proofpoint.http.client;

import com.google.common.collect.Streams;
import com.google.inject.Binder;
import com.google.inject.Key;
import com.google.inject.PrivateBinder;
import com.google.inject.Scopes;
import com.google.inject.binder.LinkedBindingBuilder;
import com.google.inject.multibindings.Multibinder;
import com.proofpoint.http.client.balancing.BalancingHttpClient;
import com.proofpoint.http.client.balancing.BalancingHttpClientBindingBuilder;
import com.proofpoint.http.client.balancing.BalancingHttpClientConfig;
import com.proofpoint.http.client.balancing.ForBalancingHttpClient;
import com.proofpoint.http.client.balancing.HttpServiceBalancer;
import com.proofpoint.http.client.balancing.HttpServiceBalancerConfig;
import com.proofpoint.http.client.balancing.HttpServiceBalancerUriConfig;
import org.weakref.jmx.ObjectNameBuilder;

import java.lang.annotation.Annotation;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Stream;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.inject.multibindings.Multibinder.newSetBinder;
import static com.proofpoint.configuration.ConfigBinder.bindConfig;
import static com.proofpoint.reporting.ReportBinder.reportBinder;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toList;
import static org.weakref.jmx.guice.ExportBinder.newExporter;

/**
 * Binds {@link HttpClient} implementations.
 *
 * 

The HttpClient Binding EDSL

* *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class);
* * Binds an {@link HttpClient} annotated with the {@code @FooClient} * annotation to an implementation that takes absolute {@link URI}s in * requests. The string {@code "foo"} specifies the prefix for configuration. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .withFilter(SomeFilter.class);
* * Adds an {@link HttpRequestFilter} for modifying requests submitted to the * client. Multiple filters may be added. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .addFilterBinding().toProvider(SomeFilterProvider.class);
* * Adds an {@link HttpRequestFilter} using the Guice binding EDSL. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .withoutTracing();
* * Suppresses the forwarding of trace tokens in the request headers. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .withPrivateIoThreadPool();
* * Specifies that the {@link HttpClient} should have its own IO thread pool * instead of using the shared pool. This is appropriate for clients that have * a high request rate. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .withAlias(BarClient.class);
* * Additionally binds the same {@link HttpClient} annotated with the * {@code @BarClient} annotation. * *
 *     httpClientBinder(binder).bindHttpClient("foo", FooClient.class)
 *         .withoutCertificateVerification();
* * Specifies that the {@link HttpClient} should disable TLS certificate verification. * *
 *     httpClientBinder(binder).bindBalancingHttpClient("foo");
* * Binds an {@link HttpClient} annotated with the {@code @ServiceType("foo")} * annotation to an implementation that takes relative {@link URI}s in * requests, interpreting them relative to URIs specified in configuration. * The string {@code "foo"} also specifies the prefix for configuration. * *
 *     httpClientBinder(binder).bindBalancingHttpClient("foo", "https://foo.example.com");
* * Binds an {@link HttpClient} annotated with the {@code @ServiceType("foo")} * annotation to an implementation that takes relative {@link URI}s in * requests, interpreting them relative to "https://foo.example.com". * The string {@code "foo"} also specifies the prefix for configuration. * *
 *     httpClientBinder(binder).bindKubernetesServiceHttpClient("foo", "bar");
* * Binds an {@link HttpClient} annotated with the {@code @ServiceType("foo")} * annotation to an implementation that takes relative {@link URI}s in * requests, interpreting them relative to the Kubernetes service "foo" in * Kubernetes namespace "bar". * The string {@code "foo"} also specifies the prefix for configuration. * */ public class HttpClientBinder { private final Binder binder; private final Binder rootBinder; private final boolean isPrivate; private HttpClientBinder(Binder binder, Binder rootBinder, boolean isPrivate) { this.binder = requireNonNull(binder, "binder is null").skipSources(getClass()); this.rootBinder = requireNonNull(rootBinder, "rootBinder is null"); this.isPrivate = isPrivate; } /** * Creates a new {@link HttpClientBinder}. See the EDSL examples at {@link HttpClientBinder}. * * @param binder The Guice {@link Binder} to use. */ public static HttpClientBinder httpClientBinder(Binder binder) { return new HttpClientBinder(binder, binder, false); } /** * Creates a new {@link HttpClientBinder} for making a private binding of * an {@link HttpClient}. The configuration and metrics will be bound * globally, as is required by those subsystems. * * See the EDSL examples at {@link HttpClientBinder}. * * @param privateBinder The Guice private {@link Binder} to use. * @param rootBinder The Guice parent {@link Binder} to use for bindings * that cannot be done privately. */ public static HttpClientBinder httpClientPrivateBinder(Binder privateBinder, Binder rootBinder) { return new HttpClientBinder(privateBinder, rootBinder, true); } /** * Binds an {@link HttpClient} to an implementation that takes absolute * {@link URI}s. See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. */ public HttpClientBindingBuilder bindHttpClient(String name, Class annotation) { HttpClientModule module = new HttpClientModule(name, annotation, rootBinder, isPrivate); binder.install(module); HttpClientBindOptions options = new HttpClientBindOptions(); binder.bind(HttpClientBindOptions.class).annotatedWith(annotation).toInstance(options); return new HttpClientBindingBuilder(module, newSetBinder(binder, HttpRequestFilter.class, annotation), options); } /** * Binds an {@link HttpClient} annotated with {@code @ServiceType(type)} * to an implementation that takes relative * {@link URI}s. The requests are balanced against a static set of prefixes * taken from configuration. * * See the EDSL examples at {@link HttpClientBinder}. * * @param type The service type. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String type) { requireNonNull(type, "type is null"); return bindBalancingHttpClient(type, ServiceTypes.serviceType(type), type); } /** * Binds an {@link HttpClient} annotated with {@code @ServiceType(type)} * to an implementation that takes relative * {@link URI}s. The requests are balanced against the provided static set * of prefixes. * * See the EDSL examples at {@link HttpClientBinder}. * * @param type The service type. * @param moreBaseUris The {@link URI} prefixes to balance across. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String type, String baseUri, String... moreBaseUris) { requireNonNull(type, "type is null"); requireNonNull(baseUri, "baseUri is null"); requireNonNull(moreBaseUris, "moreBaseUris is null"); return bindBalancingHttpClient(type, ServiceTypes.serviceType(type), type, Streams.concat(Stream.of(baseUri), Arrays.stream(moreBaseUris)).map(URI::create).collect(toList())); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against the set of prefixes * specified in configuration. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Class annotation) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); bindConfig(binder).bind(HttpServiceBalancerConfig.class).annotatedWith(annotation).prefixedWith("service-client." + annotation.getSimpleName()); bindConfig(binder).bind(HttpServiceBalancerUriConfig.class).annotatedWith(annotation).prefixedWith("service-client." + annotation.getSimpleName()); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class) .toProvider(new ConfiguredStaticHttpServiceBalancerProvider(annotation.getSimpleName(), Key.get(HttpServiceBalancerConfig.class, annotation), Key.get(HttpServiceBalancerUriConfig.class, annotation))) .in(Scopes.SINGLETON); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against the provided static set * of prefixes. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param baseUris The {@link URI} prefixes to balance across. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Class annotation, Collection baseUris) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); requireNonNull(baseUris, "baseUris is null"); checkArgument(!baseUris.isEmpty(), "baseUris is empty"); bindConfig(binder).bind(HttpServiceBalancerConfig.class).annotatedWith(annotation).prefixedWith("service-client." + annotation.getSimpleName()); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class) .toProvider(new StaticHttpServiceBalancerProvider(annotation.getSimpleName(), baseUris, Key.get(HttpServiceBalancerConfig.class, annotation))); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against an * {@link HttpServiceBalancer} obtained from Guice. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param balancerKey The {@link Key} specifying the {@link HttpServiceBalancer} to use. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Class annotation, Key balancerKey) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); requireNonNull(balancerKey, "balancerKey is null"); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class).to(balancerKey); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against the set of prefixes * specified in configuration. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix for the HttpClient. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param serviceName The name of the service being balanced. * Used in metrics and in the configuration prefix for the service balancer. * Ordinarily this is the value of the binding annotation. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Annotation annotation, String serviceName) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); requireNonNull(serviceName, "serviceName is null"); ServiceType serviceType = ServiceTypes.serviceType(serviceName); bindConfig(binder).bind(HttpServiceBalancerConfig.class).annotatedWith(serviceType).prefixedWith("service-client." + serviceName); bindConfig(binder).bind(HttpServiceBalancerUriConfig.class).annotatedWith(serviceType).prefixedWith("service-client." + serviceName); binder.bind(HttpServiceBalancer.class).annotatedWith(serviceType) .toProvider(new ConfiguredStaticHttpServiceBalancerProvider(serviceName, Key.get(HttpServiceBalancerConfig.class, serviceType), Key.get(HttpServiceBalancerUriConfig.class, serviceType))) .in(Scopes.SINGLETON); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class) .to(Key.get(HttpServiceBalancer.class, serviceType)); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation, serviceName); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against the set of prefixes * specified in configuration. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param serviceName The name of the service being balanced. * Used in metrics and in the configuration prefix for the service balancer. * Ordinarily this is the value of the binding annotation. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Class annotation, String serviceName) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); ServiceType serviceType = ServiceTypes.serviceType(serviceName); bindConfig(binder).bind(HttpServiceBalancerConfig.class).annotatedWith(serviceType).prefixedWith("service-client." + serviceName); bindConfig(binder).bind(HttpServiceBalancerUriConfig.class).annotatedWith(serviceType).prefixedWith("service-client." + serviceName); binder.bind(HttpServiceBalancer.class).annotatedWith(serviceType) .toProvider(new ConfiguredStaticHttpServiceBalancerProvider(serviceName, Key.get(HttpServiceBalancerConfig.class, serviceType), Key.get(HttpServiceBalancerUriConfig.class, serviceType))) .in(Scopes.SINGLETON); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class) .to(Key.get(HttpServiceBalancer.class, serviceType)); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation); } private BalancingHttpClientBindingBuilder createBalancingHttpClientBindingBuilder(PrivateBinder privateBinder, String name, Class annotation) { HttpClientBindingBuilder delegateBindingBuilder = httpClientPrivateBinder(privateBinder, binder).bindHttpClient(name, ForBalancingHttpClient.class); bindConfig(privateBinder).bind(BalancingHttpClientConfig.class).prefixedWith(name); privateBinder.bind(HttpClient.class).annotatedWith(annotation).to(BalancingHttpClient.class).in(Scopes.SINGLETON); privateBinder.expose(HttpClient.class).annotatedWith(annotation); reportBinder(binder).export(HttpClient.class).annotatedWith(annotation); newExporter(binder).export(HttpClient.class).annotatedWith(annotation).withGeneratedName(); binder.bind(ScheduledExecutorService.class).annotatedWith(ForBalancingHttpClient.class).toProvider(RetryExecutorProvider.class); return new BalancingHttpClientBindingBuilder(binder, annotation, delegateBindingBuilder); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against the provided static set * of prefixes. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix for the HttpClient. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param serviceName The name of the service being balanced. * Used in metrics and in the configuration prefix for the service balancer. * Ordinarily this is the value of the binding annotation. * @param baseUris The {@link URI} prefixes to balance across. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Annotation annotation, String serviceName, Collection baseUris) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); requireNonNull(baseUris, "baseUris is null"); checkArgument(!baseUris.isEmpty(), "baseUris is empty"); bindConfig(binder).bind(HttpServiceBalancerConfig.class).annotatedWith(annotation).prefixedWith("service-client." + serviceName); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class) .toProvider(new StaticHttpServiceBalancerProvider(serviceName, baseUris, Key.get(HttpServiceBalancerConfig.class, annotation))); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation, serviceName); } /** * Binds an {@link HttpClient} to an implementation that takes relative * {@link URI}s. The requests are balanced against an * {@link HttpServiceBalancer} obtained from Guice. * * See the EDSL examples at {@link HttpClientBinder}. * * @param name The configuration prefix. Should be lowercase hyphen-separated. * @param annotation The binding annotation. * @param serviceName The name of the service being balanced. Used in metrics. Ordinarily this is the value of the binding annotation. * @param balancerKey The {@link Key} specifying the {@link HttpServiceBalancer} to use. */ public BalancingHttpClientBindingBuilder bindBalancingHttpClient(String name, Annotation annotation, String serviceName, Key balancerKey) { requireNonNull(name, "name is null"); requireNonNull(annotation, "annotation is null"); requireNonNull(balancerKey, "balancerKey is null"); PrivateBinder privateBinder = binder.newPrivateBinder(); privateBinder.bind(HttpServiceBalancer.class).annotatedWith(ForBalancingHttpClient.class).to(balancerKey); return createBalancingHttpClientBindingBuilder(privateBinder, name, annotation, serviceName); } /** * Binds an {@link HttpClient} annotated with @ServiceType(type) * to an implementation that uses a Kubernetes service. * * See the EDSL examples at {@link HttpClientBinder}. * * @param type The Kubernetes service type. * @param namespace The Kubernetes namespace serving the service. */ public BalancingHttpClientBindingBuilder bindKubernetesServiceHttpClient(String type, String namespace) { requireNonNull(type, "type is null"); checkArgument(!type.equals(""), "type is empty"); requireNonNull(namespace, "namespace is null"); checkArgument(!namespace.equals(""), "namespace is empty"); return bindBalancingHttpClient(type, "https://" + type + "." + namespace + ".svc.cluster.local"); } private BalancingHttpClientBindingBuilder createBalancingHttpClientBindingBuilder(PrivateBinder privateBinder, String name, Annotation annotation, String serviceName) { HttpClientBindingBuilder delegateBindingBuilder = httpClientPrivateBinder(privateBinder, binder).bindHttpClient(name, ForBalancingHttpClient.class); bindConfig(privateBinder).bind(BalancingHttpClientConfig.class).prefixedWith(name); privateBinder.bind(HttpClient.class).annotatedWith(annotation).to(BalancingHttpClient.class).in(Scopes.SINGLETON); privateBinder.expose(HttpClient.class).annotatedWith(annotation); reportBinder(binder).export(HttpClient.class).annotatedWith(annotation).withNamePrefix("HttpClient." + serviceName); newExporter(binder).export(HttpClient.class).annotatedWith(annotation).as(new ObjectNameBuilder(HttpClient.class.getPackage().getName()) .withProperty("type", "HttpClient") .withProperty("name", name) .build() ); binder.bind(ScheduledExecutorService.class).annotatedWith(ForBalancingHttpClient.class).toProvider(RetryExecutorProvider.class); return new BalancingHttpClientBindingBuilder(binder, annotation, delegateBindingBuilder); } public static class HttpClientBindingBuilder { private final HttpClientModule module; private final Multibinder multibinder; private final HttpClientBindOptions options; private HttpClientBindingBuilder(HttpClientModule module, Multibinder multibinder, HttpClientBindOptions options) { this.module = module; this.multibinder = multibinder; this.options = options; } /** * See the EDSL examples at {@link HttpClientBinder}. */ public HttpClientBindingBuilder withAlias(Class alias) { module.addAlias(alias); return this; } /** * See the EDSL examples at {@link HttpClientBinder}. */ public HttpClientBindingBuilder withAliases(Collection> aliases) { aliases.forEach(module::addAlias); return this; } /** * See the EDSL examples at {@link HttpClientBinder}. */ public LinkedBindingBuilder addFilterBinding() { return multibinder.addBinding(); } /** * See the EDSL examples at {@link HttpClientBinder}. */ public HttpClientBindingBuilder withFilter(Class filterClass) { multibinder.addBinding().to(filterClass); return this; } /** * @deprecated No longer necessary. */ @Deprecated public HttpClientBindingBuilder withTracing() { return this; } /** * See the EDSL examples at {@link HttpClientBinder}. */ public HttpClientBindingBuilder withoutTracing() { options.setWithTracing(false); return this; } /** * @deprecated No longer necessary. */ @Deprecated public HttpClientBindingBuilder withPrivateIoThreadPool() { return this; } /** * See the EDSL examples at {@link HttpClientBinder}. */ public HttpClientBindingBuilder withoutCertificateVerification() { options.setWithoutCertificateVerification(); return this; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy