Package com.facebook.thrift.server

Source Code of com.facebook.thrift.server.TNonblockingServer$SelectThread$TcpReader

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.facebook.thrift.server;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.spi.SelectorProvider;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.facebook.thrift.TByteArrayOutputStream;
import com.facebook.thrift.TException;
import com.facebook.thrift.TProcessor;
import com.facebook.thrift.TProcessorFactory;
import com.facebook.thrift.protocol.TBinaryProtocol;
import com.facebook.thrift.protocol.THeaderProtocol;
import com.facebook.thrift.protocol.TProtocol;
import com.facebook.thrift.protocol.TProtocolFactory;
import com.facebook.thrift.transport.TFramedTransport;
import com.facebook.thrift.transport.TIOStreamTransport;
import com.facebook.thrift.transport.TNonblockingServerTransport;
import com.facebook.thrift.transport.TNonblockingTransport;
import com.facebook.thrift.transport.TTransport;
import com.facebook.thrift.transport.TTransportException;

/**
* A nonblocking TServer implementation. This allows for fairness amongst all
* connected clients in terms of invocations.
*
* This server is inherently single-threaded. If you want a limited thread pool
* coupled with invocation-fairness, see THsHaServer.
*
* To use this server, you MUST use a TFramedTransport at the outermost
* transport, otherwise this server will be unable to determine when a whole
* method call has been read off the wire. Clients must also use
* TFramedTransport.
*/
public class TNonblockingServer extends TServer {
  private static final Logger LOGGER =
    LoggerFactory.getLogger(TNonblockingServer.class.getName());
  private static final int selectWaitTime_ = 500;

  private final ExecutorService tcpWorkerPool_;
  // Flag for stopping the server
  private volatile boolean stopped_ = true;

  private SelectThread selectThread_;

  /**
   * The maximum amount of memory we will allocate to client IO buffers at a
   * time. Without this limit, the server will gladly allocate client buffers
   * right into an out of memory exception, rather than waiting.
   */
  private final long MAX_READ_BUFFER_BYTES;

  protected final Options options_;

  /**
   * How many bytes are currently allocated to read buffers.
   */
  private final AtomicLong readBufferBytesAllocated = new AtomicLong(0);

  /**
   * Create server with given processor and server transport, using
   * TBinaryProtocol for the protocol, TFramedTransport.Factory on both input
   * and output transports. A TProcessorFactory will be created that always
   * returns the specified processor.
   */
  public TNonblockingServer(TProcessor processor,
      TNonblockingServerTransport serverTransport) {
    this(new TProcessorFactory(processor), serverTransport);
  }

  /**
   * Create server with specified processor factory and server transport.
   * TBinaryProtocol is assumed. TFramedTransport.Factory is used on both input
   * and output transports.
   */
  public TNonblockingServer(TProcessorFactory processorFactory,
      TNonblockingServerTransport serverTransport) {
    this(processorFactory, serverTransport, new TFramedTransport.Factory(),
        new TFramedTransport.Factory(), new TBinaryProtocol.Factory(),
        new TBinaryProtocol.Factory());
  }

  public TNonblockingServer(TProcessor processor,
      TNonblockingServerTransport serverTransport,
      TProtocolFactory protocolFactory) {
    this(processor, serverTransport, new TFramedTransport.Factory(),
        new TFramedTransport.Factory(), protocolFactory, protocolFactory);
  }

  public TNonblockingServer(TProcessor processor,
      TNonblockingServerTransport serverTransport,
      TFramedTransport.Factory transportFactory,
      TProtocolFactory protocolFactory) {
    this(processor, serverTransport, transportFactory, transportFactory,
        protocolFactory, protocolFactory);
  }

  public TNonblockingServer(TProcessorFactory processorFactory,
      TNonblockingServerTransport serverTransport,
      TFramedTransport.Factory transportFactory,
      TProtocolFactory protocolFactory) {
    this(processorFactory, serverTransport, transportFactory, transportFactory,
        protocolFactory, protocolFactory);
  }

  public TNonblockingServer(TProcessor processor,
      TNonblockingServerTransport serverTransport,
      TFramedTransport.Factory inputTransportFactory,
      TFramedTransport.Factory outputTransportFactory,
      TProtocolFactory inputProtocolFactory,
      TProtocolFactory outputProtocolFactory) {
    this(new TProcessorFactory(processor), serverTransport,
        inputTransportFactory, outputTransportFactory, inputProtocolFactory,
        outputProtocolFactory);
  }

