From 86e89865872cf051e8272e5e33ca669fa68f4f85 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Fri, 10 Feb 2012 19:53:20 +0000 Subject: [PATCH 01/22] 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 --- configure.ac | 57 ++++++--- lib/cpp/Makefile.am | 24 +++- lib/cpp/src/qt/TQIODeviceTransport.cpp | 155 +++++++++++++++++++++++++ lib/cpp/src/qt/TQIODeviceTransport.h | 48 ++++++++ lib/cpp/src/qt/TQTcpServer.cpp | 140 ++++++++++++++++++++++ lib/cpp/src/qt/TQTcpServer.h | 48 ++++++++ lib/cpp/src/qt/moc_TQTcpServer.cpp | 85 ++++++++++++++ lib/cpp/thrift-qt.pc.in | 30 +++++ 8 files changed, 567 insertions(+), 20 deletions(-) create mode 100644 lib/cpp/src/qt/TQIODeviceTransport.cpp create mode 100644 lib/cpp/src/qt/TQIODeviceTransport.h create mode 100644 lib/cpp/src/qt/TQTcpServer.cpp create mode 100644 lib/cpp/src/qt/TQTcpServer.h create mode 100644 lib/cpp/src/qt/moc_TQTcpServer.cpp create mode 100644 lib/cpp/thrift-qt.pc.in diff --git a/configure.ac b/configure.ac index db1591f869d..27ef8d4209d 100755 --- a/configure.ac +++ b/configure.ac @@ -118,10 +118,17 @@ if test "$with_cpp" = "yes"; then AX_LIB_ZLIB([1.2.3]) have_zlib=$success + + PKG_CHECK_MODULES([QT], [QtCore >= 4.3, QtNetwork >= 4.3], have_qt=yes, have_qt=no) + if test "$have_qt" = "yes"; then + AC_PATH_PROGS([QT_MOC], [moc-qt4 moc]) + have_qt=$success + fi fi AM_CONDITIONAL([WITH_CPP], [test "$have_cpp" = "yes"]) AM_CONDITIONAL([AMX_HAVE_LIBEVENT], [test "$have_libevent" = "yes"]) AM_CONDITIONAL([AMX_HAVE_ZLIB], [test "$have_zlib" = "yes"]) +AM_CONDITIONAL([AMX_HAVE_QT], [test "$have_qt" = "yes"]) AX_THRIFT_LIB(c_glib, [C (GLib)], no) if test "$with_c_glib" = "yes"; then @@ -477,6 +484,7 @@ AC_CONFIG_FILES([ lib/cpp/test/Makefile lib/cpp/thrift-nb.pc lib/cpp/thrift-z.pc + lib/cpp/thrift-qt.pc lib/cpp/thrift.pc lib/c_glib/Makefile lib/c_glib/thrift_c_glib.pc @@ -510,7 +518,7 @@ AC_OUTPUT echo echo "$PACKAGE $VERSION" echo -echo "Building code generators ..... :$thrift_generators" +echo "Building code generators ..... : $thrift_generators" echo echo "Building C++ Library ......... : $have_cpp" echo "Building C (GLib) Library .... : $have_c_glib" @@ -525,51 +533,62 @@ echo "Building Erlang Library ...... : $have_erlang" echo "Building Go Library .......... : $have_go" if test "$have_cpp" = "yes" ; then echo - echo "Building TZlibTransport ...... : $have_zlib" - echo "Building TNonblockingServer .. : $have_libevent" + echo "C++ Library:" + echo " Build TZlibTransport ...... : $have_zlib" + echo " Build TNonblockingServer .. : $have_libevent" + echo " Build TQTcpServer (Qt) .... : $have_qt" fi if test "$have_java" = "yes" ; then echo - echo "Using javac .................. : $JAVAC" - echo "Using java ................... : $JAVA" - echo "Using ant .................... : $ANT" + echo "Java Library:" + echo " Using javac ............... : $JAVAC" + echo " Using java ................ : $JAVA" + echo " Using ant ................. : $ANT" fi if test "$have_csharp" = "yes" ; then echo - echo "Using .NET 3.5 ............... : $net_3_5" + echo "C# Library:" + echo " Using .NET 3.5 ............ : $net_3_5" fi if test "$have_python" = "yes" ; then echo - echo "Using Python ................. : $PYTHON" + echo "Python Library:" + echo " Using Python .............. : $PYTHON" fi if test "$have_php" = "yes" ; then echo - echo "Using php-config ............. : $PHP_CONFIG" + echo "PHP Library:" + echo " Using php-config .......... : $PHP_CONFIG" fi if test "$have_ruby" = "yes" ; then echo - echo "Using Ruby ................... : $RUBY" + echo "Ruby Library:" + echo " Using Ruby ................ : $RUBY" fi if test "$have_haskell" = "yes" ; then echo - echo "Using Haskell ................ : $RUNHASKELL" - echo "Using Cabal .................. : $CABAL" + echo "Haskell Library:" + echo " Using Haskell ............. : $RUNHASKELL" + echo " Using Cabal ............... : $CABAL" fi if test "$have_perl" = "yes" ; then echo - echo "Using Perl ................... : $PERL" + echo "Perl Library:" + echo " Using Perl ................ : $PERL" fi if test "$have_erlang" = "yes" ; then echo - echo "Using erlc ................... : $ERLC" + echo "Erlang Library:" + echo " Using erlc ................ : $ERLC" fi if test "$have_go" = "yes" ; then echo - echo "Using GOROOT.................. : $GOROOT" - echo "Using GOBIN................... : $GOBIN" - echo "Using GOARCH.................. : $GOARCH" - echo "Using GO Compiler............. : $GO_C" - echo "Using GO Linker............... : $GO_L" + echo "Go Library:" + echo " Using GOROOT............... : $GOROOT" + echo " Using GOBIN................ : $GOBIN" + echo " Using GOARCH............... : $GOARCH" + echo " Using GO Compiler.......... : $GO_C" + echo " Using GO Linker............ : $GO_L" fi echo echo "If something is missing that you think should be present," diff --git a/lib/cpp/Makefile.am b/lib/cpp/Makefile.am index f8093ba2d12..188b3716f4a 100644 --- a/lib/cpp/Makefile.am +++ b/lib/cpp/Makefile.am @@ -17,6 +17,9 @@ # under the License. # +moc_%.cpp: %.h + $(QT_MOC) $(QT_CFLAGS) $< -o $@ + SUBDIRS = . if WITH_TESTS @@ -39,6 +42,10 @@ if AMX_HAVE_ZLIB lib_LTLIBRARIES += libthriftz.la pkgconfig_DATA += thrift-z.pc endif +if AMX_HAVE_QT +lib_LTLIBRARIES += libthriftqt.la +pkgconfig_DATA += thrift-qt.pc +endif AM_CXXFLAGS = -Wall AM_CPPFLAGS = $(BOOST_CPPFLAGS) -I$(srcdir)/src @@ -94,18 +101,27 @@ libthriftnb_la_SOURCES = src/server/TNonblockingServer.cpp \ libthriftz_la_SOURCES = src/transport/TZlibTransport.cpp +libthriftqt_la_MOC = src/qt/moc_TQTcpServer.cpp +libthriftqt_la_SOURCES = $(libthriftqt_la_MOC) \ + src/qt/TQIODeviceTransport.cpp \ + src/qt/TQTcpServer.cpp +CLEANFILES = $(libthriftqt_la_MOC) + # Flags for the various libraries libthriftnb_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBEVENT_CPPFLAGS) libthriftz_la_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CPPFLAGS) +libthriftqt_la_CPPFLAGS = $(AM_CPPFLAGS) $(QT_CFLAGS) libthriftnb_la_CXXFLAGS = $(AM_CXXFLAGS) libthriftz_la_CXXFLAGS = $(AM_CXXFLAGS) +libthriftqt_la_CXXFLAGS = $(AM_CXXFLAGS) libthriftnb_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) libthriftz_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) +libthriftqt_la_LDFLAGS = -release $(VERSION) $(BOOST_LDFLAGS) $(QT_LIBS) include_thriftdir = $(includedir)/thrift include_thrift_HEADERS = \ $(top_builddir)/config.h \ - src/TDispatchProcessor.h \ + src/TDispatchProcessor.h \ src/Thrift.h \ src/TReflectionLocal.h \ src/TProcessor.h \ @@ -186,6 +202,11 @@ include_async_HEADERS = \ src/async/TEvhttpClientChannel.h \ src/async/TEvhttpServer.h +include_qtdir = $(include_thriftdir)/qt +include_qt_HEADERS = \ + src/qt/TQIODeviceTransport.h \ + src/qt/TQTcpServer.h + noinst_PROGRAMS = concurrency_test @@ -212,4 +233,5 @@ EXTRA_DIST = \ thrift-nb.pc.in \ thrift.pc.in \ thrift-z.pc.in \ + thrift-qt.pc.in \ $(WINDOWS_DIST) diff --git a/lib/cpp/src/qt/TQIODeviceTransport.cpp b/lib/cpp/src/qt/TQIODeviceTransport.cpp new file mode 100644 index 00000000000..c5b53a8f24c --- /dev/null +++ b/lib/cpp/src/qt/TQIODeviceTransport.cpp @@ -0,0 +1,155 @@ +#include "TQIODeviceTransport.h" + +#include + +#include + +#include + +namespace apache { namespace thrift { namespace transport { + using boost::shared_ptr; + + TQIODeviceTransport::TQIODeviceTransport(shared_ptr dev) + : dev_(dev) + { + } + + TQIODeviceTransport::~TQIODeviceTransport() + { + dev_->close(); + } + + void TQIODeviceTransport::open() + { + if (!isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "open(): underlying QIODevice isn't open"); + } + } + + bool TQIODeviceTransport::isOpen() + { + return dev_->isOpen(); + } + + bool TQIODeviceTransport::peek() + { + return dev_->bytesAvailable() > 0; + } + + void TQIODeviceTransport::close() + { + dev_->close(); + } + + uint32_t TQIODeviceTransport::readAll(uint8_t* buf, uint32_t len) { + uint32_t requestLen = len; + while (len) { + uint32_t readSize; + try { + readSize = read(buf, len); + } catch (...) { + if (len != requestLen) { + // something read already + return requestLen - len; + } + // error but nothing read yet + throw; + } + if (readSize == 0) { + // dev_->waitForReadyRead(50); + } else { + buf += readSize; + len -= readSize; + } + } + return requestLen; + } + + uint32_t TQIODeviceTransport::read(uint8_t* buf, uint32_t len) + { + uint32_t actualSize; + qint64 readSize; + + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "read(): underlying QIODevice is not open"); + } + + actualSize = (uint32_t)std::min((qint64)len, dev_->bytesAvailable()); + readSize = dev_->read(reinterpret_cast(buf), len); + + if (readSize < 0) { + QAbstractSocket* socket; + if ((socket = qobject_cast(dev_.get()))) { + throw TTransportException(TTransportException::UNKNOWN, + "Failed to read() from QAbstractSocket", + socket->error()); + } + throw TTransportException(TTransportException::UNKNOWN, + "Failed to read from from QIODevice"); + } + + return (uint32_t)readSize; + } + + void TQIODeviceTransport::write(const uint8_t* buf, uint32_t len) + { + while (len) { + uint32_t written = write_partial(buf, len); + len -= written; + // dev_->waitForBytesWritten(50); + } + } + + uint32_t TQIODeviceTransport::write_partial(const uint8_t* buf, uint32_t len) + { + qint64 written; + + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "write_partial(): underlying QIODevice is not open"); + } + + written = dev_->write(reinterpret_cast(buf), len); + if (written < 0) { + QAbstractSocket* socket; + if ((socket = qobject_cast(dev_.get()))) { + throw TTransportException(TTransportException::UNKNOWN, + "write_partial(): failed to write to QAbstractSocket", socket->error()); + } + + throw TTransportException(TTransportException::UNKNOWN, + "write_partial(): failed to write to underlying QIODevice"); + } + + return (uint32_t)written; + } + + void TQIODeviceTransport::flush() + { + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "flush(): underlying QIODevice is not open"); + } + + QAbstractSocket* socket; + + if ((socket = qobject_cast(dev_.get()))) { + socket->flush(); + } else { + dev_->waitForBytesWritten(1); + } + } + + uint8_t* TQIODeviceTransport::borrow(uint8_t* buf, uint32_t* len) + { + return NULL; + } + + void TQIODeviceTransport::consume(uint32_t len) + { + throw TTransportException(TTransportException::UNKNOWN); + } +}}} // apache::thrift::transport + diff --git a/lib/cpp/src/qt/TQIODeviceTransport.h b/lib/cpp/src/qt/TQIODeviceTransport.h new file mode 100644 index 00000000000..8ce7c782bdf --- /dev/null +++ b/lib/cpp/src/qt/TQIODeviceTransport.h @@ -0,0 +1,48 @@ +#ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ +#define _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ 1 + +#include + +#include + +#include + +namespace apache { namespace thrift { namespace protocol { +class TProtocol; +}}} // apache::thrift::protocol + +namespace apache { namespace thrift { namespace transport { + + class TQIODeviceTransport : public apache::thrift::transport::TVirtualTransport { + public: + explicit TQIODeviceTransport(boost::shared_ptr dev); + ~TQIODeviceTransport(); + + void open(); + + bool isOpen(); + + bool peek(); + + void close(); + + uint32_t readAll(uint8_t *buf, uint32_t len); + + uint32_t read(uint8_t* buf, uint32_t len); + + void write(const uint8_t* buf, uint32_t len); + + uint32_t write_partial(const uint8_t* buf, uint32_t len); + + void flush(); + + uint8_t* borrow(uint8_t* buf, uint32_t* len); + void consume(uint32_t len); + + private: + boost::shared_ptr dev_; + }; +}}} // apache::thrift::transport + +#endif // #ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ + diff --git a/lib/cpp/src/qt/TQTcpServer.cpp b/lib/cpp/src/qt/TQTcpServer.cpp new file mode 100644 index 00000000000..65ad68dc61f --- /dev/null +++ b/lib/cpp/src/qt/TQTcpServer.cpp @@ -0,0 +1,140 @@ +#include "TQTcpServer.h" + +#include +#include + +#include + +#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 connection_; + shared_ptr transport_; + shared_ptr iprot_; + shared_ptr oprot_; + + explicit ConnectionContext(shared_ptr connection, + shared_ptr transport, + shared_ptr iprot, + shared_ptr oprot) + : connection_(connection) + , transport_(transport) + , iprot_(iprot) + , oprot_(oprot) + {} +}; + +TQTcpServer::TQTcpServer(shared_ptr server, + shared_ptr processor, + shared_ptr 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 connection(server_->nextPendingConnection()); + + shared_ptr transport; + shared_ptr iprot; + shared_ptr oprot; + + try { + transport = shared_ptr(new TQIODeviceTransport(connection)); + iprot = shared_ptr(pfact_->getProtocol(transport)); + oprot = shared_ptr(pfact_->getProtocol(transport)); + } catch(...) { + qWarning("[TQTcpServer] Failed to initialize transports/protocols"); + continue; + } + + ctxMap_[connection.get()] = + shared_ptr( + 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(sender())); + Q_ASSERT(connection); + + if (ctxMap_.find(connection) == ctxMap_.end()) + { + qWarning("[TQTcpServer] Got data on an unknown QTcpSocket"); + return; + } + + shared_ptr 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(sender())); + Q_ASSERT(connection); + + if (ctxMap_.find(connection) == ctxMap_.end()) + { + qWarning("[TQTcpServer] Unknown QTcpSocket closed"); + return; + } + + ctxMap_.erase(connection); +} + +void TQTcpServer::finish(shared_ptr ctx, bool healthy) +{ + if (!healthy) + { + qWarning("[TQTcpServer] Processor failed to process data successfully"); + ctxMap_.erase(ctx->connection_.get()); + } +} + +}}} // apache::thrift::async diff --git a/lib/cpp/src/qt/TQTcpServer.h b/lib/cpp/src/qt/TQTcpServer.h new file mode 100644 index 00000000000..82bec399be6 --- /dev/null +++ b/lib/cpp/src/qt/TQTcpServer.h @@ -0,0 +1,48 @@ +#ifndef _THRIFT_TASYNC_QTCP_SERVER_H_ +#define _THRIFT_TASYNC_QTCP_SERVER_H_ + +#include +#include + +#include + +#include + +namespace apache { namespace thrift { namespace protocol { +class TProtocol; +class TProtocolFactory; +}}} // apache::thrift::protocol + +namespace apache { namespace thrift { namespace async { + +class TAsyncProcessor; + +class TQTcpServer : public QObject { + Q_OBJECT + public: + TQTcpServer(boost::shared_ptr server, + boost::shared_ptr processor, + boost::shared_ptr protocolFactory, + QT_PREPEND_NAMESPACE(QObject)* parent = NULL); + virtual ~TQTcpServer(); + + private Q_SLOTS: + void processIncoming(); + void beginDecode(); + void socketClosed(); + + private: + class ConnectionContext; + + void finish(boost::shared_ptr ctx, bool healthy); + + boost::shared_ptr server_; + boost::shared_ptr processor_; + boost::shared_ptr pfact_; + + std::map > ctxMap_; +}; + +}}} // apache::thrift::async + +#endif // #ifndef _THRIFT_TASYNC_QTCP_SERVER_H_ diff --git a/lib/cpp/src/qt/moc_TQTcpServer.cpp b/lib/cpp/src/qt/moc_TQTcpServer.cpp new file mode 100644 index 00000000000..0e94da92182 --- /dev/null +++ b/lib/cpp/src/qt/moc_TQTcpServer.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'TQTcpServer.h' +** +** Created: Thu Feb 9 20:47:09 2012 +** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "TQTcpServer.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'TQTcpServer.h' doesn't include ." +#elif Q_MOC_OUTPUT_REVISION != 62 +#error "This file was generated using the moc from 4.6.3. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +QT_BEGIN_MOC_NAMESPACE +static const uint qt_meta_data_apache__thrift__async__TQTcpServer[] = { + + // content: + 4, // revision + 0, // classname + 0, 0, // classinfo + 3, 14, // methods + 0, 0, // properties + 0, 0, // enums/sets + 0, 0, // constructors + 0, // flags + 0, // signalCount + + // slots: signature, parameters, type, tag, flags + 36, 35, 35, 35, 0x08, + 54, 35, 35, 35, 0x08, + 68, 35, 35, 35, 0x08, + + 0 // eod +}; + +static const char qt_meta_stringdata_apache__thrift__async__TQTcpServer[] = { + "apache::thrift::async::TQTcpServer\0\0" + "processIncoming()\0beginDecode()\0" + "socketClosed()\0" +}; + +const QMetaObject apache::thrift::async::TQTcpServer::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_apache__thrift__async__TQTcpServer, + qt_meta_data_apache__thrift__async__TQTcpServer, 0 } +}; + +#ifdef Q_NO_DATA_RELOCATION +const QMetaObject &apache::thrift::async::TQTcpServer::getStaticMetaObject() { return staticMetaObject; } +#endif //Q_NO_DATA_RELOCATION + +const QMetaObject *apache::thrift::async::TQTcpServer::metaObject() const +{ + return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; +} + +void *apache::thrift::async::TQTcpServer::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_apache__thrift__async__TQTcpServer)) + return static_cast(const_cast< TQTcpServer*>(this)); + return QObject::qt_metacast(_clname); +} + +int apache::thrift::async::TQTcpServer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + if (_c == QMetaObject::InvokeMetaMethod) { + switch (_id) { + case 0: processIncoming(); break; + case 1: beginDecode(); break; + case 2: socketClosed(); break; + default: ; + } + _id -= 3; + } + return _id; +} +QT_END_MOC_NAMESPACE diff --git a/lib/cpp/thrift-qt.pc.in b/lib/cpp/thrift-qt.pc.in new file mode 100644 index 00000000000..846e3623bee --- /dev/null +++ b/lib/cpp/thrift-qt.pc.in @@ -0,0 +1,30 @@ +# +# 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. +# + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Thrift +Description: Thrift Qt API +Version: @VERSION@ +Requires: thrift = @VERSION@ +Libs: -L${libdir} -lthriftqt +Cflags: -I${includedir}/thrift From 0bab154d9f3cac02b0c523e1e578bf5b87f494d0 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Fri, 10 Feb 2012 21:08:36 +0000 Subject: [PATCH 02/22] THRIFT-1348 C++ Qt bindings fix: remove moc_TQTcpServer.cpp (was added by accident) git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1242921 13f79535-47bb-0310-9956-ffa450edef68 --- lib/cpp/src/qt/moc_TQTcpServer.cpp | 85 ------------------------------ 1 file changed, 85 deletions(-) delete mode 100644 lib/cpp/src/qt/moc_TQTcpServer.cpp diff --git a/lib/cpp/src/qt/moc_TQTcpServer.cpp b/lib/cpp/src/qt/moc_TQTcpServer.cpp deleted file mode 100644 index 0e94da92182..00000000000 --- a/lib/cpp/src/qt/moc_TQTcpServer.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** Meta object code from reading C++ file 'TQTcpServer.h' -** -** Created: Thu Feb 9 20:47:09 2012 -** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) -** -** WARNING! All changes made in this file will be lost! -*****************************************************************************/ - -#include "TQTcpServer.h" -#if !defined(Q_MOC_OUTPUT_REVISION) -#error "The header file 'TQTcpServer.h' doesn't include ." -#elif Q_MOC_OUTPUT_REVISION != 62 -#error "This file was generated using the moc from 4.6.3. It" -#error "cannot be used with the include files from this version of Qt." -#error "(The moc has changed too much.)" -#endif - -QT_BEGIN_MOC_NAMESPACE -static const uint qt_meta_data_apache__thrift__async__TQTcpServer[] = { - - // content: - 4, // revision - 0, // classname - 0, 0, // classinfo - 3, 14, // methods - 0, 0, // properties - 0, 0, // enums/sets - 0, 0, // constructors - 0, // flags - 0, // signalCount - - // slots: signature, parameters, type, tag, flags - 36, 35, 35, 35, 0x08, - 54, 35, 35, 35, 0x08, - 68, 35, 35, 35, 0x08, - - 0 // eod -}; - -static const char qt_meta_stringdata_apache__thrift__async__TQTcpServer[] = { - "apache::thrift::async::TQTcpServer\0\0" - "processIncoming()\0beginDecode()\0" - "socketClosed()\0" -}; - -const QMetaObject apache::thrift::async::TQTcpServer::staticMetaObject = { - { &QObject::staticMetaObject, qt_meta_stringdata_apache__thrift__async__TQTcpServer, - qt_meta_data_apache__thrift__async__TQTcpServer, 0 } -}; - -#ifdef Q_NO_DATA_RELOCATION -const QMetaObject &apache::thrift::async::TQTcpServer::getStaticMetaObject() { return staticMetaObject; } -#endif //Q_NO_DATA_RELOCATION - -const QMetaObject *apache::thrift::async::TQTcpServer::metaObject() const -{ - return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; -} - -void *apache::thrift::async::TQTcpServer::qt_metacast(const char *_clname) -{ - if (!_clname) return 0; - if (!strcmp(_clname, qt_meta_stringdata_apache__thrift__async__TQTcpServer)) - return static_cast(const_cast< TQTcpServer*>(this)); - return QObject::qt_metacast(_clname); -} - -int apache::thrift::async::TQTcpServer::qt_metacall(QMetaObject::Call _c, int _id, void **_a) -{ - _id = QObject::qt_metacall(_c, _id, _a); - if (_id < 0) - return _id; - if (_c == QMetaObject::InvokeMetaMethod) { - switch (_id) { - case 0: processIncoming(); break; - case 1: beginDecode(); break; - case 2: socketClosed(); break; - default: ; - } - _id -= 3; - } - return _id; -} -QT_END_MOC_NAMESPACE From 19a991528f8ed41319737ee758b79ae8b66cd9f7 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Sat, 11 Feb 2012 19:09:30 +0000 Subject: [PATCH 03/22] THRIFT-1348 C++ Qt bindings Patch: Doug Rosvick qt-cleanup.patch applied git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1243124 13f79535-47bb-0310-9956-ffa450edef68 --- lib/cpp/src/qt/TQIODeviceTransport.cpp | 242 +++++++++++++------------ lib/cpp/src/qt/TQIODeviceTransport.h | 53 +++--- lib/cpp/src/qt/TQTcpServer.cpp | 19 +- lib/cpp/src/qt/TQTcpServer.h | 11 +- 4 files changed, 164 insertions(+), 161 deletions(-) diff --git a/lib/cpp/src/qt/TQIODeviceTransport.cpp b/lib/cpp/src/qt/TQIODeviceTransport.cpp index c5b53a8f24c..4172ed098e2 100644 --- a/lib/cpp/src/qt/TQIODeviceTransport.cpp +++ b/lib/cpp/src/qt/TQIODeviceTransport.cpp @@ -1,155 +1,157 @@ #include "TQIODeviceTransport.h" -#include - #include +#include -#include +#include +using boost::shared_ptr; + namespace apache { namespace thrift { namespace transport { - using boost::shared_ptr; - TQIODeviceTransport::TQIODeviceTransport(shared_ptr dev) - : dev_(dev) - { +TQIODeviceTransport::TQIODeviceTransport(shared_ptr dev) + : dev_(dev) +{ +} + +TQIODeviceTransport::~TQIODeviceTransport() +{ + dev_->close(); +} + +void TQIODeviceTransport::open() +{ + if (!isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "open(): underlying QIODevice isn't open"); } - - TQIODeviceTransport::~TQIODeviceTransport() - { - dev_->close(); - } - - void TQIODeviceTransport::open() - { - if (!isOpen()) { - throw TTransportException(TTransportException::NOT_OPEN, - "open(): underlying QIODevice isn't open"); - } - } - - bool TQIODeviceTransport::isOpen() - { - return dev_->isOpen(); - } - - bool TQIODeviceTransport::peek() - { - return dev_->bytesAvailable() > 0; - } - - void TQIODeviceTransport::close() - { - dev_->close(); - } - - uint32_t TQIODeviceTransport::readAll(uint8_t* buf, uint32_t len) { - uint32_t requestLen = len; - while (len) { - uint32_t readSize; - try { - readSize = read(buf, len); - } catch (...) { - if (len != requestLen) { - // something read already - return requestLen - len; - } - // error but nothing read yet - throw; - } - if (readSize == 0) { - // dev_->waitForReadyRead(50); - } else { - buf += readSize; - len -= readSize; +} + +bool TQIODeviceTransport::isOpen() +{ + return dev_->isOpen(); +} + +bool TQIODeviceTransport::peek() +{ + return dev_->bytesAvailable() > 0; +} + +void TQIODeviceTransport::close() +{ + dev_->close(); +} + +uint32_t TQIODeviceTransport::readAll(uint8_t* buf, uint32_t len) +{ + uint32_t requestLen = len; + while (len) { + uint32_t readSize; + try { + readSize = read(buf, len); + } catch (...) { + if (len != requestLen) { + // something read already + return requestLen - len; } + // error but nothing read yet + throw; + } + if (readSize == 0) { + dev_->waitForReadyRead(50); + } else { + buf += readSize; + len -= readSize; } - return requestLen; } + return requestLen; +} - uint32_t TQIODeviceTransport::read(uint8_t* buf, uint32_t len) - { - uint32_t actualSize; - qint64 readSize; +uint32_t TQIODeviceTransport::read(uint8_t* buf, uint32_t len) +{ + uint32_t actualSize; + qint64 readSize; - if (!dev_->isOpen()) { - throw TTransportException(TTransportException::NOT_OPEN, - "read(): underlying QIODevice is not open"); - } + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "read(): underlying QIODevice is not open"); + } - actualSize = (uint32_t)std::min((qint64)len, dev_->bytesAvailable()); - readSize = dev_->read(reinterpret_cast(buf), len); + actualSize = (uint32_t)std::min((qint64)len, dev_->bytesAvailable()); + readSize = dev_->read(reinterpret_cast(buf), actualSize); - if (readSize < 0) { - QAbstractSocket* socket; - if ((socket = qobject_cast(dev_.get()))) { - throw TTransportException(TTransportException::UNKNOWN, - "Failed to read() from QAbstractSocket", - socket->error()); - } + if (readSize < 0) { + QAbstractSocket* socket; + if ((socket = qobject_cast(dev_.get()))) { throw TTransportException(TTransportException::UNKNOWN, - "Failed to read from from QIODevice"); + "Failed to read() from QAbstractSocket", + socket->error()); } - - return (uint32_t)readSize; + throw TTransportException(TTransportException::UNKNOWN, + "Failed to read from from QIODevice"); } - void TQIODeviceTransport::write(const uint8_t* buf, uint32_t len) - { - while (len) { - uint32_t written = write_partial(buf, len); - len -= written; - // dev_->waitForBytesWritten(50); - } - } + return (uint32_t)readSize; +} - uint32_t TQIODeviceTransport::write_partial(const uint8_t* buf, uint32_t len) - { - qint64 written; +void TQIODeviceTransport::write(const uint8_t* buf, uint32_t len) +{ + while (len) { + uint32_t written = write_partial(buf, len); + len -= written; + dev_->waitForBytesWritten(50); + } +} - if (!dev_->isOpen()) { - throw TTransportException(TTransportException::NOT_OPEN, - "write_partial(): underlying QIODevice is not open"); - } +uint32_t TQIODeviceTransport::write_partial(const uint8_t* buf, uint32_t len) +{ + qint64 written; - written = dev_->write(reinterpret_cast(buf), len); - if (written < 0) { - QAbstractSocket* socket; - if ((socket = qobject_cast(dev_.get()))) { - throw TTransportException(TTransportException::UNKNOWN, - "write_partial(): failed to write to QAbstractSocket", socket->error()); - } + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "write_partial(): underlying QIODevice is not open"); + } + written = dev_->write(reinterpret_cast(buf), len); + if (written < 0) { + QAbstractSocket* socket; + if ((socket = qobject_cast(dev_.get()))) { throw TTransportException(TTransportException::UNKNOWN, - "write_partial(): failed to write to underlying QIODevice"); + "write_partial(): failed to write to QAbstractSocket", socket->error()); } - return (uint32_t)written; + throw TTransportException(TTransportException::UNKNOWN, + "write_partial(): failed to write to underlying QIODevice"); } - void TQIODeviceTransport::flush() - { - if (!dev_->isOpen()) { - throw TTransportException(TTransportException::NOT_OPEN, - "flush(): underlying QIODevice is not open"); - } - - QAbstractSocket* socket; + return (uint32_t)written; +} - if ((socket = qobject_cast(dev_.get()))) { - socket->flush(); - } else { - dev_->waitForBytesWritten(1); - } +void TQIODeviceTransport::flush() +{ + if (!dev_->isOpen()) { + throw TTransportException(TTransportException::NOT_OPEN, + "flush(): underlying QIODevice is not open"); } - uint8_t* TQIODeviceTransport::borrow(uint8_t* buf, uint32_t* len) - { - return NULL; - } + QAbstractSocket* socket; - void TQIODeviceTransport::consume(uint32_t len) - { - throw TTransportException(TTransportException::UNKNOWN); + if ((socket = qobject_cast(dev_.get()))) { + socket->flush(); + } else { + dev_->waitForBytesWritten(1); } +} + +uint8_t* TQIODeviceTransport::borrow(uint8_t* buf, uint32_t* len) +{ + return NULL; +} + +void TQIODeviceTransport::consume(uint32_t len) +{ + throw TTransportException(TTransportException::UNKNOWN); +} + }}} // apache::thrift::transport diff --git a/lib/cpp/src/qt/TQIODeviceTransport.h b/lib/cpp/src/qt/TQIODeviceTransport.h index 8ce7c782bdf..5ee8f6d5e73 100644 --- a/lib/cpp/src/qt/TQIODeviceTransport.h +++ b/lib/cpp/src/qt/TQIODeviceTransport.h @@ -1,47 +1,44 @@ #ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ #define _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ 1 -#include - #include #include -namespace apache { namespace thrift { namespace protocol { -class TProtocol; -}}} // apache::thrift::protocol +class QIODevice; namespace apache { namespace thrift { namespace transport { - class TQIODeviceTransport : public apache::thrift::transport::TVirtualTransport { - public: - explicit TQIODeviceTransport(boost::shared_ptr dev); - ~TQIODeviceTransport(); - - void open(); - - bool isOpen(); - - bool peek(); - - void close(); - - uint32_t readAll(uint8_t *buf, uint32_t len); +/** + * Transport that operates on a QIODevice (socket, file, etc). + */ +class TQIODeviceTransport : public apache::thrift::transport::TVirtualTransport { + public: + explicit TQIODeviceTransport(boost::shared_ptr dev); + virtual ~TQIODeviceTransport(); - uint32_t read(uint8_t* buf, uint32_t len); + void open(); + bool isOpen(); + bool peek(); + void close(); - void write(const uint8_t* buf, uint32_t len); + uint32_t readAll(uint8_t *buf, uint32_t len); + uint32_t read(uint8_t* buf, uint32_t len); - uint32_t write_partial(const uint8_t* buf, uint32_t len); + void write(const uint8_t* buf, uint32_t len); + uint32_t write_partial(const uint8_t* buf, uint32_t len); - void flush(); + void flush(); - uint8_t* borrow(uint8_t* buf, uint32_t* len); - void consume(uint32_t len); + uint8_t* borrow(uint8_t* buf, uint32_t* len); + void consume(uint32_t len); - private: - boost::shared_ptr dev_; - }; + private: + TQIODeviceTransport(const TQIODeviceTransport&); + TQIODeviceTransport& operator=(const TQIODeviceTransport&); + + boost::shared_ptr dev_; +}; }}} // apache::thrift::transport #endif // #ifndef _THRIFT_ASYNC_TQIODEVICE_TRANSPORT_H_ diff --git a/lib/cpp/src/qt/TQTcpServer.cpp b/lib/cpp/src/qt/TQTcpServer.cpp index 65ad68dc61f..733a3d81009 100644 --- a/lib/cpp/src/qt/TQTcpServer.cpp +++ b/lib/cpp/src/qt/TQTcpServer.cpp @@ -1,11 +1,13 @@ -#include "TQTcpServer.h" -#include -#include +#include "TQTcpServer.h" +#include "TQIODeviceTransport.h" #include -#include "TQIODeviceTransport.h" +#include + +#include +#include using boost::shared_ptr; using apache::thrift::protocol::TProtocol; @@ -91,8 +93,7 @@ void TQTcpServer::beginDecode() QTcpSocket* connection(qobject_cast(sender())); Q_ASSERT(connection); - if (ctxMap_.find(connection) == ctxMap_.end()) - { + if (ctxMap_.find(connection) == ctxMap_.end()) { qWarning("[TQTcpServer] Got data on an unknown QTcpSocket"); return; } @@ -119,8 +120,7 @@ void TQTcpServer::socketClosed() QTcpSocket* connection(qobject_cast(sender())); Q_ASSERT(connection); - if (ctxMap_.find(connection) == ctxMap_.end()) - { + if (ctxMap_.find(connection) == ctxMap_.end()) { qWarning("[TQTcpServer] Unknown QTcpSocket closed"); return; } @@ -130,8 +130,7 @@ void TQTcpServer::socketClosed() void TQTcpServer::finish(shared_ptr ctx, bool healthy) { - if (!healthy) - { + if (!healthy) { qWarning("[TQTcpServer] Processor failed to process data successfully"); ctxMap_.erase(ctx->connection_.get()); } diff --git a/lib/cpp/src/qt/TQTcpServer.h b/lib/cpp/src/qt/TQTcpServer.h index 82bec399be6..78383575911 100644 --- a/lib/cpp/src/qt/TQTcpServer.h +++ b/lib/cpp/src/qt/TQTcpServer.h @@ -6,10 +6,7 @@ #include -#include - namespace apache { namespace thrift { namespace protocol { -class TProtocol; class TProtocolFactory; }}} // apache::thrift::protocol @@ -17,6 +14,11 @@ namespace apache { namespace thrift { namespace async { class TAsyncProcessor; +/** + * Server that uses Qt to listen for connections. + * Simply give it a QTcpServer that is listening, along with an async + * processor and a protocol factory, and then run the Qt event loop. + */ class TQTcpServer : public QObject { Q_OBJECT public: @@ -32,6 +34,9 @@ class TQTcpServer : public QObject { void socketClosed(); private: + TQTcpServer(const TQTcpServer&); + TQTcpServer& operator=(const TQTcpServer&); + class ConnectionContext; void finish(boost::shared_ptr ctx, bool healthy); From b405ca6be839c5a7072cbe3cd3a584523b960058 Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Tue, 14 Feb 2012 23:35:22 +0000 Subject: [PATCH 04/22] THRIFT-1447. java: NullpointerException in ProcessFunction.class :in 'oneway' method Patch: Ihor Mysak git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1244272 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_java_generator.cc | 4 ++++ lib/java/src/org/apache/thrift/ProcessFunction.java | 12 ++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc index b76338a79a6..3f8322264e7 100644 --- a/compiler/cpp/src/generate/t_java_generator.cc +++ b/compiler/cpp/src/generate/t_java_generator.cc @@ -2673,6 +2673,10 @@ void t_java_generator::generate_process_function(t_service* tservice, indent(f_service_) << " return new " << argsname << "();" << endl; indent(f_service_) << "}" << endl << endl; + indent(f_service_) << "protected boolean isOneway() {" << endl; + indent(f_service_) << " return " << ((tfunction->is_oneway())?"true":"false") << ";" << endl; + indent(f_service_) << "}" << endl << endl; + indent(f_service_) << "public " << resultname << " getResult(I iface, " << argsname << " args) throws org.apache.thrift.TException {" << endl; indent_up(); if (!tfunction->is_oneway()) { diff --git a/lib/java/src/org/apache/thrift/ProcessFunction.java b/lib/java/src/org/apache/thrift/ProcessFunction.java index 88d57a50bee..3afd38cccba 100644 --- a/lib/java/src/org/apache/thrift/ProcessFunction.java +++ b/lib/java/src/org/apache/thrift/ProcessFunction.java @@ -30,12 +30,16 @@ public final void process(int seqid, TProtocol iprot, TProtocol oprot, I iface) } iprot.readMessageEnd(); TBase result = getResult(iface, args); - oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.REPLY, seqid)); - result.write(oprot); - oprot.writeMessageEnd(); - oprot.getTransport().flush(); + if(!isOneway()) { + oprot.writeMessageBegin(new TMessage(getMethodName(), TMessageType.REPLY, seqid)); + result.write(oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } } + protected abstract boolean isOneway(); + public abstract TBase getResult(I iface, T args) throws TException; public abstract T getEmptyArgsInstance(); From 2fbde0eff0e17b66e6c2ea931bd5882607e019c4 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Sun, 19 Feb 2012 18:53:08 +0000 Subject: [PATCH 05/22] THRIFT-1517 TTransport.ReadAll() should set exception type to EndOfFile Patch: Stefan Gmeiner git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1291039 13f79535-47bb-0310-9956-ffa450edef68 --- lib/csharp/src/Transport/TTransport.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs index 520ba4693c0..3fbc5d67d24 100644 --- a/lib/csharp/src/Transport/TTransport.cs +++ b/lib/csharp/src/Transport/TTransport.cs @@ -53,7 +53,9 @@ public int ReadAll(byte[] buf, int off, int len) ret = Read(buf, off + got, len - got); if (ret <= 0) { - throw new TTransportException("Cannot read, Remote side has closed"); + throw new TTransportException( + TTransportException.ExceptionType.EndOfFile, + "Cannot read, Remote side has closed"); } got += ret; } From 2b828b1a94a0421da098a0092fccdabec41d3124 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Wed, 22 Feb 2012 07:08:36 +0000 Subject: [PATCH 06/22] THRIFT-1516 TProtocolException in C# should be public Patch: Stefan Gmeiner git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1292148 13f79535-47bb-0310-9956-ffa450edef68 --- lib/csharp/src/Protocol/TProtocolException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/csharp/src/Protocol/TProtocolException.cs b/lib/csharp/src/Protocol/TProtocolException.cs index 207f5e919f6..4e4393f9c6f 100644 --- a/lib/csharp/src/Protocol/TProtocolException.cs +++ b/lib/csharp/src/Protocol/TProtocolException.cs @@ -25,7 +25,7 @@ namespace Thrift.Protocol { - class TProtocolException : Exception + public class TProtocolException : Exception { public const int UNKNOWN = 0; public const int INVALID_DATA = 1; From cb4e116ea77b71405cf027f4ac46cc588de5d3ce Mon Sep 17 00:00:00 2001 From: "Anthony F. Molinaro" Date: Wed, 22 Feb 2012 19:33:43 +0000 Subject: [PATCH 07/22] THRIFT-1520 : embed version number in .app file for easier integration with rebar git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1292466 13f79535-47bb-0310-9956-ffa450edef68 --- lib/erl/src/{thrift.app.src.in => thrift.app.src} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename lib/erl/src/{thrift.app.src.in => thrift.app.src} (97%) diff --git a/lib/erl/src/thrift.app.src.in b/lib/erl/src/thrift.app.src similarity index 97% rename from lib/erl/src/thrift.app.src.in rename to lib/erl/src/thrift.app.src index 176c4c1cba2..88ca4bcc80c 100644 --- a/lib/erl/src/thrift.app.src.in +++ b/lib/erl/src/thrift.app.src @@ -17,13 +17,13 @@ %% under the License. %% %%% -*- mode:erlang -*- -{application, @PACKAGE_NAME@, +{application, thrift, [ % A quick description of the application. {description, "Thrift bindings"}, % The version of the applicaton - {vsn, "@PACKAGE_VERSION@"}, + {vsn, "0.9.0-dev"}, % All modules used by the application. {modules, [ From efa2fcf32566728b7e0cad8011952fae77eca9a0 Mon Sep 17 00:00:00 2001 From: "Anthony F. Molinaro" Date: Wed, 22 Feb 2012 19:44:50 +0000 Subject: [PATCH 08/22] THRIFT-1520 : embed version number in .app file for easier integration with rebar git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1292474 13f79535-47bb-0310-9956-ffa450edef68 --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 27ef8d4209d..9960731dae9 100755 --- a/configure.ac +++ b/configure.ac @@ -491,7 +491,6 @@ AC_CONFIG_FILES([ lib/c_glib/test/Makefile lib/csharp/Makefile lib/erl/Makefile - lib/erl/src/thrift.app.src lib/hs/Makefile lib/java/Makefile lib/js/test/Makefile From b2507dbb0c9869acb8ff7fab6d3cd5b415a7c6ec Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Wed, 22 Feb 2012 21:14:10 +0000 Subject: [PATCH 09/22] THRIFT-1518. cpp: Generated C++ code only sends the first optional field in the write() function for a struct There was some incorrect else if logic added to the CPP generated code, which this patch replaces with the proper functionality. Patch: Thomas Wiggins git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1292508 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_cpp_generator.cc | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/compiler/cpp/src/generate/t_cpp_generator.cc b/compiler/cpp/src/generate/t_cpp_generator.cc index db19c90c14e..08a6fbea05a 100755 --- a/compiler/cpp/src/generate/t_cpp_generator.cc +++ b/compiler/cpp/src/generate/t_cpp_generator.cc @@ -1368,26 +1368,14 @@ void t_cpp_generator::generate_struct_writer(ofstream& out, indent(out) << "xfer += oprot->writeStructBegin(\"" << name << "\");" << endl; - bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool check_if_set = (*f_iter)->get_req() == t_field::T_OPTIONAL || (*f_iter)->get_type()->is_xception(); if (check_if_set) { - if (first) { - first = false; - out << - endl << - indent() << "if "; - } else { - out << - " else if "; - } - out << "(this->__isset." << (*f_iter)->get_name() << ") {" << endl; + out << endl << indent() << "if (this->__isset." << (*f_iter)->get_name() << ") {" << endl; indent_up(); } else { - if (!first) - out << endl; - first = true; + out << endl; } // Write field header @@ -1411,9 +1399,7 @@ void t_cpp_generator::generate_struct_writer(ofstream& out, } } - if (!first) { - out << endl; - } + out << endl; // Write the struct map out << From 5b9693c9fa0e28eda52bd3e53c458d6398af7b36 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Tue, 28 Feb 2012 19:32:55 +0000 Subject: [PATCH 10/22] THRIFT-1388 Delphi XE code-generation test case Patch: Jens Geyer git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294798 13f79535-47bb-0310-9956-ffa450edef68 --- lib/delphi/test/codegen/README.txt | 28 ++++ .../test/codegen/ReservedKeywords.thrift | 55 +++++++ .../codegen/run-Pascal-Codegen-Tests.bat.tmpl | 155 ++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 lib/delphi/test/codegen/README.txt create mode 100644 lib/delphi/test/codegen/ReservedKeywords.thrift create mode 100644 lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl diff --git a/lib/delphi/test/codegen/README.txt b/lib/delphi/test/codegen/README.txt new file mode 100644 index 00000000000..d1447a19da9 --- /dev/null +++ b/lib/delphi/test/codegen/README.txt @@ -0,0 +1,28 @@ +How to use the test case: +---------------------------------------------- +- copy and the template batch file +- open the batch file and adjust configuration as necessary +- run the batch + + +Configuration: +---------------------------------------------- +SVNWORKDIR +should point to the Thrift working copy root + +MY_THRIFT_FILES +can be set to point to a folder with more thrift IDL files. +If you don't have any such files, just leave the setting blank. + +BIN +Local MSYS binary folder. Your THRIFT.EXE is installed here. + +MINGW_BIN +Local MinGW bin folder. Contains DLL files required by THRIFT.EXE + +DCC +Identifies the Delphi Command Line compiler (dcc32.exe) +To be configuired only, if the default is not suitable. + +---------------------------------------------- +*EOF* \ No newline at end of file diff --git a/lib/delphi/test/codegen/ReservedKeywords.thrift b/lib/delphi/test/codegen/ReservedKeywords.thrift new file mode 100644 index 00000000000..83c1836f3d7 --- /dev/null +++ b/lib/delphi/test/codegen/ReservedKeywords.thrift @@ -0,0 +1,55 @@ +/* + * 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. + */ + +// make sure generated code does not produce name collisions with predefined keywords + + + +typedef i32 Cardinal +typedef string message +typedef list< map< Cardinal, message>> program + +struct unit { + 1: Cardinal downto; + 2: program procedure; +} + +typedef set< unit> units + +exception exception1 { + 1: program message; + 2: unit array; +} + +service constructor { + unit Create(1: Cardinal asm; 2: message inherited) throws (1: exception1 label); + units Destroy(); +} + +const Cardinal downto = +1 +const Cardinal published = -1 + +enum keywords { + record = 1, + repeat = 2, + deprecated = 3 +} + + + diff --git a/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl new file mode 100644 index 00000000000..8d25eaee82d --- /dev/null +++ b/lib/delphi/test/codegen/run-Pascal-Codegen-Tests.bat.tmpl @@ -0,0 +1,155 @@ +@echo off +if ""=="%1" goto CONFIG +goto HANDLEDIR + +REM ----------------------------------------------------- +:CONFIG +REM ----------------------------------------------------- + +rem * CONFIGURATION BEGIN +rem * configuration settings, adjust as necessary to meet your system setup +set SVNWORKDIR= +set MY_THRIFT_FILES= +set BIN=C:\MSys10\local\bin +set MINGW_BIN=C:\MinGW\bin +set DCC= +set SUBDIR=gen-delphi +rem * CONFIGURATION END + + +REM ----------------------------------------------------- +:START +REM ----------------------------------------------------- + +rem * configured? +if "%SVNWORKDIR%"=="" goto CONFIG_ERROR + +rem * try to find dcc32.exe +echo Looking for dcc32.exe ... +if not exist "%DCC%" set DCC=%ProgramFiles%\Embarcadero\RAD Studio\8.0\bin\dcc32.exe +if not exist "%DCC%" set DCC=%ProgramFiles(x86)%\Embarcadero\RAD Studio\8.0\bin\dcc32.exe +if not exist "%DCC%" goto CONFIG_ERROR +echo Found %DCC% +echo. + +rem * some helpers +set PATH=%BIN%;%MINGW_BIN%;%PATH% +set TARGET=%SVNWORKDIR%\..\thrift-testing +set SOURCE=%SVNWORKDIR% +set TESTAPP=TestProject +set UNITSEARCH=%SOURCE%\lib\pas\src;%SOURCE%\lib\delphi\src +set OUTDCU="%TARGET%\dcu" +set LOGFILE=%TARGET%\%SUBDIR%\codegen.log + +rem * create and/or empty target dirs +if not exist "%TARGET%" md "%TARGET%" +if not exist "%TARGET%\%SUBDIR%" md "%TARGET%\%SUBDIR%" +if not exist "%OUTDCU%" md "%OUTDCU%" +if exist "%TARGET%\*.thrift" del "%TARGET%\*.thrift" /Q +if exist "%TARGET%\%SUBDIR%\*.*" del "%TARGET%\%SUBDIR%\*.*" /Q +if exist "%OUTDCU%\*.*" del "%OUTDCU%\*.*" /Q + +rem * recurse through thrift WC and "my thrift files" folder +rem * copies all .thrift files into thrift-testing +call %0 %SOURCE% +if not "%MY_THRIFT_FILES%"=="" call %0 %MY_THRIFT_FILES% + +rem * compile all thrift files, generate PAS and C++ code +echo. +echo Generating code, please wait ... +cd "%TARGET%" +for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen delphi:ansistr_binary "%%a" >> "%LOGFILE%" +REM * for %%a in (*.thrift) do "%BIN%\thrift.exe" -v --gen cpp "%%a" >> NUL: +cmd /c start notepad "%LOGFILE%" +cd .. + +rem * check for special Delphi testcases being processed +if not exist "%TARGET%\%SUBDIR%\ReservedKeywords.pas" goto TESTCASE_MISSING + + +rem * generate a minimal DPR file that uses all generated pascal units +cd "%TARGET%\%SUBDIR%\" +if exist inherited.* ren inherited.* _inherited.* +echo program %TESTAPP%; > %TESTAPP%.dpr +echo {$APPTYPE CONSOLE} >> %TESTAPP%.dpr +echo. >> %TESTAPP%.dpr +echo uses >> %TESTAPP%.dpr +for %%a in (*.pas) do echo %%~na, >> %TESTAPP%.dpr +echo Windows, Classes, SysUtils; >> %TESTAPP%.dpr +echo. >> %TESTAPP%.dpr +echo begin >> %TESTAPP%.dpr +echo Writeln('Successfully compiled!'); >> %TESTAPP%.dpr +echo Writeln('List of units:'); >> %TESTAPP%.dpr +for %%a in (*.pas) do echo Write('%%~na':30,'':10); >> %TESTAPP%.dpr +echo Writeln; >> %TESTAPP%.dpr +echo end. >> %TESTAPP%.dpr +echo. >> %TESTAPP%.dpr +cd ..\.. + +rem * try to compile the DPR +rem * this should not throw any errors, warnings or hints +"%DCC%" -B "%TARGET%\%SUBDIR%\%TESTAPP%" -U"%UNITSEARCH%" -I"%UNITSEARCH%" -N"%OUTDCU%" -E"%TARGET%\%SUBDIR%" +dir "%TARGET%\%SUBDIR%\%TESTAPP%.exe" +if not exist "%TARGET%\%SUBDIR%\%TESTAPP%.exe" goto CODEGEN_FAILED +echo. +echo ----------------------------------------------------------------- +echo The compiled program is now executed. If it hangs or crashes, we +echo have a serious problem with the generated code. Expected output +echo is "Successfully compiled:" followed by a list of generated units. +echo ----------------------------------------------------------------- +"%TARGET%\%SUBDIR%\%TESTAPP%.exe" +echo ----------------------------------------------------------------- +echo. +pause +GOTO EOF + +REM ----------------------------------------------------- +:DXE_NOT_FOUND +REM ----------------------------------------------------- +echo Delphi Compiler (dcc32.exe) not found. +echo Please check the "DCC" setting in this batch. +echo. +cmd /c start notepad README.TXT +cmd /c start notepad %0 +pause +GOTO EOF + + +REM ----------------------------------------------------- +:CONFIG_ERROR +REM ----------------------------------------------------- +echo Missing, incomplete or wrong configuration settings! +cmd /c start notepad README.TXT +cmd /c start notepad %0 +pause +GOTO EOF + + +REM ----------------------------------------------------- +:TESTCASE_MISSING +REM ----------------------------------------------------- +echo Missing an expected Delphi testcase! +pause +GOTO EOF + + +REM ----------------------------------------------------- +:CODEGEN_FAILED +REM ----------------------------------------------------- +echo Code generation FAILED! +pause +GOTO EOF + + +REM ----------------------------------------------------- +:HANDLEDIR +REM ----------------------------------------------------- +REM echo %1 +for /D %%a in (%1\*) do call %0 %%a +if exist "%1\*.thrift" copy /b "%1\*.thrift" "%TARGET%\*.*" +GOTO EOF + + +REM ----------------------------------------------------- +:EOF +REM ----------------------------------------------------- From 8b1adf42e200be9d9e1d017e5c1d02c67912a051 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Tue, 28 Feb 2012 20:34:06 +0000 Subject: [PATCH 11/22] THRIFT-1524 TNonBlockingServer does not compile in Visual Studio 2010 Patch: Christian Taedcke git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294819 13f79535-47bb-0310-9956-ffa450edef68 --- lib/cpp/src/windows/config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cpp/src/windows/config.h b/lib/cpp/src/windows/config.h index c4bf9f316a9..d875094f5e5 100644 --- a/lib/cpp/src/windows/config.h +++ b/lib/cpp/src/windows/config.h @@ -142,9 +142,9 @@ inline int poll_win32(LPWSAPOLLFD fdArray, ULONG nfds, INT timeout) } #endif // WINVER -inline void close(SOCKET socket) +inline int close(SOCKET socket) { - ::closesocket(socket); + return ::closesocket(socket); } #endif // _THRIFT_WINDOWS_CONFIG_H_ From 35f977c28e14b45b35063b57d47c76f89d2921b3 Mon Sep 17 00:00:00 2001 From: Roger Meier Date: Tue, 28 Feb 2012 20:50:13 +0000 Subject: [PATCH 12/22] THRIFT-1524 TNonBlockingServer does not compile in Visual Studio 2010 Patch: Christian Taedcke git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294824 13f79535-47bb-0310-9956-ffa450edef68 --- lib/cpp/src/server/TNonblockingServer.cpp | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/cpp/src/server/TNonblockingServer.cpp b/lib/cpp/src/server/TNonblockingServer.cpp index 7d42a2eebf4..1ade8950311 100644 --- a/lib/cpp/src/server/TNonblockingServer.cpp +++ b/lib/cpp/src/server/TNonblockingServer.cpp @@ -944,12 +944,12 @@ void TNonblockingServer::handleEvent(int fd, short which) { nConnectionsDropped_++; nTotalConnectionsDropped_++; if (overloadAction_ == T_OVERLOAD_CLOSE_ON_ACCEPT) { - close(clientSocket); + ::close(clientSocket); return; } else if (overloadAction_ == T_OVERLOAD_DRAIN_TASK_QUEUE) { if (!drainPendingTask()) { // Nothing left to discard, so we drop connection instead. - close(clientSocket); + ::close(clientSocket); return; } } @@ -960,7 +960,7 @@ void TNonblockingServer::handleEvent(int fd, short which) { if ((flags = fcntl(clientSocket, F_GETFL, 0)) < 0 || fcntl(clientSocket, F_SETFL, flags | O_NONBLOCK) < 0) { GlobalOutput.perror("thriftServerEventHandler: set O_NONBLOCK (fcntl) ", errno); - close(clientSocket); + ::close(clientSocket); return; } @@ -971,7 +971,7 @@ void TNonblockingServer::handleEvent(int fd, short which) { // Fail fast if we could not create a TConnection object if (clientConnection == NULL) { GlobalOutput.printf("thriftServerEventHandler: failed TConnection factory"); - close(clientSocket); + ::close(clientSocket); return; } @@ -1059,7 +1059,7 @@ void TNonblockingServer::createAndListenOnSocket() { setsockopt(s, SOL_SOCKET, SO_REUSEADDR, const_cast_sockopt(&one), sizeof(one)); if (::bind(s, res->ai_addr, res->ai_addrlen) == -1) { - close(s); + ::close(s); freeaddrinfo(res0); throw TException("TNonblockingServer::serve() bind"); } @@ -1080,7 +1080,7 @@ void TNonblockingServer::listenSocket(int s) { int flags; if ((flags = fcntl(s, F_GETFL, 0)) < 0 || fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) { - close(s); + ::close(s); throw TException("TNonblockingServer::serve() O_NONBLOCK"); } @@ -1106,7 +1106,7 @@ void TNonblockingServer::listenSocket(int s) { #endif if (listen(s, LISTEN_BACKLOG) == -1) { - close(s); + ::close(s); throw TException("TNonblockingServer::serve() listen"); } @@ -1267,7 +1267,7 @@ TNonblockingIOThread::~TNonblockingIOThread() { } if (listenSocket_ >= 0) { - if (0 != close(listenSocket_)) { + if (0 != ::close(listenSocket_)) { GlobalOutput.perror("TNonblockingIOThread listenSocket_ close(): ", errno); } @@ -1292,8 +1292,8 @@ void TNonblockingIOThread::createNotificationPipe() { } if(evutil_make_socket_nonblocking(notificationPipeFDs_[0])<0 || evutil_make_socket_nonblocking(notificationPipeFDs_[1])<0) { - close(notificationPipeFDs_[0]); - close(notificationPipeFDs_[1]); + ::close(notificationPipeFDs_[0]); + ::close(notificationPipeFDs_[1]); throw TException("TNonblockingServer::createNotificationPipe() O_NONBLOCK"); } for (int i = 0; i < 2; ++i) { @@ -1304,8 +1304,8 @@ void TNonblockingIOThread::createNotificationPipe() { #else if (evutil_make_socket_closeonexec(notificationPipeFDs_[i]) < 0) { #endif - close(notificationPipeFDs_[0]); - close(notificationPipeFDs_[1]); + ::close(notificationPipeFDs_[0]); + ::close(notificationPipeFDs_[1]); throw TException("TNonblockingServer::createNotificationPipe() " "FD_CLOEXEC"); } From a81b040ee0408c1cad0c3515dd8513120754935a Mon Sep 17 00:00:00 2001 From: Ilya Maykov Date: Wed, 29 Feb 2012 00:39:38 +0000 Subject: [PATCH 13/22] THRIFT-369: sets and maps break equality Client: ruby Patch: Ilya Maykov Added a unit test demonstrating that equality is not broken for structs with nested containers. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294917 13f79535-47bb-0310-9956-ffa450edef68 --- lib/rb/spec/ThriftSpec.thrift | 53 ++++- lib/rb/spec/struct_nested_containers_spec.rb | 193 +++++++++++++++++++ 2 files changed, 245 insertions(+), 1 deletion(-) create mode 100644 lib/rb/spec/struct_nested_containers_spec.rb diff --git a/lib/rb/spec/ThriftSpec.thrift b/lib/rb/spec/ThriftSpec.thrift index e4dece4becb..b42481b32d3 100644 --- a/lib/rb/spec/ThriftSpec.thrift +++ b/lib/rb/spec/ThriftSpec.thrift @@ -129,4 +129,55 @@ struct Struct_with_union { struct StructWithEnumMap { 1: map> my_map; -} \ No newline at end of file +} + +# Nested lists +struct NestedListInList { + 1: list> value +} + +struct NestedListInSet { + 1: set> value +} + +struct NestedListInMapKey { + 1: map, byte> value +} + +struct NestedListInMapValue { + 1: map> value +} + +# Nested sets +struct NestedSetInList { + 1: list> value +} + +struct NestedSetInSet { + 1: set> value +} + +struct NestedSetInMapKey { + 1: map, byte> value +} + +struct NestedSetInMapValue { + 1: map> value +} + +# Nested maps +struct NestedMapInList { + 1: list> value +} + +struct NestedMapInSet { + 1: set> value +} + +struct NestedMapInMapKey { + 2: map, byte> value +} + +struct NestedMapInMapValue { + 2: map> value +} diff --git a/lib/rb/spec/struct_nested_containers_spec.rb b/lib/rb/spec/struct_nested_containers_spec.rb new file mode 100644 index 00000000000..e1ea64d598b --- /dev/null +++ b/lib/rb/spec/struct_nested_containers_spec.rb @@ -0,0 +1,193 @@ +# +# 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. +# + +require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper')) + +class StructNestedContainersSpec < Spec::ExampleGroup + include Thrift + include SpecNamespace + + def with_type_checking + saved_type_checking, Thrift.type_checking = Thrift.type_checking, true + begin + yield + ensure + Thrift.type_checking = saved_type_checking + end + end + + describe Struct do + # Nested container tests, see THRIFT-369. + it "should support nested lists inside lists" do + with_type_checking do + a, b = NestedListInList.new, NestedListInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ] + thrift_struct.validate + end + a.should == b + b.value.push [3, 4, 5] + a.should_not == b + end + end + + it "should support nested lists inside sets" do + with_type_checking do + a, b = NestedListInSet.new, NestedListInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3], [2, 3, 4] ].to_set + thrift_struct.validate + end + a.should == b + b.value.add [3, 4, 5] + a.should_not == b + end + end + + it "should support nested lists in map keys" do + with_type_checking do + a, b = NestedListInMapKey.new, NestedListInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { [1, 2, 3] => 1, [2, 3, 4] => 2 } + thrift_struct.validate + end + a.should == b + b.value[[3, 4, 5]] = 3 + a.should_not == b + end + end + + it "should support nested lists in map values" do + with_type_checking do + a, b = NestedListInMapValue.new, NestedListInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => [1, 2, 3], 2 => [2, 3, 4] } + thrift_struct.validate + end + a.should == b + b.value[3] = [3, 4, 5] + a.should_not == b + end + end + + it "should support nested sets inside lists" do + with_type_checking do + a, b = NestedSetInList.new, NestedSetInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ] + thrift_struct.validate + end + a.should == b + b.value.push([3, 4, 5].to_set) + a.should_not == b + end + end + + it "should support nested sets inside sets" do + with_type_checking do + a, b = NestedSetInSet.new, NestedSetInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ [1, 2, 3].to_set, [2, 3, 4].to_set ].to_set + thrift_struct.validate + end + a.should == b + b.value.add([3, 4, 5].to_set) + a.should_not == b + end + end + + it "should support nested sets in map keys" do + with_type_checking do + a, b = NestedSetInMapKey.new, NestedSetInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { [1, 2, 3].to_set => 1, [2, 3, 4].to_set => 2 } + thrift_struct.validate + end + a.should == b + b.value[[3, 4, 5].to_set] = 3 + a.should_not == b + end + end + + it "should support nested sets in map values" do + with_type_checking do + a, b = NestedSetInMapValue.new, NestedSetInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => [1, 2, 3].to_set, 2 => [2, 3, 4].to_set } + thrift_struct.validate + end + a.should == b + b.value[3] = [3, 4, 5].to_set + a.should_not == b + end + end + + it "should support nested maps inside lists" do + with_type_checking do + a, b = NestedMapInList.new, NestedMapInList.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ] + thrift_struct.validate + end + a.should == b + b.value.push({ 3 => 4, 5 => 6 }) + a.should_not == b + end + end + + it "should support nested maps inside sets" do + with_type_checking do + a, b = NestedMapInSet.new, NestedMapInSet.new + [a, b].each do |thrift_struct| + thrift_struct.value = [ {1 => 2, 3 => 4}, {2 => 3, 4 => 5} ].to_set + thrift_struct.validate + end + a.should == b + b.value.add({ 3 => 4, 5 => 6 }) + a.should_not == b + end + end + + it "should support nested maps in map keys" do + with_type_checking do + a, b = NestedMapInMapKey.new, NestedMapInMapKey.new + [a, b].each do |thrift_struct| + thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5} => 2 } + thrift_struct.validate + end + a.should == b + b.value[{3 => 4, 5 => 6}] = 3 + a.should_not == b + end + end + + it "should support nested maps in map values" do + with_type_checking do + a, b = NestedMapInMapValue.new, NestedMapInMapValue.new + [a, b].each do |thrift_struct| + thrift_struct.value = { 1 => { 1 => 2, 3 => 4}, 2 => {2 => 3, 4 => 5} } + thrift_struct.validate + end + a.should == b + b.value[3] = { 3 => 4, 5 => 6 } + a.should_not == b + end + end + end +end From f85ad517656fda3f1dad04dab50771614e4df95a Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Wed, 29 Feb 2012 01:12:27 +0000 Subject: [PATCH 14/22] THRIFT-1526. java: Union TupleSchemeFactory returns StandardSchemes This patch makes it return nice, proper TupleSchemes. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294925 13f79535-47bb-0310-9956-ffa450edef68 --- lib/java/src/org/apache/thrift/TUnion.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/java/src/org/apache/thrift/TUnion.java b/lib/java/src/org/apache/thrift/TUnion.java index 0173f9b9edc..3052ee14011 100644 --- a/lib/java/src/org/apache/thrift/TUnion.java +++ b/lib/java/src/org/apache/thrift/TUnion.java @@ -17,15 +17,14 @@ */ package org.apache.thrift; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.nio.ByteBuffer; -import org.apache.thrift.TUnion.TUnionStandardScheme; import org.apache.thrift.protocol.TField; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolException; @@ -211,7 +210,7 @@ public TUnionStandardScheme getScheme() { } } - public static class TUnionStandardScheme extends StandardScheme { + private static class TUnionStandardScheme extends StandardScheme { @Override public void read(TProtocol iprot, TUnion struct) throws TException { @@ -250,12 +249,12 @@ public void write(TProtocol oprot, TUnion struct) throws TException { } private static class TUnionTupleSchemeFactory implements SchemeFactory { - public TUnionStandardScheme getScheme() { - return new TUnionStandardScheme(); + public TUnionTupleScheme getScheme() { + return new TUnionTupleScheme(); } } - public static class TUnionTupleScheme extends TupleScheme { + private static class TUnionTupleScheme extends TupleScheme { @Override public void read(TProtocol iprot, TUnion struct) throws TException { From c00011a7522a84f17e5d53ad1db7a9128c41c1e4 Mon Sep 17 00:00:00 2001 From: Ilya Maykov Date: Wed, 29 Feb 2012 02:40:28 +0000 Subject: [PATCH 15/22] THRIFT-1204: Ruby autogenerated files should require 'thrift' gem Library: Ruby Patch: Ilya Maykov This patch does 2 things: 1) Adds a "require 'thrift'" line to the *_types.rb and *_constants.rb files, to be consistent with *_service.rb which was generating the thrift require already. 2) Adds a "rubygems" option to the --gen rb compiler target. When this option is specified, the compiler will add a "require 'rubygems'" line to the top of each gen-rb file. Defaults to false. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1294949 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_rb_generator.cc | 28 +++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc index 95404a0007a..a46dfe331bc 100644 --- a/compiler/cpp/src/generate/t_rb_generator.cc +++ b/compiler/cpp/src/generate/t_rb_generator.cc @@ -51,9 +51,10 @@ class t_rb_generator : public t_oop_generator { const std::string& option_string) : t_oop_generator(program) { - (void) parsed_options; (void) option_string; out_dir_base_ = "gen-rb"; + + require_rubygems_ = (parsed_options.find("rubygems") != parsed_options.end()); } /** @@ -165,6 +166,7 @@ class t_rb_generator : public t_oop_generator { */ std::string rb_autogen_comment(); + std::string render_require_thrift(); std::string render_includes(); std::string declare_field(t_field* tfield); std::string type_name(t_type* ttype); @@ -209,6 +211,8 @@ class t_rb_generator : public t_oop_generator { std::ofstream f_consts_; std::ofstream f_service_; + /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */ + bool require_rubygems_; }; @@ -231,18 +235,29 @@ void t_rb_generator::init_generator() { // Print header f_types_ << - rb_autogen_comment() << endl << + rb_autogen_comment() << endl << render_require_thrift() << render_includes() << endl; begin_namespace(f_types_, ruby_modules(program_)); f_consts_ << - rb_autogen_comment() << endl << + rb_autogen_comment() << endl << render_require_thrift() << "require '" << underscore(program_name_) << "_types'" << endl << endl; begin_namespace(f_consts_, ruby_modules(program_)); } +/** + * Renders the require of thrift itself, and possibly of the rubygems dependency. + */ +string t_rb_generator::render_require_thrift() { + if (require_rubygems_) { + return "require 'rubygems'\nrequire 'thrift'\n"; + } else { + return "require 'thrift'\n"; + } +} + /** * Renders all the imports necessary for including another Thrift program */ @@ -698,8 +713,7 @@ void t_rb_generator::generate_service(t_service* tservice) { f_service_.open(f_service_name.c_str()); f_service_ << - rb_autogen_comment() << endl << - "require 'thrift'" << endl; + rb_autogen_comment() << endl << render_require_thrift(); if (tservice->get_extends() != NULL) { f_service_ << @@ -1191,5 +1205,5 @@ void t_rb_generator::generate_rb_union_validator(std::ofstream& out, indent(out) << "end" << endl << endl; } -THRIFT_REGISTER_GENERATOR(rb, "Ruby", "") - +THRIFT_REGISTER_GENERATOR(rb, "Ruby", +" rubygems: Add a \"require 'rubygems'\" line to the top of each generated file.\n") From 9be7b3886027509c8c68f79fd8ac60c0005d09e2 Mon Sep 17 00:00:00 2001 From: Ilya Maykov Date: Thu, 1 Mar 2012 05:56:39 +0000 Subject: [PATCH 16/22] THRIFT-481: indentation of ruby classes is off by a few Compiler: Ruby Patch: Ilya Maykov Fixed indentation of gen-rb files by wrapping each file output stream in a class that keeps track of the indent level instead of using the global indent. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1295423 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_rb_generator.cc | 424 ++++++++++---------- 1 file changed, 217 insertions(+), 207 deletions(-) diff --git a/compiler/cpp/src/generate/t_rb_generator.cc b/compiler/cpp/src/generate/t_rb_generator.cc index a46dfe331bc..947186020d3 100644 --- a/compiler/cpp/src/generate/t_rb_generator.cc +++ b/compiler/cpp/src/generate/t_rb_generator.cc @@ -38,6 +38,26 @@ using namespace std; +/** + * A subclass of std::ofstream that includes indenting functionality. + */ +class t_rb_ofstream : public std::ofstream { + private: + int indent_; + + public: + t_rb_ofstream() : std::ofstream(), indent_(0) { } + explicit t_rb_ofstream(const char* filename, ios_base::openmode mode = ios_base::out, int indent = 0) : + std::ofstream(filename, mode), indent_(indent) { } + + t_rb_ofstream& indent() { + for (int i = 0; i < indent_; ++i) { *this << " "; } + return *this; + } + + void indent_up() { indent_++; } + void indent_down() { indent_--; } +}; /** * Ruby code generator. @@ -76,23 +96,23 @@ class t_rb_generator : public t_oop_generator { void generate_xception (t_struct* txception); void generate_service (t_service* tservice); - std::string render_const_value(t_type* type, t_const_value* value); + t_rb_ofstream& render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value); /** * Struct generation code */ - void generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception); - void generate_rb_struct_required_validator(std::ofstream& out, t_struct* tstruct); - void generate_rb_union(std::ofstream& out, t_struct* tstruct, bool is_exception); - void generate_rb_union_validator(std::ofstream& out, t_struct* tstruct); + void generate_rb_struct(t_rb_ofstream& out, t_struct* tstruct, bool is_exception); + void generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct); + void generate_rb_union(t_rb_ofstream& out, t_struct* tstruct, bool is_exception); + void generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct); void generate_rb_function_helpers(t_function* tfunction); - void generate_rb_simple_constructor(std::ofstream& out, t_struct* tstruct); - void generate_rb_simple_exception_constructor(std::ofstream& out, t_struct* tstruct); - void generate_field_constants (std::ofstream& out, t_struct* tstruct); - void generate_field_constructors (std::ofstream& out, t_struct* tstruct); - void generate_field_defns (std::ofstream& out, t_struct* tstruct); - void generate_field_data (std::ofstream& out, t_type* field_type, const std::string& field_name, t_const_value* field_value, bool optional); + void generate_rb_simple_constructor(t_rb_ofstream& out, t_struct* tstruct); + void generate_rb_simple_exception_constructor(t_rb_ofstream& out, t_struct* tstruct); + void generate_field_constants (t_rb_ofstream& out, t_struct* tstruct); + void generate_field_constructors (t_rb_ofstream& out, t_struct* tstruct); + void generate_field_defns (t_rb_ofstream& out, t_struct* tstruct); + void generate_field_data (t_rb_ofstream& out, t_type* field_type, const std::string& field_name, t_const_value* field_value, bool optional); /** * Service-level generation functions @@ -108,57 +128,57 @@ class t_rb_generator : public t_oop_generator { * Serialization constructs */ - void generate_deserialize_field (std::ofstream &out, + void generate_deserialize_field (t_rb_ofstream &out, t_field* tfield, std::string prefix="", bool inclass=false); - void generate_deserialize_struct (std::ofstream &out, + void generate_deserialize_struct (t_rb_ofstream &out, t_struct* tstruct, std::string prefix=""); - void generate_deserialize_container (std::ofstream &out, + void generate_deserialize_container (t_rb_ofstream &out, t_type* ttype, std::string prefix=""); - void generate_deserialize_set_element (std::ofstream &out, + void generate_deserialize_set_element (t_rb_ofstream &out, t_set* tset, std::string prefix=""); - void generate_deserialize_map_element (std::ofstream &out, + void generate_deserialize_map_element (t_rb_ofstream &out, t_map* tmap, std::string prefix=""); - void generate_deserialize_list_element (std::ofstream &out, + void generate_deserialize_list_element (t_rb_ofstream &out, t_list* tlist, std::string prefix=""); - void generate_serialize_field (std::ofstream &out, + void generate_serialize_field (t_rb_ofstream &out, t_field* tfield, std::string prefix=""); - void generate_serialize_struct (std::ofstream &out, + void generate_serialize_struct (t_rb_ofstream &out, t_struct* tstruct, std::string prefix=""); - void generate_serialize_container (std::ofstream &out, + void generate_serialize_container (t_rb_ofstream &out, t_type* ttype, std::string prefix=""); - void generate_serialize_map_element (std::ofstream &out, + void generate_serialize_map_element (t_rb_ofstream &out, t_map* tmap, std::string kiter, std::string viter); - void generate_serialize_set_element (std::ofstream &out, + void generate_serialize_set_element (t_rb_ofstream &out, t_set* tmap, std::string iter); - void generate_serialize_list_element (std::ofstream &out, + void generate_serialize_list_element (t_rb_ofstream &out, t_list* tlist, std::string iter); - void generate_rdoc (std::ofstream& out, + void generate_rdoc (t_rb_ofstream& out, t_doc* tdoc); /** @@ -198,8 +218,8 @@ class t_rb_generator : public t_oop_generator { return modules; } - void begin_namespace(std::ofstream&, std::vector); - void end_namespace(std::ofstream&, std::vector); + void begin_namespace(t_rb_ofstream&, std::vector); + void end_namespace(t_rb_ofstream&, std::vector); private: @@ -207,9 +227,9 @@ class t_rb_generator : public t_oop_generator { * File streams */ - std::ofstream f_types_; - std::ofstream f_consts_; - std::ofstream f_service_; + t_rb_ofstream f_types_; + t_rb_ofstream f_consts_; + t_rb_ofstream f_service_; /** If true, add a "require 'rubygems'" line to the top of each gen-rb file. */ bool require_rubygems_; @@ -312,9 +332,9 @@ void t_rb_generator::generate_typedef(t_typedef* ttypedef) { * @param tenum The enumeration */ void t_rb_generator::generate_enum(t_enum* tenum) { - indent(f_types_) << + f_types_.indent() << "module " << capitalize(tenum->get_name()) << endl; - indent_up(); + f_types_.indent_up(); vector constants = tenum->get_constants(); vector::iterator c_iter; @@ -327,11 +347,11 @@ void t_rb_generator::generate_enum(t_enum* tenum) { string name = capitalize((*c_iter)->get_name()); generate_rdoc(f_types_, *c_iter); - indent(f_types_) << name << " = " << value << endl; + f_types_.indent() << name << " = " << value << endl; } // Create a hash mapping values back to their names (as strings) since ruby has no native enum type - indent(f_types_) << "VALUE_MAP = {"; + f_types_.indent() << "VALUE_MAP = {"; bool first = true; for(c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { // Populate the hash @@ -342,7 +362,7 @@ void t_rb_generator::generate_enum(t_enum* tenum) { f_types_ << "}" << endl; // Create a set with valid values for this enum - indent(f_types_) << "VALID_VALUES = Set.new(["; + f_types_.indent() << "VALID_VALUES = Set.new(["; first = true; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { // Populate the set @@ -351,8 +371,8 @@ void t_rb_generator::generate_enum(t_enum* tenum) { } f_types_ << "]).freeze" << endl; - indent_down(); - indent(f_types_) << + f_types_.indent_down(); + f_types_.indent() << "end" << endl << endl; } @@ -366,8 +386,8 @@ void t_rb_generator::generate_const(t_const* tconst) { name[0] = toupper(name[0]); - indent(f_consts_) << name << " = " << render_const_value(type, value); - f_consts_ << endl << endl; + f_consts_.indent() << name << " = "; + render_const_value(f_consts_, type, value) << endl << endl; } /** @@ -375,9 +395,8 @@ void t_rb_generator::generate_const(t_const* tconst) { * is NOT performed in this function as it is always run beforehand using the * validate_types method in main.cc */ -string t_rb_generator::render_const_value(t_type* type, t_const_value* value) { +t_rb_ofstream& t_rb_generator::render_const_value(t_rb_ofstream& out, t_type* type, t_const_value* value) { type = get_true_type(type); - std::ostringstream out; if (type->is_base_type()) { t_base_type::t_base tbase = ((t_base_type*)type)->get_base(); switch (tbase) { @@ -404,10 +423,10 @@ string t_rb_generator::render_const_value(t_type* type, t_const_value* value) { throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } } else if (type->is_enum()) { - indent(out) << value->get_integer(); + out.indent() << value->get_integer(); } else if (type->is_struct() || type->is_xception()) { out << full_type_name(type) << ".new({" << endl; - indent_up(); + out.indent_up(); const vector& fields = ((t_struct*)type)->get_members(); vector::const_iterator f_iter; const map& val = value->get_map(); @@ -422,30 +441,26 @@ string t_rb_generator::render_const_value(t_type* type, t_const_value* value) { if (field_type == NULL) { throw "type error: " + type->get_name() + " has no field " + v_iter->first->get_string(); } - out << indent(); - out << render_const_value(g_type_string, v_iter->first); - out << " => "; - out << render_const_value(field_type, v_iter->second); - out << "," << endl; + out.indent(); + render_const_value(out, g_type_string, v_iter->first) << " => "; + render_const_value(out, field_type, v_iter->second) << "," << endl; } - indent_down(); - indent(out) << "})"; + out.indent_down(); + out.indent() << "})"; } else if (type->is_map()) { t_type* ktype = ((t_map*)type)->get_key_type(); t_type* vtype = ((t_map*)type)->get_val_type(); out << "{" << endl; - indent_up(); + out.indent_up(); const map& val = value->get_map(); map::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - out << indent(); - out << render_const_value(ktype, v_iter->first); - out << " => "; - out << render_const_value(vtype, v_iter->second); - out << "," << endl; + out.indent(); + render_const_value(out, ktype, v_iter->first) << " => "; + render_const_value(out, vtype, v_iter->second) << "," << endl; } - indent_down(); - indent(out) << "}"; + out.indent_down(); + out.indent() << "}"; } else if (type->is_list() || type->is_set()) { t_type* etype; if (type->is_list()) { @@ -458,24 +473,23 @@ string t_rb_generator::render_const_value(t_type* type, t_const_value* value) { } else { out << "[" << endl; } - indent_up(); + out.indent_up(); const vector& val = value->get_list(); vector::const_iterator v_iter; for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) { - out << indent(); - out << render_const_value(etype, *v_iter); - out << "," << endl; + out.indent(); + render_const_value(out, etype, *v_iter) << "," << endl; } - indent_down(); + out.indent_down(); if (type->is_set()) { - indent(out) << "])"; + out.indent() << "])"; } else { - indent(out) << "]"; + out.indent() << "]"; } } else { throw "CANNOT GENERATE CONSTANT FOR TYPE: " + type->get_name(); } - return out.str(); + return out; } /** @@ -502,16 +516,16 @@ void t_rb_generator::generate_xception(t_struct* txception) { /** * Generates a ruby struct */ -void t_rb_generator::generate_rb_struct(std::ofstream& out, t_struct* tstruct, bool is_exception = false) { +void t_rb_generator::generate_rb_struct(t_rb_ofstream& out, t_struct* tstruct, bool is_exception = false) { generate_rdoc(out, tstruct); - indent(out) << "class " << type_name(tstruct); + out.indent() << "class " << type_name(tstruct); if (is_exception) { out << " < ::Thrift::Exception"; } out << endl; - indent_up(); - indent(out) << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl; + out.indent_up(); + out.indent() << "include ::Thrift::Struct, ::Thrift::Struct_Union" << endl; if (is_exception) { generate_rb_simple_exception_constructor(out, tstruct); @@ -521,23 +535,23 @@ void t_rb_generator::generate_rb_struct(std::ofstream& out, t_struct* tstruct, b generate_field_defns(out, tstruct); generate_rb_struct_required_validator(out, tstruct); - indent(out) << "::Thrift::Struct.generate_accessors self" << endl; + out.indent() << "::Thrift::Struct.generate_accessors self" << endl; - indent_down(); - indent(out) << "end" << endl << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; } /** * Generates a ruby union */ -void t_rb_generator::generate_rb_union(std::ofstream& out, t_struct* tstruct, bool is_exception = false) { +void t_rb_generator::generate_rb_union(t_rb_ofstream& out, t_struct* tstruct, bool is_exception = false) { (void) is_exception; generate_rdoc(out, tstruct); - indent(out) << "class " << type_name(tstruct) << " < ::Thrift::Union" << endl; + out.indent() << "class " << type_name(tstruct) << " < ::Thrift::Union" << endl; - indent_up(); - indent(out) << "include ::Thrift::Struct_Union" << endl; + out.indent_up(); + out.indent() << "include ::Thrift::Struct_Union" << endl; generate_field_constructors(out, tstruct); @@ -545,16 +559,16 @@ void t_rb_generator::generate_rb_union(std::ofstream& out, t_struct* tstruct, bo generate_field_defns(out, tstruct); generate_rb_union_validator(out, tstruct); - indent(out) << "::Thrift::Union.generate_accessors self" << endl; + out.indent() << "::Thrift::Union.generate_accessors self" << endl; - indent_down(); - indent(out) << "end" << endl << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; } -void t_rb_generator::generate_field_constructors(std::ofstream& out, t_struct* tstruct) { +void t_rb_generator::generate_field_constructors(t_rb_ofstream& out, t_struct* tstruct) { - indent(out) << "class << self" << endl; - indent_up(); + out.indent() << "class << self" << endl; + out.indent_up(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; @@ -565,18 +579,18 @@ void t_rb_generator::generate_field_constructors(std::ofstream& out, t_struct* t } std::string field_name = (*f_iter)->get_name(); - indent(out) << "def " << field_name << "(val)" << endl; - indent(out) << " " << tstruct->get_name() << ".new(:" << field_name << ", val)" << endl; - indent(out) << "end" << endl; + out.indent() << "def " << field_name << "(val)" << endl; + out.indent() << " " << tstruct->get_name() << ".new(:" << field_name << ", val)" << endl; + out.indent() << "end" << endl; } - indent_down(); - indent(out) << "end" << endl; + out.indent_down(); + out.indent() << "end" << endl; out << endl; } -void t_rb_generator::generate_rb_simple_exception_constructor(std::ofstream& out, t_struct* tstruct) { +void t_rb_generator::generate_rb_simple_exception_constructor(t_rb_ofstream& out, t_struct* tstruct) { const vector& members = tstruct->get_members(); if (members.size() == 1) { @@ -585,21 +599,21 @@ void t_rb_generator::generate_rb_simple_exception_constructor(std::ofstream& out if ((*m_iter)->get_type()->is_string()) { string name = (*m_iter)->get_name(); - indent(out) << "def initialize(message=nil)" << endl; - indent_up(); - indent(out) << "super()" << endl; - indent(out) << "self." << name << " = message" << endl; - indent_down(); - indent(out) << "end" << endl << endl; + out.indent() << "def initialize(message=nil)" << endl; + out.indent_up(); + out.indent() << "super()" << endl; + out.indent() << "self." << name << " = message" << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; if (name != "message") { - indent(out) << "def message; " << name << " end" << endl << endl; + out.indent() << "def message; " << name << " end" << endl << endl; } } } } -void t_rb_generator::generate_field_constants(std::ofstream& out, t_struct* tstruct) { +void t_rb_generator::generate_field_constants(t_rb_ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; @@ -607,17 +621,17 @@ void t_rb_generator::generate_field_constants(std::ofstream& out, t_struct* tstr std::string field_name = (*f_iter)->get_name(); std::string cap_field_name = upcase_string(field_name); - indent(out) << cap_field_name << " = " << (*f_iter)->get_key() << endl; + out.indent() << cap_field_name << " = " << (*f_iter)->get_key() << endl; } out << endl; } -void t_rb_generator::generate_field_defns(std::ofstream& out, t_struct* tstruct) { +void t_rb_generator::generate_field_defns(t_rb_ofstream& out, t_struct* tstruct) { const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; - indent(out) << "FIELDS = {" << endl; - indent_up(); + out.indent() << "FIELDS = {" << endl; + out.indent_up(); for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (f_iter != fields.begin()) { out << "," << endl; @@ -626,21 +640,20 @@ void t_rb_generator::generate_field_defns(std::ofstream& out, t_struct* tstruct) // generate the field docstrings within the FIELDS constant. no real better place... generate_rdoc(out, *f_iter); - indent(out) << - upcase_string((*f_iter)->get_name()) << " => "; + out.indent() << upcase_string((*f_iter)->get_name()) << " => "; generate_field_data(out, (*f_iter)->get_type(), (*f_iter)->get_name(), (*f_iter)->get_value(), (*f_iter)->get_req() == t_field::T_OPTIONAL); } - indent_down(); + out.indent_down(); out << endl; - indent(out) << "}" << endl << endl; + out.indent() << "}" << endl << endl; - indent(out) << "def struct_fields; FIELDS; end" << endl << endl; + out.indent() << "def struct_fields; FIELDS; end" << endl << endl; } -void t_rb_generator::generate_field_data(std::ofstream& out, t_type* field_type, +void t_rb_generator::generate_field_data(t_rb_ofstream& out, t_type* field_type, const std::string& field_name = "", t_const_value* field_value = NULL, bool optional = false) { field_type = get_true_type(field_type); @@ -652,7 +665,8 @@ void t_rb_generator::generate_field_data(std::ofstream& out, t_type* field_type, } if (field_value != NULL) { - out << ", :default => " << render_const_value(field_type, field_value); + out << ", :default => "; + render_const_value(out, field_type, field_value); } if (!field_type->is_base_type()) { @@ -688,17 +702,17 @@ void t_rb_generator::generate_field_data(std::ofstream& out, t_type* field_type, out << "}"; } -void t_rb_generator::begin_namespace(std::ofstream& out, vector modules) { +void t_rb_generator::begin_namespace(t_rb_ofstream& out, vector modules) { for (vector::iterator m_iter = modules.begin(); m_iter != modules.end(); ++m_iter) { - indent(out) << "module " << *m_iter << endl; - indent_up(); + out.indent() << "module " << *m_iter << endl; + out.indent_up(); } } -void t_rb_generator::end_namespace(std::ofstream& out, vector modules) { +void t_rb_generator::end_namespace(t_rb_ofstream& out, vector modules) { for (vector::reverse_iterator m_iter = modules.rbegin(); m_iter != modules.rend(); ++m_iter) { - indent_down(); - indent(out) << "end" << endl; + out.indent_down(); + out.indent() << "end" << endl; } } @@ -726,17 +740,16 @@ void t_rb_generator::generate_service(t_service* tservice) { begin_namespace(f_service_, ruby_modules(tservice->get_program())); - indent(f_service_) << "module " << capitalize(tservice->get_name()) << endl; - indent_up(); + f_service_.indent() << "module " << capitalize(tservice->get_name()) << endl; + f_service_.indent_up(); // Generate the three main parts of the service (well, two for now in PHP) generate_service_client(tservice); generate_service_server(tservice); generate_service_helpers(tservice); - indent_down(); - indent(f_service_) << "end" << endl << - endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; end_namespace(f_service_, ruby_modules(tservice->get_program())); @@ -753,8 +766,7 @@ void t_rb_generator::generate_service_helpers(t_service* tservice) { vector functions = tservice->get_functions(); vector::iterator f_iter; - indent(f_service_) << - "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl; + f_service_.indent() << "# HELPER FUNCTIONS AND STRUCTURES" << endl << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); @@ -797,12 +809,10 @@ void t_rb_generator::generate_service_client(t_service* tservice) { extends_client = " < " + extends + "::Client "; } - indent(f_service_) << - "class Client" << extends_client << endl; - indent_up(); + f_service_.indent() << "class Client" << extends_client << endl; + f_service_.indent_up(); - indent(f_service_) << - "include ::Thrift::Client" << endl << endl; + f_service_.indent() << "include ::Thrift::Client" << endl << endl; // Generate client method implementations vector functions = tservice->get_functions(); @@ -814,10 +824,9 @@ void t_rb_generator::generate_service_client(t_service* tservice) { string funname = (*f_iter)->get_name(); // Open function - indent(f_service_) << - "def " << function_signature(*f_iter) << endl; - indent_up(); - indent(f_service_) << + f_service_.indent() << "def " << function_signature(*f_iter) << endl; + f_service_.indent_up(); + f_service_.indent() << "send_" << funname << "("; bool first = true; @@ -832,24 +841,24 @@ void t_rb_generator::generate_service_client(t_service* tservice) { f_service_ << ")" << endl; if (!(*f_iter)->is_oneway()) { - f_service_ << indent(); + f_service_.indent(); if (!(*f_iter)->get_returntype()->is_void()) { f_service_ << "return "; } f_service_ << "recv_" << funname << "()" << endl; } - indent_down(); - indent(f_service_) << "end" << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl; f_service_ << endl; - indent(f_service_) << + f_service_.indent() << "def send_" << function_signature(*f_iter) << endl; - indent_up(); + f_service_.indent_up(); std::string argsname = capitalize((*f_iter)->get_name() + "_args"); - indent(f_service_) << "send_message('" << funname << "', " << argsname; + f_service_.indent() << "send_message('" << funname << "', " << argsname; for (fld_iter = fields.begin(); fld_iter != fields.end(); ++fld_iter) { f_service_ << ", :" << (*fld_iter)->get_name() << " => " << (*fld_iter)->get_name(); @@ -857,8 +866,8 @@ void t_rb_generator::generate_service_client(t_service* tservice) { f_service_ << ")" << endl; - indent_down(); - indent(f_service_) << "end" << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl; if (!(*f_iter)->is_oneway()) { std::string resultname = capitalize((*f_iter)->get_name() + "_result"); @@ -868,48 +877,46 @@ void t_rb_generator::generate_service_client(t_service* tservice) { string("recv_") + (*f_iter)->get_name(), &noargs); // Open function - f_service_ << - endl << - indent() << "def " << function_signature(&recv_function) << endl; - indent_up(); + f_service_ << endl; + f_service_.indent() << "def " << function_signature(&recv_function) << endl; + f_service_.indent_up(); // TODO(mcslee): Validate message reply here, seq ids etc. - f_service_ << - indent() << "result = receive_message(" << resultname << ")" << endl; + f_service_.indent() << "result = receive_message(" << resultname << ")" << endl; // Careful, only return _result if not a void function if (!(*f_iter)->get_returntype()->is_void()) { - f_service_ << - indent() << "return result.success unless result.success.nil?" << endl; + f_service_.indent() << "return result.success unless result.success.nil?" << endl; } t_struct* xs = (*f_iter)->get_xceptions(); const std::vector& xceptions = xs->get_members(); vector::const_iterator x_iter; for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - indent(f_service_) << + f_service_.indent() << "raise result." << (*x_iter)->get_name() << " unless result." << (*x_iter)->get_name() << ".nil?" << endl; } // Careful, only return _result if not a void function if ((*f_iter)->get_returntype()->is_void()) { - indent(f_service_) << + f_service_.indent() << "return" << endl; } else { - f_service_ << - indent() << "raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, '" << (*f_iter)->get_name() << " failed: unknown result')" << endl; + f_service_.indent() << + "raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, '" << + (*f_iter)->get_name() << " failed: unknown result')" << endl; } // Close function - indent_down(); - indent(f_service_) << "end" << endl << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; } } - indent_down(); - indent(f_service_) << "end" << endl << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; } /** @@ -930,21 +937,19 @@ void t_rb_generator::generate_service_server(t_service* tservice) { } // Generate the header portion - indent(f_service_) << + f_service_.indent() << "class Processor" << extends_processor << endl; - indent_up(); + f_service_.indent_up(); - f_service_ << - indent() << "include ::Thrift::Processor" << endl << - endl; + f_service_.indent() << "include ::Thrift::Processor" << endl << endl; // Generate the process subfunctions for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { generate_process_function(tservice, *f_iter); } - indent_down(); - indent(f_service_) << "end" << endl << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; } /** @@ -956,16 +961,15 @@ void t_rb_generator::generate_process_function(t_service* tservice, t_function* tfunction) { (void) tservice; // Open function - indent(f_service_) << + f_service_.indent() << "def process_" << tfunction->get_name() << "(seqid, iprot, oprot)" << endl; - indent_up(); + f_service_.indent_up(); string argsname = capitalize(tfunction->get_name()) + "_args"; string resultname = capitalize(tfunction->get_name()) + "_result"; - f_service_ << - indent() << "args = read_args(iprot, " << argsname << ")" << endl; + f_service_.indent() << "args = read_args(iprot, " << argsname << ")" << endl; t_struct* xs = tfunction->get_xceptions(); const std::vector& xceptions = xs->get_members(); @@ -973,15 +977,13 @@ void t_rb_generator::generate_process_function(t_service* tservice, // Declare result for non oneway function if (!tfunction->is_oneway()) { - f_service_ << - indent() << "result = " << resultname << ".new()" << endl; + f_service_.indent() << "result = " << resultname << ".new()" << endl; } // Try block for a function with exceptions if (xceptions.size() > 0) { - f_service_ << - indent() << "begin" << endl; - indent_up(); + f_service_.indent() << "begin" << endl; + f_service_.indent_up(); } // Generate the function call @@ -989,7 +991,7 @@ void t_rb_generator::generate_process_function(t_service* tservice, const std::vector& fields = arg_struct->get_members(); vector::const_iterator f_iter; - f_service_ << indent(); + f_service_.indent(); if (!tfunction->is_oneway() && !tfunction->get_returntype()->is_void()) { f_service_ << "result.success = "; } @@ -1007,35 +1009,32 @@ void t_rb_generator::generate_process_function(t_service* tservice, f_service_ << ")" << endl; if (!tfunction->is_oneway() && xceptions.size() > 0) { - indent_down(); + f_service_.indent_down(); for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - f_service_ << - indent() << "rescue " << full_type_name((*x_iter)->get_type()) << " => " << (*x_iter)->get_name() << endl; + f_service_.indent() << "rescue " << full_type_name((*x_iter)->get_type()) << " => " << + (*x_iter)->get_name() << endl; if (!tfunction->is_oneway()) { - indent_up(); - f_service_ << - indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << endl; - indent_down(); + f_service_.indent_up(); + f_service_.indent() << "result." << (*x_iter)->get_name() << " = " << (*x_iter)->get_name() << endl; + f_service_.indent_down(); } } - indent(f_service_) << "end" << endl; + f_service_.indent() << "end" << endl; } // Shortcut out here for oneway functions if (tfunction->is_oneway()) { - f_service_ << - indent() << "return" << endl; - indent_down(); - indent(f_service_) << "end" << endl << endl; + f_service_.indent() << "return" << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; return; } - f_service_ << - indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)" << endl; + f_service_.indent() << "write_result(result, oprot, '" << tfunction->get_name() << "', seqid)" << endl; // Close function - indent_down(); - indent(f_service_) << "end" << endl << endl; + f_service_.indent_down(); + f_service_.indent() << "end" << endl << endl; } /** @@ -1135,17 +1134,18 @@ string t_rb_generator::type_to_enum(t_type* type) { } -void t_rb_generator::generate_rdoc(std::ofstream& out, t_doc* tdoc) { +void t_rb_generator::generate_rdoc(t_rb_ofstream& out, t_doc* tdoc) { if (tdoc->has_doc()) { + out.indent(); generate_docstring_comment(out, "", "# ", tdoc->get_doc(), ""); } } -void t_rb_generator::generate_rb_struct_required_validator(std::ofstream& out, +void t_rb_generator::generate_rb_struct_required_validator(t_rb_ofstream& out, t_struct* tstruct) { - indent(out) << "def validate" << endl; - indent_up(); + out.indent() << "def validate" << endl; + out.indent_up(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; @@ -1153,7 +1153,9 @@ void t_rb_generator::generate_rb_struct_required_validator(std::ofstream& out, for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { t_field* field = (*f_iter); if (field->get_req() == t_field::T_REQUIRED) { - indent(out) << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field " << field->get_name() << " is unset!')"; + out.indent() << + "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field " << + field->get_name() << " is unset!')"; if (field->get_type()->is_bool()) { out << " if @" << field->get_name() << ".nil?"; } else { @@ -1168,41 +1170,49 @@ void t_rb_generator::generate_rb_struct_required_validator(std::ofstream& out, t_field* field = (*f_iter); if (field->get_type()->is_enum()){ - indent(out) << "unless @" << field->get_name() << ".nil? || " << full_type_name(field->get_type()) << "::VALID_VALUES.include?(@" << field->get_name() << ")" << endl; - indent_up(); - indent(out) << "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field " << field->get_name() << "!')" << endl; - indent_down(); - indent(out) << "end" << endl; + out.indent() << "unless @" << field->get_name() << ".nil? || " << + full_type_name(field->get_type()) << "::VALID_VALUES.include?(@" << + field->get_name() << ")" << endl; + out.indent_up(); + out.indent() << + "raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field " << + field->get_name() << "!')" << endl; + out.indent_down(); + out.indent() << "end" << endl; } } - indent_down(); - indent(out) << "end" << endl << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; } -void t_rb_generator::generate_rb_union_validator(std::ofstream& out, +void t_rb_generator::generate_rb_union_validator(t_rb_ofstream& out, t_struct* tstruct) { - indent(out) << "def validate" << endl; - indent_up(); + out.indent() << "def validate" << endl; + out.indent_up(); const vector& fields = tstruct->get_members(); vector::const_iterator f_iter; - indent(out) << "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?" << endl; + out.indent() << + "raise(StandardError, 'Union fields are not set.') if get_set_field.nil? || get_value.nil?" << endl; // if field is an enum, check that its value is valid for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { const t_field* field = (*f_iter); if (field->get_type()->is_enum()){ - indent(out) << "if get_set_field == :" << field->get_name() << endl; - indent(out) << " raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field " << field->get_name() << "!') unless " << full_type_name(field->get_type()) << "::VALID_VALUES.include?(get_value)" << endl; - indent(out) << "end" << endl; + out.indent() << "if get_set_field == :" << field->get_name() << endl; + out.indent() << + " raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field " << + field->get_name() << "!') unless " << full_type_name(field->get_type()) << + "::VALID_VALUES.include?(get_value)" << endl; + out.indent() << "end" << endl; } } - indent_down(); - indent(out) << "end" << endl << endl; + out.indent_down(); + out.indent() << "end" << endl << endl; } THRIFT_REGISTER_GENERATOR(rb, "Ruby", From 38087708b5ca5a575bb3bcbbf3c24ab181e1cfc1 Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Thu, 1 Mar 2012 23:41:09 +0000 Subject: [PATCH 17/22] THRIFT-1529. java: TupleProtocol can unintentionally include an extra byte in bit vectors when number of optional fields is an integral of 8 This patch harmonizes the math between writeBitSet and readBitSet to eliminate the mismatch in number of bytes calculation, allowing structs to be serialized correctly. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1295995 13f79535-47bb-0310-9956-ffa450edef68 --- lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java b/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java index 14d50a6e6b4..74f5226c8af 100644 --- a/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java +++ b/lib/java/src/org/apache/thrift/protocol/TTupleProtocol.java @@ -86,7 +86,7 @@ public static BitSet fromByteArray(byte[] bytes) { * @return a byte array of at least length 1 */ public static byte[] toByteArray(BitSet bits, int vectorWidth) { - byte[] bytes = new byte[vectorWidth / 8 + 1]; + byte[] bytes = new byte[(int) Math.ceil(vectorWidth/8.0)]; for (int i = 0; i < bits.length(); i++) { if (bits.get(i)) { bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8); From 68839504c2f13a058b17391414f40012e54ff398 Mon Sep 17 00:00:00 2001 From: Bryan Duxbury Date: Thu, 1 Mar 2012 23:43:54 +0000 Subject: [PATCH 18/22] THRIFT-1527. java: Generated implementation of tupleReadStruct in unions return null when the setfield is unrecognized Unrecognized fields will now result in a TProtocolException when reading with the TupleProtocol. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1295997 13f79535-47bb-0310-9956-ffa450edef68 --- compiler/cpp/src/generate/t_java_generator.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/cpp/src/generate/t_java_generator.cc b/compiler/cpp/src/generate/t_java_generator.cc index 3f8322264e7..6ef383886e7 100644 --- a/compiler/cpp/src/generate/t_java_generator.cc +++ b/compiler/cpp/src/generate/t_java_generator.cc @@ -353,6 +353,7 @@ string t_java_generator::java_type_imports() { "import org.apache.thrift.scheme.StandardScheme;\n\n" + "import org.apache.thrift.scheme.TupleScheme;\n" + "import org.apache.thrift.protocol.TTupleProtocol;\n" + + "import org.apache.thrift.protocol.TProtocolException;\n" + "import org.apache.thrift.EncodingUtils;\n" + "import java.util.List;\n" + "import java.util.ArrayList;\n" + @@ -1082,7 +1083,7 @@ void t_java_generator::generate_tuple_scheme_read_value(ofstream& out, t_struct* indent_down(); indent(out) << "} else {" << endl; indent_up(); - indent(out) << "return null;" << endl; + indent(out) << "throw new TProtocolException(\"Couldn't find a field with field id \" + fieldID);" << endl; indent_down(); indent(out) << "}" << endl; indent_down(); From 363f6d414b824303f3c1f440620c4a2ac7581e47 Mon Sep 17 00:00:00 2001 From: Jake Farrell Date: Fri, 2 Mar 2012 02:50:50 +0000 Subject: [PATCH 19/22] THRIFT-1128:MAC OS X thrift.h incompatibility with Thrift.h Client: cpp, c_glib Patch: Jake Farrell Moving c_glib headers into $(includedir)/thrift/c_glib to not collide with cpp headers. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1296038 13f79535-47bb-0310-9956-ffa450edef68 --- lib/c_glib/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/c_glib/Makefile.am b/lib/c_glib/Makefile.am index ce614045d67..2f03e3d04d7 100644 --- a/lib/c_glib/Makefile.am +++ b/lib/c_glib/Makefile.am @@ -53,7 +53,7 @@ libthrift_c_glib_la_SOURCES = src/thrift.c \ libthrift_c_glib_la_CFLAGS = $(common_cflags) -include_thriftdir = $(includedir)/thrift +include_thriftdir = $(includedir)/thrift/c_glib include_thrift_HEADERS = \ $(top_builddir)/config.h \ src/thrift.h \ From 4ca8c3b8197994b9933a33556144a3403d09f75e Mon Sep 17 00:00:00 2001 From: Jake Farrell Date: Fri, 2 Mar 2012 05:22:02 +0000 Subject: [PATCH 20/22] THRIFT-1523: clientTimeout not worked as expected in TServerSocket created by TSSLTransportFactory Client: java Patch: Alexey Sviridov Adds server timeout to SSLTransportFactory git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1296053 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/org/apache/thrift/transport/TSSLTransportFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java b/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java index f5611aaba00..25df97f1708 100644 --- a/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java +++ b/lib/java/src/org/apache/thrift/transport/TSSLTransportFactory.java @@ -112,7 +112,7 @@ private static TServerSocket createServer(SSLServerSocketFactory factory, int po if (params != null && params.cipherSuites != null) { serverSocket.setEnabledCipherSuites(params.cipherSuites); } - return new TServerSocket(serverSocket); + return new TServerSocket(serverSocket, timeout); } catch (Exception e) { throw new TTransportException("Could not bind to port " + port, e); } From ca620e3788b21936b3d9f6f3055c1629cedb9c3d Mon Sep 17 00:00:00 2001 From: Jake Farrell Date: Fri, 2 Mar 2012 05:56:03 +0000 Subject: [PATCH 21/22] THRIFT-1429:The nonblocking servers is supposed to use TransportFactory to read the data Client: Java Patch: Bryan Duxbury Enforce the transport factory on the server-read side as well as on the server-write side git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1296060 13f79535-47bb-0310-9956-ffa450edef68 --- .../server/AbstractNonblockingServer.java | 7 ++- .../apache/thrift/server/ServerTestBase.java | 57 ++++++++++++++++++- .../thrift/server/TestNonblockingServer.java | 13 +++-- .../transport/TestTSSLTransportFactory.java | 10 +++- .../thrift/transport/TestTSaslTransports.java | 2 +- 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java b/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java index bdd21c5eb79..7fd75bf2f76 100644 --- a/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java +++ b/lib/java/src/org/apache/thrift/server/AbstractNonblockingServer.java @@ -332,10 +332,11 @@ public boolean read() { } // increment the amount of memory allocated to read buffers - readBufferBytesAllocated.addAndGet(frameSize); + readBufferBytesAllocated.addAndGet(frameSize + 4); // reallocate the readbuffer as a frame-sized buffer - buffer_ = ByteBuffer.allocate(frameSize); + buffer_ = ByteBuffer.allocate(frameSize + 4); + buffer_.putInt(frameSize); state_ = FrameBufferState.READING_FRAME; } else { @@ -492,7 +493,7 @@ public void invoke() { * the data it needs to handle an invocation. */ private TTransport getInputTransport() { - return new TMemoryInputTransport(buffer_.array()); + return inputTransportFactory_.getTransport(new TMemoryInputTransport(buffer_.array())); } /** diff --git a/lib/java/test/org/apache/thrift/server/ServerTestBase.java b/lib/java/test/org/apache/thrift/server/ServerTestBase.java index b9974c4a49a..08b57e23e24 100644 --- a/lib/java/test/org/apache/thrift/server/ServerTestBase.java +++ b/lib/java/test/org/apache/thrift/server/ServerTestBase.java @@ -29,16 +29,24 @@ import junit.framework.TestCase; import org.apache.thrift.TException; -import org.apache.thrift.TApplicationException; import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.protocol.TCompactProtocol; import org.apache.thrift.protocol.TProtocol; import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportFactory; +import org.apache.thrift.transport.TFramedTransport.Factory; -import thrift.test.*; +import thrift.test.Insanity; +import thrift.test.Numberz; +import thrift.test.ThriftTest; +import thrift.test.Xception; +import thrift.test.Xception2; +import thrift.test.Xtruct; +import thrift.test.Xtruct2; public abstract class ServerTestBase extends TestCase { @@ -278,7 +286,11 @@ public void testOneway(int sleepFor) { private static final Xtruct XSTRUCT = new Xtruct("Zero", (byte) 1, -3, -5); private static final Xtruct2 XSTRUCT2 = new Xtruct2((byte)1, XSTRUCT, 5); - public abstract void startServer(TProcessor processor, TProtocolFactory protoFactory) throws Exception; + public void startServer(TProcessor processor, TProtocolFactory protoFactory) throws Exception{ + startServer(processor, protoFactory, null); + } + + public abstract void startServer(TProcessor processor, TProtocolFactory protoFactory, TTransportFactory factory) throws Exception; public abstract void stopServer() throws Exception; @@ -491,6 +503,45 @@ private void testVoid(ThriftTest.Client testClient) throws TException { testClient.testVoid(); } + private static class CallCountingTransportFactory extends TTransportFactory { + public int count = 0; + private final Factory factory; + + public CallCountingTransportFactory(Factory factory) { + this.factory = factory; + } + + @Override + public TTransport getTransport(TTransport trans) { + count++; + return factory.getTransport(trans); + } + } + + public void testTransportFactory() throws Exception { + + for (TProtocolFactory protoFactory : getProtocols()) { + TestHandler handler = new TestHandler(); + ThriftTest.Processor processor = new ThriftTest.Processor(handler); + + final CallCountingTransportFactory factory = new CallCountingTransportFactory(new TFramedTransport.Factory()); + + startServer(processor, protoFactory, factory); + assertEquals(0, factory.count); + + TSocket socket = new TSocket(HOST, PORT); + socket.setTimeout(SOCKET_TIMEOUT); + TTransport transport = getClientTransport(socket); + open(transport); + + TProtocol protocol = protoFactory.getProtocol(transport); + ThriftTest.Client testClient = new ThriftTest.Client(protocol); + assertEquals(0, testClient.testByte((byte) 0)); + assertEquals(2, factory.count); + stopServer(); + } + } + private void testException(ThriftTest.Client testClient) throws TException, Xception { //@TODO testException //testClient.testException("no Exception"); diff --git a/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java b/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java index 597074ed201..b23cd5cf417 100644 --- a/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java +++ b/lib/java/test/org/apache/thrift/server/TestNonblockingServer.java @@ -28,6 +28,7 @@ import org.apache.thrift.transport.TSocket; import org.apache.thrift.transport.TTransport; import org.apache.thrift.transport.TTransportException; +import org.apache.thrift.transport.TTransportFactory; import thrift.test.ThriftTest; @@ -37,12 +38,16 @@ public class TestNonblockingServer extends ServerTestBase { private TServer server; private static final int NUM_QUERIES = 10000; - protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory) { - return new TNonblockingServer(new Args(socket).processor(processor).protocolFactory(protoFactory)); + protected TServer getServer(TProcessor processor, TNonblockingServerSocket socket, TProtocolFactory protoFactory, TTransportFactory factory) { + final Args args = new Args(socket).processor(processor).protocolFactory(protoFactory); + if (factory != null) { + args.transportFactory(factory); + } + return new TNonblockingServer(args); } @Override - public void startServer(final TProcessor processor, final TProtocolFactory protoFactory) throws Exception { + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception { serverThread = new Thread() { public void run() { try { @@ -50,7 +55,7 @@ public void run() { TNonblockingServerSocket tServerSocket = new TNonblockingServerSocket(PORT); - server = getServer(processor, tServerSocket, protoFactory); + server = getServer(processor, tServerSocket, protoFactory, factory); // Run it System.out.println("Starting the server on port " + PORT + "..."); diff --git a/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java b/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java index 4bba4511a84..478407a2f63 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java +++ b/lib/java/test/org/apache/thrift/transport/TestTSSLTransportFactory.java @@ -47,13 +47,14 @@ public TTransport getClientTransport(TTransport underlyingTransport) } @Override - public void startServer(final TProcessor processor, final TProtocolFactory protoFactory) + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception { serverThread = new Thread() { public void run() { try { TServerTransport serverTransport = TSSLTransportFactory.getServerSocket(PORT); - server = new TSimpleServer(new Args(serverTransport).processor(processor)); + final Args args = new Args(serverTransport).processor(processor); + server = new TSimpleServer(args); server.serve(); } catch (TTransportException e) { e.printStackTrace(); @@ -79,4 +80,9 @@ public void open(TTransport transport) throws Exception {} public List getProtocols() { return protocols; } + + @Override + public void testTransportFactory() throws Exception { + // this test doesn't really apply to this suite, so let's skip it. + } } diff --git a/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java b/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java index e12d6fe2080..41d08f600bb 100644 --- a/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java +++ b/lib/java/test/org/apache/thrift/transport/TestTSaslTransports.java @@ -275,7 +275,7 @@ public TTransport getClientTransport(TTransport underlyingTransport) throws Exce } @Override - public void startServer(final TProcessor processor, final TProtocolFactory protoFactory) throws Exception { + public void startServer(final TProcessor processor, final TProtocolFactory protoFactory, final TTransportFactory factory) throws Exception { serverThread = new Thread() { public void run() { try { From 17515db9349fef971f9ae4e6f104894fdd4d9694 Mon Sep 17 00:00:00 2001 From: Jake Farrell Date: Thu, 8 Mar 2012 04:05:58 +0000 Subject: [PATCH 22/22] THRIFT-1534:Required fields in the Delphi code generator. Client:delphi Patch:Jens Geyer Required field changes to isset and generated Read-methods. git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1298265 13f79535-47bb-0310-9956-ffa450edef68 --- .../cpp/src/generate/t_delphi_generator.cc | 105 ++++++++++++++---- 1 file changed, 82 insertions(+), 23 deletions(-) diff --git a/compiler/cpp/src/generate/t_delphi_generator.cc b/compiler/cpp/src/generate/t_delphi_generator.cc index 6aaaf522459..c746324c323 100644 --- a/compiler/cpp/src/generate/t_delphi_generator.cc +++ b/compiler/cpp/src/generate/t_delphi_generator.cc @@ -906,7 +906,9 @@ void t_delphi_generator::generate_delphi_struct_impl( ostream& out, string cls_p } generate_delphi_property_reader_impl( out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); generate_delphi_property_writer_impl( out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception , is_x_factory, exception_factory_name); - generate_delphi_isset_reader_impl( out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + generate_delphi_isset_reader_impl( out, cls_prefix, cls_nm, t, *m_iter, "F", is_exception); + } } if ((! is_exception) || is_x_factory) { @@ -975,15 +977,19 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream &out, t_struc if (members.size() > 0) { out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - generate_delphi_isset_reader_definition( out, *m_iter, is_exception); + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + generate_delphi_isset_reader_definition( out, *m_iter, is_exception); + } } } if (members.size() > 0) { out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - isset_name = "__isset_" + prop_name(*m_iter, is_exception); - indent(out) << "property " << isset_name << ": Boolean read Get" << isset_name << ";" << endl; + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + isset_name = "__isset_" + prop_name(*m_iter, is_exception); + indent(out) << "property " << isset_name << ": Boolean read Get" << isset_name << ";" << endl; + } } } @@ -1027,8 +1033,10 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream &out, t_struc if (members.size() > 0) { indent(out) << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - isset_name = "F__isset_" + prop_name(*m_iter, is_exception); - indent(out) << isset_name << ": Boolean;" << endl; + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + isset_name = "F__isset_" + prop_name(*m_iter, is_exception); + indent(out) << isset_name << ": Boolean;" << endl; + } } } @@ -1042,8 +1050,10 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream &out, t_struc if (members.size() > 0) { out << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - isset_name = "__isset_" + prop_name(*m_iter, is_exception); - indent(out) << "function Get" << isset_name << ": Boolean;" << endl; + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + isset_name = "__isset_" + prop_name(*m_iter, is_exception); + indent(out) << "function Get" << isset_name << ": Boolean;" << endl; + } } } @@ -1095,8 +1105,10 @@ void t_delphi_generator::generate_delphi_struct_definition(ostream &out, t_struc out << endl; indent(out) << "// isset" << endl; for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) { - isset_name = "__isset_" + prop_name(*m_iter, is_exception); - indent(out) << "property " << isset_name << ": Boolean read Get" << isset_name << ";" << endl; + if ((*m_iter)->get_req() != t_field::T_REQUIRED) { + isset_name = "__isset_" + prop_name(*m_iter, is_exception); + indent(out) << "property " << isset_name << ": Boolean read Get" << isset_name << ";" << endl; + } } } @@ -2392,7 +2404,9 @@ void t_delphi_generator::generate_delphi_property_writer_impl(ostream& out, std: indent_impl(out) << "procedure " << cls_prefix << name << "." << "Set" << prop_name(tfield, is_xception_class) << "( const Value: " << type_name(ftype,false,true,is_xception,true) << ");" << endl; indent_impl(out) << "begin" << endl; indent_up_impl(); - indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := True;" << endl; + if (tfield->get_req() != t_field::T_REQUIRED) { + indent_impl(out) << "F__isset_" << prop_name(tfield, is_xception_class) << " := True;" << endl; + } indent_impl(out) << fieldPrefix << prop_name(tfield, is_xception_class) << " := Value;" << endl; if (is_xception_class && (! is_xception_factory) ) { @@ -2452,12 +2466,16 @@ void t_delphi_generator::generate_delphi_create_exception_impl(ostream& out, str for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { propname = prop_name(*f_iter, is_exception); - indent_impl(out) << "if __isset_" << propname << " then" << endl; - indent_impl(out) << "begin" << endl; - indent_up_impl(); + if ((*f_iter)->get_req() != t_field::T_REQUIRED) { + indent_impl(out) << "if __isset_" << propname << " then" << endl; + indent_impl(out) << "begin" << endl; + indent_up_impl(); + } indent_impl(out) << "Result." << propname << " := " << propname << ";" << endl; - indent_down_impl(); - indent_impl(out) << "end;" << endl; + if ((*f_iter)->get_req() != t_field::T_REQUIRED) { + indent_down_impl(); + indent_impl(out) << "end;" << endl; + } } indent_impl(out) << "Result.UpdateMessageProperty;" << endl; @@ -2478,6 +2496,16 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, string indent_impl(code_block) << "begin" << endl; indent_up_impl(); + // local bools for required fields + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(local_vars) << + "_req_isset_" << prop_name(*f_iter, is_exception) << " : Boolean;" << endl; + indent_impl(code_block) << + "_req_isset_" << prop_name(*f_iter, is_exception) << " := FALSE;" << endl; + } + } + indent_impl(code_block) << "struc := iprot.ReadStructBegin;" << endl; indent_impl(code_block) << "try" << endl; @@ -2518,6 +2546,12 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, string generate_deserialize_field(code_block, is_exception, *f_iter, "", local_vars); + // required field? + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(code_block) << + "_req_isset_" << prop_name(*f_iter, is_exception) << " := TRUE;" << endl; + } + indent_down_impl(); indent_impl(code_block) << "end else" << endl; @@ -2558,6 +2592,18 @@ void t_delphi_generator::generate_delphi_struct_reader_impl(ostream& out, string indent_impl(code_block) << "iprot.ReadStructEnd;" << endl; indent_down_impl(); indent_impl(code_block) << "end;" << endl; + + // all required fields have been read? + for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { + if ((*f_iter)->get_req() == t_field::T_REQUIRED) { + indent_impl(code_block) << + "if not _req_isset_" << prop_name(*f_iter, is_exception) << endl; + indent_impl(code_block) << + "then raise TProtocolException.Create( TProtocolException.INVALID_DATA, '" << + prop_name(*f_iter, is_exception) << "');" << endl; + } + } + indent_down_impl(); indent_impl(code_block) << "end;" << endl << endl; @@ -2671,17 +2717,28 @@ void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out, string for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { bool null_allowed = type_can_be_null((*f_iter)->get_type()); + bool is_optional = ((*f_iter)->get_req() != t_field::T_REQUIRED); if (null_allowed) { indent_impl(code_block) << - "if ((" << prop_name((*f_iter), is_exception) << " <> nil) and __isset_" << prop_name(*f_iter,is_exception) << ") then" << endl; + "if (" << prop_name((*f_iter), is_exception) << " <> nil)"; + if (is_optional) { + code_block << + " and __isset_" << prop_name(*f_iter,is_exception); + } + code_block << + " then" << endl; indent_impl(code_block) << "begin" << endl; indent_up_impl(); } else { - indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter,is_exception) << ") then" << endl; - indent_impl(code_block) << "begin" << endl; - indent_up_impl(); + if (is_optional) { + indent_impl(code_block) << "if (__isset_" << prop_name(*f_iter,is_exception) << ") then" << endl; + indent_impl(code_block) << "begin" << endl; + indent_up_impl(); + } else { + indent_impl(code_block) << "// required field" << endl; + } } - indent_impl(code_block) << + indent_impl(code_block) << "field_.Name := '" << (*f_iter)->get_name() << "';" << endl; indent_impl(code_block) << "field_.Type_ := " << type_to_enum((*f_iter)->get_type()) << ";" << endl; @@ -2691,8 +2748,10 @@ void t_delphi_generator::generate_delphi_struct_writer_impl(ostream& out, string "oprot.WriteFieldBegin(field_);" << endl; generate_serialize_field(code_block, is_exception, *f_iter, "", local_vars); indent_impl(code_block) << "oprot.WriteFieldEnd();" << endl; - indent_down_impl(); - indent_impl(code_block) << "end;" << endl; + if (null_allowed || is_optional) { + indent_down_impl(); + indent_impl(code_block) << "end;" << endl; + } } indent_impl(code_block) << "oprot.WriteFieldStop();" << endl;