/*
 * 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.
 */

#ifndef _THRIFT_SERVER_TSERVER_H_
#define _THRIFT_SERVER_TSERVER_H_ 1

#include <thrift/TProcessor.h>
#include <thrift/transport/TServerTransport.h>
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/concurrency/Thread.h>

#include <boost/shared_ptr.hpp>

namespace apache { namespace thrift { namespace server {

using apache::thrift::TProcessor;
using apache::thrift::protocol::TBinaryProtocolFactory;
using apache::thrift::protocol::TProtocol;
using apache::thrift::protocol::TProtocolFactory;
using apache::thrift::transport::TServerTransport;
using apache::thrift::transport::TTransport;
using apache::thrift::transport::TTransportFactory;

/**
 * Virtual interface class that can handle events from the server core. To
 * use this you should subclass it and implement the methods that you care
 * about. Your subclass can also store local data that you may care about,
 * such as additional "arguments" to these methods (stored in the object
 * instance's state).
 */
class TServerEventHandler {
 public:

  virtual ~TServerEventHandler() {}

  /**
   * Called before the server begins.
   */
  virtual void preServe() {}

  /**
   * Called when a new client has connected and is about to being processing.
   */
  virtual void* createContext(boost::shared_ptr<TProtocol> input,
                              boost::shared_ptr<TProtocol> output) {
    (void)input;
    (void)output;
    return NULL;
  }

  /**
   * Called when a client has finished request-handling to delete server
   * context.
   */
  virtual void deleteContext(void* serverContext,
                             boost::shared_ptr<TProtocol>input,
                             boost::shared_ptr<TProtocol>output) {
    (void)serverContext;
    (void)input;
    (void)output;
  }

  /**
   * Called when a client is about to call the processor.
   */
  virtual void processContext(void* serverContext,
                              boost::shared_ptr<TTransport> transport) {
    (void)serverContext;
    (void)transport;
}

 protected:

  /**
   * Prevent direct instantiation.
   */
  TServerEventHandler() {}

};

/**
 * Thrift server.
 *
 */
class TServer : public concurrency::Runnable {
 public:

  virtual ~TServer() {}

  virtual void serve() = 0;

  virtual void stop() {}

  // Allows running the server as a Runnable thread
  virtual void run() {
    serve();
  }

  boost::shared_ptr<TProcessorFactory> getProcessorFactory() {
    return processorFactory_;
  }

  boost::shared_ptr<TServerTransport> getServerTransport() {
    return serverTransport_;
  }

  boost::shared_ptr<TTransportFactory> getInputTransportFactory() {
    return inputTransportFactory_;
  }

  boost::shared_ptr<TTransportFactory> getOutputTransportFactory() {
    return outputTransportFactory_;
  }

  boost::shared_ptr<TProtocolFactory> getInputProtocolFactory() {
    return inputProtocolFactory_;
  }

  boost::shared_ptr<TProtocolFactory> getOutputProtocolFactory() {
    return outputProtocolFactory_;
  }

  boost::shared_ptr<TServerEventHandler> getEventHandler() {
    return eventHandler_;
  }

protected:
  template<typename ProcessorFactory>
  TServer(const boost::shared_ptr<ProcessorFactory>& processorFactory,
          THRIFT_OVERLOAD_IF(ProcessorFactory, TProcessorFactory)):
    processorFactory_(processorFactory) {
    setInputTransportFactory(boost::shared_ptr<TTransportFactory>(
          new TTransportFactory()));
    setOutputTransportFactory(boost::shared_ptr<TTransportFactory>(
          new TTransportFactory()));
    setInputProtocolFactory(boost::shared_ptr<TProtocolFactory>(
          new TBinaryProtocolFactory()));
    setOutputProtocolFactory(boost::shared_ptr<TProtocolFactory>(
          new TBinaryProtocolFactory()));
  }

