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