  public TNonblockingServer(TProcessorFactory processorFactory,
      TNonblockingServerTransport serverTransport,
      TFramedTransport.Factory inputTransportFactory,
      TFramedTransport.Factory outputTransportFactory,
      TProtocolFactory inputProtocolFactory,
      TProtocolFactory outputProtocolFactory) {
    this(processorFactory, serverTransport, inputTransportFactory,
        outputTransportFactory, inputProtocolFactory, outputProtocolFactory,
        new Options());
  }

  public TNonblockingServer(TProcessorFactory processorFactory,
      TNonblockingServerTransport serverTransport,
      TFramedTransport.Factory inputTransportFactory,
      TFramedTransport.Factory outputTransportFactory,
      TProtocolFactory inputProtocolFactory,
      TProtocolFactory outputProtocolFactory, Options options) {
    super(processorFactory, serverTransport, inputTransportFactory,
        outputTransportFactory, inputProtocolFactory, outputProtocolFactory);
    options_ = options;
    options_.validate();
    MAX_READ_BUFFER_BYTES = options.maxReadBufferBytes;
    BlockingQueue<Runnable> queue = (options_.queueSize == 0) ? new SynchronousQueue<Runnable>()
        : new LinkedBlockingQueue<Runnable>(options_.queueSize);
    tcpWorkerPool_ = new ThreadPoolExecutor(options_.minWorkerThreads,
        options_.maxWorkerThreads, Integer.MAX_VALUE, TimeUnit.MILLISECONDS,
        queue, new TThreadFactoryImpl("thrift-tcpworker"));
  }

  protected void submitTask(Runnable r) throws ServerOverloadedException {
    try {
      if (options_.forceSingleThreaded) {
        r.run();
      } else {
        tcpWorkerPool_.execute(r);
      }
    }
    catch (RejectedExecutionException e) {
      throw new ServerOverloadedException(e);
    }
  }

  /**
   * Begin accepting connections and processing invocations.
   */
  public void serve() {
    // start listening, or exit
    if (!startListening()) {
      return;
    }

    // start the selector, or exit
    if (!startSelectorThread()) {
      return;
    }

    // this will block while we serve
    joinSelector();

    // do a little cleanup
    stopListening();
  }

  /**
   * Have the server transport start accepting connections.
   *
   * @return true if we started listening successfully, false if something went
   *         wrong.
   */
  protected boolean startListening() {
    try {
      serverTransport_.listen();
      return true;
    } catch (TTransportException ttx) {
      LOGGER.error("Failed to start listening on server socket!", ttx);
      return false;
    }
  }

  /**
   * Stop listening for connections.
   */
  protected void stopListening() {
    serverTransport_.close();
  }

  /**
   * Start the selector thread running to deal with clients.
   *
   * @return true if everything went ok, false if we couldn't start for some
   *         reason.
   */
  protected boolean startSelectorThread() {
    // start the selector
    try {
      selectThread_ = new SelectThread(
          (TNonblockingServerTransport)serverTransport_);
      stopped_ = false;
      selectThread_.start();
      return true;
    } catch (IOException e) {
      LOGGER.error("Failed to start selector thread!", e);
      return false;
    }
  }

  /**
   * Block until the selector exits.
   */
  protected void joinSelector() {
    // wait until the selector thread exits
    try {
      selectThread_.join();
    } catch (InterruptedException e) {
      // for now, just silently ignore. technically this means we'll have
      // less of
      // a graceful shutdown as a result.
    }
  }

  /**
   * Stop serving and shut everything down.
   */
  public void stop() {
    stopped_ = true;
    if (selectThread_ != null) {
      selectThread_.wakeupSelector();
    }
  }

  protected long getReadBufferBytesAllocated() {
    return readBufferBytesAllocated.get();
  }

  /**
   * Perform an invocation. This method could behave several different ways
   * - invoke immediately inline, queue for separate execution, etc.
   */
  protected void requestInvoke(FrameBuffer frameBuffer)
    throws ServerOverloadedException {
    frameBuffer.invoke();
  }

  public boolean isStopped() {
    return selectThread_.isStopped();
  }

  /**
   * Frame buffer requests cancellation of its preferences.
   *
   * @param frameBuffer
   *          for which cancellation is requested.
   */
  protected void requestCancellation(FrameBuffer frameBuffer) {
    selectThread_.requestCancellation(frameBuffer);
  }

