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

com.hubspot.horizon.ning.NingAsyncHttpClient Maven / Gradle / Ivy

There is a newer version: 0.3.1
Show newest version
package com.hubspot.horizon.ning;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.jboss.netty.channel.socket.nio.BossPool;
import org.jboss.netty.channel.socket.nio.NioClientBoss;
import org.jboss.netty.channel.socket.nio.NioClientBossPool;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioWorker;
import org.jboss.netty.channel.socket.nio.NioWorkerPool;
import org.jboss.netty.channel.socket.nio.WorkerPool;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.ThreadNameDeterminer;
import org.jboss.netty.util.ThreadRenamingRunnable;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.hubspot.horizon.AsyncHttpClient;
import com.hubspot.horizon.HttpConfig;
import com.hubspot.horizon.HttpRequest;
import com.hubspot.horizon.HttpRequest.Options;
import com.hubspot.horizon.HttpResponse;
import com.hubspot.horizon.ning.internal.AcceptEncodingRequestFilter;
import com.hubspot.horizon.ning.internal.EmptyCallback;
import com.hubspot.horizon.ning.internal.NingCompletionHandler;
import com.hubspot.horizon.ning.internal.NingFuture;
import com.hubspot.horizon.ning.internal.NingHostnameVerifier;
import com.hubspot.horizon.ning.internal.NingHttpRequestConverter;
import com.hubspot.horizon.ning.internal.NingRetryHandler;
import com.hubspot.horizon.ning.internal.NingSSLContext;
import com.ning.http.client.AsyncHttpClientConfig;
import com.ning.http.client.Request;
import com.ning.http.client.extra.ThrottleRequestFilter;
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;

public class NingAsyncHttpClient implements AsyncHttpClient {
  private static final ExecutorService BOSS_EXECUTOR = newExecutor("NioBoss");
  private static final HashedWheelTimer TIMER = new HashedWheelTimer(newThreadFactory("NioTimer"));

  static {
    ThreadRenamingRunnable.setThreadNameDeterminer(ThreadNameDeterminer.CURRENT);
  }

  private final com.ning.http.client.AsyncHttpClient ningClient;
  private final NingHttpRequestConverter requestConverter;
  private final Options defaultOptions;
  private final ObjectMapper mapper;
  private final NioClientSocketChannelFactory channelFactory;
  private final ExecutorService workerExecutorService;

  public NingAsyncHttpClient() {
    this(HttpConfig.newBuilder().build());
  }

  public NingAsyncHttpClient(HttpConfig config) {
    Preconditions.checkNotNull(config);

    NettyAsyncHttpProviderConfig nettyConfig = new NettyAsyncHttpProviderConfig();
    int workerThreads = Math.min(Runtime.getRuntime().availableProcessors(), 4);
    this.workerExecutorService = newWorkerThreadPool(workerThreads);
    this.channelFactory = newSocketChannelFactory(this.workerExecutorService, workerThreads);
    nettyConfig.setSocketChannelFactory(this.channelFactory);
    nettyConfig.setNettyTimer(TIMER);

    AsyncHttpClientConfig ningConfig = new AsyncHttpClientConfig.Builder()
            .addRequestFilter(new ThrottleRequestFilter(config.getMaxConnections()))
            .addRequestFilter(new AcceptEncodingRequestFilter())
            .setMaxConnectionsPerHost(config.getMaxConnectionsPerHost())
            .setConnectionTTL(config.getConnectionTtlMillis())
            .setConnectTimeout(config.getConnectTimeoutMillis())
            .setRequestTimeout(config.getRequestTimeoutMillis())
            .setReadTimeout(config.getRequestTimeoutMillis())
            .setMaxRedirects(config.getMaxRedirects())
            .setFollowRedirect(config.isFollowRedirects())
            .setHostnameVerifier(new NingHostnameVerifier(config.getSSLConfig()))
            .setSSLContext(NingSSLContext.forConfig(config.getSSLConfig()))
            .setAsyncHttpClientProviderConfig(nettyConfig)
            .setUserAgent(config.getUserAgent())
            .setIOThreadMultiplier(1)
            .build();

    this.ningClient = new com.ning.http.client.AsyncHttpClient(ningConfig);
    this.requestConverter = new NingHttpRequestConverter(config.getObjectMapper());
    this.defaultOptions = config.getOptions();
    this.mapper = config.getObjectMapper();
  }

  @Override
  public ListenableFuture execute(HttpRequest request) {
    return execute(Preconditions.checkNotNull(request), Options.DEFAULT);
  }

  @Override
  public ListenableFuture execute(HttpRequest request, Options options) {
    return internalExecute(request, options, EmptyCallback.INSTANCE);
  }

  @Override
  public void execute(HttpRequest request, Callback callback) {
    execute(Preconditions.checkNotNull(request), Options.DEFAULT, callback);
  }

  @Override
  public void execute(HttpRequest request, Options options, Callback callback) {
    internalExecute(request, options, callback);
  }

  private ListenableFuture internalExecute(HttpRequest request, Options options, Callback callback) {
    Preconditions.checkNotNull(request);
    Preconditions.checkNotNull(options);
    Preconditions.checkNotNull(callback);

    NingRetryHandler retryHandler = new NingRetryHandler(defaultOptions.mergeFrom(options));
    NingFuture future = new NingFuture(callback);

    final NingCompletionHandler completionHandler = new NingCompletionHandler(request, future, retryHandler, mapper);
    final Request ningRequest = requestConverter.convert(request);
    Runnable runnable = new Runnable() {

      @Override
      public void run() {
        try {
          ningClient.executeRequest(ningRequest, completionHandler);
        } catch (RuntimeException e) {
          completionHandler.onThrowable(e);
        }
      }
    };
    retryHandler.setRetryRunnable(runnable);

    runnable.run();
    return future;
  }

  private static ExecutorService newWorkerThreadPool(int threads) {
    ThreadPoolExecutor workerPool = new ThreadPoolExecutor(
            threads,
            threads,
            60L,
            TimeUnit.SECONDS,
            new SynchronousQueue(),
            newThreadFactory("NioWorker")
    );

    workerPool.allowCoreThreadTimeOut(true);
    return workerPool;
  }

  private static NioClientSocketChannelFactory newSocketChannelFactory(
          ExecutorService workerThreadPool,
          int workerThreads
  ) {
    BossPool bossPool = new NioClientBossPool(BOSS_EXECUTOR, 1, TIMER, null);
    WorkerPool workerPool = new NioWorkerPool(workerThreadPool, workerThreads);
    return new NioClientSocketChannelFactory(bossPool, workerPool);
  }

  private static ExecutorService newExecutor(String qualifier) {
    return Executors.newCachedThreadPool(newThreadFactory(qualifier));
  }

  private static ThreadFactory newThreadFactory(String qualifier) {
    String nameFormat = "NingAsyncHttpClient-" + qualifier + "-%d";
    return new ThreadFactoryBuilder().setNameFormat(nameFormat).setDaemon(true).build();
  }

  @Override
  public void close() throws IOException {
    try {
      ningClient.close();
    } finally {
      // Do NOT call releaseExternalResources() here
      // since we maintain our own executors.
      try {
        channelFactory.shutdown();
      } finally {
        workerExecutorService.shutdown();
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy