rjr 0.12.2 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/README.md +49 -36
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +11 -9
  4. data/bin/rjr-server +12 -10
  5. data/examples/amqp.rb +29 -0
  6. data/examples/client.rb +32 -0
  7. data/examples/complete.rb +36 -0
  8. data/examples/local.rb +29 -0
  9. data/examples/server.rb +26 -0
  10. data/examples/tcp.rb +29 -0
  11. data/examples/web.rb +22 -0
  12. data/examples/ws.rb +29 -0
  13. data/lib/rjr/common.rb +7 -12
  14. data/lib/rjr/dispatcher.rb +171 -239
  15. data/lib/rjr/em_adapter.rb +33 -66
  16. data/lib/rjr/message.rb +43 -12
  17. data/lib/rjr/node.rb +197 -103
  18. data/lib/rjr/nodes/amqp.rb +216 -0
  19. data/lib/rjr/nodes/easy.rb +159 -0
  20. data/lib/rjr/nodes/local.rb +118 -0
  21. data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
  22. data/lib/rjr/nodes/multi.rb +79 -0
  23. data/lib/rjr/nodes/tcp.rb +211 -0
  24. data/lib/rjr/nodes/web.rb +197 -0
  25. data/lib/rjr/nodes/ws.rb +187 -0
  26. data/lib/rjr/stats.rb +70 -0
  27. data/lib/rjr/thread_pool.rb +178 -123
  28. data/site/index.html +45 -0
  29. data/site/jquery-latest.js +9404 -0
  30. data/site/jrw.js +297 -0
  31. data/site/json.js +199 -0
  32. data/specs/dispatcher_spec.rb +244 -198
  33. data/specs/em_adapter_spec.rb +52 -80
  34. data/specs/message_spec.rb +223 -197
  35. data/specs/node_spec.rb +67 -163
  36. data/specs/nodes/amqp_spec.rb +82 -0
  37. data/specs/nodes/easy_spec.rb +13 -0
  38. data/specs/nodes/local_spec.rb +72 -0
  39. data/specs/nodes/multi_spec.rb +65 -0
  40. data/specs/nodes/tcp_spec.rb +75 -0
  41. data/specs/nodes/web_spec.rb +77 -0
  42. data/specs/nodes/ws_spec.rb +78 -0
  43. data/specs/stats_spec.rb +59 -0
  44. data/specs/thread_pool_spec.rb +44 -35
  45. metadata +40 -30
  46. data/lib/rjr/amqp_node.rb +0 -330
  47. data/lib/rjr/inspect.rb +0 -65
  48. data/lib/rjr/local_node.rb +0 -150
  49. data/lib/rjr/multi_node.rb +0 -65
  50. data/lib/rjr/tcp_node.rb +0 -323
  51. data/lib/rjr/thread_pool2.rb +0 -272
  52. data/lib/rjr/util.rb +0 -104
  53. data/lib/rjr/web_node.rb +0 -266
  54. data/lib/rjr/ws_node.rb +0 -289
  55. data/lib/rjr.rb +0 -16
  56. data/specs/amqp_node_spec.rb +0 -31
  57. data/specs/inspect_spec.rb +0 -60
  58. data/specs/local_node_spec.rb +0 -43
  59. data/specs/multi_node_spec.rb +0 -45
  60. data/specs/tcp_node_spec.rb +0 -33
  61. data/specs/util_spec.rb +0 -46
  62. data/specs/web_node_spec.rb +0 -32
  63. data/specs/ws_node_spec.rb +0 -32
  64. /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
  65. /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
@@ -0,0 +1,159 @@
1
+ # RJR Easy Node
2
+ #
3
+ # Implements the RJR::Node client interface to
4
+ # issue JSON-RPC requests over a variety of protocols
5
+ #
6
+ # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
7
+ # Licensed under the Apache License, Version 2.0
8
+
9
+ require 'rjr/nodes/tcp'
10
+ require 'rjr/nodes/ws'
11
+ require 'rjr/nodes/web'
12
+ require 'rjr/nodes/amqp'
13
+ require 'rjr/nodes/multi'
14
+ require 'rjr/node'
15
+
16
+ module RJR
17
+ module Nodes
18
+
19
+ # Easy node definition.
20
+ #
21
+ # Clients should specify the transports that they would like to use
22
+ # and their relevant config upon instantating this call. After which
23
+ # invocations and notifications will be routed via the correct transport
24
+ # depending on the format of the destination.
25
+ #
26
+ # All nodes managed locally will share the same dispatcher so that json-rpc methods
27
+ # only need to be registered once, with the multi-node itself.
28
+ #
29
+ # @example invoking requests via multiple protocols
30
+ # easy = RJR::Nodes::Easy.new :tcp => { :host => 'localhost', :port => 8999 },
31
+ # :amqp => { :broker => 'localhost' }
32
+ #
33
+ # easy.invoke 'tcp://localhost:9000/', 'hello world'
34
+ # # => sent via tcp
35
+ #
36
+ # easy.notify 'dest-queue', 'hello world'
37
+ # # => sent via amqp
38
+ #
39
+ class Easy < RJR::Node
40
+ private
41
+
42
+ # Internal helper, retrieved the registered node type depending on
43
+ # the dst. If matching node type can't be found, nil is returned
44
+ def get_node(dst)
45
+ type = nil
46
+ if dst.is_a?(String)
47
+ if /tcp:\/\/.*/ =~ dst ||
48
+ /jsonrpc:\/\/.*/ =~ dst ||
49
+ /json-rpc:\/\/.*/ =~ dst
50
+ type = RJR::Nodes::TCP
51
+
52
+ elsif /ws:\/\/.*/ =~ dst
53
+ type = RJR::Nodes::WS
54
+
55
+ elsif /http:\/\/.*/ =~ dst
56
+ type = RJR::Nodes::Web
57
+
58
+ elsif /.*-queue$/ =~ dst
59
+ type = RJR::Nodes::AMQP
60
+
61
+ # else # TODO
62
+ # type = RJR::Nodes::Local
63
+
64
+ end
65
+ end
66
+
67
+ return @multi_node.nodes.find { |n| n.is_a?(type) } unless type.nil?
68
+ nil
69
+ end
70
+
71
+ public
72
+
73
+ # Easy Node initializer
74
+ # @param [Hash] args the options to create the node with
75
+ # @option args [Hash] :amqp options to create the amqp node with
76
+ # @option args [Hash] :ws options to create the ws node with
77
+ # @option args [Hash] :tcp options to create the ws node with
78
+ # @option args [Hash] :web options to create the web node with
79
+ def initialize(args = {})
80
+ super(args)
81
+
82
+ nodes = []
83
+ args.keys.each { |n|
84
+ node =
85
+ case n
86
+ when :amqp then
87
+ RJR::Nodes::AMQP.new args[:amqp].merge(args)
88
+ when :ws then
89
+ RJR::Nodes::WS.new args[:ws].merge(args)
90
+ when :tcp then
91
+ RJR::Nodes::TCP.new args[:tcp].merge(args)
92
+ when :web then
93
+ RJR::Nodes::Web.new args[:web].merge(args)
94
+ end
95
+
96
+ if node
97
+ nodes << node
98
+ end
99
+ }
100
+
101
+ @multi_node = RJR::Nodes::Multi.new :nodes => nodes
102
+ @dispatcher = @multi_node.dispatcher
103
+ end
104
+
105
+ # Send data using specified connection
106
+ #
107
+ # Implementation of {RJR::Node#send_msg}
108
+ def send_msg(data, connection)
109
+ # TODO
110
+ end
111
+
112
+ # Instruct Nodes to start listening for and dispatching rpc requests
113
+ #
114
+ # Implementation of {RJR::Node#listen}
115
+ def listen
116
+ @multi_node.listen
117
+ end
118
+
119
+ # Instructs node to send rpc request, and wait for and return response.
120
+ #
121
+ # Implementation of {RJR::Node#invoke}
122
+ #
123
+ # @param [String] dst destination send request to
124
+ # @param [String] rpc_method json-rpc method to invoke on destination
125
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
126
+ # @return [Object] the json result retrieved from destination converted to a ruby object
127
+ # @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
128
+ def invoke(dst, rpc_method, *args)
129
+ n = get_node(dst)
130
+ # TODO raise exception if n.nil?
131
+ n.invoke dst, rpc_method, *args
132
+ end
133
+
134
+ # Instructs node to send rpc notification (immadiately returns / no response is generated)
135
+ #
136
+ # Implementation of {RJR::Node#notify}
137
+ #
138
+ # @param [String] dst destination to send notification to
139
+ # @param [String] rpc_method json-rpc method to invoke on destination
140
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
141
+ def notify(dst, rpc_method, *args)
142
+ n = get_node(dst)
143
+ n.notify dst, rpc_method, *args
144
+ end
145
+
146
+ # Stop node on the specified signal
147
+ #
148
+ # @param [Singnal] signal signal to stop the node on
149
+ # @return self
150
+ def stop_on(signal)
151
+ Signal.trap(signal) {
152
+ @multi_node.stop
153
+ }
154
+ self
155
+ end
156
+ end
157
+
158
+ end # module Nodes
159
+ end # module RJR
@@ -0,0 +1,118 @@
1
+ # RJR Local Endpoint
2
+ #
3
+ # Implements the RJR::Node interface to satisty JSON-RPC requests via local method calls
4
+ #
5
+ # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
6
+ # Licensed under the Apache License, Version 2.0
7
+
8
+ require 'rjr/node'
9
+ require 'rjr/message'
10
+
11
+ module RJR
12
+ module Nodes
13
+
14
+ # Local node definition, implements the {RJR::Node} interface to
15
+ # listen for and invoke json-rpc requests via local handlers
16
+ #
17
+ # This is useful for situations in which you would like to invoke registered
18
+ # json-rpc handlers locally, enforcing the same constraints as
19
+ # you would on a json-rpc request coming in remotely.
20
+ #
21
+ # *Note* this only dispatches to the methods defined on the local dispatcher!
22
+ #
23
+ # If you have two local nodes, they will have seperate dispatchers unless you
24
+ # assign them the same object (eg node2.dispatcher = node1.dispatcher or
25
+ # node2 = new RJR::Nodes::Local.new(:dispatcher :=> node1.dispatcher))
26
+ #
27
+ # @example Listening for and dispatching json-rpc requests locally
28
+ # # initialize node
29
+ # node = RJR::LocalNode.new :node_id => 'node'
30
+ #
31
+ # node.dispatcher.handle('hello') do |name|
32
+ # @rjr_node_type == :local ? "Hello superuser #{name}" : "Hello #{name}!"
33
+ # end
34
+ #
35
+ # # invoke request
36
+ # node.invoke('hello', 'mo')
37
+ #
38
+ class Local < RJR::Node
39
+ RJR_NODE_TYPE = :local
40
+
41
+ # allows clients to override the node type for the local node
42
+ attr_accessor :node_type
43
+
44
+ # LocalNode initializer
45
+ # @param [Hash] args the options to create the local node with
46
+ def initialize(args = {})
47
+ super(args)
48
+ @node_type = RJR_NODE_TYPE
49
+ end
50
+
51
+ # Send data using specified connection.
52
+ #
53
+ # Simply dispatch local notification.
54
+ #
55
+ # Implementation of {RJR::Node#send_msg}
56
+ def send_msg(msg, connection)
57
+ # ignore response message
58
+ unless msg.is_a?(ResponseMessage)
59
+ handle_request(msg, true, nil)
60
+ end
61
+ end
62
+
63
+ # Instruct Nodes to start listening for and dispatching rpc requests
64
+ #
65
+ # Implementation of {RJR::Node#listen}
66
+ def listen
67
+ # do nothing
68
+ self
69
+ end
70
+
71
+ # Instructs node to send rpc request, and wait for and return response
72
+ #
73
+ # Implementation of {RJR::Node#invoke}
74
+ #
75
+ # If strictly confirming to other nodes, this would use event machine to launch
76
+ # a thread pool job to dispatch request and block on result.
77
+ # Optimized for performance reasons but recognize that the semantics of using
78
+ # the local node will be somewhat different.
79
+ #
80
+ # @param [String] rpc_method json-rpc method to invoke on destination
81
+ # @param [Array] args array of arguments to convert to json and invoke remote method with
82
+ # @return [Object] the json result retrieved from destination converted to a ruby object
83
+ # @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
84
+ def invoke(rpc_method, *args)
85
+ 0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
86
+ message = RequestMessage.new(:method => rpc_method,
87
+ :args => args,
88
+ :headers => @message_headers).to_s
89
+ res = handle_request(message, false, nil)
90
+ return @dispatcher.handle_response(res.result)
91
+ end
92
+
93
+ # Instructs node to send rpc notification (immediately returns / no response is generated)
94
+ #
95
+ # Implementation of {RJR::Node#notify}
96
+ #
97
+ # Same performance comment as invoke_request above
98
+ #
99
+ # @param [String] rpc_method json-rpc method to invoke on destination
100
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
101
+ def notify(rpc_method, *args)
102
+ # TODO run in thread & immediately return?
103
+ begin
104
+ 0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
105
+ message = NotificationMessage.new(:method => rpc_method,
106
+ :args => args,
107
+ :headers => @message_headers).to_s
108
+ handle_request(message, true, nil)
109
+ rescue
110
+ end
111
+ nil
112
+ end
113
+
114
+
115
+ end # class Local
116
+
117
+ end # module Nodes
118
+ end # module RJR
@@ -3,15 +3,17 @@
3
3
  # Provides a entity able to be associated with a rjr endpoint