  template<typename Processor>
  TServer(const boost::shared_ptr<Processor>& processor,
          THRIFT_OVERLOAD_IF(Processor, TProcessor)):
    processorFactory_(new TSingletonProcessorFactory(processor)) {
    setInputTransportFactory(boost::shared_ptr<TTransportFactory>(new TTransportFactory()));
    setOutputTransportFactory(boost::shared_ptr<TTransportFactory>(new TTransportFactory()));
    setInputProtocolFactory(boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
    setOutputProtocolFactory(boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
  }

  template<typename ProcessorFactory>
  TServer(const boost::shared_ptr<ProcessorFactory>& processorFactory,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          THRIFT_OVERLOAD_IF(ProcessorFactory, TProcessorFactory)):
    processorFactory_(processorFactory),
    serverTransport_(serverTransport) {
    setInputTransportFactory(boost::shared_ptr<TTransportFactory>(
          new TTransportFactory()));
    setOutputTransportFactory(boost::shared_ptr<TTransportFactory>(
          new TTransportFactory()));
    setInputProtocolFactory(boost::shared_ptr<TProtocolFactory>(
          new TBinaryProtocolFactory()));
    setOutputProtocolFactory(boost::shared_ptr<TProtocolFactory>(
          new TBinaryProtocolFactory()));
  }

  template<typename Processor>
  TServer(const boost::shared_ptr<Processor>& processor,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          THRIFT_OVERLOAD_IF(Processor, TProcessor)):
    processorFactory_(new TSingletonProcessorFactory(processor)),
    serverTransport_(serverTransport) {
    setInputTransportFactory(boost::shared_ptr<TTransportFactory>(new TTransportFactory()));
    setOutputTransportFactory(boost::shared_ptr<TTransportFactory>(new TTransportFactory()));
    setInputProtocolFactory(boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
    setOutputProtocolFactory(boost::shared_ptr<TProtocolFactory>(new TBinaryProtocolFactory()));
  }

  template<typename ProcessorFactory>
  TServer(const boost::shared_ptr<ProcessorFactory>& processorFactory,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          const boost::shared_ptr<TTransportFactory>& transportFactory,
          const boost::shared_ptr<TProtocolFactory>& protocolFactory,
          THRIFT_OVERLOAD_IF(ProcessorFactory, TProcessorFactory)):
    processorFactory_(processorFactory),
    serverTransport_(serverTransport),
    inputTransportFactory_(transportFactory),
    outputTransportFactory_(transportFactory),
    inputProtocolFactory_(protocolFactory),
    outputProtocolFactory_(protocolFactory) {}

  template<typename Processor>
  TServer(const boost::shared_ptr<Processor>& processor,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          const boost::shared_ptr<TTransportFactory>& transportFactory,
          const boost::shared_ptr<TProtocolFactory>& protocolFactory,
          THRIFT_OVERLOAD_IF(Processor, TProcessor)):
    processorFactory_(new TSingletonProcessorFactory(processor)),
    serverTransport_(serverTransport),
    inputTransportFactory_(transportFactory),
    outputTransportFactory_(transportFactory),
    inputProtocolFactory_(protocolFactory),
    outputProtocolFactory_(protocolFactory) {}

  template<typename ProcessorFactory>
  TServer(const boost::shared_ptr<ProcessorFactory>& processorFactory,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          const boost::shared_ptr<TTransportFactory>& inputTransportFactory,
          const boost::shared_ptr<TTransportFactory>& outputTransportFactory,
          const boost::shared_ptr<TProtocolFactory>& inputProtocolFactory,
          const boost::shared_ptr<TProtocolFactory>& outputProtocolFactory,
          THRIFT_OVERLOAD_IF(ProcessorFactory, TProcessorFactory)):
    processorFactory_(processorFactory),
    serverTransport_(serverTransport),
    inputTransportFactory_(inputTransportFactory),
    outputTransportFactory_(outputTransportFactory),
    inputProtocolFactory_(inputProtocolFactory),
    outputProtocolFactory_(outputProtocolFactory) {}

  template<typename Processor>
  TServer(const boost::shared_ptr<Processor>& processor,
          const boost::shared_ptr<TServerTransport>& serverTransport,
          const boost::shared_ptr<TTransportFactory>& inputTransportFactory,
          const boost::shared_ptr<TTransportFactory>& outputTransportFactory,
          const boost::shared_ptr<TProtocolFactory>& inputProtocolFactory,
          const boost::shared_ptr<TProtocolFactory>& outputProtocolFactory,
          THRIFT_OVERLOAD_IF(Processor, TProcessor)):
    processorFactory_(new TSingletonProcessorFactory(processor)),
    serverTransport_(serverTransport),
    inputTransportFactory_(inputTransportFactory),
    outputTransportFactory_(outputTransportFactory),
    inputProtocolFactory_(inputProtocolFactory),
    outputProtocolFactory_(outputProtocolFactory) {}

  /**
   * Get a TProcessor to handle calls on a particular connection.
   *
   * This method should only be called once per connection (never once per
   * call).  This allows the TProcessorFactory to return a different processor
   * for each connection if it desires.
   */
  boost::shared_ptr<TProcessor> getProcessor(
      boost::shared_ptr<TProtocol> inputProtocol,
      boost::shared_ptr<TProtocol> outputProtocol,
      boost::shared_ptr<TTransport> transport) {
    TConnectionInfo connInfo;
    connInfo.input = inputProtocol;
    connInfo.output = outputProtocol;
    connInfo.transport = transport;
    return processorFactory_->getProcessor(connInfo);
  }

  // Class variables
  boost::shared_ptr<TProcessorFactory> processorFactory_;
  boost::shared_ptr<TServerTransport> serverTransport_;

  boost::shared_ptr<TTransportFactory> inputTransportFactory_;
  boost::shared_ptr<TTransportFactory> outputTransportFactory_;

  boost::shared_ptr<TProtocolFactory> inputProtocolFactory_;
  boost::shared_ptr<TProtocolFactory> outputProtocolFactory_;

  boost::shared_ptr<TServerEventHandler> eventHandler_;

public:
  void setInputTransportFactory(boost::shared_ptr<TTransportFactory> inputTransportFactory) {
    inputTransportFactory_ = inputTransportFactory;
  }

  void setOutputTransportFactory(boost::shared_ptr<TTransportFactory> outputTransportFactory) {
    outputTransportFactory_ = outputTransportFactory;
  }

  void setInputProtocolFactory(boost::shared_ptr<TProtocolFactory> inputProtocolFactory) {
    inputProtocolFactory_ = inputProtocolFactory;
  }

  void setOutputProtocolFactory(boost::shared_ptr<TProtocolFactory> outputProtocolFactory) {
    outputProtocolFactory_ = outputProtocolFactory;
  }

  void setServerEventHandler(boost::shared_ptr<TServerEventHandler> eventHandler) {
    eventHandler_ = eventHandler;
  }

};

/**
 * Helper function to increase the max file descriptors limit
 * for the current process and all of its children.
 * By default, tries to increase it to as much as 2^24.
 */
#ifdef HAVE_SYS_RESOURCE_H
 int increase_max_fds(int max_fds=(1<<24));
#endif

}}} // apache::thrift::server

#endif // #ifndef _THRIFT_SERVER_TSERVER_H_
