rjr 0.12.2 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/README.md +49 -36
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +11 -9
  4. data/bin/rjr-server +12 -10
  5. data/examples/amqp.rb +29 -0
  6. data/examples/client.rb +32 -0
  7. data/examples/complete.rb +36 -0
  8. data/examples/local.rb +29 -0
  9. data/examples/server.rb +26 -0
  10. data/examples/tcp.rb +29 -0
  11. data/examples/web.rb +22 -0
  12. data/examples/ws.rb +29 -0
  13. data/lib/rjr/common.rb +7 -12
  14. data/lib/rjr/dispatcher.rb +171 -239
  15. data/lib/rjr/em_adapter.rb +33 -66
  16. data/lib/rjr/message.rb +43 -12
  17. data/lib/rjr/node.rb +197 -103
  18. data/lib/rjr/nodes/amqp.rb +216 -0
  19. data/lib/rjr/nodes/easy.rb +159 -0
  20. data/lib/rjr/nodes/local.rb +118 -0
  21. data/lib/rjr/{missing_node.rb → nodes/missing.rb} +4 -2
  22. data/lib/rjr/nodes/multi.rb +79 -0
  23. data/lib/rjr/nodes/tcp.rb +211 -0
  24. data/lib/rjr/nodes/web.rb +197 -0
  25. data/lib/rjr/nodes/ws.rb +187 -0
  26. data/lib/rjr/stats.rb +70 -0
  27. data/lib/rjr/thread_pool.rb +178 -123
  28. data/site/index.html +45 -0
  29. data/site/jquery-latest.js +9404 -0
  30. data/site/jrw.js +297 -0
  31. data/site/json.js +199 -0
  32. data/specs/dispatcher_spec.rb +244 -198
  33. data/specs/em_adapter_spec.rb +52 -80
  34. data/specs/message_spec.rb +223 -197
  35. data/specs/node_spec.rb +67 -163
  36. data/specs/nodes/amqp_spec.rb +82 -0
  37. data/specs/nodes/easy_spec.rb +13 -0
  38. data/specs/nodes/local_spec.rb +72 -0
  39. data/specs/nodes/multi_spec.rb +65 -0
  40. data/specs/nodes/tcp_spec.rb +75 -0
  41. data/specs/nodes/web_spec.rb +77 -0
  42. data/specs/nodes/ws_spec.rb +78 -0
  43. data/specs/stats_spec.rb +59 -0
  44. data/specs/thread_pool_spec.rb +44 -35
  45. metadata +40 -30
  46. data/lib/rjr/amqp_node.rb +0 -330
  47. data/lib/rjr/inspect.rb +0 -65
  48. data/lib/rjr/local_node.rb +0 -150
  49. data/lib/rjr/multi_node.rb +0 -65
  50. data/lib/rjr/tcp_node.rb +0 -323
  51. data/lib/rjr/thread_pool2.rb +0 -272
  52. data/lib/rjr/util.rb +0 -104
  53. data/lib/rjr/web_node.rb +0 -266
  54. data/lib/rjr/ws_node.rb +0 -289
  55. data/lib/rjr.rb +0 -16
  56. data/specs/amqp_node_spec.rb +0 -31
  57. data/specs/inspect_spec.rb +0 -60
  58. data/specs/local_node_spec.rb +0 -43
  59. data/specs/multi_node_spec.rb +0 -45
  60. data/specs/tcp_node_spec.rb +0 -33
  61. data/specs/util_spec.rb +0 -46
  62. data/specs/web_node_spec.rb +0 -32
  63. data/specs/ws_node_spec.rb +0 -32
  64. /data/lib/rjr/{tcp_node2.rb → nodes/tcp2.rb} +0 -0
  65. /data/lib/rjr/{udp_node.rb → nodes/udp.rb} +0 -0
@@ -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