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.
- data/README.md +49 -36
- data/Rakefile +2 -0
- data/bin/rjr-client +11 -9
- data/bin/rjr-server +12 -10
- data/examples/amqp.rb +29 -0
- data/examples/client.rb +32 -0
- data/examples/complete.rb +36 -0
- data/examples/local.rb +29 -0
- data/examples/server.rb +26 -0
- data/examples/tcp.rb +29 -0
- data/examples/web.rb +22 -0
- data/examples/ws.rb +29 -0
- data/lib/rjr/common.rb +7 -12
- data/lib/rjr/dispatcher.rb +171 -239
- data/lib/rjr/em_adapter.rb +33 -66
- data/lib/rjr/message.rb +43 -12
- data/lib/rjr/node.rb +197 -103
- data/lib/rjr/nodes/amqp.rb +216 -0
- data/lib/rjr/nodes/easy.rb +159 -0
- data/lib/rjr/nodes/local.rb +118 -0
- data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
- data/lib/rjr/nodes/multi.rb +79 -0
- data/lib/rjr/nodes/tcp.rb +211 -0
- data/lib/rjr/nodes/web.rb +197 -0
- data/lib/rjr/nodes/ws.rb +187 -0
- data/lib/rjr/stats.rb +70 -0
- data/lib/rjr/thread_pool.rb +178 -123
- data/site/index.html +45 -0
- data/site/jquery-latest.js +9404 -0
- data/site/jrw.js +297 -0
- data/site/json.js +199 -0
- data/specs/dispatcher_spec.rb +244 -198
- data/specs/em_adapter_spec.rb +52 -80
- data/specs/message_spec.rb +223 -197
- data/specs/node_spec.rb +67 -163
- data/specs/nodes/amqp_spec.rb +82 -0
- data/specs/nodes/easy_spec.rb +13 -0
- data/specs/nodes/local_spec.rb +72 -0
- data/specs/nodes/multi_spec.rb +65 -0
- data/specs/nodes/tcp_spec.rb +75 -0
- data/specs/nodes/web_spec.rb +77 -0
- data/specs/nodes/ws_spec.rb +78 -0
- data/specs/stats_spec.rb +59 -0
- data/specs/thread_pool_spec.rb +44 -35
- metadata +40 -30
- data/lib/rjr/amqp_node.rb +0 -330
- data/lib/rjr/inspect.rb +0 -65
- data/lib/rjr/local_node.rb +0 -150
- data/lib/rjr/multi_node.rb +0 -65
- data/lib/rjr/tcp_node.rb +0 -323
- data/lib/rjr/thread_pool2.rb +0 -272
- data/lib/rjr/util.rb +0 -104
- data/lib/rjr/web_node.rb +0 -266
- data/lib/rjr/ws_node.rb +0 -289
- data/lib/rjr.rb +0 -16
- data/specs/amqp_node_spec.rb +0 -31
- data/specs/inspect_spec.rb +0 -60
- data/specs/local_node_spec.rb +0 -43
- data/specs/multi_node_spec.rb +0 -45
- data/specs/tcp_node_spec.rb +0 -33
- data/specs/util_spec.rb +0 -46
- data/specs/web_node_spec.rb +0 -32
- data/specs/ws_node_spec.rb +0 -32
- /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
- /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
data/lib/rjr/dispatcher.rb
CHANGED
@@ -3,95 +3,32 @@
|
|
3
3
|
# Representation of a json-rpc request, response and mechanisms which to
|
4
4
|
# register methods to handle requests and return responses
|
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
|
-
require 'rjr/common'
|
10
9
|
require 'json'
|
10
|
+
require 'rjr/common'
|
11
11
|
|
12
12
|
module RJR
|
13
13
|
|
14
|
-
# JSON-RPC request representation
|
15
|
-
class Request
|
16
|
-
# name of the method which request is for
|
17
|
-
attr_accessor :method
|
18
|
-
alias :rjr_method :method
|
19
|
-
|
20
|
-
# array of arguments which to pass to the rpc method handler
|
21
|
-
attr_accessor :method_args
|
22
|
-
alias :rjr_method_args :method_args
|
23
|
-
|
24
|
-
# hash of keys/values corresponding to optional headers received as part of of the request
|
25
|
-
attr_accessor :headers
|
26
|
-
|
27
|
-
# callback through which additional requests may be invoked
|
28
|
-
attr_accessor :rjr_callback
|
29
|
-
|
30
|
-
# type of the rjr node which request was received on
|
31
|
-
attr_accessor :rjr_node_type
|
32
|
-
|
33
|
-
# id of the rjr node which request was received on
|
34
|
-
attr_accessor :rjr_node_id
|
35
|
-
|
36
|
-
# callable object registered to the specified method which to invoke request on with arguments
|
37
|
-
attr_accessor :handler
|
38
|
-
|
39
|
-
# RJR request intializer
|
40
|
-
# @param [Hash] args options to set on request
|
41
|
-
# @option args [String] :method name of the method which request is for
|
42
|
-
# @option args [Array] :method_args array of arguments which to pass to the rpc method handler
|
43
|
-
# @option args [Hash] :headers hash of keys/values corresponding to optional headers received as part of of the request
|
44
|
-
# @option args [String] :client_ip ip address of client which invoked the request (if applicable)
|
45
|
-
# @option args [String] :client_port port of client which invoked the request (if applicable)
|
46
|
-
# @option args [RJR::Callback] :rjr_callback callback through which additional requests may be invoked
|
47
|
-
# @option args [RJR::Node] :rjr_node rjr node which request was received on
|
48
|
-
# @option args [String] :rjr_node_id id of the rjr node which request was received on
|
49
|
-
# @option args [Symbol] :rjr_node_type type of the rjr node which request was received on
|
50
|
-
# @option args [Callable] :handler callable object registered to the specified method which to invoke request on with arguments
|
51
|
-
def initialize(args = {})
|
52
|
-
@method = args[:method] || args['method']
|
53
|
-
@rjr_method = @method
|
54
|
-
@method_args = args[:method_args] || args['method_args']
|
55
|
-
@rjr_method_args = @method_args
|
56
|
-
@headers = args[:headers] || args['headers']
|
57
|
-
@client_ip = args[:client_ip]
|
58
|
-
@client_port = args[:client_port]
|
59
|
-
@rjr_callback = args[:rjr_callback]
|
60
|
-
@rjr_node = args[:rjr_node]
|
61
|
-
@rjr_node_id = args[:rjr_node_id] || args['rjr_node_id']
|
62
|
-
@rjr_node_type = args[:rjr_node_type] || args['rjr_node_type']
|
63
|
-
@handler = args[:handler]
|
64
|
-
end
|
65
|
-
|
66
|
-
# Actually invoke the request by calling the registered handler with the specified
|
67
|
-
# method parameters in the local scope
|
68
|
-
def handle
|
69
|
-
RJR::Logger.info "Dispatching '#{@method}' request with parameters (#{@method_args.join(',')}) on #{@rjr_node_type}-node(#{@rjr_node_id})"
|
70
|
-
# TODO compare arity of method to number of args ?
|
71
|
-
retval = instance_exec(*@method_args, &@handler)
|
72
|
-
RJR::Logger.info "#{@method} request with parameters (#{@method_args.join(',')}) returning #{retval}"
|
73
|
-
return retval
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
14
|
# JSON-RPC result representation
|
78
15
|
class Result
|
79
|
-
#
|
16
|
+
# Boolean indicating if request was successfully invoked
|
80
17
|
attr_accessor :success
|
81
18
|
|
82
|
-
#
|
19
|
+
# Boolean indicating if request failed in some manner
|
83
20
|
attr_accessor :failed
|
84
21
|
|
85
|
-
#
|
22
|
+
# Return value of the json-rpc call if successful
|
86
23
|
attr_accessor :result
|
87
24
|
|
88
|
-
#
|
25
|
+
# Code corresponding to json-rpc error if problem occured during request invocation
|
89
26
|
attr_accessor :error_code
|
90
27
|
|
91
|
-
#
|
28
|
+
# Message corresponding to json-rpc error if problem occured during request invocation
|
92
29
|
attr_accessor :error_msg
|
93
30
|
|
94
|
-
#
|
31
|
+
# Class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
95
32
|
attr_accessor :error_class
|
96
33
|
|
97
34
|
# RJR result intializer
|
@@ -101,34 +38,23 @@ class Result
|
|
101
38
|
# @option args [String] :error_msg message corresponding to json-rpc error if problem occured during request invocation
|
102
39
|
# @option args [Class] :error_class class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
103
40
|
def initialize(args = {})
|
104
|
-
@result =
|
105
|
-
@error_code =
|
106
|
-
@
|
107
|
-
@error_class
|
108
|
-
|
109
|
-
if args.has_key?(:result) || args.has_key?('result')
|
110
|
-
@success = true
|
111
|
-
@failed = false
|
112
|
-
@result = args[:result] || args['result']
|
113
|
-
|
114
|
-
elsif args.has_key?(:error_code) || args.has_key?('error_code')
|
115
|
-
@success = false
|
116
|
-
@failed = true
|
117
|
-
@error_code = args[:error_code] || args['error_code']
|
118
|
-
@error_msg = args[:error_msg] || args['error_msg']
|
119
|
-
@error_class = args[:error_class] || args['error_class']
|
41
|
+
@result = args[:result] || args['result']
|
42
|
+
@error_code = args[:error_code] || args['error_code']
|
43
|
+
@error_msg = args[:error_msg] || args['error_msg']
|
44
|
+
@error_class = args[:error_class] || args['error_class']
|
120
45
|
|
121
|
-
|
46
|
+
@success = @error_code.nil?
|
47
|
+
@failed = !@error_code.nil?
|
122
48
|
end
|
123
49
|
|
124
50
|
# Compare Result against other result, returning true if both correspond
|
125
51
|
# to equivalent json-rpc results else false
|
126
52
|
def ==(other)
|
127
|
-
@success
|
128
|
-
@failed
|
129
|
-
@result
|
130
|
-
@error_code
|
131
|
-
@error_msg
|
53
|
+
@success == other.success &&
|
54
|
+
@failed == other.failed &&
|
55
|
+
@result == other.result &&
|
56
|
+
@error_code == other.error_code &&
|
57
|
+
@error_msg == other.error_msg &&
|
132
58
|
@error_class == other.error_class
|
133
59
|
end
|
134
60
|
|
@@ -150,191 +76,198 @@ class Result
|
|
150
76
|
return Result.new(:error_code => -32602,
|
151
77
|
:error_msg => "Method '#{name}' not found")
|
152
78
|
end
|
153
|
-
|
154
79
|
end
|
155
80
|
|
156
|
-
#
|
157
|
-
# be invoked on new requests.
|
81
|
+
# JSON-RPC request representation.
|
158
82
|
#
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
class
|
163
|
-
|
164
|
-
attr_accessor :
|
165
|
-
|
166
|
-
# RJR::Handler intializer
|
167
|
-
# @param [Hash] args options to set on handler
|
168
|
-
# @option args [String] :method name of json-rpc method to which handler is bound
|
169
|
-
# @option args [Callable] :handle callable object which to bind to method name
|
170
|
-
def initialize(args = {})
|
171
|
-
@method_name = args[:method]
|
172
|
-
@handler_proc = args[:handler]
|
173
|
-
end
|
83
|
+
# Registered request handlers will be invoked in the context of
|
84
|
+
# instances of this class, meaning all member variables will be available
|
85
|
+
# for use in the handler.
|
86
|
+
class Request
|
87
|
+
# Result of the request operation, set by dispatcher
|
88
|
+
attr_accessor :result
|
174
89
|
|
175
|
-
#
|
176
|
-
|
177
|
-
# Creates new {RJR::Request} with the local method name and handler and the
|
178
|
-
# arguments received as part of the request. Uses it to invoke handler and
|
179
|
-
# creates and returns new {RJR::Result} encapsulating the return value if
|
180
|
-
# successful or error code/message/class if not.
|
181
|
-
#
|
182
|
-
# If invalid method_name is specified returns a json-rpc 'Method not found'
|
183
|
-
def handle(args = {})
|
184
|
-
return Result.method_not_found(args[:missing_name]) if @method_name.nil?
|
185
|
-
|
186
|
-
result = nil
|
187
|
-
begin
|
188
|
-
request = Request.new args.merge(:method => @method_name,
|
189
|
-
:handler => @handler_proc)
|
190
|
-
retval = request.handle
|
191
|
-
result = Result.new(:result => retval)
|
192
|
-
|
193
|
-
rescue Exception => e
|
194
|
-
RJR::Logger.warn ["Exception Raised in #{method_name} handler #{e}"] + e.backtrace
|
195
|
-
result = Result.new(:error_code => -32000,
|
196
|
-
:error_msg => e.to_s,
|
197
|
-
:error_class => e.class)
|
90
|
+
# Method which request is for
|
91
|
+
attr_accessor :rjr_method
|
198
92
|
|
199
|
-
|
93
|
+
# Arguments be passed to method
|
94
|
+
attr_accessor :rjr_method_args
|
200
95
|
|
201
|
-
|
202
|
-
|
203
|
-
end
|
204
|
-
end
|
96
|
+
# Headers which came w/ request
|
97
|
+
attr_accessor :rjr_headers
|
205
98
|
|
206
|
-
#
|
207
|
-
|
208
|
-
# Request invoked
|
209
|
-
attr_reader :request
|
99
|
+
# Type of node which request came in on
|
100
|
+
attr_accessor :rjr_node_type
|
210
101
|
|
211
|
-
#
|
212
|
-
|
102
|
+
# ID of node which request came in on
|
103
|
+
attr_accessor :rjr_node_id
|
213
104
|
|
214
|
-
#
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
105
|
+
# RJR Request initializer
|
106
|
+
# @param [Hash] args options to set on request
|
107
|
+
# @option args [String] :rjr_method name of the method which request is for
|
108
|
+
# @option args [Array] :rjr_method_args array of arguments which to pass to the rpc method handler
|
109
|
+
# @option args [Hash] :rjr_headers hash of keys/values corresponding to optional headers received as part of of the request
|
110
|
+
# @option args [String] :rjr_client_ip ip address of client which invoked the request (if applicable)
|
111
|
+
# @option args [String] :rjr_client_port port of client which invoked the request (if applicable)
|
112
|
+
# @option args [RJR::Callback] :rjr_callback callback through which requests/notifications can be sent to remote node
|
113
|
+
# @option args [RJR::Node] :rjr_node rjr node which request was received on
|
114
|
+
# @option args [String] :rjr_node_id id of the rjr node which request was received on
|
115
|
+
# @option args [Symbol] :rjr_node_type type of the rjr node which request was received on
|
116
|
+
# @option args [Callable] :rjr_handler callable object registered to the specified method which to invoke request on with arguments
|
117
|
+
def initialize(args = {})
|
118
|
+
@rjr_method = args[:rjr_method] || args['rjr_method']
|
119
|
+
@rjr_method_args = args[:rjr_method_args] || args['rjr_method_args'] || []
|
120
|
+
@rjr_headers = args[:rjr_headers] || args['rjr_headers']
|
219
121
|
|
220
|
-
|
221
|
-
|
222
|
-
@stats ||= []
|
223
|
-
end
|
122
|
+
@rjr_client_ip = args[:rjr_client_ip]
|
123
|
+
@rjr_client_port = args[:rjr_client_port]
|
224
124
|
|
225
|
-
|
226
|
-
|
227
|
-
@
|
125
|
+
@rjr_callback = args[:rjr_callback]
|
126
|
+
@rjr_node = args[:rjr_node]
|
127
|
+
@rjr_node_id = args[:rjr_node_id] || args['rjr_node_id']
|
128
|
+
@rjr_node_type = args[:rjr_node_type] || args['rjr_node_type']
|
129
|
+
|
130
|
+
@rjr_handler = args[:rjr_handler]
|
131
|
+
|
132
|
+
@result = nil
|
228
133
|
end
|
229
134
|
|
230
|
-
#
|
231
|
-
|
232
|
-
|
233
|
-
@
|
234
|
-
|
135
|
+
# Invoke the request by calling the registered handler with the registered
|
136
|
+
# method parameters in the local scope
|
137
|
+
def handle
|
138
|
+
RJR::Logger.info "Dispatching '#{@rjr_method}' request with parameters (#{@rjr_method_args.join(',')}) on #{@rjr_node_type}-node(#{@rjr_node_id})"
|
139
|
+
retval = instance_exec(*@rjr_method_args, &@rjr_handler)
|
140
|
+
RJR::Logger.info "#{@rjr_method} request with parameters (#{@rjr_method_args.join(',')}) returning #{retval}"
|
141
|
+
return retval
|
235
142
|
end
|
236
143
|
|
237
|
-
# Convert
|
144
|
+
# Convert request to json representation and return it
|
238
145
|
def to_json(*a)
|
239
146
|
{
|
240
147
|
'json_class' => self.class.name,
|
241
148
|
'data' =>
|
242
|
-
{:request => {:
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
:result
|
249
|
-
|
250
|
-
|
251
|
-
|
149
|
+
{:request => { :rjr_method => @rjr_method,
|
150
|
+
:rjr_method_args => @rjr_method_args,
|
151
|
+
:rjr_headers => @rjr_headers,
|
152
|
+
:rjr_node_type => @rjr_node_type,
|
153
|
+
:rjr_node_id => @rjr_node_id },
|
154
|
+
|
155
|
+
:result => { :result => @result.result,
|
156
|
+
:error_code => @result.error_code,
|
157
|
+
:error_msg => @result.error_msg,
|
158
|
+
:error_class => @result.error_class } }
|
252
159
|
}.to_json(*a)
|
253
160
|
end
|
254
161
|
|
255
|
-
# Create new
|
162
|
+
# Create new request from json representation
|
256
163
|
def self.json_create(o)
|
257
|
-
|
258
|
-
|
164
|
+
result = Result.new(o['data']['result'])
|
165
|
+
request = Request.new(o['data']['request'])
|
166
|
+
request.result = result
|
167
|
+
return request
|
259
168
|
end
|
169
|
+
|
260
170
|
end
|
261
171
|
|
262
|
-
# Primary RJR JSON-RPC method dispatcher
|
263
|
-
#
|
264
|
-
# Provides class methods which to register global handlers to json-rpc methods and
|
265
|
-
# to handle requests and responses.
|
172
|
+
# Primary RJR JSON-RPC method dispatcher.
|
266
173
|
class Dispatcher
|
267
|
-
#
|
268
|
-
|
269
|
-
@@handlers = {}
|
270
|
-
end
|
174
|
+
# Registered json-rpc request signatures and corresponding handlers
|
175
|
+
attr_reader :handlers
|
271
176
|
|
272
|
-
#
|
273
|
-
|
274
|
-
# *WARNING* Do not invoke 'return' in registered handlers as these are blocks and *not* lambdas
|
275
|
-
# (see {http://stackoverflow.com/questions/626/when-to-use-lambda-when-to-use-proc-new Ruby Lambdas vs Procs})
|
276
|
-
#
|
277
|
-
# If specifying a single method name pass in a string, else pass in an array of strings.
|
278
|
-
# The block argument will be used as the method handler and will be invoked when
|
279
|
-
# json-rpc requests are received corresponding to the method name(s)
|
280
|
-
# @param [String,Array<String>] method_names one or more string method names
|
281
|
-
# @param [Hash] args options to initialize handler with, current unused
|
282
|
-
# @param [Callable] handler block to invoke when json-rpc requests to method are received
|
283
|
-
#
|
284
|
-
# @example
|
285
|
-
# RJR::Dispatcher.add_handler("hello_world") {
|
286
|
-
# "hello world"
|
287
|
-
# }
|
288
|
-
#
|
289
|
-
# RJR::Dispatcher.add_handler(["echo", "ECHO"]) { |val|
|
290
|
-
# val
|
291
|
-
# }
|
292
|
-
#
|
293
|
-
# # TODO define yard macros to construct documentation for a rjr based api
|
294
|
-
# from calls to add_handler
|
295
|
-
def self.add_handler(method_names, args = {}, &handler)
|
296
|
-
method_names = Array(method_names) unless method_names.is_a?(Array)
|
297
|
-
@@handlers ||= {}
|
298
|
-
method_names.each { |method_name|
|
299
|
-
# TODO support registering multiple handlers per method? (and in dispatch_request below)
|
300
|
-
@@handlers[method_name] = Handler.new args.merge(:method => method_name,
|
301
|
-
:handler => handler)
|
302
|
-
}
|
303
|
-
end
|
177
|
+
# Requests which have been dispatched
|
178
|
+
def requests ; @requests_lock.synchronize { Array.new(@requests) } ; end
|
304
179
|
|
305
|
-
#
|
306
|
-
def
|
307
|
-
|
180
|
+
# RJR::Dispatcher intializer
|
181
|
+
def initialize
|
182
|
+
@handlers = Hash.new()
|
183
|
+
|
184
|
+
@requests_lock = Mutex.new
|
185
|
+
@requests = []
|
308
186
|
end
|
309
187
|
|
310
|
-
#
|
311
|
-
|
312
|
-
|
313
|
-
|
188
|
+
# Loads module from fs and adds handlers defined there
|
189
|
+
#
|
190
|
+
# Assumes module includes a 'dispatch_<module_name>' method
|
191
|
+
# which accepts a dispatcher and defines handlers on it.
|
192
|
+
#
|
193
|
+
# @param [String] name location which to load module(s) from, may be
|
194
|
+
# a file, directory, or path specification (dirs seperated with ':')
|
195
|
+
# @return self
|
196
|
+
def add_module(name)
|
197
|
+
name.split(':').each { |p|
|
198
|
+
if File.directory?(p)
|
199
|
+
# TODO also .so files? allow user to specify suffix
|
200
|
+
Dir.glob(File.join(p, '*.rb')).all? { |m|
|
201
|
+
require m
|
202
|
+
}
|
203
|
+
|
204
|
+
else
|
205
|
+
require p
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
m = p.split(File::SEPARATOR).last
|
210
|
+
method("dispatch_#{m}".intern).call(self)
|
211
|
+
}
|
212
|
+
self
|
314
213
|
end
|
214
|
+
alias :add_modules :add_module
|
315
215
|
|
316
|
-
#
|
317
|
-
|
318
|
-
|
216
|
+
# Register json-rpc handler with dispatcher
|
217
|
+
#
|
218
|
+
# @param [String] signature request signature to match
|
219
|
+
# @param [Callable] callable callable object which to bind to signature
|
220
|
+
# @param [Callable] &bl block parameter will be set to callback if specified
|
221
|
+
# @return self
|
222
|
+
def handle(signature, callback = nil, &bl)
|
223
|
+
if signature.is_a?(Array)
|
224
|
+
signature.each { |s| handle(s, callback, &bl) }
|
225
|
+
return
|
226
|
+
end
|
227
|
+
@handlers[signature] = callback unless callback.nil?
|
228
|
+
@handlers[signature] = bl unless bl.nil?
|
229
|
+
self
|
319
230
|
end
|
320
231
|
|
321
|
-
#
|
322
|
-
#
|
323
|
-
|
324
|
-
|
325
|
-
|
232
|
+
# Dispatch received request. (used internally by nodes).
|
233
|
+
#
|
234
|
+
# Arguments should include :rjr_method and other parameters
|
235
|
+
# required to construct a valid Request instance
|
236
|
+
def dispatch(args = {})
|
237
|
+
# currently we just match method name against signature
|
238
|
+
# TODO if signature if a regex, match against method name
|
239
|
+
# or if callable, invoke it w/ request checking boolean return value for match
|
240
|
+
# TODO not using concurrent access portection, assumes all handlers are registered
|
241
|
+
# before first dispatch occurs
|
242
|
+
handler = @handlers[args[:rjr_method]]
|
243
|
+
|
244
|
+
return Result.method_not_found(args[:rjr_method]) if handler.nil?
|
245
|
+
|
246
|
+
# TODO compare arity of handler to number of method_args passed in
|
247
|
+
request = Request.new args.merge(:rjr_handler => handler)
|
248
|
+
|
249
|
+
begin
|
250
|
+
retval = request.handle
|
251
|
+
request.result = Result.new(:result => retval)
|
252
|
+
|
253
|
+
rescue Exception => e
|
254
|
+
RJR::Logger.warn ["Exception Raised in #{args[:rjr_method]} handler #{e}"] +
|
255
|
+
e.backtrace
|
256
|
+
request.result =
|
257
|
+
Result.new(:error_code => -32000,
|
258
|
+
:error_msg => e.to_s,
|
259
|
+
:error_class => e.class)
|
326
260
|
|
327
|
-
if handler.nil?
|
328
|
-
@@generic_handler ||= Handler.new :method => nil
|
329
|
-
return @@generic_handler.handle(args.merge(:missing_name => method_name))
|
330
261
|
end
|
331
262
|
|
332
|
-
|
263
|
+
@requests_lock.synchronize { @requests << request }
|
264
|
+
return request.result
|
333
265
|
end
|
334
266
|
|
335
|
-
#
|
336
|
-
#
|
337
|
-
|
267
|
+
# Handle responses received from rjr requests. (used internally by nodes)
|
268
|
+
#
|
269
|
+
# Returns return-value of method handler or raises error
|
270
|
+
def handle_response(result)
|
338
271
|
unless result.success
|
339
272
|
#if result.error_class
|
340
273
|
# TODO needs to be constantized first (see TODO in lib/rjr/message)
|
@@ -345,7 +278,6 @@ class Dispatcher
|
|
345
278
|
end
|
346
279
|
return result.result
|
347
280
|
end
|
348
|
-
|
349
281
|
end
|
350
282
|
|
351
283
|
end # module RJR
|
data/lib/rjr/em_adapter.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
1
|
# EventMachine Adapter
|
2
2
|
#
|
3
|
-
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
3
|
+
# Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
require 'singleton'
|
7
7
|
require 'eventmachine'
|
8
8
|
|
9
|
-
|
9
|
+
module RJR
|
10
|
+
|
11
|
+
# EventMachine adapater interface, ties reactor
|
10
12
|
# lifecycle to an instance of this class.
|
11
|
-
|
12
|
-
|
13
|
-
class EMManager
|
13
|
+
class EMAdapter
|
14
|
+
include Singleton
|
14
15
|
|
15
16
|
# Run reactor in its own interally managed thread
|
16
17
|
attr_accessor :reactor_thread
|
17
18
|
|
18
|
-
#
|
19
|
+
# EMAdapter initializer
|
19
20
|
def initialize
|
20
21
|
@em_lock = Mutex.new
|
22
|
+
|
23
|
+
EventMachine.error_handler { |e|
|
24
|
+
puts "EventMachine raised critical error #{e} #{e.backtrace}"
|
25
|
+
# TODO dispatch to registered event handlers
|
26
|
+
}
|
21
27
|
end
|
22
28
|
|
23
29
|
# Start the eventmachine reactor thread if not running
|
@@ -33,78 +39,39 @@ class EMManager
|
|
33
39
|
# TODO option to autorestart the reactor on errors ?
|
34
40
|
puts "Critical exception #{e}\n#{e.backtrace.join("\n")}"
|
35
41
|
ensure
|
36
|
-
@reactor_thread = nil
|
42
|
+
@em_lock.synchronize { @reactor_thread = nil }
|
37
43
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# Schedule a new job to be run in event machine
|
44
|
-
# @param [Callable] bl callback to be invoked by eventmachine
|
45
|
-
def schedule(&bl)
|
46
|
-
EventMachine.schedule &bl
|
47
|
-
end
|
48
|
-
|
49
|
-
# Schedule a job to be run once after a specified interval in event machine
|
50
|
-
# @param [Integer] seconds int interval which to wait before invoking specified block
|
51
|
-
# @param [Callable] bl callback to be invoked by eventmachine
|
52
|
-
def add_timer(seconds, &bl)
|
53
|
-
EventMachine.add_timer(seconds, &bl)
|
54
|
-
end
|
55
|
-
|
56
|
-
# Schedule a block to be run periodically in event machine
|
57
|
-
# @param [Integer] seconds int interval which to invoke specified block
|
58
|
-
# @param [Callable] bl callback to be invoked by eventmachine
|
59
|
-
def add_periodic_timer(seconds, &bl)
|
60
|
-
EventMachine.add_periodic_timer(seconds, &bl)
|
44
|
+
} unless @reactor_thread
|
45
|
+
}
|
46
|
+
sleep 0.01 until EventMachine.reactor_running? # XXX hack but needed
|
47
|
+
self
|
61
48
|
end
|
62
49
|
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
50
|
+
# Halt the reactor if running
|
51
|
+
#
|
52
|
+
# @return self
|
53
|
+
def halt
|
54
|
+
self.stop_event_loop if self.reactor_running?
|
55
|
+
self
|
68
56
|
end
|
69
57
|
|
70
58
|
# Block until reactor thread is terminated
|
59
|
+
#
|
60
|
+
# @return self
|
71
61
|
def join
|
72
|
-
th =
|
73
|
-
@em_lock.synchronize{
|
74
|
-
th = @reactor_thread
|
75
|
-
}
|
62
|
+
th = @em_lock.synchronize{ @reactor_thread }
|
76
63
|
th.join unless th.nil?
|
64
|
+
self
|
77
65
|
end
|
78
66
|
|
79
|
-
#
|
80
|
-
|
67
|
+
# Delegates everything else directly to eventmachine
|
68
|
+
# (eg schedule, add_timer, add_periodic_timer,
|
69
|
+
# reactor_running?, stop_event_loop, etc)
|
70
|
+
def method_missing(method_id, *args, &bl)
|
81
71
|
@em_lock.synchronize{
|
82
|
-
EventMachine.
|
72
|
+
EventMachine.send method_id, *args, &bl
|
83
73
|
}
|
84
74
|
end
|
85
75
|
end
|
86
76
|
|
87
|
-
|
88
|
-
# Provides an interface which to access a shared EMManager
|
89
|
-
#
|
90
|
-
# EMManager operations may be invoked on this class after
|
91
|
-
# the 'init' method is called
|
92
|
-
#
|
93
|
-
# EMAdapter.init
|
94
|
-
# EMAdapter.start
|
95
|
-
class EMAdapter
|
96
|
-
# Initialize EM subsystem
|
97
|
-
def self.init
|
98
|
-
if @em_manager.nil?
|
99
|
-
@em_manager = EMManager.new
|
100
|
-
end
|
101
|
-
|
102
|
-
@em_manager.start
|
103
|
-
end
|
104
|
-
|
105
|
-
# Delegates all methods invoked on calls to EMManager
|
106
|
-
def self.method_missing(method_id, *args, &bl)
|
107
|
-
@em_manager.send method_id, *args, &bl
|
108
|
-
end
|
109
|
-
|
110
|
-
end
|
77
|
+
end # module RJR
|