  /**
   * The thread that will be doing all the selecting, managing new connections
   * and those that still need to be read.
   */
  protected class SelectThread extends Thread {

    private static final String name_ = "Thrift-Selector";

    private abstract class TcpHandler implements Runnable {
      protected final SelectionKey sKey_;

      TcpHandler(SelectionKey sKey) {
        sKey_ = sKey;
      }
    }

    private class TcpReader extends TcpHandler {
      TcpReader(SelectionKey sKey) {
        super(sKey);
      }

      public void run() {
        SelectThread.this.handleRead(sKey_);
      }
    }

    private class TcpWriter extends TcpHandler {
      TcpWriter(SelectionKey sKey) {
        super(sKey);
      }

      public void run() {
        SelectThread.this.handleWrite(sKey_);
      }
    }

    private final TNonblockingServerTransport serverTransport;
    private final Selector selector;

    // List of FrameBuffers that want to cancel their keys.
    private final Set<FrameBuffer> cancellations = new HashSet<FrameBuffer>();

    /**
     * Set up the SelectorThread.
     */
    public SelectThread(final TNonblockingServerTransport serverTransport)
      throws IOException {
      super(SelectThread.name_);
      this.serverTransport = serverTransport;
      this.selector = SelectorProvider.provider().openSelector();
      serverTransport.registerSelector(selector);
    }

    public boolean isStopped() {
      return stopped_;
    }

    /**
     * The work loop. Handles both selecting (all IO operations) and managing
     * the selection preferences of all existing connections.
     */
    public void run() {
      while (!stopped_) {
        /* Keeps track of iteration through the selector */
        select();
        processCancellations();
      }
    }

    /**
     * If the selector is blocked, wake it up.
     */
    public void wakeupSelector() {
      selector.wakeup();
    }

    public void requestCancellation(FrameBuffer frameBuffer) {
      synchronized (cancellations) {
        cancellations.add(frameBuffer);
      }
      // wakeup the selector, if it's currently blocked.
      selector.wakeup();
    }

    /**
     * Select and process IO events appropriately:
     * If there are connections to be accepted, accept them.
     * If there are existing connections with data waiting to be read, read it,
     * buffering until a whole frame has been read.
     * If there are any pending responses, buffer them until their target client
     * is available, and then send the data.
     */
    private void select() {
      try {
        // wait for io events.
        selector.select(TNonblockingServer.selectWaitTime_);

        // process the io events we received
        Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
          .iterator();
        while (!stopped_ && selectedKeys.hasNext()) {
          SelectionKey key = selectedKeys.next();
          selectedKeys.remove();

          // skip if not valid
          if (!key.isValid()) {
            cleanupSelectionkey(key);
            continue;
          }

          // if the key is marked Accept, then it has to be the server
          // transport.
          if (key.isAcceptable()) {
            handleAccept();
          } else if (key.isReadable()) {
            // deal with reads
            key.interestOps(0);
            TcpHandler readHandler = new TcpReader(key);
            try {
              TNonblockingServer.this.submitTask(readHandler);
            } catch (ServerOverloadedException e) {
              cleanupSelectionkey(key);
            }
          } else if (key.isWritable()) {
            // deal with writes
            key.interestOps(0);
            TcpHandler writeHandler = new TcpWriter(key);
            try {
              TNonblockingServer.this.submitTask(writeHandler);
            } catch (ServerOverloadedException e) {
              cleanupSelectionkey(key);
            }
          } else {
            LOGGER.warn("Unexpected state in select! " + key.interestOps());
          }
        }
      } catch (IOException e) {
        LOGGER.warn("Got an IOException while selecting!", e);
      }
    }

    private void processCancellations() {
      synchronized (cancellations) {
        for (FrameBuffer fb : cancellations) {
          fb.cancel();
        }
        cancellations.clear();
      }
    }

    /**
     * Accept a new connection.
     */
    private void handleAccept() throws IOException {
      TNonblockingTransport client = null;
      try {
        client = (TNonblockingTransport)serverTransport.accept();
      } catch (TTransportException tte) {
        LOGGER.warn("Exception trying to accept!", tte);
        return;
      }

      SelectionKey clientKey = client.registerSelector(selector, SelectionKey.OP_READ);

      // add this key to the map
      FrameBuffer frameBuffer = new FrameBuffer(client, clientKey);
      clientKey.attach(frameBuffer);
    }

