From: Massimiliano Mirra Date: 2001-12-23T04:50:12+09:00 Subject: [ruby-talk:29318] RPC via tuple space A while ago I wrote about a framework I had in mind for accessing distributed objects in a language independent way, using tuples spaces and xml marshalling. I looked into XML-RPC for the same purpose, but it appeared that the procedure call was being made the HTTP/1.0 way, i.e. one connection for each call. Since what started the whole thing was the idea of writing remote GUIs in arbitrary languages, and since GUIs need prompt feedback, the need to establish a new one for each call was definitely overkill, so I discarded XML-RPC. (As Horacio pointed out, HTTP/1.1 supports persistent connections and indeed takes them for granted where not differently specified. Anyone knows whether this is available in XML-RPC too?) So far I've only written a tiny xml marshalling system, mostly made up of pieces from XMarshal by Masaki Fukushima, and the following very simple proxying mechanism (based on simpletuple.rb from drb by Seki Masatoshi), on which I'd be grateful for comments and caveats. require 'simpletuple' class Sample def ping true end def hello "hello world" end def sum(a,b) a+b end end class RPCHandler # lies in the same space as proxied objects def initialize(ts) @ts = ts end def run loop do name, method, args = @ts.in('rpc') # continually monitors for rpc's args ||= [] object = @ts.in(name) # simulate rd by first in'ning and then out'ing @ts.out(name, object) @ts.out(['rpc-result', name], object.send(method, *args)) end end end class Proxy def initialize(object_name, tuplespace) @proxied_name = object_name # objects are identified in tuple space by a name @ts = tuplespace end def method_missing(method_id, *args, &block) @ts.out('rpc', [@proxied_name, method_id.to_s, args]) # send out a method call @ts.in(['rpc-result', @proxied_name]) # get results end end require 'Lapidary/TestCase' class Test < Lapidary::TestCase def setup @ts = SimpleTupleSpace.new end def testSpaceGetsTuple @ts.out('req', 'try') assert_equal 'try', @ts.in('req') end # just put and get an object in tuple space, and then call a method # def testTupleSimpleProxying @ts.out('obj', Sample.new) object = @ts.in('obj') assert_equal true, object.ping end # put an object in tuple space, start the rpc handler, send multiple # calls by accessing the space directly and get results back by # accessing the space directly # def testDirectRPCwithoutParams @ts.out('obj', Sample.new) Thread.new { RPCHandler.new(@ts).run } 3.times { @ts.out('rpc', ['obj', 'ping']) assert_equal true, @ts.in(['rpc-result', 'obj']) } 3.times { @ts.out('rpc', ['obj', 'hello']) assert_equal 'hello world', @ts.in(['rpc-result', 'obj']) } end # as above, but also provide arguments to the method # def testDirectRPCwithParams @ts.out('obj', Sample.new) Thread.new { RPCHandler.new(@ts).run } 3.times { @ts.out('rpc', ['obj', 'sum', [3, 2]]) assert_equal 5, @ts.in(['rpc-result', 'obj']) } end # put an object in tuple space, start the rpc handler, proxy the # object, then access it transparently (i.e. never caring about the # tuple space) # def testProxiedRPC @ts.out('obj', Sample.new) Thread.new { RPCHandler.new(@ts).run } p = Proxy.new('obj', @ts) 3.times { assert_equal true, p.ping } 3.times { assert_equal "hello world", p.hello } 3.times { assert_equal 5, p.sum(3,2) } end end if $0 == __FILE__ require 'Lapidary/UI/Console/TestRunner' Lapidary::UI::Console::TestRunner.run(Test) end Of course it is just a prototype. In a real implementation, the Proxy object would reside in the client (GUI) application, and would marshal the method calls and forward them to a socket, then read the results back from the socket, unmarshal them and forward them to the client. The tuple space would sit behind a series of sockets, and the RPCHandler object would reside in the same space as the proxied objects, continually monitoring for calls. I don't know whether the project will ever go beyond exploration phase, since all at once I'm dealing with a lot of subjects I never touched before in my programming experience (sockets, threads, tuple spaces, xml...), but if anybody is interested in it or deems it of any use, I'd be happy to hear suggestions and ideas (requests are as welcome of course, but I'm probably less than ready to grant them. ;-) ) Massimiliano