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
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