4
4
  # if the corresponding node cannot be loaded for whatever reason
5
5
  #
6
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
6
+ # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
7
7
  # Licensed under the Apache License, Version 2.0
8
8
 
9
9
  require 'rjr/node'
10
10
 
11
11
  module RJR
12
- class MissingNode < RJR::Node
12
+ module Nodes
13
+ class Missing < RJR::Node
13
14
  def method_missing(method_id, *args, &bl)
14
15
  raise "rjr node #{node_id} is missing a dependency - cannot invoke #{method_id}"
15
16
  end
16
17
  end
17
18
  end
19
+ end
@@ -0,0 +1,79 @@
1
+ # RJR Multi Node
2
+ #
3
+ # Implements the RJR::Node server interface to satisty
4
+ # JSON-RPC requests over multiple protocols
5
+ #
6
+ # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
7
+ # Licensed under the Apache License, Version 2.0
8
+
9
+ require 'rjr/node'
10
+
11
+ module RJR
12
+ module Nodes
13
+
14
+ # Multiple node definition, allows a developer to easily multiplex transport
15
+ # mechanisms to serve JSON-RPC requests over.
16
+ #
17
+ # All nodes used locally will share the same dispatcher so that json-rpc methods
18
+ # only need to be registered once, with the multi-node itself.
19
+ #
20
+ # This node does not support client operations (eg send_msg, invoke, and notify)
21
+ #
22
+ # @example Listening for json-rpc requests over amqp, tcp, http, and websockets
23
+ # # instantiate worker nodes
24
+ # amqp_server = RJR::Nodes::AMQP.new :node_id => 'amqp_server', :broker => 'localhost'
25
+ # tcp_server = RJR::Nodes::TCP.new :node_id => 'tcp_server', :host => 'localhost', :port => '7777'
26
+ # web_server = RJR::Nodes::Web.new :node_id => 'tcp_server', :host => 'localhost', :port => '80'
27
+ # ws_server = RJR::Nodes::WS.new :node_id => 'tcp_server', :host => 'localhost', :port => '8080'
28
+ #
29
+ # # instantiate multi node
30
+ # server = RJR::Nodes::Multi.new :node_id => 'server',
31
+ # :nodes => [amqp_server, tcp_server, web_server, ws_server]
32
+ #
33
+ # # register rjr dispatchers (see RJR::Dispatcher)
34
+ # server.dispatcher.handle('hello') do |name|
35
+ # # optionally use @rjr_node_type to handle different transport types
36
+ # "Hello #{name}!"
37
+ # end
38
+ #
39
+ # server.listen
40
+ # server.join
41
+ #
42
+ # # invoke requests as you normally would via any protocol
43
+ #
44
+ class Multi < RJR::Node
45
+ # Return the nodes
46
+ attr_reader :nodes
47
+
48
+ # MultiNode initializer
49
+ # @param [Hash] args the options to create the tcp node with
50
+ # @option args [Array<RJR::Node>] :nodes array of nodes to use to listen to new requests on
51
+ def initialize(args = {})
52
+ super(args)
53
+ @nodes = []
54
+ args[:nodes].each { |n|
55
+ self << n
56
+ } if args[:nodes]
57
+ end
58
+
59
+ # Add node to multinode
60
+ # @param [RJR::Node] node the node to add
61
+ def <<(node)
62
+ node.dispatcher = @dispatcher
63
+ @nodes << node
64
+ end
65
+
66
+
67
+ # Instruct Node to start listening for and dispatching rpc requests
68
+ #
69
+ # Implementation of {RJR::Node#listen}
70
+ def listen
71
+ @nodes.each { |node|
72
+ node.listen
73
+ }
74
+ self
75
+ end
76
+ end
77
+
78
+ end # module NODES
79
+ end # module RJR
@@ -0,0 +1,211 @@
1
+ # RJR TCP Node
2
+ #
3
+ # Implements the RJR::Node interface to satisty JSON-RPC requests over the TCP protocol
4
+ #
5
+ # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
6
+ # Licensed under the Apache License, Version 2.0
7
+
8
+ require 'uri'
9
+ require 'thread'
10
+ require 'eventmachine'
11
+
12
+ require 'rjr/node'
13
+ require 'rjr/message'
14
+
15
+ module RJR
16
+ module Nodes
17
+
18
+ # @private
19
+ # Helper class intialized by eventmachine encapsulating a socket connection
20
+ class TCPConnection < EventMachine::Connection
21
+ attr_reader :host
22
+ attr_reader :port
23
+
24
+ # TCPConnection intializer
25
+ #
26
+ # Specify the TCP Node establishing the connection and
27
+ # optionaly remote host/port which this connection is connected to
28
+ def initialize(args = {})
29
+ @rjr_node = args[:rjr_node]
30
+ @host = args[:host]
31
+ @port = args[:port]
32
+
33
+ @send_lock = Mutex.new
34
+ @data = ""
35
+ end
36
+
37
+ # {EventMachine::Connection#receive_data} callback, handle request / response messages
38
+ def receive_data(data)
39
+ # a large json-rpc message may be split over multiple packets
40
+ # (invocations of receive_data)
41
+ # and multiple messages may be concatinated into one packet
42
+ @data += data
43
+ while extracted = MessageUtil.retrieve_json(@data)
44
+ msg, @data = *extracted
45
+ @rjr_node.send(:handle_message, msg, self) # XXX private method
46
+ end
47
+ end
48
+
49
+ # Send data safely using local connection
50
+ def send_msg(data)
51
+ @send_lock.synchronize{
52
+ send_data(data)
53
+ }
54
+ end
55
+
56
+ end
57
+
58
+ # TCP node definition, listen for and invoke json-rpc requests via TCP sockets
59
+ #
60
+ # Clients should specify the hostname / port when listening for requests and
61
+ # when invoking them.
62
+ #
63
+ # @example Listening for json-rpc requests over tcp
64
+ # # initialize node
65
+ # server = RJR::Nodes::TCP.new :node_id => 'server', :host => 'localhost', :port => '7777'
66
+ #
67
+ # # register rjr dispatchers (see RJR::Dispatcher)
68
+ # server.dispatcher.handle('hello') { |name|
69
+ # "Hello #{name}!"
70
+ # }
71
+ #
72
+ # # listen and block
73
+ # server.listen
74
+ # server.join
75
+ #
76
+ # @example Invoking json-rpc requests over tcp
77
+ # client = RJR::Nodes::TCP.new :node_id => 'client', :host => 'localhost', :port => '8888'
78
+ # puts client.invoke('jsonrpc://localhost:7777', 'hello', 'mo')
79
+ #
80
+ class TCP < RJR::Node
81
+ RJR_NODE_TYPE = :tcp
82
+
83
+ attr_accessor :connections
84
+
85
+ private
86
+ # Internal helper, initialize new client
87
+ def init_client(args={}, &on_init)
88
+ host,port = args[:host], args[:port]
89
+ connection = nil
90
+ @connections_lock.synchronize {
91
+ connection = @connections.find { |c|
92
+ port == c.port && host == c.host
93
+ }
94
+ if connection.nil?
95
+ connection =
96
+ EventMachine::connect host, port,
97
+ TCPConnection, args
98
+ @connections << connection
99
+ end
100
+ }
101
+ on_init.call(connection) # TODO move to tcpnode event ?
102
+ end
103
+
104
+ public
105
+
106
+ # TCP initializer
107
+ # @param [Hash] args the options to create the tcp node with
108
+ # @option args [String] :host the hostname/ip which to listen on
109
+ # @option args [Integer] :port the port which to listen on
110
+ def initialize(args = {})
111
+ super(args)
112
+ @host = args[:host]
113
+ @port = args[:port]
114
+
115
+ @connections = []
116
+ @connections_lock = Mutex.new
117
+ end
118
+
119
+ # Send data using specified connection
120
+ #
121
+ # Implementation of {RJR::Node#send_msg}
122
+ def send_msg(data, connection)
123
+ connection.send_msg(data)
124
+ end
125
+
126
+ # Instruct Node to start listening for and dispatching rpc requests
127
+ #
128
+ # Implementation of {RJR::Node#listen}
129
+ def listen
130
+ @em.schedule {
131
+ @em.start_server @host, @port, TCPConnection, { :rjr_node => self }
132
+ }
133
+ self
134
+ end
135
+
136
+ # Instructs node to send rpc request, and wait for / return response.
137
+ #
138
+ # Implementation of {RJR::Node#invoke}
139
+ #
140
+ # Do not invoke directly from em event loop or callback as will block the message
141
+ # subscription used to receive responses
142
+ #
143
+ # @param [String] uri location of node to send request to, should be
144
+ # in format of jsonrpc://hostname:port or tcp://hostname:port
145
+ # @param [String] rpc_method json-rpc method to invoke on destination
146
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
147
+ def invoke(uri, rpc_method, *args)
148
+ uri = URI.parse(uri)
149
+ host,port = uri.host, uri.port
150
+
151
+ message = RequestMessage.new :method => rpc_method,
152
+ :args => args,
153
+ :headers => @message_headers
154
+ connection = nil
155
+ @em.schedule {
156
+ init_client(:host => host, :port => port,
157
+ :rjr_node => self) { |c|
158
+ connection = c
159
+ c.send_msg message.to_s
160
+ }
161
+ }
162
+
163
+ # TODO optional timeout for response ?
164
+ result = wait_for_result(message)
165
+
166
+ if result.size > 2
167
+ raise Exception, result[2]
168
+ end
169
+ return result[1]
170
+ end
171
+
172
+ # Instructs node to send rpc notification (immadiately returns / no response is generated)
173
+ #
174
+ # Implementation of {RJR::Node#notify}
175
+ #
176
+ # @param [String] uri location of node to send notification to, should be
177
+ # in format of jsonrpc://hostname:port
178
+ # @param [String] rpc_method json-rpc method to invoke on destination
179
+ # @param [Array] args array of arguments to convert to json and invoke remote method wtih
180
+ def notify(uri, rpc_method, *args)
181
+ # will block until message is published
182
+ published_l = Mutex.new
183
+ published_c = ConditionVariable.new
184
+
185
+ uri = URI.parse(uri)
186
+ host,port = uri.host, uri.port
187
+
188
+ invoked = false
189
+ conn = nil
190
+ message = NotificationMessage.new :method => rpc_method,
191
+ :args => args,
192
+ :headers => @message_headers
193
+ @em.schedule {
194
+ init_client(:host => host, :port => port,
195
+ :rjr_node => self) { |c|
196
+ conn = c
197
+ c.send_msg message.to_s
198
+ # XXX, this should be invoked only when we are sure event
199
+ # machine sent message. Shouldn't pose a problem unless event
200
+ # machine is killed immediately after
201
+ published_l.synchronize { invoked = true ; published_c.signal }
202
+ }
203
+ }
204
+ published_l.synchronize { published_c.wait published_l unless invoked }
205
+ #sleep 0.01 until conn.get_outbound_data_size == 0
206
+ nil
207
+ end
208
+ end
209
+
210
+ end # module Nodes
211
+ end # module RJR