From: Roger Meier Date: Wed, 6 Apr 2011 21:30:53 +0000 (+0000) Subject: THRIFT-1087 Nonblocking asynchronous JS services X-Git-Tag: 0.7.0~124 X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=08b309997f3d45238c1c755bbb0c3cf42a3b0105;p=common%2Fthrift.git THRIFT-1087 Nonblocking asynchronous JS services Patch: Henrique Mendonca git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1089637 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/compiler/cpp/src/generate/t_js_generator.cc b/compiler/cpp/src/generate/t_js_generator.cc index 9def5da9..fa06b051 100644 --- a/compiler/cpp/src/generate/t_js_generator.cc +++ b/compiler/cpp/src/generate/t_js_generator.cc @@ -47,6 +47,9 @@ class t_js_generator : public t_oop_generator { iter = parsed_options.find("node"); gen_node_ = (iter != parsed_options.end()); + + iter = parsed_options.find("jquery"); + gen_jquery_ = (iter != parsed_options.end()); if (gen_node_) { out_dir_base_ = "gen-nodejs"; @@ -160,7 +163,7 @@ class t_js_generator : public t_oop_generator { std::string render_includes(); std::string declare_field(t_field* tfield, bool init=false, bool obj=false); std::string function_signature(t_function* tfunction, std::string prefix="", bool include_callback=false); - std::string argument_list(t_struct* tstruct); + std::string argument_list(t_struct* tstruct, bool include_callback=false); std::string type_to_enum(t_type* ttype); std::string autogen_comment() { @@ -226,6 +229,11 @@ class t_js_generator : public t_oop_generator { */ bool gen_node_; + /** + * True if we should generate services that use jQuery ajax (async/sync). + */ + bool gen_jquery_; + /** * File streams */ @@ -341,11 +349,11 @@ void t_js_generator::generate_enum(t_enum* tenum) { vector::iterator c_iter; for (c_iter = constants.begin(); c_iter != constants.end(); ++c_iter) { int value = (*c_iter)->get_value(); - f_types_ << "'" << (*c_iter)->get_name() << "' : " << value; - if (c_iter != constants.end()-1) + f_types_ << "'" << (*c_iter)->get_name() << "' : " << value; + if (c_iter != constants.end()-1) { f_types_ << ","; - - f_types_ << endl; + } + f_types_ << endl; } f_types_ << "};"<& fields = arg_struct->get_members(); vector::const_iterator fld_iter; string funname = (*f_iter)->get_name(); + string arglist = argument_list(arg_struct); // Open function - f_service_ << js_namespace(tservice->get_program())<get_program())<get_name(); - } - f_service_ << ");" << endl; + f_service_ << indent() << + "this.send_" << funname << "(" << arglist << ");" << endl; if (!gen_node_ && !(*f_iter)->is_oneway()) { f_service_ << indent(); @@ -1025,12 +1028,28 @@ void t_js_generator::generate_service_client(t_service* tservice) { "this.recv_" << funname << "();" << endl; } + if (gen_jquery_) { + indent_down(); + f_service_ << indent() << "} else {" << endl; + indent_up(); + f_service_ << indent() << "var postData = this.send_" << funname << + "(" << arglist << (arglist.empty() ? "" : ", ") << "true);" << endl; + f_service_ << indent() << "return this.output.getTransport()" << endl; + indent_up(); + f_service_ << indent() << ".jqRequest(this, postData, arguments, this.recv_" << funname << ");" << endl; + indent_down(); + indent_down(); + f_service_ << indent() << "}" << endl; + } + indent_down(); f_service_ << "};" << endl << endl; + + // Send function f_service_ << js_namespace(tservice->get_program())<get_name() + " = function("; - - //Need to create js function arg inputs - const vector &fields = tfunction->get_arglist()->get_members(); - vector::const_iterator f_iter; - - for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) { - - if(f_iter != fields.begin()) - str += ", "; - - str += (*f_iter)->get_name(); - } - - if (include_callback) { - if (!fields.empty()) { - str += ", "; - } - str += "callback"; - } + str += argument_list(tfunction->get_arglist(), include_callback); str += ")"; return str; @@ -1702,7 +1707,8 @@ string t_js_generator::function_signature(t_function* tfunction, /** * Renders a field list */ -string t_js_generator::argument_list(t_struct* tstruct) { +string t_js_generator::argument_list(t_struct* tstruct, + bool include_callback) { string result = ""; const vector& fields = tstruct->get_members(); @@ -1716,6 +1722,14 @@ string t_js_generator::argument_list(t_struct* tstruct) { } result += (*f_iter)->get_name(); } + + if (include_callback) { + if (!fields.empty()) { + result += ", "; + } + result += "callback"; + } + return result; } @@ -1761,6 +1775,7 @@ string t_js_generator ::type_to_enum(t_type* type) { } -THRIFT_REGISTER_GENERATOR(js, "Javascript", +THRIFT_REGISTER_GENERATOR(js, "Javascript", +" jquery: Generate jQuery compatible code.\n" " node: Generate node.js compatible code.\n") diff --git a/lib/js/test/build.xml b/lib/js/test/build.xml index c1a17e89..843ec6b5 100644 --- a/lib/js/test/build.xml +++ b/lib/js/test/build.xml @@ -66,7 +66,7 @@ You need libthrift*.jar and libthrift*test.jar located at - ${thrift.java.dir} + ${thrift.java.dir}/build Did you compile Thrift Java library and its test suite by "ant compile-test"? @@ -101,7 +101,7 @@ - + @@ -117,14 +117,14 @@ - + - + diff --git a/lib/js/test/test.html b/lib/js/test/test.html index 4615f88f..f99da014 100644 --- a/lib/js/test/test.html +++ b/lib/js/test/test.html @@ -27,7 +27,7 @@ - + @@ -39,9 +39,9 @@ - - + ////////////////////////////////// + //Run same tests asynchronously + jQuery.ajaxSetup({ timeout: 0 }); + $(document).ajaxError( function() { QUnit.start(); } ); + + module("Async Manual"); + + test("testI32", function() { + expect( 2 ); + QUnit.stop(); + + var transport = new Thrift.Transport(); + var protocol = new Thrift.Protocol(transport); + var client = new ThriftTest.ThriftTestClient(protocol); + + var jqxhr = jQuery.ajax({ + url: "/service", + data: client.send_testI32(Math.pow(-2,31)), + type: "POST", + cache: false, + dataType: "text", + success: function(res){ + transport.setRecvBuffer( res ); + equals(client.recv_testI32(), Math.pow(-2,31)); + }, + error: function() { ok(false); }, + complete: function() { + ok(true); + QUnit.start(); + } + }); + }); - + + +

Thrift Javascript Bindings: Unit Test (ThriftTest.thrift)

diff --git a/lib/js/thrift.js b/lib/js/thrift.js index b8ca2c02..c7a46fdc 100644 --- a/lib/js/thrift.js +++ b/lib/js/thrift.js @@ -56,7 +56,7 @@ var Thrift = { var length = 0; for (var k in obj) { if (obj.hasOwnProperty(k)) { - length++; + length++; } } @@ -75,8 +75,8 @@ var Thrift = { Thrift.TException = {}; Thrift.TException.prototype = { initialize: function(message, code) { - this.message = message; - this.code = (code === null) ? 0 : code; + this.message = message; + this.code = (code === null) ? 0 : code; } }; @@ -186,19 +186,16 @@ Thrift.Transport.prototype = { //Gets the browser specific XmlHttpRequest Object getXmlHttpRequestObject: function() { - try { return new XMLHttpRequest(); } catch (e1) { } try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { } try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { } throw "Your browser doesn't support the XmlHttpRequest object."; - }, - flush: function() { - + flush: function(async) { //async mode - if (this.url === undefined || this.url === '') { + if (async || this.url === undefined || this.url === '') { return this.send_buf; } @@ -225,6 +222,54 @@ Thrift.Transport.prototype = { this.rpos = 0; }, + jqRequest: function(client, postData, args, recv_method) { + if (typeof jQuery === 'undefined' || + typeof jQuery.Deferred === 'undefined') { + throw 'Thrift.js requires jQuery 1.5+ to use asynchronous requests'; + } + + // Deferreds + var deferred = jQuery.Deferred(); + var completeDfd = jQuery._Deferred(); + var dfd = deferred.promise(); + dfd.success = dfd.done; + dfd.error = dfd.fail; + dfd.complete = completeDfd.done; + + var jqXHR = jQuery.ajax({ + url: this.url, + data: postData, + type: 'POST', + cache: false, + dataType: 'text', + context: this, + success: this.jqResponse, + error: function(xhr, status, e) { + deferred.rejectWith(client, jQuery.merge([e], xhr.tArgs)); + }, + complete: function(xhr, status) { + completeDfd.resolveWith(client, [xhr, status]); + } + }); + + deferred.done(jQuery.makeArray(args).pop()); //pop callback from args + jqXHR.tArgs = args; + jqXHR.tClient = client; + jqXHR.tRecvFn = recv_method; + jqXHR.tDfd = deferred; + return dfd; + }, + + jqResponse: function(responseData, textStatus, jqXHR) { + this.setRecvBuffer(responseData); + try { + var value = jqXHR.tRecvFn.call(jqXHR.tClient); + jqXHR.tDfd.resolveWith(jqXHR, jQuery.merge([value], jqXHR.tArgs)); + } catch (ex) { + jqXHR.tDfd.rejectWith(jqXHR, jQuery.merge([ex], jqXHR.tArgs)); + } + }, + setRecvBuffer: function(buf) { this.recv_buf = buf; this.recv_buf_sz = this.recv_buf.length; @@ -531,7 +576,11 @@ Thrift.Protocol.prototype = { this.rstack = []; this.rpos = []; - this.robj = eval(this.transport.readAll()); + if (typeof jQuery !== 'undefined') { + this.robj = jQuery.parseJSON(this.transport.readAll()); + } else { + this.robj = eval(this.transport.readAll()); + } var r = {}; var version = this.robj.shift(); @@ -551,7 +600,6 @@ Thrift.Protocol.prototype = { return r; }, - readMessageEnd: function() { }, @@ -617,7 +665,6 @@ Thrift.Protocol.prototype = { r.ftype = ftype; r.fid = fid; - return r; }, @@ -632,7 +679,6 @@ Thrift.Protocol.prototype = { }, readMapBegin: function(keyType, valType, size) { - var map = this.rstack.pop(); var r = {}; @@ -652,14 +698,12 @@ Thrift.Protocol.prototype = { }, readListBegin: function(elemType, size) { - var list = this.rstack[this.rstack.length - 1]; var r = {}; r.etype = Thrift.Protocol.RType[list.shift()]; r.size = list.shift(); - this.rpos.push(this.rstack.length); this.rstack.push(list); @@ -698,7 +742,6 @@ Thrift.Protocol.prototype = { return this.readI32(); }, - readI32: function(f) { if (f === undefined) { f = this.rstack[this.rstack.length - 1]; @@ -753,5 +796,4 @@ Thrift.Protocol.prototype = { skip: function(type) { throw 'skip not supported yet'; } - };