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
@@ -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
- # boolean indicating if request was successfully invoked
16
+ # Boolean indicating if request was successfully invoked
80
17
  attr_accessor :success
81
18
 
82
- # boolean indicating if request was not successfully invoked
19
+ # Boolean indicating if request failed in some manner
83
20
  attr_accessor :failed
84
21
 
85
- # return value of the json-rpc call if successful
22
+ # Return value of the json-rpc call if successful
86
23
  attr_accessor :result
87
24
 
88
- # code corresponding to json-rpc error if problem occured during request invocation
25
+ # Code corresponding to json-rpc error if problem occured during request invocation
89
26
  attr_accessor :error_code
90
27
 
91
- # message corresponding to json-rpc error if problem occured during request invocation
28
+ # Message corresponding to json-rpc error if problem occured during request invocation
92
29
  attr_accessor :error_msg
93
30
 
94
- # class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
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 = nil
105
- @error_code = nil
106
- @error_message = nil
107
- @error_class = nil
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
- end
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 == other.success &&
128
- @failed == other.failed &&
129
- @result == other.result &&
130
- @error_code == other.error_code &&
131
- @error_msg == other.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
- # Association between json-rpc method name and registered handler to
157
- # be invoked on new requests.
81
+ # JSON-RPC request representation.
158
82
  #
159
- # When invoked, creates new {RJR::Request} object with specified request
160
- # params and uses it to invoke handler in its context. Formats and returns
161
- # return of operation
162
- class Handler
163
- attr_accessor :method_name
164
- attr_accessor :handler_proc
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
- # Handle new json-rpc request to registered method.
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
- end
93
+ # Arguments be passed to method
94
+ attr_accessor :rjr_method_args
200
95
 
201
- DispatcherStat << DispatcherStat.new(request, result)
202
- return result
203
- end
204
- end
96
+ # Headers which came w/ request
97
+ attr_accessor :rjr_headers
205
98
 
206
- # Tracks high level dispatcher states
207
- class DispatcherStat
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
- # Result returned
212
- attr_reader :result
102
+ # ID of node which request came in on
103
+ attr_accessor :rjr_node_id
213
104
 
214
- # Initialized the stat w/ the corresponding request/result
215
- def initialize(request, result)
216
- @request = request
217
- @result = result
218
- end
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
- # Global stats registry
221
- def self.stats
222
- @stats ||= []
223
- end
122
+ @rjr_client_ip = args[:rjr_client_ip]
123
+ @rjr_client_port = args[:rjr_client_port]
224
124
 
225
- # Reinit the stats registry
226
- def self.reset
227
- @stats = []
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
- # Add stat to the global registry
231
- def self.<<(s)
232
- @stats ||= []
233
- @stats << s
234
- self
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 stat to json representation and return it
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 => {:method => request.method,
243
- :method_args => request.method_args,
244
- :headers => request.headers,
245
- :rjr_node_type => request.rjr_node_type,
246
- :rjr_node_id => request.rjr_node_id
247
- },
248
- :result => {:result => result.result,
249
- :error_code => result.error_code,
250
- :error_msg => result.error_msg,
251
- :error_class => result.error_class} }
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 stat from json representation
162
+ # Create new request from json representation
256
163
  def self.json_create(o)
257
- stat = new(Request.new(o['data']['request']), Result.new(o['data']['result']))
258
- return stat
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 interface.
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
- # Clear all registered json-rpc handlers
268
- def self.init_handlers
269
- @@handlers = {}
270
- end
174
+ # Registered json-rpc request signatures and corresponding handlers
175
+ attr_reader :handlers
271
176
 
272
- # Register a handler for the specified method(s)
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
- # Clear registered method handlers
306
- def self.clear!
307
- @@handlers = {}
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
- # Return boolean indicating if handler for the specifed method has been registered
311
- def self.has_handler_for?(method_name)
312
- @@handlers ||= {}
313
- !@@handlers.find { |k,v| k == method_name }.nil?
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
- # Return the handler for the specified method
317
- def self.handler_for(method_name)
318
- @@handlers[method_name]
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
- # Helper used by RJR nodes to dispatch requests received via transports to
322
- # registered handlers.
323
- def self.dispatch_request(method_name, args = {})
324
- @@handlers ||= {}
325
- handler = @@handlers[method_name]
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
- return handler.handle args
263
+ @requests_lock.synchronize { @requests << request }
264
+ return request.result
333
265
  end
334
266
 
335
- # Helper used by RJR nodes to handle responses received from rjr requests
336
- # (returns return-value of method handler or raises error)
337
- def self.handle_response(result)
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
@@ -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
- # EventMachine wrapper / helper interface, ties reactor
9
+ module RJR
10
+
11
+ # EventMachine adapater interface, ties reactor
10
12
  # lifecycle to an instance of this class.
11
- #
12
- # TODO move to the RJR namespace
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
- # EMManager initializer
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
- } unless @reactor_thread
39
- }
40
- sleep 0.01 until EventMachine.reactor_running? # XXX hack but needed
41
- end
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
- # Return boolean indicating if event machine reactor is running
64
- def running?
65
- @em_lock.synchronize{
66
- EventMachine.reactor_running?
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 = nil
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
- # Terminate the event machine reactor under all conditions
80
- def halt
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.stop_event_loop
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