rjr 0.12.2 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
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