    /**
     * Do the work required to read from a readable client. If the frame is
     * fully read, then invoke the method call.
     */
    private void handleRead(SelectionKey key) {
      FrameBuffer buffer = (FrameBuffer)key.attachment();
      if (buffer.read()) {
        // if the buffer's frame read is complete, invoke the method.
        if (buffer.isFrameFullyRead()) {
          try {
            requestInvoke(buffer);
          } catch (ServerOverloadedException e) {
            TNonblockingServer.this.requestCancellation(buffer);
          }
        } else {
          buffer.changeSelectInterests();
        }
      } else {
        TNonblockingServer.this.requestCancellation(buffer);
      }
    }

    /**
     * Let a writable client get written, if there's data to be written.
     */
    private void handleWrite(SelectionKey key) {
      FrameBuffer buffer = (FrameBuffer)key.attachment();
      if (buffer.write()) {
        buffer.changeSelectInterests();
      } else {
        TNonblockingServer.this.requestCancellation(buffer);
      }
    }

    /**
     * Do connection-close cleanup on a given SelectionKey.
     */
    private void cleanupSelectionkey(SelectionKey key) {
      // remove the records from the two maps
      FrameBuffer buffer = (FrameBuffer)key.attachment();
      if (buffer != null) {
        // close the buffer
        buffer.close();
      }
      // cancel the selection key
      key.cancel();
    }
  } // SelectorThread

  /**
   * Class that implements a sort of state machine around the interaction with
   * a client and an invoker. It manages reading the frame size and frame data,
   * getting it handed off as wrapped transports, and then the writing of
   * response data back to the client. In the process it manages flipping the
   * read and write bits on the selection key for its client.
   */
  protected class FrameBuffer {
    //
    // Possible states for the FrameBuffer state machine.
    //
    // in the midst of reading the frame size off the wire
    private static final int READING_FRAME_SIZE = 1;
    // reading the actual frame data now, but not all the way done yet
    private static final int READING_FRAME = 2;
    // completely read the frame, so an invocation can now happen
    private static final int READ_FRAME_COMPLETE = 3;
    // waiting to get switched to listening for write events
    private static final int AWAITING_REGISTER_WRITE = 4;
    // started writing response data, not fully complete yet
    private static final int WRITING = 6;
    // another thread wants this framebuffer to go back to reading
    private static final int AWAITING_REGISTER_READ = 7;
    // we want our transport and selection key invalidated in the selector
    // thread
    private static final int AWAITING_CLOSE = 8;

    //
    // Instance variables
    //

    private final long creationTime_;
    // the actual transport hooked up to the client.
    private final TNonblockingTransport trans_;

    // the SelectionKey that corresponds to our transport
    private final SelectionKey selectionKey_;

    // where in the process of reading/writing are we?
    private int state_ = READING_FRAME_SIZE;

    // the ByteBuffer we'll be using to write and read, depending on the state
    private ByteBuffer buffer_;

    private TByteArrayOutputStream response_;

    public FrameBuffer(final TNonblockingTransport trans,
        final SelectionKey selectionKey) {
      creationTime_ = System.currentTimeMillis();
      trans_ = trans;
      selectionKey_ = selectionKey;
      buffer_ = ByteBuffer.allocate(4);
    }

    public boolean isValid() {
      long currentTime = System.currentTimeMillis();
      return (currentTime - creationTime_) < options_.timeout;
    }

