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
data/lib/rjr/util.rb DELETED
@@ -1,104 +0,0 @@
1
- # High level rjr utility mechanisms
2
- #
3
- # Copyright (C) 2013 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the Apache License, Version 2.0
5
-
6
- require 'rjr/dispatcher'
7
-
8
- module RJR
9
-
10
- # Mixin providing utility methods to define rjr methods and messages
11
- module Definitions
12
- # Define one or more rjr methods, parameters should be in the form
13
- # :id => Callable
14
- #
15
- # id may be a single id or an array of them
16
- def rjr_method(args = {})
17
- args.each { |k, v|
18
- RJR::Dispatcher.add_handler(k.to_s, &v)
19
- }
20
- nil
21
- end
22
-
23
- # Define/retrieve rjr messages. When defining pass a hash
24
- # of mesasge ids to options to use in the defintions, eg
25
- # :id => { :foo => :bar }
26
- #
27
- # When retrieving simply specify the id
28
- def rjr_message(args={})
29
- if args.is_a?(Hash)
30
- args.each { |k,v|
31
- RJR::Definitions.message(k.to_s, v)
32
- }
33
- nil
34
- else
35
- RJR::Definitions.message(args.to_s)
36
- end
37
- end
38
-
39
- # Helper providing access to messages
40
- def self.message(k, v=nil)
41
- @rjr_messages ||= {}
42
- @rjr_messages[k] = v unless v.nil?
43
- @rjr_messages[k]
44
- end
45
-
46
- # Reset message registry
47
- def self.reset
48
- # TODO also invoke 'Dispatcher.init_handlers' ?
49
- @rjr_messages = {}
50
- end
51
-
52
- # Generate / return random message. Optionally specify the transport which
53
- # the message must accept
54
- def self.rand_msg(transport = nil)
55
- @rjr_messages ||= {}
56
- messages = @rjr_messages.select { |mid,m| m[:transports].nil? || transport.nil? ||
57
- m[:transports].include?(transport) }
58
- messages[messages.keys[rand(messages.keys.size)]]
59
- end
60
- end
61
-
62
- # Class to encapsulate any number of rjr nodes
63
- class EasyNode
64
- def initialize(node_args = {})
65
- nodes = node_args.keys.collect { |n|
66
- case n
67
- when :amqp then
68
- RJR::AMQPNode.new node_args[:amqp]
69
- when :ws then
70
- RJR::WSNode.new node_args[:ws]
71
- when :tcp then
72
- RJR::TCPNode.new node_args[:tcp]
73
- when :www then
74
- RJR::WebNode.new node_args[:www]
75
- end
76
- }
77
- @multi_node = RJR::MultiNode.new :nodes => nodes
78
- end
79
-
80
- def invoke_request(dst, method, *params)
81
- # TODO allow selection of node, eg automatically deduce which node type to use from 'dst'
82
- @multi_node.nodes.first.invoke_request(dst, method, *params)
83
- end
84
-
85
- # Stop node on the specified signal
86
- def stop_on(signal)
87
- Signal.trap(signal) {
88
- @multi_node.stop
89
- }
90
- self
91
- end
92
-
93
- def listen
94
- @multi_node.listen
95
- self
96
- end
97
-
98
- def join
99
- @multi_node.join
100
- self
101
- end
102
- end
103
-
104
- end # module RJR
data/lib/rjr/web_node.rb DELETED
@@ -1,266 +0,0 @@
1
- # RJR WWW Endpoint
2
- #
3
- # Implements the RJR::Node interface to satisty JSON-RPC requests over the HTTP protocol
4
- #
5
- # The web node does not support callbacks at the moment, though at some point we may
6
- # allow a client to specify an optional webserver to send callback requests to. (TODO)
7
- #
8
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
9
- # Licensed under the Apache License, Version 2.0
10
-
11
- skip_module = false
12
- begin
13
- require 'evma_httpserver'
14
- require 'em-http-request'
15
- # TODO also support fallback clients ? (curb / net/http / etc)
16
- rescue LoadError
17
- skip_module = true
18
- end
19
-
20
- if skip_module
21
- # TODO output: "curb/evma_httpserver gems could not be loaded, skipping web node definition"
22
- require 'rjr/missing_node'
23
- RJR::WebNode = RJR::MissingNode
24
-
25
- else
26
- require 'socket'
27
-
28
- require 'rjr/node'
29
- require 'rjr/message'
30
- require 'rjr/dispatcher'
31
- require 'rjr/thread_pool2'
32
-
33
- module RJR
34
-
35
- # Web node callback interface, *note* callbacks are not supported on the web
36
- # node and thus this currently does nothing
37
- class WebNodeCallback
38
- def initialize()
39
- end
40
-
41
- def invoke(callback_method, *data)
42
- # TODO throw error?
43
- end
44
- end
45
-
46
- # @private
47
- # Helper class intialized by eventmachine encapsulating a http connection
48
- class WebRequestHandler < EventMachine::Connection
49
- include EventMachine::HttpServer
50
-
51
- RJR_NODE_TYPE = :web
52
-
53
- # WebRequestHandler initializer.
54
- #
55
- # specify the WebNode establishing the connection
56
- def initialize(*args)
57
- @web_node = args[0]
58
- end
59
-
60
- # {EventMachine::Connection#process_http_request} callback, handle request messages
61
- def process_http_request
62
- # TODO support http protocols other than POST
63
- msg = @http_post_content.nil? ? '' : @http_post_content
64
- ThreadPool2Manager << ThreadPool2Job.new(msg) { |m| handle_request(m) }
65
- end
66
-
67
- private
68
-
69
- # Internal helper, handle request message received
70
- def handle_request(message)
71
- msg = nil
72
- result = nil
73
- notification = NotificationMessage.is_notification_message?(msg)
74
-
75
- begin
76
- client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
77
- msg = notification ? NotificationMessage.new(:message => message, :headers => @web_node.message_headers) :
78
- RequestMessage.new(:message => message, :headers => @web_node.message_headers)
79
- headers = @web_node.message_headers.merge(msg.headers)
80
- result = Dispatcher.dispatch_request(msg.jr_method,
81
- :method_args => msg.jr_args,
82
- :headers => headers,
83
- :client_ip => client_ip,
84
- :client_port => client_port,
85
- :rjr_node => @web_node,
86
- :rjr_node_id => @web_node.node_id,
87
- :rjr_node_type => RJR_NODE_TYPE,
88
- :rjr_callback => WebNodeCallback.new())
89
- rescue JSON::ParserError => e
90
- result = Result.invalid_request
91
- end
92
-
93
- msg_id = msg.nil? ? nil : msg.msg_id
94
- response = ResponseMessage.new(:id => msg_id, :result => result, :headers => headers)
95
-
96
- unless notification
97
- resp = EventMachine::DelegatedHttpResponse.new(self)
98
- #resp.status = response.result.success ? 200 : 500
99
- resp.status = 200
100
- resp.content = response.to_s
101
- resp.content_type "application/json"
102
- resp.send_response
103
- end
104
- end
105
- end
106
-
107
- # Web node definition, listen for and invoke json-rpc requests via web requests
108
- #
109
- # Clients should specify the hostname / port when listening for requests and
110
- # when invoking them.
111
- #
112
- # *note* the RJR javascript client also supports sending / receiving json-rpc
113
- # messages over http
114
- #
115
- # @example Listening for json-rpc requests over tcp
116
- # # register rjr dispatchers (see RJR::Dispatcher)
117
- # RJR::Dispatcher.add_handler('hello') { |name|
118
- # "Hello #{name}!"
119
- # }
120
- #
121
- # # initialize node, listen, and block
122
- # server = RJR::WebNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
123
- # server.listen
124
- # server.join
125
- #
126
- # @example Invoking json-rpc requests over http using rjr
127
- # client = RJR::WebNode.new :node_id => 'client'
128
- # puts client.invoke_request('http://localhost:7777', 'hello', 'mo')
129
- #
130
- # @example Invoking json-rpc requests over http using curl
131
- # $ curl -X POST http://localhost:7777 -d '{"jsonrpc":"2.0","method":"hello","params":["mo"],"id":"123"}'
132
- # > {"jsonrpc":"2.0","id":"123","result":"Hello mo!"}
133
- #
134
- class WebNode < RJR::Node
135
- private
136
-
137
- # Internal helper, handle response message received
138
- def handle_response(http)
139
- msg = ResponseMessage.new(:message => http.response, :headers => @message_headers)
140
- res = err = nil
141
- begin
142
- res = Dispatcher.handle_response(msg.result)
143
- rescue Exception => e
144
- err = e
145
- end
146
-
147
- @response_lock.synchronize {
148
- result = [msg.msg_id, res]
149
- result << err if !err.nil?
150
- @responses << result
151
- @response_cv.signal
152
- }
153
- end
154
-
155
- # Internal helper, block until response matching message id is received
156
- def wait_for_result(message)
157
- res = nil
158
- while res.nil?
159
- @response_lock.synchronize{
160
- # FIXME throw err if more than 1 match found
161
- res = @responses.select { |response| message.msg_id == response.first }.first
162
- if !res.nil?
163
- @responses.delete(res)
164
-
165
- else
166
- @response_cv.signal
167
- @response_cv.wait @response_lock
168
-
169
- end
170
- }
171
- end
172
- return res
173
- end
174
-
175
- public
176
-
177
- # WebNode initializer
178
- # @param [Hash] args the options to create the tcp node with
179
- # @option args [String] :host the hostname/ip which to listen on
180
- # @option args [Integer] :port the port which to listen on
181
- def initialize(args = {})
182
- super(args)
183
- @host = args[:host]
184
- @port = args[:port]
185
-
186
- @response_lock = Mutex.new
187
- @response_cv = ConditionVariable.new
188
- @responses = []
189
- end
190
-
191
- # Register connection event handler,
192
- #
193
- # *note* Since web node connections aren't persistant, we don't do anything here.
194
- # @param [:error, :close] event the event to register the handler for
195
- # @param [Callable] handler block param to be added to array of handlers that are called when event occurs
196
- # @yield [LocalNode] self is passed to each registered handler when event occurs
197
- def on(event, &handler)
198
- # TODO raise error?
199
- end
200
-
201
- # Instruct Node to start listening for and dispatching rpc requests
202
- #
203
- # Implementation of {RJR::Node#listen}
204
- def listen
205
- em_run do
206
- EventMachine::start_server(@host, @port, WebRequestHandler, self)
207
- end
208
- end
209
-
210
- # Instructs node to send rpc request, and wait for / return response
211
- # @param [String] uri location of node to send request to, should be
212
- # in format of http://hostname:port
213
- # @param [String] rpc_method json-rpc method to invoke on destination
214
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
215
- def invoke_request(uri, rpc_method, *args)
216
- message = RequestMessage.new :method => rpc_method,
217
- :args => args,
218
- :headers => @message_headers
219
- cb = lambda { |http|
220
- # TODO handle errors
221
- handle_response(http)
222
- }
223
-
224
- em_run do
225
- http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
226
- http.errback &cb
227
- http.callback &cb
228
- end
229
-
230
- # will block until response message is received
231
- # TODO optional timeout for response ?
232
- result = wait_for_result(message)
233
- if result.size > 2
234
- raise Exception, result[2]
235
- end
236
- return result[1]
237
- end
238
-
239
- # Instructs node to send rpc notification (immadiately returns / no response is generated)
240
- #
241
- # @param [String] uri location of node to send request to, should be
242
- # in format of http://hostname:port
243
- # @param [String] rpc_method json-rpc method to invoke on destination
244
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
245
- def send_notification(uri, rpc_method, *args)
246
- # will block until message is published
247
- published_l = Mutex.new
248
- published_c = ConditionVariable.new
249
-
250
- invoked = false
251
- message = NotificationMessage.new :method => rpc_method,
252
- :args => args,
253
- :headers => @message_headers
254
- cb = lambda { |arg| published_l.synchronize { invoked = true ; published_c.signal }}
255
- em_run do
256
- http = EventMachine::HttpRequest.new(uri).post :body => message.to_s
257
- http.errback &cb
258
- http.callback &cb
259
- end
260
- published_l.synchronize { published_c.wait published_l unless invoked }
261
- nil
262
- end
263
- end
264
-
265
- end # module RJR
266
- end
data/lib/rjr/ws_node.rb DELETED
@@ -1,289 +0,0 @@
1
- # RJR WebSockets Endpoint
2
- #
3
- # Implements the RJR::Node interface to satisty JSON-RPC requests over the websockets protocol
4
- #
5
- # Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
6
- # Licensed under the Apache License, Version 2.0
7
-
8
- skip_module = false
9
- begin
10
- require 'em-websocket'
11
- require 'em-websocket-client'
12
- rescue LoadError
13
- skip_module = true
14
- end
15
-
16
- if skip_module
17
- # TODO output: "ws dependencies could not be loaded, skipping ws node definition"
18
- require 'rjr/missing_node'
19
- RJR::WSNode = RJR::MissingNode
20
-
21
- else
22
- require 'socket'
23
- require 'rjr/node'
24
- require 'rjr/message'
25
- require 'rjr/dispatcher'
26
- require 'rjr/errors'
27
- require 'rjr/thread_pool2'
28
-
29
- module RJR
30
-
31
- # Web Socket node callback interface, used to invoke json-rpc methods
32
- # against a remote node via a web socket connection previously established
33
- #
34
- # After a node sends a json-rpc request to another, the either node may send
35
- # additional requests to each other via the socket already established until
36
- # it is closed on either end
37
- class WSNodeCallback
38
- # WSNodeCallback initializer
39
- # @param [Hash] args the options to create the websocket node callback with
40
- # @option args [Socket] :socket socket connection used to send/receive messages
41
- # @option args [Hash] :headers hash of rjr message headers present in client request when callback is established
42
- def initialize(args = {})
43
- @socket = args[:socket]
44
- @message_headers = args[:headers]
45
- end
46
-
47
- # Implementation of {RJR::NodeCallback#invoke}
48
- def invoke(callback_method, *data)
49
- msg = NotificationMessage.new :method => callback_method, :args => data, :headers => @message_headers
50
- raise RJR::Errors::ConnectionError.new("websocket closed") if @socket.state == :closed
51
- # TODO surround w/ begin/rescue block incase of other socket errors?
52
- @socket.send(msg.to_s)
53
- end
54
- end
55
-
56
- # Web socket node definition, listen for and invoke json-rpc requests via web sockets
57
- #
58
- # Clients should specify the hostname / port when listening for and invoking requests.
59
- #
60
- # *note* the RJR javascript client also supports sending / receiving json-rpc
61
- # messages over web sockets
62
- #
63
- # @example Listening for json-rpc requests over tcp
64
- # # register rjr dispatchers (see RJR::Dispatcher)
65
- # RJR::Dispatcher.add_handler('hello') { |name|
66
- # "Hello #{name}!"
67
- # }
68
- #
69
- # # initialize node, listen, and block
70
- # server = RJR::WSNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
71
- # server.listen
72
- # server.join
73
- #
74
- # @example Invoking json-rpc requests over web sockets using rjr
75
- # client = RJR::WsNode.new :node_id => 'client'
76
- # puts client.invoke_request('ws://localhost:7777', 'hello', 'mo')
77
- #
78
- class WSNode < RJR::Node
79
- RJR_NODE_TYPE = :ws
80
-
81
- private
82
-
83
- # Internal helper initialize new connection
84
- def init_node(uri, &on_init)
85
- connection = nil
86
- @connections_lock.synchronize {
87
- connection = @connections.find { |c|
88
- c.url == uri
89
- }
90
- if connection.nil?
91
- connection = EventMachine::WebSocketClient.connect(uri)
92
- connection.callback do
93
- on_init.call(connection)
94
- end
95
- @connections << connection
96
- # TODO sleep until connected?
97
- else
98
- on_init.call(connection)
99
- end
100
- }
101
- connection
102
- end
103
-
104
- # Internal helper handle messages
105
- def handle_msg(endpoint, msg)
106
- # TODO use messageutil incase of large messages?
107
- if RequestMessage.is_request_message?(msg)
108
- ThreadPool2Manager << ThreadPool2Job.new { handle_request(endpoint, msg, false) }
109
-
110
- elsif NotificationMessage.is_notification_message?(msg)
111
- ThreadPool2Manager << ThreadPool2Job.new { handle_request(endpoint, msg, true) }
112
-
113
- elsif ResponseMessage.is_response_message?(msg)
114
- handle_response(msg)
115
-
116
- end
117
- end
118
-
119
- # Internal helper, handle request message received
120
- def handle_request(endpoint, message, notification=false)
121
- # XXX hack to handle client disconnection (should grap port/ip immediately on connection and use that)
122
- client_port,client_ip = nil,nil
123
- begin
124
- client_port, client_ip = Socket.unpack_sockaddr_in(endpoint.get_peername)
125
- rescue Exception=>e
126
- end
127
-
128
- msg = notification ? NotificationMessage.new(:message => message, :headers => @message_headers) :
129
- RequestMessage.new(:message => message, :headers => @message_headers)
130
- headers = @message_headers.merge(msg.headers)
131
- result = Dispatcher.dispatch_request(msg.jr_method,
132
- :method_args => msg.jr_args,
133
- :headers => headers,
134
- :client_ip => client_ip,
135
- :client_port => client_port,
136
- :rjr_node => self,
137
- :rjr_node_id => @node_id,
138
- :rjr_node_type => RJR_NODE_TYPE,
139
- :rjr_callback =>
140
- WSNodeCallback.new(:socket => endpoint,
141
- :headers => headers))
142
- unless notification
143
- response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
144
- endpoint.send(response.to_s)
145
- end
146
- end
147
-
148
- # Internal helper, handle response message received
149
- def handle_response(data)
150
- msg = ResponseMessage.new(:message => data, :headers => @message_headers)
151
- res = err = nil
152
- begin
153
- res = Dispatcher.handle_response(msg.result)
154
- rescue Exception => e
155
- err = e
156
- end
157
-
158
- @response_lock.synchronize {
159
- result = [msg.msg_id, res]
160
- result << err if !err.nil?
161
- @responses << result
162
- @response_cv.signal
163
- }
164
- end
165
-
166
- # Internal helper, block until response matching message id is received
167
- def wait_for_result(message)
168
- res = nil
169
- while res.nil?
170
- @response_lock.synchronize{
171
- # FIXME throw err if more than 1 match found
172
- res = @responses.select { |response| message.msg_id == response.first }.first
173
- if !res.nil?
174
- @responses.delete(res)
175
-
176
- else
177
- @response_cv.signal
178
- @response_cv.wait @response_lock
179
-
180
- end
181
- }
182
- end
183
- return res
184
- end
185
-
186
-
187
- public
188
- # WSNode initializer
189
- # @param [Hash] args the options to create the web socket node with
190
- # @option args [String] :host the hostname/ip which to listen on
191
- # @option args [Integer] :port the port which to listen on
192
- def initialize(args = {})
193
- super(args)
194
- @host = args[:host]
195
- @port = args[:port]
196
-
197
- @connections = []
198
- @connections_lock = Mutex.new
199
-
200
- @response_lock = Mutex.new
201
- @response_cv = ConditionVariable.new
202
- @responses = []
203
-
204
- @connection_event_handlers = {:closed => [], :error => []}
205
- end
206
-
207
- # Register connection event handler
208
- # @param [:error, :close] event the event to register the handler for
209
- # @param [Callable] handler block param to be added to array of handlers that are called when event occurs
210
- # @yield [WSNode] self is passed to each registered handler when event occurs
211
- def on(event, &handler)
212
- if @connection_event_handlers.keys.include?(event)
213
- @connection_event_handlers[event] << handler
214
- end
215
- end
216
-
217
- # Instruct Node to start listening for and dispatching rpc requests
218
- #
219
- # Implementation of {RJR::Node#listen}
220
- def listen
221
- em_run do
222
- EventMachine::WebSocket.start(:host => @host, :port => @port) do |ws|
223
- ws.onopen { }
224
- ws.onclose { @connection_event_handlers[:closed].each { |h| h.call self } }
225
- ws.onerror { |e| @connection_event_handlers[:error].each { |h| h.call self } }
226
- ws.onmessage { |msg| handle_msg(ws, msg) }
227
- end
228
- end
229
- end
230
-
231
- # Instructs node to send rpc request, and wait for / return response
232
- # @param [String] uri location of node to send request to, should be
233
- # in format of ws://hostname:port
234
- # @param [String] rpc_method json-rpc method to invoke on destination
235
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
236
- def invoke_request(uri, rpc_method, *args)
237
- message = RequestMessage.new :method => rpc_method,
238
- :args => args,
239
- :headers => @message_headers
240
-
241
- em_run{
242
- init_node(uri) do |c|
243
- c.stream { |msg| handle_msg(c, msg) }
244
-
245
- c.send_msg message.to_s
246
- end
247
- }
248
-
249
- # TODO optional timeout for response ?
250
- result = wait_for_result(message)
251
-
252
- if result.size > 2
253
- raise Exception, result[2]
254
- end
255
- return result[1]
256
- end
257
-
258
- # Instructs node to send rpc notification (immadiately returns / no response is generated)
259
- #
260
- # @param [String] uri location of node to send notification to, should be
261
- # in format of ws://hostname:port
262
- # @param [String] rpc_method json-rpc method to invoke on destination
263
- # @param [Array] args array of arguments to convert to json and invoke remote method wtih
264
- def send_notification(uri, rpc_method, *args)
265
- # will block until message is published
266
- published_l = Mutex.new
267
- published_c = ConditionVariable.new
268
-
269
- invoked = false
270
- message = NotificationMessage.new :method => rpc_method,
271
- :args => args,
272
- :headers => @message_headers
273
- em_run{
274
- init_node(uri) do |c|
275
- c.send_msg message.to_s
276
-
277
- # XXX same bug w/ tcp node, due to nature of event machine
278
- # we aren't guaranteed that message is actually written to socket
279
- # here, process must be kept alive until data is sent or will be lost
280
- published_l.synchronize { invoked = true ; published_c.signal }
281
- end
282
- }
283
- published_l.synchronize { published_c.wait published_l unless invoked }
284
- nil
285
- end
286
- end
287
-
288
- end # module RJR
289
- end
data/lib/rjr.rb DELETED
@@ -1,16 +0,0 @@
1
- # rjr - Ruby Json Rpc
2
- #
3
- # Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
4
- # Licensed under the Apache License, Version 2.0
5
-
6
- # rjr - Ruby Json Rpc
7
- module RJR ; end
8
-
9
- require 'rjr/amqp_node'
10
- require 'rjr/tcp_node'
11
- require 'rjr/web_node'
12
- require 'rjr/ws_node'
13
- require 'rjr/local_node'
14
- require 'rjr/multi_node'
15
-
16
- require 'rjr/util'
@@ -1,31 +0,0 @@
1
- require 'rjr/amqp_node'
2
- require 'rjr/dispatcher'
3
-
4
- describe RJR::AMQPNode do
5
- it "should invoke and satisfy amqp requests" do
6
- server = RJR::AMQPNode.new :node_id => 'amqp', :broker => 'localhost'
7
- client = RJR::AMQPNode.new :node_id => 'client', :broker => 'localhost'
8
-
9
- foozbar_invoked = false
10
- RJR::Dispatcher.init_handlers
11
- RJR::Dispatcher.add_handler('foozbar') { |param|
12
- @client_ip.should == nil
13
- @client_port.should == nil
14
- @rjr_node.should == server
15
- @rjr_node_id.should == 'amqp'
16
- @rjr_node_type.should == :amqp
17
- param.should == 'myparam'
18
- foozbar_invoked = true
19
- 'retval'
20
- }
21
-
22
- server.listen
23
- res = client.invoke_request 'amqp-queue', 'foozbar', 'myparam'
24
- server.halt
25
- server.join
26
- res.should == 'retval'
27
- foozbar_invoked.should == true
28
- end
29
-
30
- # TODO ensure closed / error event handlers are invoked
31
- end