THRIFT-1348 C++ Qt bindings
Patch: Doug Rosvick and Vitali Lovich

git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1242900 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/cpp/src/qt/TQTcpServer.cpp b/lib/cpp/src/qt/TQTcpServer.cpp
new file mode 100644
index 0000000..65ad68d
--- /dev/null
+++ b/lib/cpp/src/qt/TQTcpServer.cpp
@@ -0,0 +1,140 @@
+#include "TQTcpServer.h"
+
+#include <protocol/TProtocol.h>
+#include <async/TAsyncProcessor.h>
+
+#include <QTcpSocket>
+
+#include "TQIODeviceTransport.h"
+
+using boost::shared_ptr;
+using apache::thrift::protocol::TProtocol;
+using apache::thrift::protocol::TProtocolFactory;
+using apache::thrift::transport::TTransport;
+using apache::thrift::transport::TTransportException;
+using apache::thrift::transport::TQIODeviceTransport;
+using std::tr1::function;
+using std::tr1::bind;
+
+QT_USE_NAMESPACE
+
+namespace apache { namespace thrift { namespace async {
+
+struct TQTcpServer::ConnectionContext {
+  shared_ptr<QTcpSocket> connection_;
+  shared_ptr<TTransport> transport_;
+  shared_ptr<TProtocol> iprot_;
+  shared_ptr<TProtocol> oprot_;
+
+  explicit ConnectionContext(shared_ptr<QTcpSocket> connection,
+                             shared_ptr<TTransport> transport,
+                             shared_ptr<TProtocol> iprot,
+                             shared_ptr<TProtocol> oprot)
+    : connection_(connection)
+    , transport_(transport)
+    , iprot_(iprot)
+    , oprot_(oprot)
+  {}
+};
+
+TQTcpServer::TQTcpServer(shared_ptr<QTcpServer> server,
+                         shared_ptr<TAsyncProcessor> processor,
+                         shared_ptr<TProtocolFactory> pfact,
+                         QObject* parent)
+  : QObject(parent)
+  , server_(server)
+  , processor_(processor)
+  , pfact_(pfact)
+{
+  connect(server.get(), SIGNAL(newConnection()), SLOT(processIncoming()));
+}
+
+TQTcpServer::~TQTcpServer()
+{
+}
+
+void TQTcpServer::processIncoming()
+{
+  while (server_->hasPendingConnections()) {
+    // take ownership of the QTcpSocket; technically it could be deleted
+    // when the QTcpServer is destroyed, but any real app should delete this
+    // class before deleting the QTcpServer that we are using
+    shared_ptr<QTcpSocket> connection(server_->nextPendingConnection());
+    
+    shared_ptr<TTransport> transport;
+    shared_ptr<TProtocol> iprot;
+    shared_ptr<TProtocol> oprot;
+    
+    try {
+      transport = shared_ptr<TTransport>(new TQIODeviceTransport(connection));
+      iprot = shared_ptr<TProtocol>(pfact_->getProtocol(transport));
+      oprot = shared_ptr<TProtocol>(pfact_->getProtocol(transport));
+    } catch(...) {
+      qWarning("[TQTcpServer] Failed to initialize transports/protocols");
+      continue;
+    }
+    
+    ctxMap_[connection.get()] =
+      shared_ptr<ConnectionContext>(
+         new ConnectionContext(connection, transport, iprot, oprot));
+    
+    connect(connection.get(), SIGNAL(readyRead()), SLOT(beginDecode()));
+    
+    // need to use QueuedConnection since we will be deleting the socket in the slot
+    connect(connection.get(), SIGNAL(disconnected()), SLOT(socketClosed()),
+            Qt::QueuedConnection);
+  }
+}
+
+void TQTcpServer::beginDecode()
+{
+  QTcpSocket* connection(qobject_cast<QTcpSocket*>(sender()));
+  Q_ASSERT(connection);
+
+  if (ctxMap_.find(connection) == ctxMap_.end())
+  {
+    qWarning("[TQTcpServer] Got data on an unknown QTcpSocket");
+    return;
+  }
+  
+  shared_ptr<ConnectionContext> ctx = ctxMap_[connection];
+  
+  try {
+    processor_->process(
+      bind(&TQTcpServer::finish, this,
+           ctx, std::tr1::placeholders::_1),
+      ctx->iprot_, ctx->oprot_);
+  } catch(const TTransportException& ex) {
+    qWarning("[TQTcpServer] TTransportException during processing: '%s'",
+             ex.what());
+    ctxMap_.erase(connection);
+  } catch(...) {
+    qWarning("[TQTcpServer] Unknown processor exception");
+    ctxMap_.erase(connection);
+  }
+}
+
+void TQTcpServer::socketClosed()
+{
+  QTcpSocket* connection(qobject_cast<QTcpSocket*>(sender()));
+  Q_ASSERT(connection);
+
+  if (ctxMap_.find(connection) == ctxMap_.end())
+  {
+    qWarning("[TQTcpServer] Unknown QTcpSocket closed");
+    return;
+  }
+  
+  ctxMap_.erase(connection);
+}
+
+void TQTcpServer::finish(shared_ptr<ConnectionContext> ctx, bool healthy)
+{
+  if (!healthy)
+  {
+    qWarning("[TQTcpServer] Processor failed to process data successfully");
+    ctxMap_.erase(ctx->connection_.get());
+  }
+}
+
+}}} // apache::thrift::async