    /**
     * Give this FrameBuffer a chance to read. The selector loop should have
     * received a read event for this FrameBuffer.
     *
     * @return true if the connection should live on, false if it should be
     *         closed
     */
    public synchronized boolean read() {
      if (state_ == READING_FRAME_SIZE) {
        // try to read the frame size completely
        if (!internalRead()) {
          return false;
        }

        // if the frame size has been read completely, then prepare to read the
        // actual frame.
        if (buffer_.remaining() == 0) {
          // pull out the frame size as an integer.
          int frameSize = buffer_.getInt(0);
          if (frameSize <= 0) {
            LOGGER.error("Read an invalid frame size of " + frameSize
              + ". Are you using TFramedTransport on the client side?");
            return false;
          }

          // if this frame will always be too large for this server, log the
          // error and close the connection.
          int newBufferSize = frameSize + 4;
          if (newBufferSize > MAX_READ_BUFFER_BYTES) {
            LOGGER.error("Read a frame size of " + frameSize
              + ", which is bigger than the maximum allowable buffer size "
              + "for ALL connections.");
            return false;
          }

          // increment the amount of memory allocated to read buffers
          // if this frame will push us over the memory limit, then return.
          // with luck, more memory will free up the next time around.
          if (readBufferBytesAllocated.addAndGet(newBufferSize) > MAX_READ_BUFFER_BYTES) {
            readBufferBytesAllocated.addAndGet(-newBufferSize);
            return true;
          }
          // reallocate the readbuffer as a frame-sized buffer
          buffer_ = ByteBuffer.allocate(newBufferSize);
          // put the frame size at the head of the buffer
          buffer_.putInt(frameSize);

          state_ = READING_FRAME;
        } else {
          // this skips the check of READING_FRAME state below, since we can't
          // possibly go on to that state if there's data left to be read at
          // this one.
          return true;
        }
      }

      // it is possible to fall through from the READING_FRAME_SIZE section
      // to READING_FRAME if there's already some frame data available once
      // READING_FRAME_SIZE is complete.

      if (state_ == READING_FRAME) {
        if (!internalRead()) {
          return false;
        }

        // since we're already in the select loop here for sure, we can just
        // modify our selection key directly.
        if (buffer_.remaining() == 0) {
          state_ = READ_FRAME_COMPLETE;
        }

        return true;
      }

      // if we fall through to this point, then the state must be invalid.
      LOGGER.error("Read was called but state is invalid (" + state_ + ")");

      return false;
    }

    /**
     * Give this FrameBuffer a chance to write its output to the final client.
     */
    public synchronized boolean write() {
      if (state_ == WRITING) {
        try {
          if (trans_.write(buffer_) < 0) {
            return false;
          }
        } catch (IOException e) {
          LOGGER.warn("Got an IOException during write!", e);
          return false;
        }

        // we're done writing. now we need to switch back to reading.
        if (buffer_.remaining() == 0) {
          state_ = AWAITING_REGISTER_READ;
        }

        return true;
      }

      LOGGER.error("Write was called, but state is invalid (" + state_ + ")");
      return false;
    }

    /**
     * Give this FrameBuffer a chance to set its interest depending on state
     */
    public synchronized void changeSelectInterests() {
      if ((state_ == AWAITING_REGISTER_WRITE) || (state_ == WRITING)) {
        state_ = WRITING;
        // set the OP_WRITE interest. Again set it after the assigning of the
        // state in the state machine to avoid inconsistencies between the
        // state machine and the interests.
        selectionKey_.interestOps(SelectionKey.OP_WRITE);
      } else if (state_ == AWAITING_REGISTER_READ) {
        // get ready for another go-around
        buffer_ = ByteBuffer.allocate(4);
        state_ = READING_FRAME_SIZE;
        // Once you set the interest, the selector thread could schedule a read
        // or write on a different thread before you can set the state machine
        // state. That's why the state machine state *MUST* be set before
        // setting the interest
        selectionKey_.interestOps(SelectionKey.OP_READ);
      } else if (state_ == AWAITING_CLOSE) {
        cancel();
      } else if ((state_ == READING_FRAME_SIZE) || (state_ == READING_FRAME)) {
        // Register interest to continue reading.
        selectionKey_.interestOps(SelectionKey.OP_READ);
      } else {
        LOGGER.error("changeSelectInterest was called, but state is invalid ("
                       + state_ + ")");
      }

      // All selector interest ops updates MUST be followed with a call to wake
      // up the selector, unless this thread is currently the selector.
      if (Thread.currentThread() != selectThread_) {
        selectThread_.wakeupSelector();
      }
    }

    public synchronized void cancel() {
      close();
      selectionKey_.cancel();
    }

    /**
     * Shut the connection down.
     */
    public synchronized void close() {
      // if we're being closed due to an error, we might have allocated a
      // buffer that we need to subtract for our memory accounting.
      if (state_ == READING_FRAME || state_ == READ_FRAME_COMPLETE) {
        readBufferBytesAllocated.addAndGet(-buffer_.capacity());
      }
      trans_.close();
    }

    /**
     * Check if this FrameBuffer has a full frame read.
     */
    public synchronized boolean isFrameFullyRead() {
      return state_ == READ_FRAME_COMPLETE;
    }

