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

com.outbrain.ob1k.client.ClientBuilder Maven / Gradle / Ivy

The newest version!
package com.outbrain.ob1k.client;

import com.outbrain.ob1k.HttpRequestMethodType;
import com.outbrain.ob1k.client.endpoints.AbstractClientEndpoint;
import com.outbrain.ob1k.client.endpoints.AsyncClientEndpoint;
import com.outbrain.ob1k.client.endpoints.StreamClientEndpoint;
import com.outbrain.ob1k.client.endpoints.SyncClientEndpoint;
import com.outbrain.ob1k.client.targets.EmptyTargetProvider;
import com.outbrain.ob1k.client.targets.TargetProvider;
import com.outbrain.ob1k.common.concurrent.ComposableFutureHelper;
import com.outbrain.ob1k.common.filters.AsyncFilter;
import com.outbrain.ob1k.Service;
import com.outbrain.ob1k.common.filters.ServiceFilter;
import com.outbrain.ob1k.common.filters.StreamFilter;
import com.outbrain.ob1k.common.filters.SyncFilter;
import com.outbrain.ob1k.http.common.ContentType;
import com.outbrain.ob1k.common.marshalling.RequestMarshallerRegistry;
import com.outbrain.ob1k.common.marshalling.TypeHelper;
import com.outbrain.ob1k.concurrent.ComposableFuture;
import com.outbrain.ob1k.http.HttpClient;
import com.outbrain.swinfra.metrics.api.MetricFactory;
import rx.Observable;

import java.io.Closeable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author aronen
 */
public class ClientBuilder {

  private final Class type;
  private final List syncFilters;
  private final List asyncFilters;
  private final List streamFilters;
  private final Map endpointDescriptors;
  private final HttpClient.Builder httpClientBuilder;

  private TargetProvider targetProvider = new EmptyTargetProvider();
  private ContentType clientType = ContentType.JSON;

  public ClientBuilder(final Class type) {
    this.type = type;
    this.httpClientBuilder = new HttpClient.Builder();
    this.syncFilters = new ArrayList<>();
    this.asyncFilters = new ArrayList<>();
    this.streamFilters = new ArrayList<>();
    this.endpointDescriptors = new HashMap<>();
  }

  public ClientBuilder addFilter(final ServiceFilter filter) {
    if (filter instanceof SyncFilter) {
      syncFilters.add((SyncFilter) filter);
    }

    if (filter instanceof AsyncFilter) {
      asyncFilters.add((AsyncFilter) filter);
    }

    if (filter instanceof StreamFilter) {
      streamFilters.add((StreamFilter) filter);
    }

    return this;
  }

  public ClientBuilder setProtocol(final ContentType clientType) {
    this.clientType = clientType;
    return this;
  }

  public ClientBuilder followRedirect(final boolean followRedirect) {
    httpClientBuilder.setFollowRedirect(followRedirect);
    return this;
  }

  public ClientBuilder setMetricFactory(final MetricFactory metricFactory) {
    httpClientBuilder.setMetricFactory(metricFactory);
    return this;
  }

  public ClientBuilder setCompression(final boolean compression) {
    httpClientBuilder.setCompressionEnforced(compression);
    return this;
  }

  public ClientBuilder setMaxConnectionsPerHost(final int maxConnectionsPerHost) {
    httpClientBuilder.setMaxConnectionsPerHost(maxConnectionsPerHost);
    setTotalMaxConnections(maxConnectionsPerHost * 2);
    return this;
  }

  public ClientBuilder setTotalMaxConnections(final int maxConnections) {
    httpClientBuilder.setMaxTotalConnections(maxConnections);
    return this;
  }

  public ClientBuilder setResponseMaxSize(final long responseMaxSize) {
    httpClientBuilder.setResponseMaxSize(responseMaxSize);
    return this;
  }

  public ClientBuilder setRetries(final int retries) {
    httpClientBuilder.setRetries(retries);
    return this;
  }

  public ClientBuilder setConnectionTimeout(final int timeout) {
    httpClientBuilder.setConnectionTimeout(timeout);
    return this;
  }

  public ClientBuilder setRequestTimeout(final int timeout) {
    httpClientBuilder.setRequestTimeout(timeout);
    return this;
  }

  public ClientBuilder setReadTimeout(final int timeout) {
    httpClientBuilder.setReadTimeout(timeout);
    return this;
  }

  public ClientBuilder setTargetProvider(final TargetProvider targetProvider) {
    this.targetProvider = targetProvider == null ? new EmptyTargetProvider() : targetProvider;
    return this;
  }

  public ClientBuilder bindEndpoint(final String methodName, final String path, final ServiceFilter... filters) {
    bindEndpoint(methodName, HttpRequestMethodType.ANY, path, filters);
    return this;
  }

  public ClientBuilder bindEndpoint(final String methodName, final HttpRequestMethodType requestMethodType) {
    bindEndpoint(methodName, requestMethodType, methodName);
    return this;
  }

