/**
 * Copyright 2011 Facebook
 * @author Wei Chen (weichen@fb.com)
 *
 * A thrift server class that is built on top of DirectServer.
 */

package com.facebook.thrift.direct_server;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import com.facebook.thrift.TProcessor;
import com.facebook.thrift.TProcessorFactory;
import com.facebook.thrift.protocol.TProtocolFactory;
import com.facebook.thrift.server.TServer;
import com.facebook.thrift.transport.TServerTransport;
import com.facebook.thrift.transport.TTransportFactory;


public class TDirectServer extends TServer {

  private final DirectServer directServer_;


  /**
   * Create a TDirectServer instance.
   *
   * Only call this directly if you know what you're doing, otherwise, call
   * either asFiberServer() or asHsHaServer() to get hold of an instance.
   *
   * @param port The server port number
   * @param numSelectors How many selector threads the server needs
   * @param numThreads How many worker threads in the thread pool
   * @param maxPending The max number of tasks in thread pool task queue
   * @param p The TProcessor instance
   * @param onServerListens Runnable to be run when server is listening on port
   */
  public TDirectServer(
    int port,
    int numSelectors,
    int numThreads,
    int maxPending,
    int numSyncHandlers,
    TProcessor p,
    Runnable onServerListens) {

    super(new TProcessorFactory(p), (TServerTransport)null,
          (TTransportFactory)null, (TProtocolFactory)null);

    ThreadPoolExecutor executorService = null;
    if (numThreads > 0) {
        executorService = new ThreadPoolExecutor(
            numThreads, numThreads, 0, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(maxPending),
            new ThreadPoolExecutor.AbortPolicy());
    }

    directServer_ = new DirectServer(
        port, new FramedTransportChannelHandlerFactory(this),
        numSelectors, numSyncHandlers, executorService, onServerListens);
  }

  /**
   * Helper constructor to create a TDirectServer that do not need an extra
   * hook to know when the TDirectServer is ready to serve traffic
   */
  public TDirectServer(
    int port,
    int numSelectors,
    int numThreads,
    int maxPending,
    int numSyncHandlers,
    TProcessor p) {

    this(port, numSelectors, numThreads, maxPending, numSyncHandlers,
      p, new DirectServer.DoNothing());
  }

  /**
   * Create a TDirectServer in fiber server mode.
   *
   * Fiber server mode is for services that requires high throughput
   * and that each request has very short latency.
   *
   * @param port the port the server is listening on
   * @param numSelectors the number of selector threads
   * @param p the processor instance
   * @return a TDirectServer instance in fiber server mode
   */
  static public final TDirectServer asFiberServer(
      int port, int numSelectors, TProcessor p) {
    return new TDirectServer(port, numSelectors, 0, 0, 64, p);
  }

  /**
   * Create a TDirectServer in fiber server mode.
   *
   * Fiber server mode is for services that requires high throughput
   * and that each request has very short latency.
   *
   * @param port the port the server is listening on
   * @param numSelectors the number of selector threads
   * @param p the processor instance
   * @param onServerListens runnable to be run when server is listening
   * @return a TDirectServer instance in fiber server mode
   */
  static public final TDirectServer asFiberServer(
      int port, int numSelectors, TProcessor p, Runnable onServerListens) {
    return new TDirectServer(port, numSelectors, 0, 0, 64, p, onServerListens);
  }

  /**
   * Create a TDirectServer in HsHaServer mode
   *
   * HsHaServer mode is for services that individual requests can take
   * long time to be served, so that we should not handle requests
   * in selector thread.
   *
   * @param port the port the server is listening on
   * @param numThreads the number of threads in the thread pool
   * @param maxPending the max pending tasks in task queue
   * @param p the processor instance
   * @return a TDirectServer instance in HsHaServer mode
   */
  static public final TDirectServer asHsHaServer(
      int port, int numThreads, int maxPending, TProcessor p) {
    // We have a listener thread, plus 3 selector threads.
    return new TDirectServer(port, 3, numThreads, maxPending, 0, p);
  }

  /**
   * Create a TDirectServer in HsHaServer mode
   *
   * HsHaServer mode is for services that individual requests can take
   * long time to be served, so that we should not handle requests
   * in selector thread.
   *
   * @param port the port the server is listening on
   * @param numThreads the number of threads in the thread pool
   * @param maxPending the max pending tasks in task queue
   * @param p the processor instance
   * @param onServerListens runnable to be run when server is listening
   * @return a TDirectServer instance in HsHaServer mode
   */
  static public final TDirectServer asHsHaServer(
      int port, int numThreads, int maxPending, TProcessor p,
      Runnable onServerListens) {
    // We have a listener thread, plus 3 selector threads.
    return new TDirectServer(
        port, 3, numThreads, maxPending, 0, p, onServerListens);
  }

  // Expose DirectServer so that callers can tune parameters
  public DirectServer directServer() {
    return directServer_;
  }

  TProcessor getProcessor() {
    return processorFactory_.getProcessor(null);
  }

  // Main serving loop, will block until the server quit.
  @Override public void serve() {
    directServer_.serve();
  }

  @Override
  public void stop() {
    directServer_.stop();
  }
};