    /**
     * After the processor has processed the invocation, whatever thread is
     * managing invocations should call this method on this FrameBuffer so we
     * know it's time to start trying to write again. Also, if it turns out that
     * there actually isn't any data in the response buffer, we'll skip trying
     * to write and instead go back to reading.
     */
    private void responseReady() {
      // the read buffer is definitely no longer in use, so we will decrement
      // our read buffer count. we do this here as well as in close because
      // we'd like to free this read memory up as quickly as possible for other
      // clients.
      readBufferBytesAllocated.addAndGet(-buffer_.capacity());

      if (response_.len() == 0) {
        // go straight to reading again. this was probably an oneway method
        state_ = AWAITING_REGISTER_READ;
        buffer_ = null;
      } else {
        buffer_ = ByteBuffer.wrap(response_.get(), 0, response_.len());

        // set state that we're waiting to be switched to write. we do this
        // asynchronously through requestSelectInterestChange() because
        // there is a
        // possibility that we're not in the main thread, and thus currently
        // blocked in select(). (this functionality is in place for the sake of
        // the HsHa server.)
        state_ = AWAITING_REGISTER_WRITE;
      }
      changeSelectInterests();
    }

    /**
     * Actually invoke the method signified by this FrameBuffer.
     */
    public synchronized void invoke() {
      TTransport inTrans;
      TProtocol inProt;
      TProtocol outProt;
      // For THeader inProt and outProt must be the same object
      if (inputProtocolFactory_ instanceof THeaderProtocol.Factory) {
        inTrans = getInputOutputTransport();
        inProt = inputProtocolFactory_.getProtocol(inTrans);
        outProt = inProt;
      } else {
        inTrans = getInputTransport();
        inProt = inputProtocolFactory_.getProtocol(inTrans);
        outProt = outputProtocolFactory_
          .getProtocol(getOutputTransport());
      }

      try {
        TRpcConnectionContext server_ctx = new TRpcConnectionContext(trans_,
                                                                     inProt,
                                                                     outProt);
        processorFactory_.getProcessor(inTrans).process(inProt, outProt, server_ctx);
        responseReady();
        return;
      } catch (TException te) {
        LOGGER.warn("Exception while invoking!", te);
      } catch (Exception e) {
        LOGGER.error("Unexpected exception while invoking!", e);
      }

      // Exceptions will fall through to here.
      // We can clear out the buffer here because it is no longer being used
      readBufferBytesAllocated.addAndGet(-buffer_.capacity());
      buffer_ = null;
      state_ = AWAITING_CLOSE;
      changeSelectInterests();
    }

    /**
     * Wrap the read buffer in a memory-based transport so a processor can read
     * the data it needs to handle an invocation.
     */
    public synchronized TTransport getInputTransport() {
      return inputTransportFactory_.getTransport(new TIOStreamTransport(
          new ByteArrayInputStream(buffer_.array())));
    }

    /**
     * Get the transport that should be used by the invoker for responding.
     */
    private TTransport getOutputTransport() {
      response_ = new TByteArrayOutputStream();
      return outputTransportFactory_.getTransport(new TIOStreamTransport(
          response_));
    }

    /**
     * Get a transport that works for reading and responding
     */
    private TTransport getInputOutputTransport() {
      response_ = new TByteArrayOutputStream();
      return inputTransportFactory_.getTransport(
        new TIOStreamTransport(
          new ByteArrayInputStream(buffer_.array()),
          response_));
    }

    /**
     * Perform a read into buffer.
     *
     * @return true if the read succeeded, false if there was an error or the
     *         connection closed.
     */
    private boolean internalRead() {
      try {
        if (trans_.read(buffer_) < 0) {
          return false;
        }
        return true;
      } catch (IOException e) {
        LOGGER.warn("Got an IOException in internalRead!", e);
        return false;
      }
    }
  } // FrameBuffer

  public static class ServerOverloadedException extends Exception {
    private static final long serialVersionUID = 1L;

    public ServerOverloadedException() {

    }

    public ServerOverloadedException(Throwable th) {
      super(th);
    }
  }

  public static class Options {
    public int minWorkerThreads = 8;
    public int maxWorkerThreads = Integer.MAX_VALUE;
    public boolean forceSingleThreaded = false;
    public int queueSize = Integer.MAX_VALUE;
    public long timeout = 2000;
    public long maxReadBufferBytes = Long.MAX_VALUE;

    public Options() {
    }

    public void validate() {
      if (maxReadBufferBytes <= 1024) {
        throw new IllegalArgumentException(
            "You must allocate at least 1KB to the read buffer.");
      }
    }
  }
}
TOP

Related Classes of com.facebook.thrift.server.TNonblockingServer$SelectThread$TcpReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.