  public ClientBuilder bindEndpoint(final String methodName, final HttpRequestMethodType requestMethodType,
                                       final ServiceFilter... filters) {
    bindEndpoint(methodName, requestMethodType, methodName, filters);
    return this;
  }

  public ClientBuilder bindEndpoint(final String methodName, final ServiceFilter... filters) {
    bindEndpoint(methodName, HttpRequestMethodType.ANY, methodName, filters);
    return this;
  }

  public ClientBuilder bindEndpoint(final String methodName, final HttpRequestMethodType requestMethodType,
                                       final String path, final ServiceFilter... filters) {
    final List serviceFilters;
    if (filters == null) {
      serviceFilters = new ArrayList<>();
    } else {
      serviceFilters = Arrays.asList(filters);
    }
    endpointDescriptors.put(methodName, new EndpointDescriptor(methodName, path, serviceFilters, requestMethodType));
    return this;
  }

  public T build() {
    final ClassLoader loader = ClientBuilder.class.getClassLoader();
    final HttpClient httpClient = httpClientBuilder.build();
    final Map endpoints = extractEndpointsFromType(httpClient);
    final HttpInvocationHandler handler = new HttpInvocationHandler(targetProvider, httpClient, endpoints);

    @SuppressWarnings("unchecked")
    final T proxy = (T) Proxy.newProxyInstance(loader, new Class[]{type, Closeable.class}, handler);
    return proxy;
  }

  private static RequestMarshallerRegistry createRegistry(final Class type) {
    final RequestMarshallerRegistry registry = new RequestMarshallerRegistry();

    final Method[] methods = type.getDeclaredMethods();
    for (final Method method : methods) {
      registry.registerTypes(TypeHelper.extractTypes(method));
    }

    return registry;
  }

  private Map extractEndpointsFromType(final HttpClient httpClient) {

    final Map endpoints = new HashMap<>();
    final Method[] methods = type.getDeclaredMethods();

    for (final Method method : methods) {
      if (isValidEndpoint(method)) {
        final String methodName = method.getName();
        final EndpointDescriptor endpointDescriptor = getEndpointDescriptor(methodName);
        final RequestMarshallerRegistry registry = createRegistry(type);
        final AbstractClientEndpoint.Endpoint endpoint = new AbstractClientEndpoint.Endpoint(method, type, clientType,
                endpointDescriptor.path, endpointDescriptor.requestMethodType);
        final AbstractClientEndpoint clientEndpoint;

        if (isAsync(method)) {
          final List filters = mergeFilters(AsyncFilter.class, asyncFilters, endpointDescriptor.filters);
          clientEndpoint = new AsyncClientEndpoint(httpClient, registry, endpoint, filters.toArray(new AsyncFilter[filters.size()]));
        } else if (isStreaming(method)) {
          final List filters = mergeFilters(StreamFilter.class, streamFilters, endpointDescriptor.filters);
          clientEndpoint = new StreamClientEndpoint(httpClient, registry, endpoint, filters.toArray(new StreamFilter[filters.size()]));
        } else {
          final List filters = mergeFilters(SyncFilter.class, syncFilters, endpointDescriptor.filters);
          clientEndpoint = new SyncClientEndpoint(httpClient, registry, endpoint, filters.toArray(new SyncFilter[filters.size()]));
        }

        endpoints.put(method, clientEndpoint);
      }
    }

    return endpoints;
  }

  private EndpointDescriptor getEndpointDescriptor(final String methodName) {
    return endpointDescriptors.containsKey(methodName) ?
            endpointDescriptors.get(methodName) :
            new EndpointDescriptor(methodName, methodName, null, HttpRequestMethodType.ANY);
  }

  private boolean isValidEndpoint(final Method method) {
    final int modifiers = method.getModifiers();
    return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers);
  }

  private static  List mergeFilters(final Class filterType, final List baseFilters,
                                                                final List specificFilters) {
    final List filters = new ArrayList<>();
    filters.addAll(baseFilters);
    if (specificFilters != null) {
      for (final ServiceFilter filter : specificFilters) {
        if (filter instanceof AsyncFilter) {
          filters.add(filterType.cast(filter));
        }
      }
    }

    return filters;
  }

  private static boolean isAsync(final Method method) {
    return ComposableFutureHelper.isComposableFuture(method.getReturnType());
  }

  private static boolean isStreaming(final Method method) {
    return method.getReturnType() == Observable.class;
  }

  /**
   * Describes how endpoint of service looks for the client builder
   *
   * @author marenzon
   */
  private class EndpointDescriptor {

    public final String method;
    public final String path;
    public final List filters;
    public final HttpRequestMethodType requestMethodType;

    public EndpointDescriptor(final String method, final String path, final List filters,
                              final HttpRequestMethodType requestMethodType) {
      this.method = method;
      this.path = path;
      this.filters = filters;
      this.requestMethodType = requestMethodType;
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy