From b2138d28741c8921392d65f907ec1d3f6973d286 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sat, 18 Oct 2014 01:40:20 +0200 Subject: [PATCH 1/7] multiple library fixes/improvements: - TSocket ignores timeout on read() - TFramedTransport throws Eof after first receive cycle - TFramedTransport .flush() did not invoke the read callback - incorrect declaration of logDelegate member variable - added FlashDevelop project files - added Haxe files to EXTRA_DIST etc. - added haxelib.json control file for haxelib.org --- lib/Makefile.am | 1 + lib/haxe/haxelib.json | 11 +++ .../src/org/apache/thrift/server/TServer.hx | 7 +- .../org/apache/thrift/server/TSimpleServer.hx | 14 ++-- .../thrift/transport/TFramedTransport.hx | 62 ++++++++++------- .../apache/thrift/transport/TServerSocket.hx | 4 +- .../org/apache/thrift/transport/TSocket.hx | 17 +++-- lib/haxe/test/HaxeTests.hxproj | 67 +++++++++++++++++++ lib/haxe/test/Makefile.am | 14 ++++ test/haxe/Makefile.am | 14 ++++ test/haxe/TestClientServer.hxproj | 67 +++++++++++++++++++ tutorial/haxe/Makefile.am | 16 ++++- tutorial/haxe/Tutorial.hxproj | 67 +++++++++++++++++++ 13 files changed, 319 insertions(+), 42 deletions(-) create mode 100644 lib/haxe/haxelib.json create mode 100644 lib/haxe/test/HaxeTests.hxproj create mode 100644 test/haxe/TestClientServer.hxproj create mode 100644 tutorial/haxe/Tutorial.hxproj diff --git a/lib/Makefile.am b/lib/Makefile.am index 5751a2cf56d..7b235d048b6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -85,6 +85,7 @@ EXTRA_DIST = \ cocoa \ d \ delphi \ + haxe \ javame \ js \ ocaml \ diff --git a/lib/haxe/haxelib.json b/lib/haxe/haxelib.json new file mode 100644 index 00000000000..a3bde74f968 --- /dev/null +++ b/lib/haxe/haxelib.json @@ -0,0 +1,11 @@ +{ + "name": "thrift", + "url" : "http://thrift.apache.org", + "license": "Apache", + "tags": ["thrift", "rpc", "serialization", "cross", "framework"], + "description": "Haxe bindings for the Apache Thrift RPC and serialization framework", + "version": "0.9.2-beta.1", + "releasenote": "First release, based on Apache Thrift 0.9.2. For details see THRIFT-2644.", + "contributors": ["JensG"], + "dependencies": {} +} diff --git a/lib/haxe/src/org/apache/thrift/server/TServer.hx b/lib/haxe/src/org/apache/thrift/server/TServer.hx index e689b327b1c..56eee0addbf 100644 --- a/lib/haxe/src/org/apache/thrift/server/TServer.hx +++ b/lib/haxe/src/org/apache/thrift/server/TServer.hx @@ -38,7 +38,7 @@ class TServer // Log delegation private var _logDelegate : Dynamic->Void = null; - public var logDelegate(default,set) : Dynamic->Void; + public var logDelegate(get,set) : Dynamic->Void; public function new( processor : TProcessor, serverTransport : TServerTransport, @@ -87,6 +87,11 @@ class TServer } + private function get_logDelegate() : Dynamic->Void { + return _logDelegate; + } + + private function DefaultLogDelegate(value : Dynamic) : Void { trace( value); } diff --git a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx index cb7cbd6433e..f3408e27265 100644 --- a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx +++ b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx @@ -33,15 +33,14 @@ class TSimpleServer extends TServer { serverTransport : TServerTransport, transportFactory : TTransportFactory = null, protocolFactory : TProtocolFactory = null, - logDelegate : Dynamic->Void = null) { + logger : Dynamic->Void = null) { super( processor, serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, - logDelegate); + logger); } - public override function Serve() : Void { try @@ -102,12 +101,15 @@ class TSimpleServer extends TServer { } catch( ttx : TTransportException) { - // Usually a client disconnect, expected + // Usually a client disconnect, expected + } + catch( pex : TProtocolException) + { + logDelegate(pex); // Unexpected } catch( e : Dynamic) { - // Unexpected - logDelegate(e); + logDelegate(e); // Unexpected } // Fire deleteContext server event after client disconnects diff --git a/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx b/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx index 5f0168a545a..cef82ef615a 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx @@ -21,6 +21,7 @@ package org.apache.thrift.transport; import org.apache.thrift.transport.*; +import haxe.io.Eof; import haxe.io.Bytes; import haxe.io.BytesBuffer; import haxe.io.BytesOutput; @@ -73,31 +74,41 @@ class TFramedTransport extends TTransport } public override function read(buf : BytesBuffer, off : Int, len : Int) : Int { - var data = Bytes.alloc(len); - - if (readBuffer_ != null) { - var got : Int = readBuffer_.readBytes(data, off, len); - if (got > 0) { - buf.addBytes(data,0,got); - return got; + try { + var data = Bytes.alloc(len); + + if ((readBuffer_ != null) && (readBuffer_.position < readBuffer_.length)) { + var got : Int = readBuffer_.readBytes(data, off, len); + if (got > 0) { + buf.addBytes(data,0,got); + return got; + }; }; - }; - // Read another frame of data - readFrame(); + // Read another frame of data + readFrame(); - var got = readBuffer_.readBytes(data, off, len); - buf.addBytes(data,0,got); - return got; + var got = readBuffer_.readBytes(data, off, len); + buf.addBytes(data,0,got); + return got; + } + catch (eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!'); + } } function readFrameSize() : Int { - var buffer = new BytesBuffer(); - var len = transport_.readAll( buffer, 0, 4); - var inp = new BytesInput( buffer.getBytes(), 0, 4); - inp.bigEndian = true; - return inp.readInt32(); + try { + var buffer = new BytesBuffer(); + var len = transport_.readAll( buffer, 0, 4); + var inp = new BytesInput( buffer.getBytes(), 0, 4); + inp.bigEndian = true; + return inp.readInt32(); + } + catch(eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read 4 bytes!'); + } } @@ -111,10 +122,15 @@ class TFramedTransport extends TTransport throw new TTransportException(TTransportException.UNKNOWN, 'Frame size ($size) larger than max length ($maxLength_)!'); }; - var buffer = new BytesBuffer(); - size = transport_.readAll( buffer, 0, size); - readBuffer_ = new BytesInput( buffer.getBytes(), 0, size); - readBuffer_.bigEndian = true; + try { + var buffer = new BytesBuffer(); + size = transport_.readAll( buffer, 0, size); + readBuffer_ = new BytesInput( buffer.getBytes(), 0, size); + readBuffer_.bigEndian = true; + } + catch(eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $size bytes!'); + } } public override function write(buf : Bytes, off : Int, len : Int) : Void { @@ -135,7 +151,7 @@ class TFramedTransport extends TTransport writeFrameSize(len); transport_.write(buf, 0, len); - transport_.flush(); + transport_.flush(callback); } } diff --git a/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx b/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx index f38b5845943..586a8b6bf5d 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx @@ -43,13 +43,13 @@ class TServerSocket extends TServerTransport { private var _port : Int = 0; // Timeout for client sockets from accept - private var _clientTimeout : Int = 0; + private var _clientTimeout : Float = 0; // Whether or not to wrap new TSocket connections in buffers private var _useBufferedSockets : Bool = false; - public function new( port : Int, clientTimeout : Int = 0, useBufferedSockets : Bool = false) + public function new( port : Int, clientTimeout : Float = 0, useBufferedSockets : Bool = false) { _port = port; _clientTimeout = clientTimeout; diff --git a/lib/haxe/src/org/apache/thrift/transport/TSocket.hx b/lib/haxe/src/org/apache/thrift/transport/TSocket.hx index 9d5e2dc399b..b6f2119bf98 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TSocket.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TSocket.hx @@ -83,6 +83,7 @@ class TSocket extends TTransport { #else this.host = new Host(host); #end + this.port = port; } @@ -143,7 +144,7 @@ class TSocket extends TTransport { #else - socket.waitForRead(); + //socket.waitForRead(); - no, this ignores timeout and blocks infinitely if(readCount < off) { input.read(off-readCount); readCount = off; @@ -208,7 +209,6 @@ class TSocket extends TTransport { } var bytes = outbuf.buffer; - #else var bytes = obuffer.getBytes(); @@ -222,11 +222,13 @@ class TSocket extends TTransport { ioCallback = callback; try { readCount = 0; + #if js output.send( bytes); #else output.writeBytes( bytes, 0, bytes.length); #end + if(ioCallback != null) { ioCallback(null); // success call } @@ -260,15 +262,14 @@ class TSocket extends TTransport { } #elseif flash - var socket = new Socket(); socket.connect(host, port); #else - var socket = new Socket(); socket.setBlocking(true); socket.setFastSend(true); + socket.setTimeout(5.0); socket.connect(host, port); #end @@ -286,10 +287,12 @@ class TSocket extends TTransport { #if (flash || js) output = socket; - input = socket; + input = socket; + #else - output = socket.output; - input = socket.input; + output = socket.output; + input = socket.input; + #end } diff --git a/lib/haxe/test/HaxeTests.hxproj b/lib/haxe/test/HaxeTests.hxproj new file mode 100644 index 00000000000..4e8929bb417 --- /dev/null +++ b/lib/haxe/test/HaxeTests.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../../../test/ThriftTest.thrift + + + + + + + + \ No newline at end of file diff --git a/lib/haxe/test/Makefile.am b/lib/haxe/test/Makefile.am index 5e92f98c2e4..357436cb25d 100644 --- a/lib/haxe/test/Makefile.am +++ b/lib/haxe/test/Makefile.am @@ -48,3 +48,17 @@ clean-local: check: $(BIN_CPP) $(BIN_CPP) +EXTRA_DIST = \ + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + HaxeTests.hxproj \ + make_all.bat \ + make_all.sh diff --git a/test/haxe/Makefile.am b/test/haxe/Makefile.am index 127c45d5cb7..3d4894a9365 100644 --- a/test/haxe/Makefile.am +++ b/test/haxe/Makefile.am @@ -50,3 +50,17 @@ check: $(BIN_CPP) sleep 1 $(BIN_CPP) client +EXTRA_DIST = \ + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + TestClientServer.hxproj \ + make_all.bat \ + make_all.sh diff --git a/test/haxe/TestClientServer.hxproj b/test/haxe/TestClientServer.hxproj new file mode 100644 index 00000000000..6696d80c2bb --- /dev/null +++ b/test/haxe/TestClientServer.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../ThriftTest.thrift + + + + + + + + \ No newline at end of file diff --git a/tutorial/haxe/Makefile.am b/tutorial/haxe/Makefile.am index 82126480597..a781b5abd60 100644 --- a/tutorial/haxe/Makefile.am +++ b/tutorial/haxe/Makefile.am @@ -45,6 +45,16 @@ clean-local: $(RM) -r gen-haxe bin EXTRA_DIST = \ - src/Main.hx \ - src/CalculatorHandler.hx - + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + Tutorial.hxproj \ + make_all.bat \ + make_all.sh diff --git a/tutorial/haxe/Tutorial.hxproj b/tutorial/haxe/Tutorial.hxproj new file mode 100644 index 00000000000..796f648a5b4 --- /dev/null +++ b/tutorial/haxe/Tutorial.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../tutorial.thrift + + + + + + + + \ No newline at end of file From 6bfefaec6834e134c338a20aeb1ac55ce8305873 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sun, 9 Nov 2014 23:05:37 +0100 Subject: [PATCH 2/7] args/results class not found without namespaces - one source file per Haxe class - ensure correctly capitalized names --- compiler/cpp/src/generate/t_haxe_generator.cc | 180 +++++++++--------- 1 file changed, 94 insertions(+), 86 deletions(-) diff --git a/compiler/cpp/src/generate/t_haxe_generator.cc b/compiler/cpp/src/generate/t_haxe_generator.cc index 3c2207fc486..2a275187d5f 100644 --- a/compiler/cpp/src/generate/t_haxe_generator.cc +++ b/compiler/cpp/src/generate/t_haxe_generator.cc @@ -86,9 +86,9 @@ class t_haxe_generator : public t_oop_generator { * Service-level generation functions */ - void generate_haxe_struct(t_struct* tstruct, bool is_exception); + void generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result = false); - void generate_haxe_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); + void generate_haxe_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); //removed -- equality,compare_to void generate_haxe_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_haxe_validator(std::ofstream& out, t_struct* tstruct); @@ -201,22 +201,22 @@ class t_haxe_generator : public t_oop_generator { bool type_can_be_null(t_type* ttype) { ttype = get_true_type(ttype); - if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) { - return true; - } + if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) { + return true; + } - if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_STRING: - case t_base_type::TYPE_I64: - return true; - default: - return false; - } - } + if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + case t_base_type::TYPE_I64: + return true; + default: + return false; + } + } - return false; + return false; } std::string constant_name(std::string name); @@ -388,14 +388,14 @@ void t_haxe_generator::generate_typedef(t_typedef* ttypedef) { */ void t_haxe_generator::generate_enum(t_enum* tenum) { // Make output file - string f_enum_name = package_dir_+"/"+(tenum->get_name()) + ".hx"; + string f_enum_name = package_dir_ + "/" + get_cap_name(tenum->get_name()) + ".hx"; ofstream f_enum; f_enum.open(f_enum_name.c_str()); // Comment and package it f_enum << autogen_comment() << - haxe_package() << ";" << endl; + haxe_package() << ";" << endl << endl; // Add haxe imports f_enum << string() + @@ -403,7 +403,7 @@ void t_haxe_generator::generate_enum(t_enum* tenum) { endl; indent(f_enum) << - "class " << tenum->get_name() << " "; + "class " << get_cap_name(tenum->get_name()) << " "; scope_up(f_enum); vector constants = tenum->get_constants(); @@ -454,13 +454,13 @@ void t_haxe_generator::generate_consts(std::vector consts) { return; } - string f_consts_name = package_dir_+ "/" + program_name_ + "Constants.hx"; + string f_consts_name = package_dir_ + "/" + get_cap_name(program_name_) + "Constants.hx"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); // Print header f_consts << - autogen_comment() << haxe_package() << ";" << endl; + autogen_comment() << haxe_package() << ";" << endl << endl; f_consts << endl; @@ -469,7 +469,7 @@ void t_haxe_generator::generate_consts(std::vector consts) { indent(f_consts) << - "class " << program_name_ << + "class " << get_cap_name(program_name_) << "Constants {" << endl << endl; indent_up(); vector::iterator c_iter; @@ -678,9 +678,10 @@ void t_haxe_generator::generate_xception(t_struct* txception) { * @param tstruct The struct definition */ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, - bool is_exception) { + bool is_exception, + bool is_result) { // Make output file - string f_struct_name = package_dir_+"/"+(tstruct->get_name()) + ".hx"; + string f_struct_name = package_dir_ + "/" + get_cap_name(tstruct->get_name()) + ".hx"; ofstream f_struct; f_struct.open(f_struct_name.c_str()); @@ -697,9 +698,7 @@ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, haxe_thrift_imports() << haxe_thrift_gen_imports(tstruct, imports) << endl; - generate_haxe_struct_definition(f_struct, - tstruct, - is_exception); + generate_haxe_struct_definition(f_struct, tstruct, is_exception, is_result); f_struct.close(); } @@ -717,9 +716,7 @@ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, void t_haxe_generator::generate_haxe_struct_definition(ofstream &out, t_struct* tstruct, bool is_exception, - bool in_class, bool is_result) { - (void) in_class; generate_haxe_doc(out, tstruct); string clsname = get_cap_name( tstruct->get_name()); @@ -754,7 +751,7 @@ void t_haxe_generator::generate_haxe_struct_definition(ofstream &out, generate_haxe_doc(out, *m_iter); //indent(out) << "private var _" << (*m_iter)->get_name() + " : " + type_name((*m_iter)->get_type()) << ";" << endl; indent(out) << "@:isVar" << endl; - indent(out) << "public var " << (*m_iter)->get_name() + "(get,set) : " + type_name((*m_iter)->get_type()) << ";" << endl; + indent(out) << "public var " << (*m_iter)->get_name() + "(get,set) : " + get_cap_name(type_name((*m_iter)->get_type())) << ";" << endl; } out << endl; @@ -1205,8 +1202,8 @@ void t_haxe_generator::generate_property_getters_setters(ofstream& out, // Simple getter generate_haxe_doc(out, field); - indent(out) << "public function get_" << field_name << "():" << - type_name(type) << " {" << endl; + indent(out) << "public function get_" << field_name << "() : " << + get_cap_name(type_name(type)) << " {" << endl; indent_up(); indent(out) << "return this." << field_name << ";" << endl; indent_down(); @@ -1216,8 +1213,8 @@ void t_haxe_generator::generate_property_getters_setters(ofstream& out, generate_haxe_doc(out, field); indent(out) << "public function set_" << field_name << - "(" << field_name << ":" << type_name(type) << ") : " << - type_name(type) << " {" << endl; + "(" << field_name << ":" << get_cap_name(type_name(type)) << ") : " << + get_cap_name(type_name(type)) << " {" << endl; indent_up(); indent(out) << "this." << field_name << " = " << field_name << ";" << endl; @@ -1443,7 +1440,7 @@ void t_haxe_generator::generate_field_value_meta_data(std::ofstream& out, t_type */ void t_haxe_generator::generate_service(t_service* tservice) { // Make interface file - string f_service_name = package_dir_+"/"+service_name_ + ".hx"; + string f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + ".hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1469,7 +1466,7 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_.close(); // Now make the implementation/client file - f_service_name = package_dir_+"/"+service_name_+"Impl.hx"; + f_service_name = package_dir_+"/"+get_cap_name(service_name_)+"Impl.hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1491,12 +1488,14 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_ << endl; generate_service_client(tservice); - generate_service_helpers(tservice); - + f_service_.close(); - + + // Now make the helper class files + generate_service_helpers(tservice); + // Now make the processor/server file - f_service_name = package_dir_+"/"+service_name_+"Processor.hx"; + f_service_name = package_dir_+"/"+get_cap_name(service_name_)+"Processor.hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1508,12 +1507,11 @@ void t_haxe_generator::generate_service(t_service* tservice) { if(!package_name_.empty()) { f_service_ << "import " << package_name_ << ".*;" << endl; - f_service_ << "import " << package_name_ << "." << service_name_.c_str() << "Impl;" << endl; + f_service_ << "import " << package_name_ << "." << get_cap_name(service_name_).c_str() << "Impl;" << endl; f_service_ << endl; } generate_service_server(tservice); - //generate_service_helpers(tservice); - once is enough, see client file f_service_.close(); @@ -1612,7 +1610,7 @@ void t_haxe_generator::generate_service_interface(t_service* tservice) { } generate_haxe_doc(f_service_, tservice); - f_service_ << indent() << "interface " << service_name_ << extends_iface << + f_service_ << indent() << "interface " << get_cap_name(service_name_) << extends_iface << " {" << endl << endl; indent_up(); vector functions = tservice->get_functions(); @@ -1638,7 +1636,7 @@ void t_haxe_generator::generate_service_helpers(t_service* tservice) { vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); - generate_haxe_struct_definition(f_service_, ts, false, true); + generate_haxe_struct(ts, false); generate_function_helpers(*f_iter); } } @@ -1653,14 +1651,14 @@ void t_haxe_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; if (tservice->get_extends() != NULL) { - extends = tservice->get_extends()->get_name(); + extends = get_cap_name(tservice->get_extends()->get_name()); extends_client = " extends " + extends + "Impl"; } indent(f_service_) << - "class " << service_name_ << - "Impl" << extends_client << - " implements " << service_name_ << + "class " << get_cap_name(service_name_) << + "Impl" << extends_client << + " implements " << get_cap_name(service_name_) << " {" << endl << endl; indent_up(); @@ -1731,7 +1729,7 @@ void t_haxe_generator::generate_service_client(t_service* tservice) { const vector& fields = arg_struct->get_members(); // Serialize the request - string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL"; + string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL"; f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", TMessageType." << calltype << ", seqid_));" << endl << indent() << "var args : " << argsname << " = new " << argsname << "();" << endl; @@ -1879,20 +1877,20 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { string extends = ""; string extends_processor = ""; if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); + extends = get_cap_name(type_name(tservice->get_extends())); extends_processor = " extends " + extends + "Processor"; } // Generate the header portion indent(f_service_) << - "class " << service_name_ << - "Processor" << extends_processor << + "class " << get_cap_name(service_name_) << + "Processor" << extends_processor << " implements TProcessor {" << endl << endl; indent_up(); f_service_ << - indent() << "private var " << service_name_ << "_iface_ : " << service_name_ << ";" << endl; + indent() << "private var " << get_cap_name(service_name_) << "_iface_ : " << get_cap_name(service_name_) << ";" << endl; if (extends.empty()) { f_service_ << @@ -1902,14 +1900,14 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { f_service_ << endl; indent(f_service_) << - "public function new( iface : " << service_name_ << ")" << endl; + "public function new( iface : " << get_cap_name(service_name_) << ")" << endl; scope_up(f_service_); if (!extends.empty()) { f_service_ << indent() << "super(iface);" << endl; } f_service_ << - indent() << service_name_ << "_iface_ = iface;" << endl; + indent() << get_cap_name(service_name_) << "_iface_ = iface;" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << @@ -1989,7 +1987,7 @@ void t_haxe_generator::generate_function_helpers(t_function* tfunction) { result.append(*f_iter); } - generate_haxe_struct_definition(f_service_, &result, false, true, true); + generate_haxe_struct(&result, false, true); } /** @@ -2044,7 +2042,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, f_service_ << indent(); f_service_ << - service_name_ << "_iface_." << tfunction->get_name() << "("; + get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { @@ -2094,7 +2092,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, f_service_ << "result.success = "; } f_service_ << - service_name_ << "_iface_." << tfunction->get_name() << "("; + get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { @@ -2113,7 +2111,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, if( ! tfunction->is_oneway()) { // catch exceptions defined in the IDL for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - f_service_ << " catch (" << (*x_iter)->get_name() << ":" << type_name((*x_iter)->get_type(), false, false) << ") {" << endl; + f_service_ << " catch (" << (*x_iter)->get_name() << ":" << get_cap_name(type_name((*x_iter)->get_type(), false, false)) << ") {" << endl; if (!tfunction->is_oneway()) { indent_up(); f_service_ << @@ -2254,7 +2252,7 @@ void t_haxe_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { out << - indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << + indent() << prefix << " = new " << get_cap_name(type_name(tstruct)) << "();" << endl << indent() << prefix << ".read(iprot);" << endl; } @@ -2604,9 +2602,9 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini if (ttype->is_map()) { t_type* tkey = get_true_type(((t_map*)ttype)->get_key_type()); - t_type* tval = get_true_type(((t_map*)ttype)->get_val_type()); - if (tkey->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + t_type* tval = get_true_type(((t_map*)ttype)->get_val_type()); + if (tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: if( ! (((t_base_type*)tkey)->is_binary())) { @@ -2614,24 +2612,24 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini } case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - return "IntMap< " + type_name(tval) + ">"; - case t_base_type::TYPE_I64: - return "Int64Map< " + type_name(tval) + ">"; - default: + case t_base_type::TYPE_I32: + return "IntMap< " + type_name(tval) + ">"; + case t_base_type::TYPE_I64: + return "Int64Map< " + type_name(tval) + ">"; + default: break; // default to ObjectMap<> } - } - if (tkey->is_enum()) { - return "IntMap< " + type_name(tval) + ">"; - } - return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">"; + } + if (tkey->is_enum()) { + return "IntMap< " + type_name(tval) + ">"; + } + return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">"; } if (ttype->is_set()) { t_type* tkey = get_true_type(((t_list*)ttype)->get_elem_type()); - if( tkey->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + if( tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: if( ! (((t_base_type*)tkey)->is_binary())) { @@ -2640,17 +2638,17 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - return "IntSet"; - case t_base_type::TYPE_I64: - return "Int64Set"; - default: + return "IntSet"; + case t_base_type::TYPE_I64: + return "Int64Set"; + default: break; // default to ObjectSet } } - if (tkey->is_enum()) { - return "IntSet"; - } - return "ObjectSet< " + type_name(tkey) + ">"; + if (tkey->is_enum()) { + return "IntSet"; + } + return "ObjectSet< " + type_name(tkey) + ">"; } if (ttype->is_list()) { @@ -2745,7 +2743,7 @@ string t_haxe_generator::declare_field(t_field* tfield, bool init) { } else if (ttype->is_container()) { result += " = new " + type_name(ttype, false, true) + "()"; } else { - result += " = new " + type_name(ttype, false, true) + "()";; + result += " = new " + type_name(ttype, false, true) + "()"; } } return result + ";"; @@ -2855,10 +2853,20 @@ string t_haxe_generator::type_to_enum(t_type* type) { } /** - * Applies the correct style to a string based on the value of nocamel_style_ + * Haxe class names must start with uppercase letter, but Haxe namespaces must not. */ std::string t_haxe_generator::get_cap_name(std::string name){ - name[0] = toupper(name[0]); // class name must start with uppercase letter + size_t index = name.rfind('.'); + if (index != std::string::npos) { + ++index; + } else { + index = 0; + } + + if (index < name.length()) { + name[index] = toupper(name[index]); + } + return name; } From 3f6984db86aabb25408ae5a6e64a7a3d9c960a70 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sun, 9 Nov 2014 18:52:48 +0100 Subject: [PATCH 3/7] Multiplex protocol on any language --- .../thrift/protocol/TMultiplexedProcessor.hx | 174 ++++++++++++++ .../thrift/protocol/TMultiplexedProtocol.hx | 97 ++++++++ .../thrift/protocol/TProtocolDecorator.hx | 218 +++++++++++++++++ lib/haxe/test/HaxeTests.hxproj | 8 +- lib/haxe/test/Makefile.am | 12 +- lib/haxe/test/make_all.bat | 4 +- lib/haxe/test/make_all.sh | 2 + lib/haxe/test/src/Main.hx | 50 +++- lib/haxe/test/src/MultiplexTest.hx | 224 ++++++++++++++++++ lib/haxe/test/src/StreamTest.hx | 32 +-- lib/haxe/test/src/TestBase.hx | 10 +- 11 files changed, 808 insertions(+), 23 deletions(-) create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx create mode 100644 lib/haxe/test/src/MultiplexTest.hx diff --git a/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx new file mode 100644 index 00000000000..7354ff45040 --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx @@ -0,0 +1,174 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import haxe.ds.StringMap; +import org.apache.thrift.TApplicationException; +import org.apache.thrift.TProcessor; + +import org.apache.thrift.transport.TTransport; + + +/** + * TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services. + * To do so, you instantiate the processor and then register additional processors with it, + * as shown in the following example: + * + * TMultiplexedProcessor processor = new TMultiplexedProcessor(); + * + * processor.registerProcessor( + * "Calculator", + * new Calculator.Processor(new CalculatorHandler())); + * + * processor.registerProcessor( + * "WeatherReport", + * new WeatherReport.Processor(new WeatherReportHandler())); + * + * TServerTransport t = new TServerSocket(9090); + * TSimpleServer server = new TSimpleServer(processor, t); + * + * server.serve(); + */ +class TMultiplexedProcessor implements TProcessor +{ + private var serviceProcessorMap : StringMap = new StringMap(); + private var defaultProcessor : TProcessor = null; + + /** + * 'Register' a service with this TMultiplexedProcessor. This allows us to broker + * requests to individual services by using the service name to select them at request time. + * + * Args: + * - serviceName Name of a service, has to be identical to the name + * declared in the Thrift IDL, e.g. "WeatherReport". + * - processor Implementation of a service, ususally referred to as "handlers", + * e.g. WeatherReportHandler implementing WeatherReport.Iface. + */ + public function RegisterProcessor(serviceName : String, processor : TProcessor, asDefault : Bool = false) : Void { + serviceProcessorMap.set(serviceName, processor); + if ( asDefault) { + if( defaultProcessor != null) { + throw new TApplicationException( TApplicationException.UNKNOWN, "Can't have multiple default processors"); + } else { + defaultProcessor = processor; + } + } + } + + + private function Fail( oprot : TProtocol, message : TMessage, extype : Int, etxt : String) : Void { + var appex = new TApplicationException( extype, etxt); + + var newMessage = new TMessage(message.name, TMessageType.EXCEPTION, message.seqid); + + oprot.writeMessageBegin(newMessage); + appex.write( oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } + + + /** + * This implementation of process performs the following steps: + * + * - Read the beginning of the message. + * - Extract the service name from the message. + * - Using the service name to locate the appropriate processor. + * - Dispatch to the processor, with a decorated instance of TProtocol + * that allows readMessageBegin() to return the original TMessage. + * + * Throws an exception if + * - the message type is not CALL or ONEWAY, + * - the service name was not found in the message, or + * - the service name has not been RegisterProcessor()ed. + */ + public function process( iprot : TProtocol, oprot : TProtocol) : Bool { + /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the + message header. This pulls the message "off the wire", which we'll + deal with at the end of this method. */ + + var message : TMessage = iprot.readMessageBegin(); + var methodName : String = ""; + + if ((message.type != TMessageType.CALL) && (message.type != TMessageType.ONEWAY)) + { + Fail(oprot, message, + TApplicationException.INVALID_MESSAGE_TYPE, + "Message type CALL or ONEWAY expected"); + return false; + } + + // Extract the service name + var actualProcessor : TProcessor = null; + var index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR); + if (index < 0) { + // fallback to default processor + methodName = message.name; + actualProcessor = defaultProcessor; + if( actualProcessor == null) { + Fail(oprot, message, + TApplicationException.INVALID_PROTOCOL, + "Service name not found in message name: " + message.name + " and no default processor defined. " + + "Did you forget to use a TMultiplexProtocol in your client?"); + return false; + } + + } else { + // service name given + var serviceName = message.name.substring(0, index); + methodName = message.name.substring( serviceName.length + TMultiplexedProtocol.SEPARATOR.length); + actualProcessor = serviceProcessorMap.get( serviceName); + if( actualProcessor == null) { + Fail(oprot, message, + TApplicationException.INTERNAL_ERROR, + "Service name not found: " + serviceName + ". " + + "Did you forget to call RegisterProcessor()?"); + return false; + } + } + + // Create a new TMessage, removing the service name + // Dispatch processing to the stored processor + var newMessage = new TMessage( methodName, message.type, message.seqid); + var storedMsg = new StoredMessageProtocol( iprot, newMessage); + return actualProcessor.process( storedMsg, oprot); + } +} + + +/** + * Our goal was to work with any protocol. In order to do that, we needed + * to allow them to call readMessageBegin() and get a TMessage in exactly + * the standard format, without the service name prepended to TMessage.name. + */ +class StoredMessageProtocol extends TProtocolDecorator +{ + private var messageBegin : TMessage; + + public function new( protocol : TProtocol, messageBegin : TMessage) { + super( protocol); + this.messageBegin = messageBegin; + } + + public override function readMessageBegin() : TMessage { + return messageBegin; + } +} + diff --git a/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx new file mode 100644 index 00000000000..cacd1d7829b --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import org.apache.thrift.transport.TTransport; + + +/** + * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift + * client to communicate with a multiplexing Thrift server, by prepending the service name + * to the function name during function calls. + * + * NOTE: THIS IS NOT TO BE USED BY SERVERS. + * On the server, use TMultiplexedProcessor to handle requests from a multiplexing client. + * + * This example uses a single socket transport to invoke two services: + * + * TSocket transport = new TSocket("localhost", 9090); + * transport.open(); + * + * TBinaryProtocol protocol = new TBinaryProtocol(transport); + * + * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator"); + * Calculator.Client service = new Calculator.Client(mp); + * + * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport"); + * WeatherReport.Client service2 = new WeatherReport.Client(mp2); + * + * System.out.println(service.add(2,2)); + * System.out.println(service2.getTemperature()); + * + */ +class TMultiplexedProtocol extends TProtocolDecorator { + + /** Used to delimit the service name from the function name */ + public static inline var SEPARATOR : String = ":"; + + private var service : String; + + /** + * Wrap the specified protocol, allowing it to be used to communicate with a + * multiplexing server. The serviceName is required as it is + * prepended to the message header so that the multiplexing server can broker + * the function call to the proper service. + * + * Args: + * protocol Your communication protocol of choice, e.g. TBinaryProtocol + * serviceName The service name of the service communicating via this protocol. + */ + public function new( protocol : TProtocol, serviceName : String) { + super( protocol); + service = serviceName; + } + + /** + * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR. + * Args: + * tMessage The original message. + */ + public override function writeMessageBegin( message : TMessage) : Void { + switch( message.type) + { + case TMessageType.CALL: + super.writeMessageBegin(new TMessage( + service + SEPARATOR + message.name, + message.type, + message.seqid)); + + case TMessageType.ONEWAY: + super.writeMessageBegin(new TMessage( + service + SEPARATOR + message.name, + message.type, + message.seqid)); + + default: + super.writeMessageBegin(message); + } + } +} + diff --git a/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx b/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx new file mode 100644 index 00000000000..e43d2d932c6 --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx @@ -0,0 +1,218 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import haxe.io.Bytes; +import haxe.Int64; + +import org.apache.thrift.transport.TTransport; + + +/** + * TProtocolDecorator forwards all requests to an enclosed TProtocol instance, + * providing a way to author concise concrete decorator subclasses. While it has + * no abstract methods, it is marked abstract as a reminder that by itself, + * it does not modify the behaviour of the enclosed TProtocol. + * + * See p.175 of Design Patterns (by Gamma et al.) + * See TMultiplexedProtocol + */ +class TProtocolDecorator implements TProtocol +{ + private var wrapped : TProtocol; + + /** + * Encloses the specified protocol. + * @param protocol All operations will be forward to this protocol. Must be non-null. + */ + private function new( protocol : TProtocol) // not to be instantiated, must derive a class + { + wrapped = protocol; + } + + public function getTransport() : TTransport { + return wrapped.getTransport(); + } + + public function writeMessageBegin( value : TMessage) : Void { + wrapped.writeMessageBegin( value); + } + + public function writeMessageEnd() : Void { + wrapped.writeMessageEnd(); + } + + public function writeStructBegin(value : TStruct) : Void { + wrapped.writeStructBegin( value); + } + + public function writeStructEnd() : Void { + wrapped.writeStructEnd(); + } + + public function writeFieldBegin(value : TField) : Void { + wrapped.writeFieldBegin( value); + } + + public function writeFieldEnd() : Void { + wrapped.writeFieldEnd(); + } + + public function writeFieldStop() : Void { + wrapped.writeFieldStop(); + } + + public function writeMapBegin( value : TMap) : Void { + wrapped.writeMapBegin( value); + } + + public function writeMapEnd() : Void { + wrapped.writeMapEnd(); + } + + public function writeListBegin( value : TList) : Void { + wrapped.writeListBegin( value); + } + + public function writeListEnd() : Void { + wrapped.writeListEnd(); + } + + public function writeSetBegin( value : TSet) : Void { + wrapped.writeSetBegin( value); + } + + public function writeSetEnd() : Void { + wrapped.writeSetEnd(); + } + + public function writeBool(value : Bool) : Void { + wrapped.writeBool( value); + } + + public function writeByte(value : Int) : Void { + wrapped.writeByte( value); + } + + public function writeI16(value : Int) : Void { + wrapped.writeI16( value); + } + + public function writeI32(value : Int) : Void { + wrapped.writeI32( value); + } + + public function writeI64(value : haxe.Int64) : Void { + wrapped.writeI64( value); + } + + public function writeDouble(value : Float) : Void { + wrapped.writeDouble( value); + } + + public function writeString(value : String) : Void { + wrapped.writeString( value); + } + + public function writeBinary(value : Bytes ) : Void { + wrapped.writeBinary( value); + } + + public function readMessageBegin() : TMessage { + return wrapped.readMessageBegin(); + } + + public function readMessageEnd() : Void { + wrapped.readMessageEnd(); + } + + public function readStructBegin() : TStruct { + return wrapped.readStructBegin(); + } + + public function readStructEnd() : Void { + wrapped.readStructEnd(); + } + + public function readFieldBegin() : TField { + return wrapped.readFieldBegin(); + } + + public function readFieldEnd() : Void { + wrapped.readFieldEnd(); + } + + public function readMapBegin() : TMap { + return wrapped.readMapBegin(); + } + + public function readMapEnd() : Void { + wrapped.readMapEnd(); + } + + public function readListBegin() : TList { + return wrapped.readListBegin(); + } + + public function readListEnd() : Void { + wrapped.readListEnd(); + } + + public function readSetBegin() : TSet { + return wrapped.readSetBegin(); + } + + public function readSetEnd() : Void { + wrapped.readSetEnd(); + } + + public function readBool() : Bool + { + return wrapped.readBool(); + } + + public function readByte() : Int { + return wrapped.readByte(); + } + + public function readI16() : Int { + return wrapped.readI16(); + } + + public function readI32() : Int { + return wrapped.readI32(); + } + + public function readI64() : haxe.Int64 { + return wrapped.readI64(); + } + + public function readDouble() : Float { + return wrapped.readDouble(); + } + + public function readString() : String { + return wrapped.readString(); + } + + public function readBinary() : Bytes { + return wrapped.readBinary(); + } +} diff --git a/lib/haxe/test/HaxeTests.hxproj b/lib/haxe/test/HaxeTests.hxproj index 4e8929bb417..3beed824459 100644 --- a/lib/haxe/test/HaxeTests.hxproj +++ b/lib/haxe/test/HaxeTests.hxproj @@ -53,14 +53,16 @@ - thrift -r -gen haxe ../../../test/ThriftTest.thrift + thrift -r -gen haxe ../../../test/ThriftTest.thrift +thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift +thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift diff --git a/lib/haxe/test/Makefile.am b/lib/haxe/test/Makefile.am index 357436cb25d..13b42662689 100644 --- a/lib/haxe/test/Makefile.am +++ b/lib/haxe/test/Makefile.am @@ -20,15 +20,25 @@ THRIFT = $(top_srcdir)/compiler/cpp/thrift THRIFTCMD = $(THRIFT) --gen haxe -r THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift +AGGR = $(top_srcdir)/contrib/async-test/aggr.thrift +BENCHMARK = $(top_srcdir)/lib/rb/benchmark/Benchmark.thrift BIN_CPP = bin/Main-debug gen-haxe/thrift/test/ThriftTest.hx: $(THRIFTTEST) $(THRIFTCMD) $(THRIFTTEST) +gen-haxe/thrift/test/Aggr.hx: $(AGGR) + $(THRIFTCMD) $(AGGR) + +gen-haxe/thrift/test/BenchmarkService.hx: $(BENCHMARK) + $(THRIFTCMD) $(BENCHMARK) + all-local: $(BIN_CPP) -$(BIN_CPP): gen-haxe/thrift/test/ThriftTest.hx +$(BIN_CPP): gen-haxe/thrift/test/ThriftTest.hx \ + gen-haxe/thrift/test/Aggr.hx \ + gen-haxe/thrift/test/BenchmarkService.hx $(HAXE) --cwd . cpp.hxml diff --git a/lib/haxe/test/make_all.bat b/lib/haxe/test/make_all.bat index ee18f104708..0314e18a3db 100644 --- a/lib/haxe/test/make_all.bat +++ b/lib/haxe/test/make_all.bat @@ -26,7 +26,9 @@ if "%HAXEPATH%"=="" goto NOTINSTALLED set path=%HAXEPATH%;%HAXEPATH%\..\neko;%path% rem # invoke Thrift comnpiler -thrift -r -gen haxe ..\..\..\test\ThriftTest.thrift +thrift -r -gen haxe ..\..\..\test\ThriftTest.thrift +thrift -r -gen haxe ..\..\..\contrib\async-test\aggr.thrift +thrift -r -gen haxe ..\..\..\lib\rb\benchmark\Benchmark.thrift if errorlevel 1 goto STOP rem # invoke Haxe compiler for all targets diff --git a/lib/haxe/test/make_all.sh b/lib/haxe/test/make_all.sh index 13b57549b19..512f5ec03df 100644 --- a/lib/haxe/test/make_all.sh +++ b/lib/haxe/test/make_all.sh @@ -20,6 +20,8 @@ # invoke Thrift comnpiler thrift -r -gen haxe ../../../test/ThriftTest.thrift +thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift +thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift # output folder if [ ! -d bin ]; then diff --git a/lib/haxe/test/src/Main.hx b/lib/haxe/test/src/Main.hx index da0a7f5b6e8..6c262d78f53 100644 --- a/lib/haxe/test/src/Main.hx +++ b/lib/haxe/test/src/Main.hx @@ -27,19 +27,67 @@ import org.apache.thrift.meta_data.*; import thrift.test.*; // generated code + +enum WhatTests { + Normal; + Multiplex; +} + class Main { + static private var tests : WhatTests = Normal; + static private var server : Bool = false; + + static private inline var CMDLINEHELP : String + = "\nHaxeTests [client|server] [multiplex]\n" + + " client|server ... determines run mode for some tests, default is client\n" + + " multiplex ........ run multiplex test server or client\n"; + + static private function ParseArgs() { + #if sys + + var args = Sys.args(); + if ( args != null) { + for ( arg in args) { + switch(arg.toLowerCase()) { + case "client": + server = false; + case "server" : + server = true; + case "multiplex" : + tests = Multiplex; + default: + throw 'Invalid argument "$arg"\n'+CMDLINEHELP; + } + } + } + + #end + } + static public function main() { try { - StreamTest.Run(); + ParseArgs(); + + switch( tests) { + case Normal: + StreamTest.Run(server); + case Multiplex: + MultiplexTest.Run(server); + default: + throw "Unhandled test mode $tests"; + } trace("All tests completed."); } catch( e: Dynamic) { trace('$e'); + #if sys + Sys.exit(1); // indicate error + #end } } } \ No newline at end of file diff --git a/lib/haxe/test/src/MultiplexTest.hx b/lib/haxe/test/src/MultiplexTest.hx new file mode 100644 index 00000000000..3818b660971 --- /dev/null +++ b/lib/haxe/test/src/MultiplexTest.hx @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package; + +import haxe.Int64; +import haxe.Int32; + +import org.apache.thrift.*; +import org.apache.thrift.protocol.*; +import org.apache.thrift.transport.*; +import org.apache.thrift.server.*; +import org.apache.thrift.meta_data.*; + +// debug only +import org.apache.thrift.protocol.TProtocolDecorator; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TMultiplexedProcessor; + +// generated code imports +import Aggr; +import AggrImpl; +import AggrProcessor; +import BenchmarkService; +import BenchmarkServiceImpl; +import BenchmarkServiceProcessor; +import Error; + + +class BenchmarkServiceHandler implements BenchmarkService +{ + public function new() { + } + + public function fibonacci(n : haxe.Int32) : haxe.Int32 { + trace('Benchmark.fibonacci($n)'); + var next : Int; + var prev = 0; + var result = 1; + while( n > 0) + { + next = result + prev; + prev = result; + result = next; + --n; + } + return result; + } +} + + +class AggrServiceHandler implements Aggr +{ + private var values : List = new List(); + + public function new() { + } + + public function addValue(value : haxe.Int32) : Void { + trace('Aggr.addValue($value)'); + values.add( value); + } + + public function getValues() : List< haxe.Int32> { + trace('Aggr.getValues()'); + return values; + } +} + + + +class MultiplexTest extends TestBase { + + private inline static var NAME_BENCHMARKSERVICE : String = "BenchmarkService"; + private inline static var NAME_AGGR : String = "Aggr"; + + + public static override function Run(server : Bool) : Void { + if ( server) { + RunMultiplexServer(); + } else { + RunMultiplexClient(); + RunDefaultClient(); + } + } + + + // run the multiplex server + public static override function RunMultiplexServer() : Void { + try + { + var benchHandler : BenchmarkService = new BenchmarkServiceHandler(); + var benchProcessor : TProcessor = new BenchmarkServiceProcessor( benchHandler); + + var aggrHandler : Aggr = new AggrServiceHandler(); + var aggrProcessor : TProcessor = new AggrProcessor( aggrHandler); + + var multiplex : TMultiplexedProcessor = new TMultiplexedProcessor(); + multiplex.RegisterProcessor( NAME_BENCHMARKSERVICE, benchProcessor, true); // default + multiplex.RegisterProcessor( NAME_AGGR, aggrProcessor); + + // protocol+transport stack + var protfact : TProtocolFactory = new TBinaryProtocolFactory(true,true); + var servertrans : TServerTransport = new TServerSocket( 9090, 5, false); + var transfact : TTransportFactory = new TFramedTransportFactory(); + + var server : TServer = new TSimpleServer( multiplex, servertrans, transfact, protfact); + + trace("Starting the server ..."); + server.Serve(); + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + + + // run multiplex client against multiplex server + public static override function RunMultiplexClient() : Void { + try + { + var trans : TTransport; + trans = new TSocket("localhost", 9090); + trans = new TFramedTransport(trans); + trans.open(); + + var protocol : TProtocol = new TBinaryProtocol(trans,true,true); + var multiplex : TMultiplexedProtocol; + + multiplex = new TMultiplexedProtocol( protocol, NAME_BENCHMARKSERVICE); + var bench = new BenchmarkServiceImpl( multiplex); + + multiplex = new TMultiplexedProtocol( protocol, NAME_AGGR); + var aggr = new AggrImpl( multiplex); + + trace('calling aggr.add( bench.fibo())...'); + for( i in 1 ... 10) + { + trace('$i'); + aggr.addValue( bench.fibonacci(i)); + } + + trace('calling aggr ...'); + var i = 1; + var values = aggr.getValues(); + TestBase.Expect(values != null,'aggr.getValues() == null'); + for( k in values) + { + trace('fib($i) = $k'); + ++i; + } + + trans.close(); + trace('done.'); + + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + + + // run non-multiplex client against multiplex server to test default fallback + public static override function RunDefaultClient() : Void { + try + { + var trans : TTransport; + trans = new TSocket("localhost", 9090); + trans = new TFramedTransport(trans); + trans.open(); + + var protocol : TProtocol = new TBinaryProtocol(trans,true,true); + + var bench = new BenchmarkServiceImpl( protocol); + + trace('calling bench (via default) ...'); + for( i in 1 ... 10) + { + var k = bench.fibonacci(i); + trace('fib($i) = $k'); + } + + trans.close(); + trace('done.'); + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + +} + + diff --git a/lib/haxe/test/src/StreamTest.hx b/lib/haxe/test/src/StreamTest.hx index 7500eee1aec..244f1ea9d2f 100644 --- a/lib/haxe/test/src/StreamTest.hx +++ b/lib/haxe/test/src/StreamTest.hx @@ -20,6 +20,7 @@ package; import haxe.Int64; +import sys.FileSystem; import org.apache.thrift.*; import org.apache.thrift.protocol.*; @@ -33,15 +34,9 @@ import thrift.test.*; // generated code class StreamTest extends TestBase { - private inline static var tmpfile : String = "bin/data.tmp"; + private inline static var tmpfile : String = "data.tmp"; - private static function Expect( expr : Bool, info : String, ?pos : haxe.PosInfos) : Void { - if( ! expr) { - throw ('Test "$info" failed at '+pos.methodName+' in '+pos.fileName+':'+pos.lineNumber); - } - } - private static function MakeTestData() : Xtruct { var data : Xtruct = new Xtruct(); data.string_thing = "Streamtest"; @@ -77,15 +72,22 @@ class StreamTest extends TestBase { return data; } - public static override function Run() : Void + public static override function Run(server : Bool) : Void { - var written = WriteData(); - var read = ReadData(); - - Expect( read.string_thing == written.string_thing, "string data"); - Expect( read.byte_thing == written.byte_thing, "byte data"); - Expect( read.i32_thing == written.i32_thing, "i32 data"); - Expect( Int64.compare( read.i64_thing, written.i64_thing) == 0, "i64 data"); + try { + var written = WriteData(); + var read = ReadData(); + FileSystem.deleteFile(tmpfile); + + TestBase.Expect( read.string_thing == written.string_thing, "string data"); + TestBase.Expect( read.byte_thing == written.byte_thing, "byte data"); + TestBase.Expect( read.i32_thing == written.i32_thing, "i32 data"); + TestBase.Expect( Int64.compare( read.i64_thing, written.i64_thing) == 0, "i64 data"); + + } catch(e:Dynamic) { + FileSystem.deleteFile(tmpfile); + throw e; + } } } diff --git a/lib/haxe/test/src/TestBase.hx b/lib/haxe/test/src/TestBase.hx index 2a344d6d755..12327737a92 100644 --- a/lib/haxe/test/src/TestBase.hx +++ b/lib/haxe/test/src/TestBase.hx @@ -25,7 +25,6 @@ import org.apache.thrift.transport.*; import org.apache.thrift.server.*; import org.apache.thrift.meta_data.*; -import thrift.test.*; // generated code class TestBase { @@ -33,8 +32,15 @@ class TestBase { // override, if necessary } - public static function Run() : Void { + public static function Run(server : Bool) : Void { throw new AbstractMethodError(); } + + public static function Expect( expr : Bool, info : String, ?pos : haxe.PosInfos) : Void { + if( ! expr) { + throw ('Test "$info" failed at '+pos.methodName+' in '+pos.fileName+':'+pos.lineNumber); + } + } + } \ No newline at end of file From 26f5fdc3724873adf9f728b6f3818a1edd52e775 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sat, 18 Oct 2014 01:40:20 +0200 Subject: [PATCH 4/7] multiple library fixes/improvements: - TSocket ignores timeout on read() - TFramedTransport throws Eof after first receive cycle - TFramedTransport .flush() did not invoke the read callback - incorrect declaration of logDelegate member variable - added FlashDevelop project files - added Haxe files to EXTRA_DIST etc. - added haxelib.json control file for haxelib.org --- lib/Makefile.am | 1 + lib/haxe/haxelib.json | 11 +++ .../src/org/apache/thrift/server/TServer.hx | 7 +- .../org/apache/thrift/server/TSimpleServer.hx | 14 ++-- .../thrift/transport/TFramedTransport.hx | 62 ++++++++++------- .../apache/thrift/transport/TServerSocket.hx | 4 +- .../org/apache/thrift/transport/TSocket.hx | 17 +++-- lib/haxe/test/HaxeTests.hxproj | 67 +++++++++++++++++++ lib/haxe/test/Makefile.am | 14 ++++ test/haxe/Makefile.am | 14 ++++ test/haxe/TestClientServer.hxproj | 67 +++++++++++++++++++ tutorial/haxe/Makefile.am | 16 ++++- tutorial/haxe/Tutorial.hxproj | 67 +++++++++++++++++++ 13 files changed, 319 insertions(+), 42 deletions(-) create mode 100644 lib/haxe/haxelib.json create mode 100644 lib/haxe/test/HaxeTests.hxproj create mode 100644 test/haxe/TestClientServer.hxproj create mode 100644 tutorial/haxe/Tutorial.hxproj diff --git a/lib/Makefile.am b/lib/Makefile.am index 5751a2cf56d..7b235d048b6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -85,6 +85,7 @@ EXTRA_DIST = \ cocoa \ d \ delphi \ + haxe \ javame \ js \ ocaml \ diff --git a/lib/haxe/haxelib.json b/lib/haxe/haxelib.json new file mode 100644 index 00000000000..a3bde74f968 --- /dev/null +++ b/lib/haxe/haxelib.json @@ -0,0 +1,11 @@ +{ + "name": "thrift", + "url" : "http://thrift.apache.org", + "license": "Apache", + "tags": ["thrift", "rpc", "serialization", "cross", "framework"], + "description": "Haxe bindings for the Apache Thrift RPC and serialization framework", + "version": "0.9.2-beta.1", + "releasenote": "First release, based on Apache Thrift 0.9.2. For details see THRIFT-2644.", + "contributors": ["JensG"], + "dependencies": {} +} diff --git a/lib/haxe/src/org/apache/thrift/server/TServer.hx b/lib/haxe/src/org/apache/thrift/server/TServer.hx index e689b327b1c..56eee0addbf 100644 --- a/lib/haxe/src/org/apache/thrift/server/TServer.hx +++ b/lib/haxe/src/org/apache/thrift/server/TServer.hx @@ -38,7 +38,7 @@ class TServer // Log delegation private var _logDelegate : Dynamic->Void = null; - public var logDelegate(default,set) : Dynamic->Void; + public var logDelegate(get,set) : Dynamic->Void; public function new( processor : TProcessor, serverTransport : TServerTransport, @@ -87,6 +87,11 @@ class TServer } + private function get_logDelegate() : Dynamic->Void { + return _logDelegate; + } + + private function DefaultLogDelegate(value : Dynamic) : Void { trace( value); } diff --git a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx index cb7cbd6433e..f3408e27265 100644 --- a/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx +++ b/lib/haxe/src/org/apache/thrift/server/TSimpleServer.hx @@ -33,15 +33,14 @@ class TSimpleServer extends TServer { serverTransport : TServerTransport, transportFactory : TTransportFactory = null, protocolFactory : TProtocolFactory = null, - logDelegate : Dynamic->Void = null) { + logger : Dynamic->Void = null) { super( processor, serverTransport, transportFactory, transportFactory, protocolFactory, protocolFactory, - logDelegate); + logger); } - public override function Serve() : Void { try @@ -102,12 +101,15 @@ class TSimpleServer extends TServer { } catch( ttx : TTransportException) { - // Usually a client disconnect, expected + // Usually a client disconnect, expected + } + catch( pex : TProtocolException) + { + logDelegate(pex); // Unexpected } catch( e : Dynamic) { - // Unexpected - logDelegate(e); + logDelegate(e); // Unexpected } // Fire deleteContext server event after client disconnects diff --git a/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx b/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx index 5f0168a545a..cef82ef615a 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TFramedTransport.hx @@ -21,6 +21,7 @@ package org.apache.thrift.transport; import org.apache.thrift.transport.*; +import haxe.io.Eof; import haxe.io.Bytes; import haxe.io.BytesBuffer; import haxe.io.BytesOutput; @@ -73,31 +74,41 @@ class TFramedTransport extends TTransport } public override function read(buf : BytesBuffer, off : Int, len : Int) : Int { - var data = Bytes.alloc(len); - - if (readBuffer_ != null) { - var got : Int = readBuffer_.readBytes(data, off, len); - if (got > 0) { - buf.addBytes(data,0,got); - return got; + try { + var data = Bytes.alloc(len); + + if ((readBuffer_ != null) && (readBuffer_.position < readBuffer_.length)) { + var got : Int = readBuffer_.readBytes(data, off, len); + if (got > 0) { + buf.addBytes(data,0,got); + return got; + }; }; - }; - // Read another frame of data - readFrame(); + // Read another frame of data + readFrame(); - var got = readBuffer_.readBytes(data, off, len); - buf.addBytes(data,0,got); - return got; + var got = readBuffer_.readBytes(data, off, len); + buf.addBytes(data,0,got); + return got; + } + catch (eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $len bytes!'); + } } function readFrameSize() : Int { - var buffer = new BytesBuffer(); - var len = transport_.readAll( buffer, 0, 4); - var inp = new BytesInput( buffer.getBytes(), 0, 4); - inp.bigEndian = true; - return inp.readInt32(); + try { + var buffer = new BytesBuffer(); + var len = transport_.readAll( buffer, 0, 4); + var inp = new BytesInput( buffer.getBytes(), 0, 4); + inp.bigEndian = true; + return inp.readInt32(); + } + catch(eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read 4 bytes!'); + } } @@ -111,10 +122,15 @@ class TFramedTransport extends TTransport throw new TTransportException(TTransportException.UNKNOWN, 'Frame size ($size) larger than max length ($maxLength_)!'); }; - var buffer = new BytesBuffer(); - size = transport_.readAll( buffer, 0, size); - readBuffer_ = new BytesInput( buffer.getBytes(), 0, size); - readBuffer_.bigEndian = true; + try { + var buffer = new BytesBuffer(); + size = transport_.readAll( buffer, 0, size); + readBuffer_ = new BytesInput( buffer.getBytes(), 0, size); + readBuffer_.bigEndian = true; + } + catch(eof : Eof) { + throw new TTransportException(TTransportException.END_OF_FILE, 'Can\'t read $size bytes!'); + } } public override function write(buf : Bytes, off : Int, len : Int) : Void { @@ -135,7 +151,7 @@ class TFramedTransport extends TTransport writeFrameSize(len); transport_.write(buf, 0, len); - transport_.flush(); + transport_.flush(callback); } } diff --git a/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx b/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx index f38b5845943..586a8b6bf5d 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TServerSocket.hx @@ -43,13 +43,13 @@ class TServerSocket extends TServerTransport { private var _port : Int = 0; // Timeout for client sockets from accept - private var _clientTimeout : Int = 0; + private var _clientTimeout : Float = 0; // Whether or not to wrap new TSocket connections in buffers private var _useBufferedSockets : Bool = false; - public function new( port : Int, clientTimeout : Int = 0, useBufferedSockets : Bool = false) + public function new( port : Int, clientTimeout : Float = 0, useBufferedSockets : Bool = false) { _port = port; _clientTimeout = clientTimeout; diff --git a/lib/haxe/src/org/apache/thrift/transport/TSocket.hx b/lib/haxe/src/org/apache/thrift/transport/TSocket.hx index 9d5e2dc399b..b6f2119bf98 100644 --- a/lib/haxe/src/org/apache/thrift/transport/TSocket.hx +++ b/lib/haxe/src/org/apache/thrift/transport/TSocket.hx @@ -83,6 +83,7 @@ class TSocket extends TTransport { #else this.host = new Host(host); #end + this.port = port; } @@ -143,7 +144,7 @@ class TSocket extends TTransport { #else - socket.waitForRead(); + //socket.waitForRead(); - no, this ignores timeout and blocks infinitely if(readCount < off) { input.read(off-readCount); readCount = off; @@ -208,7 +209,6 @@ class TSocket extends TTransport { } var bytes = outbuf.buffer; - #else var bytes = obuffer.getBytes(); @@ -222,11 +222,13 @@ class TSocket extends TTransport { ioCallback = callback; try { readCount = 0; + #if js output.send( bytes); #else output.writeBytes( bytes, 0, bytes.length); #end + if(ioCallback != null) { ioCallback(null); // success call } @@ -260,15 +262,14 @@ class TSocket extends TTransport { } #elseif flash - var socket = new Socket(); socket.connect(host, port); #else - var socket = new Socket(); socket.setBlocking(true); socket.setFastSend(true); + socket.setTimeout(5.0); socket.connect(host, port); #end @@ -286,10 +287,12 @@ class TSocket extends TTransport { #if (flash || js) output = socket; - input = socket; + input = socket; + #else - output = socket.output; - input = socket.input; + output = socket.output; + input = socket.input; + #end } diff --git a/lib/haxe/test/HaxeTests.hxproj b/lib/haxe/test/HaxeTests.hxproj new file mode 100644 index 00000000000..4e8929bb417 --- /dev/null +++ b/lib/haxe/test/HaxeTests.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../../../test/ThriftTest.thrift + + + + + + + + \ No newline at end of file diff --git a/lib/haxe/test/Makefile.am b/lib/haxe/test/Makefile.am index 5e92f98c2e4..357436cb25d 100644 --- a/lib/haxe/test/Makefile.am +++ b/lib/haxe/test/Makefile.am @@ -48,3 +48,17 @@ clean-local: check: $(BIN_CPP) $(BIN_CPP) +EXTRA_DIST = \ + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + HaxeTests.hxproj \ + make_all.bat \ + make_all.sh diff --git a/test/haxe/Makefile.am b/test/haxe/Makefile.am index 127c45d5cb7..3d4894a9365 100644 --- a/test/haxe/Makefile.am +++ b/test/haxe/Makefile.am @@ -50,3 +50,17 @@ check: $(BIN_CPP) sleep 1 $(BIN_CPP) client +EXTRA_DIST = \ + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + TestClientServer.hxproj \ + make_all.bat \ + make_all.sh diff --git a/test/haxe/TestClientServer.hxproj b/test/haxe/TestClientServer.hxproj new file mode 100644 index 00000000000..6696d80c2bb --- /dev/null +++ b/test/haxe/TestClientServer.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../ThriftTest.thrift + + + + + + + + \ No newline at end of file diff --git a/tutorial/haxe/Makefile.am b/tutorial/haxe/Makefile.am index 82126480597..a781b5abd60 100644 --- a/tutorial/haxe/Makefile.am +++ b/tutorial/haxe/Makefile.am @@ -45,6 +45,16 @@ clean-local: $(RM) -r gen-haxe bin EXTRA_DIST = \ - src/Main.hx \ - src/CalculatorHandler.hx - + src \ + cpp.hxml \ + csharp.hxml \ + flash.hxml \ + java.hxml \ + javascript.hxml \ + neko.hxml \ + php.hxml \ + python.hxml \ + project.hide \ + Tutorial.hxproj \ + make_all.bat \ + make_all.sh diff --git a/tutorial/haxe/Tutorial.hxproj b/tutorial/haxe/Tutorial.hxproj new file mode 100644 index 00000000000..796f648a5b4 --- /dev/null +++ b/tutorial/haxe/Tutorial.hxproj @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + thrift -r -gen haxe ../tutorial.thrift + + + + + + + + \ No newline at end of file From cc3e11d8a3040541662980d3ac2cd69ec4fa6bb2 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sun, 9 Nov 2014 23:05:37 +0100 Subject: [PATCH 5/7] args/results class not found without namespaces - one source file per Haxe class - ensure correctly capitalized names --- compiler/cpp/src/generate/t_haxe_generator.cc | 180 +++++++++--------- 1 file changed, 94 insertions(+), 86 deletions(-) diff --git a/compiler/cpp/src/generate/t_haxe_generator.cc b/compiler/cpp/src/generate/t_haxe_generator.cc index 3c2207fc486..2a275187d5f 100644 --- a/compiler/cpp/src/generate/t_haxe_generator.cc +++ b/compiler/cpp/src/generate/t_haxe_generator.cc @@ -86,9 +86,9 @@ class t_haxe_generator : public t_oop_generator { * Service-level generation functions */ - void generate_haxe_struct(t_struct* tstruct, bool is_exception); + void generate_haxe_struct(t_struct* tstruct, bool is_exception, bool is_result = false); - void generate_haxe_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool in_class=false, bool is_result=false); + void generate_haxe_struct_definition(std::ofstream& out, t_struct* tstruct, bool is_xception=false, bool is_result=false); //removed -- equality,compare_to void generate_haxe_struct_reader(std::ofstream& out, t_struct* tstruct); void generate_haxe_validator(std::ofstream& out, t_struct* tstruct); @@ -201,22 +201,22 @@ class t_haxe_generator : public t_oop_generator { bool type_can_be_null(t_type* ttype) { ttype = get_true_type(ttype); - if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) { - return true; - } + if (ttype->is_container() || ttype->is_struct() || ttype->is_xception() || ttype->is_string()) { + return true; + } - if (ttype->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); - switch (tbase) { - case t_base_type::TYPE_STRING: - case t_base_type::TYPE_I64: - return true; - default: - return false; - } - } + if (ttype->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)ttype)->get_base(); + switch (tbase) { + case t_base_type::TYPE_STRING: + case t_base_type::TYPE_I64: + return true; + default: + return false; + } + } - return false; + return false; } std::string constant_name(std::string name); @@ -388,14 +388,14 @@ void t_haxe_generator::generate_typedef(t_typedef* ttypedef) { */ void t_haxe_generator::generate_enum(t_enum* tenum) { // Make output file - string f_enum_name = package_dir_+"/"+(tenum->get_name()) + ".hx"; + string f_enum_name = package_dir_ + "/" + get_cap_name(tenum->get_name()) + ".hx"; ofstream f_enum; f_enum.open(f_enum_name.c_str()); // Comment and package it f_enum << autogen_comment() << - haxe_package() << ";" << endl; + haxe_package() << ";" << endl << endl; // Add haxe imports f_enum << string() + @@ -403,7 +403,7 @@ void t_haxe_generator::generate_enum(t_enum* tenum) { endl; indent(f_enum) << - "class " << tenum->get_name() << " "; + "class " << get_cap_name(tenum->get_name()) << " "; scope_up(f_enum); vector constants = tenum->get_constants(); @@ -454,13 +454,13 @@ void t_haxe_generator::generate_consts(std::vector consts) { return; } - string f_consts_name = package_dir_+ "/" + program_name_ + "Constants.hx"; + string f_consts_name = package_dir_ + "/" + get_cap_name(program_name_) + "Constants.hx"; ofstream f_consts; f_consts.open(f_consts_name.c_str()); // Print header f_consts << - autogen_comment() << haxe_package() << ";" << endl; + autogen_comment() << haxe_package() << ";" << endl << endl; f_consts << endl; @@ -469,7 +469,7 @@ void t_haxe_generator::generate_consts(std::vector consts) { indent(f_consts) << - "class " << program_name_ << + "class " << get_cap_name(program_name_) << "Constants {" << endl << endl; indent_up(); vector::iterator c_iter; @@ -678,9 +678,10 @@ void t_haxe_generator::generate_xception(t_struct* txception) { * @param tstruct The struct definition */ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, - bool is_exception) { + bool is_exception, + bool is_result) { // Make output file - string f_struct_name = package_dir_+"/"+(tstruct->get_name()) + ".hx"; + string f_struct_name = package_dir_ + "/" + get_cap_name(tstruct->get_name()) + ".hx"; ofstream f_struct; f_struct.open(f_struct_name.c_str()); @@ -697,9 +698,7 @@ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, haxe_thrift_imports() << haxe_thrift_gen_imports(tstruct, imports) << endl; - generate_haxe_struct_definition(f_struct, - tstruct, - is_exception); + generate_haxe_struct_definition(f_struct, tstruct, is_exception, is_result); f_struct.close(); } @@ -717,9 +716,7 @@ void t_haxe_generator::generate_haxe_struct(t_struct* tstruct, void t_haxe_generator::generate_haxe_struct_definition(ofstream &out, t_struct* tstruct, bool is_exception, - bool in_class, bool is_result) { - (void) in_class; generate_haxe_doc(out, tstruct); string clsname = get_cap_name( tstruct->get_name()); @@ -754,7 +751,7 @@ void t_haxe_generator::generate_haxe_struct_definition(ofstream &out, generate_haxe_doc(out, *m_iter); //indent(out) << "private var _" << (*m_iter)->get_name() + " : " + type_name((*m_iter)->get_type()) << ";" << endl; indent(out) << "@:isVar" << endl; - indent(out) << "public var " << (*m_iter)->get_name() + "(get,set) : " + type_name((*m_iter)->get_type()) << ";" << endl; + indent(out) << "public var " << (*m_iter)->get_name() + "(get,set) : " + get_cap_name(type_name((*m_iter)->get_type())) << ";" << endl; } out << endl; @@ -1205,8 +1202,8 @@ void t_haxe_generator::generate_property_getters_setters(ofstream& out, // Simple getter generate_haxe_doc(out, field); - indent(out) << "public function get_" << field_name << "():" << - type_name(type) << " {" << endl; + indent(out) << "public function get_" << field_name << "() : " << + get_cap_name(type_name(type)) << " {" << endl; indent_up(); indent(out) << "return this." << field_name << ";" << endl; indent_down(); @@ -1216,8 +1213,8 @@ void t_haxe_generator::generate_property_getters_setters(ofstream& out, generate_haxe_doc(out, field); indent(out) << "public function set_" << field_name << - "(" << field_name << ":" << type_name(type) << ") : " << - type_name(type) << " {" << endl; + "(" << field_name << ":" << get_cap_name(type_name(type)) << ") : " << + get_cap_name(type_name(type)) << " {" << endl; indent_up(); indent(out) << "this." << field_name << " = " << field_name << ";" << endl; @@ -1443,7 +1440,7 @@ void t_haxe_generator::generate_field_value_meta_data(std::ofstream& out, t_type */ void t_haxe_generator::generate_service(t_service* tservice) { // Make interface file - string f_service_name = package_dir_+"/"+service_name_ + ".hx"; + string f_service_name = package_dir_ + "/" + get_cap_name(service_name_) + ".hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1469,7 +1466,7 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_.close(); // Now make the implementation/client file - f_service_name = package_dir_+"/"+service_name_+"Impl.hx"; + f_service_name = package_dir_+"/"+get_cap_name(service_name_)+"Impl.hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1491,12 +1488,14 @@ void t_haxe_generator::generate_service(t_service* tservice) { f_service_ << endl; generate_service_client(tservice); - generate_service_helpers(tservice); - + f_service_.close(); - + + // Now make the helper class files + generate_service_helpers(tservice); + // Now make the processor/server file - f_service_name = package_dir_+"/"+service_name_+"Processor.hx"; + f_service_name = package_dir_+"/"+get_cap_name(service_name_)+"Processor.hx"; f_service_.open(f_service_name.c_str()); f_service_ << @@ -1508,12 +1507,11 @@ void t_haxe_generator::generate_service(t_service* tservice) { if(!package_name_.empty()) { f_service_ << "import " << package_name_ << ".*;" << endl; - f_service_ << "import " << package_name_ << "." << service_name_.c_str() << "Impl;" << endl; + f_service_ << "import " << package_name_ << "." << get_cap_name(service_name_).c_str() << "Impl;" << endl; f_service_ << endl; } generate_service_server(tservice); - //generate_service_helpers(tservice); - once is enough, see client file f_service_.close(); @@ -1612,7 +1610,7 @@ void t_haxe_generator::generate_service_interface(t_service* tservice) { } generate_haxe_doc(f_service_, tservice); - f_service_ << indent() << "interface " << service_name_ << extends_iface << + f_service_ << indent() << "interface " << get_cap_name(service_name_) << extends_iface << " {" << endl << endl; indent_up(); vector functions = tservice->get_functions(); @@ -1638,7 +1636,7 @@ void t_haxe_generator::generate_service_helpers(t_service* tservice) { vector::iterator f_iter; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { t_struct* ts = (*f_iter)->get_arglist(); - generate_haxe_struct_definition(f_service_, ts, false, true); + generate_haxe_struct(ts, false); generate_function_helpers(*f_iter); } } @@ -1653,14 +1651,14 @@ void t_haxe_generator::generate_service_client(t_service* tservice) { string extends = ""; string extends_client = ""; if (tservice->get_extends() != NULL) { - extends = tservice->get_extends()->get_name(); + extends = get_cap_name(tservice->get_extends()->get_name()); extends_client = " extends " + extends + "Impl"; } indent(f_service_) << - "class " << service_name_ << - "Impl" << extends_client << - " implements " << service_name_ << + "class " << get_cap_name(service_name_) << + "Impl" << extends_client << + " implements " << get_cap_name(service_name_) << " {" << endl << endl; indent_up(); @@ -1731,7 +1729,7 @@ void t_haxe_generator::generate_service_client(t_service* tservice) { const vector& fields = arg_struct->get_members(); // Serialize the request - string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL"; + string calltype = (*f_iter)->is_oneway() ? "ONEWAY" : "CALL"; f_service_ << indent() << "oprot_.writeMessageBegin(new TMessage(\"" << funname << "\", TMessageType." << calltype << ", seqid_));" << endl << indent() << "var args : " << argsname << " = new " << argsname << "();" << endl; @@ -1879,20 +1877,20 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { string extends = ""; string extends_processor = ""; if (tservice->get_extends() != NULL) { - extends = type_name(tservice->get_extends()); + extends = get_cap_name(type_name(tservice->get_extends())); extends_processor = " extends " + extends + "Processor"; } // Generate the header portion indent(f_service_) << - "class " << service_name_ << - "Processor" << extends_processor << + "class " << get_cap_name(service_name_) << + "Processor" << extends_processor << " implements TProcessor {" << endl << endl; indent_up(); f_service_ << - indent() << "private var " << service_name_ << "_iface_ : " << service_name_ << ";" << endl; + indent() << "private var " << get_cap_name(service_name_) << "_iface_ : " << get_cap_name(service_name_) << ";" << endl; if (extends.empty()) { f_service_ << @@ -1902,14 +1900,14 @@ void t_haxe_generator::generate_service_server(t_service* tservice) { f_service_ << endl; indent(f_service_) << - "public function new( iface : " << service_name_ << ")" << endl; + "public function new( iface : " << get_cap_name(service_name_) << ")" << endl; scope_up(f_service_); if (!extends.empty()) { f_service_ << indent() << "super(iface);" << endl; } f_service_ << - indent() << service_name_ << "_iface_ = iface;" << endl; + indent() << get_cap_name(service_name_) << "_iface_ = iface;" << endl; for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) { f_service_ << @@ -1989,7 +1987,7 @@ void t_haxe_generator::generate_function_helpers(t_function* tfunction) { result.append(*f_iter); } - generate_haxe_struct_definition(f_service_, &result, false, true, true); + generate_haxe_struct(&result, false, true); } /** @@ -2044,7 +2042,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, f_service_ << indent(); f_service_ << - service_name_ << "_iface_." << tfunction->get_name() << "("; + get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { @@ -2094,7 +2092,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, f_service_ << "result.success = "; } f_service_ << - service_name_ << "_iface_." << tfunction->get_name() << "("; + get_cap_name(service_name_) << "_iface_." << tfunction->get_name() << "("; bool first = true; for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { if (first) { @@ -2113,7 +2111,7 @@ void t_haxe_generator::generate_process_function(t_service* tservice, if( ! tfunction->is_oneway()) { // catch exceptions defined in the IDL for (x_iter = xceptions.begin(); x_iter != xceptions.end(); ++x_iter) { - f_service_ << " catch (" << (*x_iter)->get_name() << ":" << type_name((*x_iter)->get_type(), false, false) << ") {" << endl; + f_service_ << " catch (" << (*x_iter)->get_name() << ":" << get_cap_name(type_name((*x_iter)->get_type(), false, false)) << ") {" << endl; if (!tfunction->is_oneway()) { indent_up(); f_service_ << @@ -2254,7 +2252,7 @@ void t_haxe_generator::generate_deserialize_struct(ofstream& out, t_struct* tstruct, string prefix) { out << - indent() << prefix << " = new " << type_name(tstruct) << "();" << endl << + indent() << prefix << " = new " << get_cap_name(type_name(tstruct)) << "();" << endl << indent() << prefix << ".read(iprot);" << endl; } @@ -2604,9 +2602,9 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini if (ttype->is_map()) { t_type* tkey = get_true_type(((t_map*)ttype)->get_key_type()); - t_type* tval = get_true_type(((t_map*)ttype)->get_val_type()); - if (tkey->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + t_type* tval = get_true_type(((t_map*)ttype)->get_val_type()); + if (tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: if( ! (((t_base_type*)tkey)->is_binary())) { @@ -2614,24 +2612,24 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini } case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: - case t_base_type::TYPE_I32: - return "IntMap< " + type_name(tval) + ">"; - case t_base_type::TYPE_I64: - return "Int64Map< " + type_name(tval) + ">"; - default: + case t_base_type::TYPE_I32: + return "IntMap< " + type_name(tval) + ">"; + case t_base_type::TYPE_I64: + return "Int64Map< " + type_name(tval) + ">"; + default: break; // default to ObjectMap<> } - } - if (tkey->is_enum()) { - return "IntMap< " + type_name(tval) + ">"; - } - return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">"; + } + if (tkey->is_enum()) { + return "IntMap< " + type_name(tval) + ">"; + } + return "ObjectMap< " + type_name(tkey) + ", " + type_name(tval) + ">"; } if (ttype->is_set()) { t_type* tkey = get_true_type(((t_list*)ttype)->get_elem_type()); - if( tkey->is_base_type()) { - t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); + if( tkey->is_base_type()) { + t_base_type::t_base tbase = ((t_base_type*)tkey)->get_base(); switch (tbase) { case t_base_type::TYPE_STRING: if( ! (((t_base_type*)tkey)->is_binary())) { @@ -2640,17 +2638,17 @@ string t_haxe_generator::type_name(t_type* ttype, bool in_container, bool in_ini case t_base_type::TYPE_BYTE: case t_base_type::TYPE_I16: case t_base_type::TYPE_I32: - return "IntSet"; - case t_base_type::TYPE_I64: - return "Int64Set"; - default: + return "IntSet"; + case t_base_type::TYPE_I64: + return "Int64Set"; + default: break; // default to ObjectSet } } - if (tkey->is_enum()) { - return "IntSet"; - } - return "ObjectSet< " + type_name(tkey) + ">"; + if (tkey->is_enum()) { + return "IntSet"; + } + return "ObjectSet< " + type_name(tkey) + ">"; } if (ttype->is_list()) { @@ -2745,7 +2743,7 @@ string t_haxe_generator::declare_field(t_field* tfield, bool init) { } else if (ttype->is_container()) { result += " = new " + type_name(ttype, false, true) + "()"; } else { - result += " = new " + type_name(ttype, false, true) + "()";; + result += " = new " + type_name(ttype, false, true) + "()"; } } return result + ";"; @@ -2855,10 +2853,20 @@ string t_haxe_generator::type_to_enum(t_type* type) { } /** - * Applies the correct style to a string based on the value of nocamel_style_ + * Haxe class names must start with uppercase letter, but Haxe namespaces must not. */ std::string t_haxe_generator::get_cap_name(std::string name){ - name[0] = toupper(name[0]); // class name must start with uppercase letter + size_t index = name.rfind('.'); + if (index != std::string::npos) { + ++index; + } else { + index = 0; + } + + if (index < name.length()) { + name[index] = toupper(name[index]); + } + return name; } From 6aa05f31ae8cc5ea6b4a6220359626c64d20adea Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Sun, 9 Nov 2014 18:52:48 +0100 Subject: [PATCH 6/7] Multiplex protocol on any language --- .../thrift/protocol/TMultiplexedProcessor.hx | 174 ++++++++++++++ .../thrift/protocol/TMultiplexedProtocol.hx | 97 ++++++++ .../thrift/protocol/TProtocolDecorator.hx | 218 +++++++++++++++++ lib/haxe/test/HaxeTests.hxproj | 8 +- lib/haxe/test/Makefile.am | 12 +- lib/haxe/test/make_all.bat | 4 +- lib/haxe/test/make_all.sh | 2 + lib/haxe/test/src/Main.hx | 50 +++- lib/haxe/test/src/MultiplexTest.hx | 224 ++++++++++++++++++ lib/haxe/test/src/StreamTest.hx | 32 +-- lib/haxe/test/src/TestBase.hx | 10 +- 11 files changed, 808 insertions(+), 23 deletions(-) create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx create mode 100644 lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx create mode 100644 lib/haxe/test/src/MultiplexTest.hx diff --git a/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx new file mode 100644 index 00000000000..7354ff45040 --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProcessor.hx @@ -0,0 +1,174 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import haxe.ds.StringMap; +import org.apache.thrift.TApplicationException; +import org.apache.thrift.TProcessor; + +import org.apache.thrift.transport.TTransport; + + +/** + * TMultiplexedProcessor is a TProcessor allowing a single TServer to provide multiple services. + * To do so, you instantiate the processor and then register additional processors with it, + * as shown in the following example: + * + * TMultiplexedProcessor processor = new TMultiplexedProcessor(); + * + * processor.registerProcessor( + * "Calculator", + * new Calculator.Processor(new CalculatorHandler())); + * + * processor.registerProcessor( + * "WeatherReport", + * new WeatherReport.Processor(new WeatherReportHandler())); + * + * TServerTransport t = new TServerSocket(9090); + * TSimpleServer server = new TSimpleServer(processor, t); + * + * server.serve(); + */ +class TMultiplexedProcessor implements TProcessor +{ + private var serviceProcessorMap : StringMap = new StringMap(); + private var defaultProcessor : TProcessor = null; + + /** + * 'Register' a service with this TMultiplexedProcessor. This allows us to broker + * requests to individual services by using the service name to select them at request time. + * + * Args: + * - serviceName Name of a service, has to be identical to the name + * declared in the Thrift IDL, e.g. "WeatherReport". + * - processor Implementation of a service, ususally referred to as "handlers", + * e.g. WeatherReportHandler implementing WeatherReport.Iface. + */ + public function RegisterProcessor(serviceName : String, processor : TProcessor, asDefault : Bool = false) : Void { + serviceProcessorMap.set(serviceName, processor); + if ( asDefault) { + if( defaultProcessor != null) { + throw new TApplicationException( TApplicationException.UNKNOWN, "Can't have multiple default processors"); + } else { + defaultProcessor = processor; + } + } + } + + + private function Fail( oprot : TProtocol, message : TMessage, extype : Int, etxt : String) : Void { + var appex = new TApplicationException( extype, etxt); + + var newMessage = new TMessage(message.name, TMessageType.EXCEPTION, message.seqid); + + oprot.writeMessageBegin(newMessage); + appex.write( oprot); + oprot.writeMessageEnd(); + oprot.getTransport().flush(); + } + + + /** + * This implementation of process performs the following steps: + * + * - Read the beginning of the message. + * - Extract the service name from the message. + * - Using the service name to locate the appropriate processor. + * - Dispatch to the processor, with a decorated instance of TProtocol + * that allows readMessageBegin() to return the original TMessage. + * + * Throws an exception if + * - the message type is not CALL or ONEWAY, + * - the service name was not found in the message, or + * - the service name has not been RegisterProcessor()ed. + */ + public function process( iprot : TProtocol, oprot : TProtocol) : Bool { + /* Use the actual underlying protocol (e.g. TBinaryProtocol) to read the + message header. This pulls the message "off the wire", which we'll + deal with at the end of this method. */ + + var message : TMessage = iprot.readMessageBegin(); + var methodName : String = ""; + + if ((message.type != TMessageType.CALL) && (message.type != TMessageType.ONEWAY)) + { + Fail(oprot, message, + TApplicationException.INVALID_MESSAGE_TYPE, + "Message type CALL or ONEWAY expected"); + return false; + } + + // Extract the service name + var actualProcessor : TProcessor = null; + var index = message.name.indexOf(TMultiplexedProtocol.SEPARATOR); + if (index < 0) { + // fallback to default processor + methodName = message.name; + actualProcessor = defaultProcessor; + if( actualProcessor == null) { + Fail(oprot, message, + TApplicationException.INVALID_PROTOCOL, + "Service name not found in message name: " + message.name + " and no default processor defined. " + + "Did you forget to use a TMultiplexProtocol in your client?"); + return false; + } + + } else { + // service name given + var serviceName = message.name.substring(0, index); + methodName = message.name.substring( serviceName.length + TMultiplexedProtocol.SEPARATOR.length); + actualProcessor = serviceProcessorMap.get( serviceName); + if( actualProcessor == null) { + Fail(oprot, message, + TApplicationException.INTERNAL_ERROR, + "Service name not found: " + serviceName + ". " + + "Did you forget to call RegisterProcessor()?"); + return false; + } + } + + // Create a new TMessage, removing the service name + // Dispatch processing to the stored processor + var newMessage = new TMessage( methodName, message.type, message.seqid); + var storedMsg = new StoredMessageProtocol( iprot, newMessage); + return actualProcessor.process( storedMsg, oprot); + } +} + + +/** + * Our goal was to work with any protocol. In order to do that, we needed + * to allow them to call readMessageBegin() and get a TMessage in exactly + * the standard format, without the service name prepended to TMessage.name. + */ +class StoredMessageProtocol extends TProtocolDecorator +{ + private var messageBegin : TMessage; + + public function new( protocol : TProtocol, messageBegin : TMessage) { + super( protocol); + this.messageBegin = messageBegin; + } + + public override function readMessageBegin() : TMessage { + return messageBegin; + } +} + diff --git a/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx new file mode 100644 index 00000000000..cacd1d7829b --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TMultiplexedProtocol.hx @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import org.apache.thrift.transport.TTransport; + + +/** + * TMultiplexedProtocol is a protocol-independent concrete decorator that allows a Thrift + * client to communicate with a multiplexing Thrift server, by prepending the service name + * to the function name during function calls. + * + * NOTE: THIS IS NOT TO BE USED BY SERVERS. + * On the server, use TMultiplexedProcessor to handle requests from a multiplexing client. + * + * This example uses a single socket transport to invoke two services: + * + * TSocket transport = new TSocket("localhost", 9090); + * transport.open(); + * + * TBinaryProtocol protocol = new TBinaryProtocol(transport); + * + * TMultiplexedProtocol mp = new TMultiplexedProtocol(protocol, "Calculator"); + * Calculator.Client service = new Calculator.Client(mp); + * + * TMultiplexedProtocol mp2 = new TMultiplexedProtocol(protocol, "WeatherReport"); + * WeatherReport.Client service2 = new WeatherReport.Client(mp2); + * + * System.out.println(service.add(2,2)); + * System.out.println(service2.getTemperature()); + * + */ +class TMultiplexedProtocol extends TProtocolDecorator { + + /** Used to delimit the service name from the function name */ + public static inline var SEPARATOR : String = ":"; + + private var service : String; + + /** + * Wrap the specified protocol, allowing it to be used to communicate with a + * multiplexing server. The serviceName is required as it is + * prepended to the message header so that the multiplexing server can broker + * the function call to the proper service. + * + * Args: + * protocol Your communication protocol of choice, e.g. TBinaryProtocol + * serviceName The service name of the service communicating via this protocol. + */ + public function new( protocol : TProtocol, serviceName : String) { + super( protocol); + service = serviceName; + } + + /** + * Prepends the service name to the function name, separated by TMultiplexedProtocol.SEPARATOR. + * Args: + * tMessage The original message. + */ + public override function writeMessageBegin( message : TMessage) : Void { + switch( message.type) + { + case TMessageType.CALL: + super.writeMessageBegin(new TMessage( + service + SEPARATOR + message.name, + message.type, + message.seqid)); + + case TMessageType.ONEWAY: + super.writeMessageBegin(new TMessage( + service + SEPARATOR + message.name, + message.type, + message.seqid)); + + default: + super.writeMessageBegin(message); + } + } +} + diff --git a/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx b/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx new file mode 100644 index 00000000000..e43d2d932c6 --- /dev/null +++ b/lib/haxe/src/org/apache/thrift/protocol/TProtocolDecorator.hx @@ -0,0 +1,218 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.thrift.protocol; + +import haxe.io.Bytes; +import haxe.Int64; + +import org.apache.thrift.transport.TTransport; + + +/** + * TProtocolDecorator forwards all requests to an enclosed TProtocol instance, + * providing a way to author concise concrete decorator subclasses. While it has + * no abstract methods, it is marked abstract as a reminder that by itself, + * it does not modify the behaviour of the enclosed TProtocol. + * + * See p.175 of Design Patterns (by Gamma et al.) + * See TMultiplexedProtocol + */ +class TProtocolDecorator implements TProtocol +{ + private var wrapped : TProtocol; + + /** + * Encloses the specified protocol. + * @param protocol All operations will be forward to this protocol. Must be non-null. + */ + private function new( protocol : TProtocol) // not to be instantiated, must derive a class + { + wrapped = protocol; + } + + public function getTransport() : TTransport { + return wrapped.getTransport(); + } + + public function writeMessageBegin( value : TMessage) : Void { + wrapped.writeMessageBegin( value); + } + + public function writeMessageEnd() : Void { + wrapped.writeMessageEnd(); + } + + public function writeStructBegin(value : TStruct) : Void { + wrapped.writeStructBegin( value); + } + + public function writeStructEnd() : Void { + wrapped.writeStructEnd(); + } + + public function writeFieldBegin(value : TField) : Void { + wrapped.writeFieldBegin( value); + } + + public function writeFieldEnd() : Void { + wrapped.writeFieldEnd(); + } + + public function writeFieldStop() : Void { + wrapped.writeFieldStop(); + } + + public function writeMapBegin( value : TMap) : Void { + wrapped.writeMapBegin( value); + } + + public function writeMapEnd() : Void { + wrapped.writeMapEnd(); + } + + public function writeListBegin( value : TList) : Void { + wrapped.writeListBegin( value); + } + + public function writeListEnd() : Void { + wrapped.writeListEnd(); + } + + public function writeSetBegin( value : TSet) : Void { + wrapped.writeSetBegin( value); + } + + public function writeSetEnd() : Void { + wrapped.writeSetEnd(); + } + + public function writeBool(value : Bool) : Void { + wrapped.writeBool( value); + } + + public function writeByte(value : Int) : Void { + wrapped.writeByte( value); + } + + public function writeI16(value : Int) : Void { + wrapped.writeI16( value); + } + + public function writeI32(value : Int) : Void { + wrapped.writeI32( value); + } + + public function writeI64(value : haxe.Int64) : Void { + wrapped.writeI64( value); + } + + public function writeDouble(value : Float) : Void { + wrapped.writeDouble( value); + } + + public function writeString(value : String) : Void { + wrapped.writeString( value); + } + + public function writeBinary(value : Bytes ) : Void { + wrapped.writeBinary( value); + } + + public function readMessageBegin() : TMessage { + return wrapped.readMessageBegin(); + } + + public function readMessageEnd() : Void { + wrapped.readMessageEnd(); + } + + public function readStructBegin() : TStruct { + return wrapped.readStructBegin(); + } + + public function readStructEnd() : Void { + wrapped.readStructEnd(); + } + + public function readFieldBegin() : TField { + return wrapped.readFieldBegin(); + } + + public function readFieldEnd() : Void { + wrapped.readFieldEnd(); + } + + public function readMapBegin() : TMap { + return wrapped.readMapBegin(); + } + + public function readMapEnd() : Void { + wrapped.readMapEnd(); + } + + public function readListBegin() : TList { + return wrapped.readListBegin(); + } + + public function readListEnd() : Void { + wrapped.readListEnd(); + } + + public function readSetBegin() : TSet { + return wrapped.readSetBegin(); + } + + public function readSetEnd() : Void { + wrapped.readSetEnd(); + } + + public function readBool() : Bool + { + return wrapped.readBool(); + } + + public function readByte() : Int { + return wrapped.readByte(); + } + + public function readI16() : Int { + return wrapped.readI16(); + } + + public function readI32() : Int { + return wrapped.readI32(); + } + + public function readI64() : haxe.Int64 { + return wrapped.readI64(); + } + + public function readDouble() : Float { + return wrapped.readDouble(); + } + + public function readString() : String { + return wrapped.readString(); + } + + public function readBinary() : Bytes { + return wrapped.readBinary(); + } +} diff --git a/lib/haxe/test/HaxeTests.hxproj b/lib/haxe/test/HaxeTests.hxproj index 4e8929bb417..3beed824459 100644 --- a/lib/haxe/test/HaxeTests.hxproj +++ b/lib/haxe/test/HaxeTests.hxproj @@ -53,14 +53,16 @@ - thrift -r -gen haxe ../../../test/ThriftTest.thrift + thrift -r -gen haxe ../../../test/ThriftTest.thrift +thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift +thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift diff --git a/lib/haxe/test/Makefile.am b/lib/haxe/test/Makefile.am index 357436cb25d..13b42662689 100644 --- a/lib/haxe/test/Makefile.am +++ b/lib/haxe/test/Makefile.am @@ -20,15 +20,25 @@ THRIFT = $(top_srcdir)/compiler/cpp/thrift THRIFTCMD = $(THRIFT) --gen haxe -r THRIFTTEST = $(top_srcdir)/test/ThriftTest.thrift +AGGR = $(top_srcdir)/contrib/async-test/aggr.thrift +BENCHMARK = $(top_srcdir)/lib/rb/benchmark/Benchmark.thrift BIN_CPP = bin/Main-debug gen-haxe/thrift/test/ThriftTest.hx: $(THRIFTTEST) $(THRIFTCMD) $(THRIFTTEST) +gen-haxe/thrift/test/Aggr.hx: $(AGGR) + $(THRIFTCMD) $(AGGR) + +gen-haxe/thrift/test/BenchmarkService.hx: $(BENCHMARK) + $(THRIFTCMD) $(BENCHMARK) + all-local: $(BIN_CPP) -$(BIN_CPP): gen-haxe/thrift/test/ThriftTest.hx +$(BIN_CPP): gen-haxe/thrift/test/ThriftTest.hx \ + gen-haxe/thrift/test/Aggr.hx \ + gen-haxe/thrift/test/BenchmarkService.hx $(HAXE) --cwd . cpp.hxml diff --git a/lib/haxe/test/make_all.bat b/lib/haxe/test/make_all.bat index ee18f104708..0314e18a3db 100644 --- a/lib/haxe/test/make_all.bat +++ b/lib/haxe/test/make_all.bat @@ -26,7 +26,9 @@ if "%HAXEPATH%"=="" goto NOTINSTALLED set path=%HAXEPATH%;%HAXEPATH%\..\neko;%path% rem # invoke Thrift comnpiler -thrift -r -gen haxe ..\..\..\test\ThriftTest.thrift +thrift -r -gen haxe ..\..\..\test\ThriftTest.thrift +thrift -r -gen haxe ..\..\..\contrib\async-test\aggr.thrift +thrift -r -gen haxe ..\..\..\lib\rb\benchmark\Benchmark.thrift if errorlevel 1 goto STOP rem # invoke Haxe compiler for all targets diff --git a/lib/haxe/test/make_all.sh b/lib/haxe/test/make_all.sh index 13b57549b19..512f5ec03df 100644 --- a/lib/haxe/test/make_all.sh +++ b/lib/haxe/test/make_all.sh @@ -20,6 +20,8 @@ # invoke Thrift comnpiler thrift -r -gen haxe ../../../test/ThriftTest.thrift +thrift -r -gen haxe ../../../contrib/async-test/aggr.thrift +thrift -r -gen haxe ../../../lib/rb/benchmark/Benchmark.thrift # output folder if [ ! -d bin ]; then diff --git a/lib/haxe/test/src/Main.hx b/lib/haxe/test/src/Main.hx index da0a7f5b6e8..6c262d78f53 100644 --- a/lib/haxe/test/src/Main.hx +++ b/lib/haxe/test/src/Main.hx @@ -27,19 +27,67 @@ import org.apache.thrift.meta_data.*; import thrift.test.*; // generated code + +enum WhatTests { + Normal; + Multiplex; +} + class Main { + static private var tests : WhatTests = Normal; + static private var server : Bool = false; + + static private inline var CMDLINEHELP : String + = "\nHaxeTests [client|server] [multiplex]\n" + + " client|server ... determines run mode for some tests, default is client\n" + + " multiplex ........ run multiplex test server or client\n"; + + static private function ParseArgs() { + #if sys + + var args = Sys.args(); + if ( args != null) { + for ( arg in args) { + switch(arg.toLowerCase()) { + case "client": + server = false; + case "server" : + server = true; + case "multiplex" : + tests = Multiplex; + default: + throw 'Invalid argument "$arg"\n'+CMDLINEHELP; + } + } + } + + #end + } + static public function main() { try { - StreamTest.Run(); + ParseArgs(); + + switch( tests) { + case Normal: + StreamTest.Run(server); + case Multiplex: + MultiplexTest.Run(server); + default: + throw "Unhandled test mode $tests"; + } trace("All tests completed."); } catch( e: Dynamic) { trace('$e'); + #if sys + Sys.exit(1); // indicate error + #end } } } \ No newline at end of file diff --git a/lib/haxe/test/src/MultiplexTest.hx b/lib/haxe/test/src/MultiplexTest.hx new file mode 100644 index 00000000000..3818b660971 --- /dev/null +++ b/lib/haxe/test/src/MultiplexTest.hx @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package; + +import haxe.Int64; +import haxe.Int32; + +import org.apache.thrift.*; +import org.apache.thrift.protocol.*; +import org.apache.thrift.transport.*; +import org.apache.thrift.server.*; +import org.apache.thrift.meta_data.*; + +// debug only +import org.apache.thrift.protocol.TProtocolDecorator; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TMultiplexedProcessor; + +// generated code imports +import Aggr; +import AggrImpl; +import AggrProcessor; +import BenchmarkService; +import BenchmarkServiceImpl; +import BenchmarkServiceProcessor; +import Error; + + +class BenchmarkServiceHandler implements BenchmarkService +{ + public function new() { + } + + public function fibonacci(n : haxe.Int32) : haxe.Int32 { + trace('Benchmark.fibonacci($n)'); + var next : Int; + var prev = 0; + var result = 1; + while( n > 0) + { + next = result + prev; + prev = result; + result = next; + --n; + } + return result; + } +} + + +class AggrServiceHandler implements Aggr +{ + private var values : List = new List(); + + public function new() { + } + + public function addValue(value : haxe.Int32) : Void { + trace('Aggr.addValue($value)'); + values.add( value); + } + + public function getValues() : List< haxe.Int32> { + trace('Aggr.getValues()'); + return values; + } +} + + + +class MultiplexTest extends TestBase { + + private inline static var NAME_BENCHMARKSERVICE : String = "BenchmarkService"; + private inline static var NAME_AGGR : String = "Aggr"; + + + public static override function Run(server : Bool) : Void { + if ( server) { + RunMultiplexServer(); + } else { + RunMultiplexClient(); + RunDefaultClient(); + } + } + + + // run the multiplex server + public static override function RunMultiplexServer() : Void { + try + { + var benchHandler : BenchmarkService = new BenchmarkServiceHandler(); + var benchProcessor : TProcessor = new BenchmarkServiceProcessor( benchHandler); + + var aggrHandler : Aggr = new AggrServiceHandler(); + var aggrProcessor : TProcessor = new AggrProcessor( aggrHandler); + + var multiplex : TMultiplexedProcessor = new TMultiplexedProcessor(); + multiplex.RegisterProcessor( NAME_BENCHMARKSERVICE, benchProcessor, true); // default + multiplex.RegisterProcessor( NAME_AGGR, aggrProcessor); + + // protocol+transport stack + var protfact : TProtocolFactory = new TBinaryProtocolFactory(true,true); + var servertrans : TServerTransport = new TServerSocket( 9090, 5, false); + var transfact : TTransportFactory = new TFramedTransportFactory(); + + var server : TServer = new TSimpleServer( multiplex, servertrans, transfact, protfact); + + trace("Starting the server ..."); + server.Serve(); + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + + + // run multiplex client against multiplex server + public static override function RunMultiplexClient() : Void { + try + { + var trans : TTransport; + trans = new TSocket("localhost", 9090); + trans = new TFramedTransport(trans); + trans.open(); + + var protocol : TProtocol = new TBinaryProtocol(trans,true,true); + var multiplex : TMultiplexedProtocol; + + multiplex = new TMultiplexedProtocol( protocol, NAME_BENCHMARKSERVICE); + var bench = new BenchmarkServiceImpl( multiplex); + + multiplex = new TMultiplexedProtocol( protocol, NAME_AGGR); + var aggr = new AggrImpl( multiplex); + + trace('calling aggr.add( bench.fibo())...'); + for( i in 1 ... 10) + { + trace('$i'); + aggr.addValue( bench.fibonacci(i)); + } + + trace('calling aggr ...'); + var i = 1; + var values = aggr.getValues(); + TestBase.Expect(values != null,'aggr.getValues() == null'); + for( k in values) + { + trace('fib($i) = $k'); + ++i; + } + + trans.close(); + trace('done.'); + + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + + + // run non-multiplex client against multiplex server to test default fallback + public static override function RunDefaultClient() : Void { + try + { + var trans : TTransport; + trans = new TSocket("localhost", 9090); + trans = new TFramedTransport(trans); + trans.open(); + + var protocol : TProtocol = new TBinaryProtocol(trans,true,true); + + var bench = new BenchmarkServiceImpl( protocol); + + trace('calling bench (via default) ...'); + for( i in 1 ... 10) + { + var k = bench.fibonacci(i); + trace('fib($i) = $k'); + } + + trans.close(); + trace('done.'); + } + catch( e : TApplicationException) + { + TestBase.Expect(false,'${e.errorID} ${e.errorMsg}'); + } + catch( e : TException) + { + TestBase.Expect(false,'$e'); + } + } + +} + + diff --git a/lib/haxe/test/src/StreamTest.hx b/lib/haxe/test/src/StreamTest.hx index 7500eee1aec..244f1ea9d2f 100644 --- a/lib/haxe/test/src/StreamTest.hx +++ b/lib/haxe/test/src/StreamTest.hx @@ -20,6 +20,7 @@ package; import haxe.Int64; +import sys.FileSystem; import org.apache.thrift.*; import org.apache.thrift.protocol.*; @@ -33,15 +34,9 @@ import thrift.test.*; // generated code class StreamTest extends TestBase { - private inline static var tmpfile : String = "bin/data.tmp"; + private inline static var tmpfile : String = "data.tmp"; - private static function Expect( expr : Bool, info : String, ?pos : haxe.PosInfos) : Void { - if( ! expr) { - throw ('Test "$info" failed at '+pos.methodName+' in '+pos.fileName+':'+pos.lineNumber); - } - } - private static function MakeTestData() : Xtruct { var data : Xtruct = new Xtruct(); data.string_thing = "Streamtest"; @@ -77,15 +72,22 @@ class StreamTest extends TestBase { return data; } - public static override function Run() : Void + public static override function Run(server : Bool) : Void { - var written = WriteData(); - var read = ReadData(); - - Expect( read.string_thing == written.string_thing, "string data"); - Expect( read.byte_thing == written.byte_thing, "byte data"); - Expect( read.i32_thing == written.i32_thing, "i32 data"); - Expect( Int64.compare( read.i64_thing, written.i64_thing) == 0, "i64 data"); + try { + var written = WriteData(); + var read = ReadData(); + FileSystem.deleteFile(tmpfile); + + TestBase.Expect( read.string_thing == written.string_thing, "string data"); + TestBase.Expect( read.byte_thing == written.byte_thing, "byte data"); + TestBase.Expect( read.i32_thing == written.i32_thing, "i32 data"); + TestBase.Expect( Int64.compare( read.i64_thing, written.i64_thing) == 0, "i64 data"); + + } catch(e:Dynamic) { + FileSystem.deleteFile(tmpfile); + throw e; + } } } diff --git a/lib/haxe/test/src/TestBase.hx b/lib/haxe/test/src/TestBase.hx index 2a344d6d755..12327737a92 100644 --- a/lib/haxe/test/src/TestBase.hx +++ b/lib/haxe/test/src/TestBase.hx @@ -25,7 +25,6 @@ import org.apache.thrift.transport.*; import org.apache.thrift.server.*; import org.apache.thrift.meta_data.*; -import thrift.test.*; // generated code class TestBase { @@ -33,8 +32,15 @@ class TestBase { // override, if necessary } - public static function Run() : Void { + public static function Run(server : Bool) : Void { throw new AbstractMethodError(); } + + public static function Expect( expr : Bool, info : String, ?pos : haxe.PosInfos) : Void { + if( ! expr) { + throw ('Test "$info" failed at '+pos.methodName+' in '+pos.fileName+':'+pos.lineNumber); + } + } + } \ No newline at end of file From 2d318f7b95351daefd09d6b22fd9b7e0fe238980 Mon Sep 17 00:00:00 2001 From: Jens Geyer Date: Fri, 14 Nov 2014 22:03:39 +0100 Subject: [PATCH 7/7] THRIFT-2828 slightly wrong help screen indent Client: Compiler (general) Patch: Jens Geyer --- compiler/cpp/src/generate/t_as3_generator.cc | 2 +- compiler/cpp/src/generate/t_go_generator.cc | 6 +++--- compiler/cpp/src/generate/t_haxe_generator.cc | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/cpp/src/generate/t_as3_generator.cc b/compiler/cpp/src/generate/t_as3_generator.cc index aacc77d5069..a8c48893c76 100644 --- a/compiler/cpp/src/generate/t_as3_generator.cc +++ b/compiler/cpp/src/generate/t_as3_generator.cc @@ -2724,6 +2724,6 @@ std::string t_as3_generator::get_enum_class_name(t_type* type) { } THRIFT_REGISTER_GENERATOR(as3, "AS3", -" bindable: Add [bindable] metadata to all the struct classes.\n" +" bindable: Add [bindable] metadata to all the struct classes.\n" ) diff --git a/compiler/cpp/src/generate/t_go_generator.cc b/compiler/cpp/src/generate/t_go_generator.cc index 0fdb519d31b..adc4221cca4 100644 --- a/compiler/cpp/src/generate/t_go_generator.cc +++ b/compiler/cpp/src/generate/t_go_generator.cc @@ -3544,6 +3544,6 @@ bool format_go_output(const string &file_path) THRIFT_REGISTER_GENERATOR(go, "Go", - " package_prefix= Package prefix for generated files.\n" \ - " thrift_import= Override thrift package import path (default:" + default_thrift_import + ")\n" \ - " package= Package name (default: inferred from thrift file name)\n") + " package_prefix= Package prefix for generated files.\n" \ + " thrift_import= Override thrift package import path (default:" + default_thrift_import + ")\n" \ + " package= Package name (default: inferred from thrift file name)\n") diff --git a/compiler/cpp/src/generate/t_haxe_generator.cc b/compiler/cpp/src/generate/t_haxe_generator.cc index 2a275187d5f..7e636d8446b 100644 --- a/compiler/cpp/src/generate/t_haxe_generator.cc +++ b/compiler/cpp/src/generate/t_haxe_generator.cc @@ -2953,6 +2953,6 @@ std::string t_haxe_generator::get_enum_class_name(t_type* type) { } THRIFT_REGISTER_GENERATOR(haxe, "Haxe", -" callbacks: Use onError()/onSuccess() callbacks for service methods (like AS3)\n" -) + " callbacks: Use onError()/onSuccess() callbacks for service methods (like AS3)\n" + )