rjr 0.18.2 → 0.19.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +2 -0
  3. data/bin/rjr-client +16 -9
  4. data/bin/rjr-server +2 -1
  5. data/examples/client.rb +21 -19
  6. data/examples/server.rb +1 -1
  7. data/examples/structured_server.rb +1 -0
  8. data/examples/tcp.rb +1 -0
  9. data/lib/rjr/common.rb +1 -226
  10. data/lib/rjr/core_ext.rb +63 -0
  11. data/lib/rjr/dispatcher.rb +75 -219
  12. data/lib/rjr/messages.rb +8 -0
  13. data/lib/rjr/messages/compressed.rb +264 -0
  14. data/lib/rjr/messages/notification.rb +95 -0
  15. data/lib/rjr/messages/request.rb +99 -0
  16. data/lib/rjr/messages/response.rb +128 -0
  17. data/lib/rjr/node.rb +100 -97
  18. data/lib/rjr/node_callback.rb +43 -0
  19. data/lib/rjr/nodes/amqp.rb +12 -11
  20. data/lib/rjr/nodes/easy.rb +4 -4
  21. data/lib/rjr/nodes/local.rb +13 -12
  22. data/lib/rjr/nodes/multi.rb +1 -1
  23. data/lib/rjr/nodes/tcp.rb +15 -13
  24. data/lib/rjr/nodes/template.rb +4 -4
  25. data/lib/rjr/nodes/unix.rb +15 -13
  26. data/lib/rjr/nodes/web.rb +15 -14
  27. data/lib/rjr/nodes/ws.rb +12 -11
  28. data/lib/rjr/request.rb +128 -0
  29. data/lib/rjr/result.rb +75 -0
  30. data/lib/rjr/util/args.rb +145 -0
  31. data/lib/rjr/{em_adapter.rb → util/em_adapter.rb} +0 -0
  32. data/lib/rjr/util/handles_methods.rb +115 -0
  33. data/lib/rjr/util/has_messages.rb +50 -0
  34. data/lib/rjr/{inspect.rb → util/inspect.rb} +1 -1
  35. data/lib/rjr/util/json_parser.rb +101 -0
  36. data/lib/rjr/util/logger.rb +128 -0
  37. data/lib/rjr/{thread_pool.rb → util/thread_pool.rb} +2 -0
  38. data/lib/rjr/version.rb +1 -1
  39. data/site/jrw.js +1 -1
  40. data/specs/args_spec.rb +144 -0
  41. data/specs/dispatcher_spec.rb +399 -211
  42. data/specs/em_adapter_spec.rb +31 -18
  43. data/specs/handles_methods_spec.rb +154 -0
  44. data/specs/has_messages_spec.rb +54 -0
  45. data/specs/inspect_spec.rb +1 -1
  46. data/specs/json_parser_spec.rb +169 -0
  47. data/specs/messages/notification_spec.rb +59 -0
  48. data/specs/messages/request_spec.rb +66 -0
  49. data/specs/messages/response_spec.rb +94 -0
  50. data/specs/node_callbacks_spec.rb +47 -0
  51. data/specs/node_spec.rb +465 -56
  52. data/specs/request_spec.rb +147 -0
  53. data/specs/result_spec.rb +144 -0
  54. data/specs/thread_pool_spec.rb +1 -1
  55. metadata +41 -11
  56. data/lib/rjr/errors.rb +0 -23
  57. data/lib/rjr/message.rb +0 -351
  58. data/lib/rjr/semaphore.rb +0 -58
  59. data/specs/message_spec.rb +0 -229
@@ -0,0 +1,95 @@
1
+ # RJR Notification Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/util/json_parser'
8
+
9
+ module RJR
10
+ module Messages
11
+
12
+ # Message sent to a JSON-RPC server to invoke a rpc method but
13
+ # indicate the result should _not_ be returned
14
+ class Notification
15
+ # Message string received from the source
16
+ attr_accessor :json_message
17
+
18
+ # Method source is invoking on the destination
19
+ attr_accessor :jr_method
20
+
21
+ # Arguments source is passing to destination method
22
+ attr_accessor :jr_args
23
+
24
+ # Optional headers to add to json outside of standard json-rpc request
25
+ attr_accessor :headers
26
+
27
+ # RJR Notification Message initializer
28
+ #
29
+ # No message id will be generated in accordance w/ the jsonrpc standard
30
+ #
31
+ # @param [Hash] args options to set on request
32
+ # @option args [String] :message json string received from sender
33
+ # @option args [Hash] :headers optional headers to set in request and subsequent messages
34
+ # @option args [String] :method method to invoke on server
35
+ # @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
36
+ def initialize(args = {})
37
+ parse_args(args)
38
+ end
39
+
40
+ private
41
+
42
+ def parse_args(args)
43
+ @jr_method = args[:method]
44
+ @jr_args = args[:args] || []
45
+ @headers = args[:headers] || {}
46
+
47
+ parse_message(args[:message]) if args.has_key?(:message)
48
+ end
49
+
50
+ def parse_message(message)
51
+ @json_message = message
52
+ notification = JSONParser.parse(@json_message)
53
+ @jr_method = notification['method']
54
+ @jr_args = notification['params']
55
+
56
+ parse_headers(notification)
57
+ end
58
+
59
+ def parse_headers(notification)
60
+ notification.keys.select { |k|
61
+ !['jsonrpc', 'method', 'params'].include?(k)
62
+ }.each { |k| @headers[k] = notification[k] }
63
+ end
64
+
65
+ public
66
+
67
+ # Class helper to determine if the specified string is a valid json-rpc
68
+ # notification
69
+ #
70
+ # @param [String] message string message to check
71
+ # @return [true,false] indicating if message is a notification message
72
+ def self.is_notification_message?(message)
73
+ begin
74
+ # FIXME log error
75
+ parsed = JSONParser.parse(message)
76
+ parsed.has_key?('method') && !parsed.has_key?('id')
77
+ rescue Exception => e
78
+ false
79
+ end
80
+ end
81
+
82
+ # Convert notification message to json
83
+ def to_json(*a)
84
+ {'jsonrpc' => '2.0',
85
+ 'method' => @jr_method,
86
+ 'params' => @jr_args}.merge(@headers).to_json(*a)
87
+ end
88
+
89
+ # Convert request to string format
90
+ def to_s
91
+ to_json.to_s
92
+ end
93
+ end # class Notification
94
+ end # module Messages
95
+ end # module RJR
@@ -0,0 +1,99 @@
1
+ # RJR Request Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/util/json_parser'
8
+
9
+ module RJR
10
+ module Messages
11
+
12
+ # Message sent from client to server to invoke a JSON-RPC method
13
+ class Request
14
+ # Message string received from the source
15
+ attr_accessor :json_message
16
+
17
+ # Method source is invoking on the destination
18
+ attr_accessor :jr_method
19
+
20
+ # Arguments source is passing to destination method
21
+ attr_accessor :jr_args
22
+
23
+ # ID of the message in accordance w/ json-rpc specification
24
+ attr_accessor :msg_id
25
+
26
+ # Optional headers to add to json outside of standard json-rpc request
27
+ attr_accessor :headers
28
+
29
+ # RJR Request Message initializer
30
+ #
31
+ # @param [Hash] args options to set on request
32
+ # @option args [String] :message json string received from sender
33
+ # @option args [Hash] :headers optional headers to set in request
34
+ # and subsequent messages
35
+ # @option args [String] :method method to invoke on server
36
+ # @option args [Array<Object>] :args to pass to server method, all
37
+ # must be convertable to/from json
38
+ def initialize(args = {})
39
+ parse_args(args)
40
+ end
41
+
42
+ private
43
+
44
+ def parse_args(args)
45
+ @jr_method = args[:method]
46
+ @jr_args = args[:args] || []
47
+ @headers = args[:headers] || {}
48
+ @msg_id = args[:id] || gen_uuid
49
+
50
+ parse_message(args[:message]) if args.has_key?(:message)
51
+ end
52
+
53
+ def parse_message(message)
54
+ @json_message = message
55
+ request = JSONParser.parse(@json_message)
56
+ @jr_method = request['method']
57
+ @jr_args = request['params']
58
+ @msg_id = request['id']
59
+
60
+ parse_headers(request)
61
+ end
62
+
63
+ def parse_headers(request)
64
+ request.keys.select { |k|
65
+ !['jsonrpc', 'id', 'method', 'params'].include?(k)
66
+ }.each { |k| @headers[k] = request[k] }
67
+ end
68
+
69
+ public
70
+
71
+ # Class helper to determine if the specified string is a valid json-rpc
72
+ # method request
73
+ # @param [String] message string message to check
74
+ # @return [true,false] indicating if message is request message
75
+ def self.is_request_message?(message)
76
+ begin
77
+ # FIXME log error
78
+ parsed = JSONParser.parse(message)
79
+ parsed.has_key?('method') && parsed.has_key?('id')
80
+ rescue Exception => e
81
+ false
82
+ end
83
+ end
84
+
85
+ # Convert request message to json
86
+ def to_json(*a)
87
+ {'jsonrpc' => '2.0',
88
+ 'id' => @msg_id,
89
+ 'method' => @jr_method,
90
+ 'params' => @jr_args}.merge(@headers).to_json(*a)
91
+ end
92
+
93
+ # Convert request to string format
94
+ def to_s
95
+ to_json.to_s
96
+ end
97
+ end # class Request
98
+ end # module Messages
99
+ end # module RJR
@@ -0,0 +1,128 @@
1
+ # RJR Response Message
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ require 'json'
7
+ require 'rjr/result'
8
+ require 'rjr/util/json_parser'
9
+
10
+ module RJR
11
+ module Messages
12
+
13
+ # Message sent from server to client in response to a JSON-RPC request
14
+ class Response
15
+ # Message string received from the source
16
+ attr_accessor :json_message
17
+
18
+ # ID of the message in accordance w/ json-rpc specification
19
+ attr_accessor :msg_id
20
+
21
+ # Result encapsulated in the response message
22
+ # @see RJR::Result
23
+ attr_accessor :result
24
+
25
+ # Optional headers to add to json outside of standard json-rpc request
26
+ attr_accessor :headers
27
+
28
+ # ResponseMessage initializer
29
+ #
30
+ # @param [Hash] args options to set on request
31
+ # @option args [String] :message json string received from sender
32
+ # @option args [Hash] :headers optional headers to set in request
33
+ # and subsequent messages
34
+ # @option args [String] :id id to set in response message, should
35
+ # be same as that in received message
36
+ # @option args [RJR::Result] :result result of json-rpc method invocation
37
+ def initialize(args = {})
38
+ parse_args(args)
39
+ end
40
+
41
+ private
42
+
43
+ def parse_args(args)
44
+ @msg_id = args[:id]
45
+ @result = args[:result]
46
+ @headers = args[:headers] || {}
47
+
48
+ parse_message(args[:message]) if args.has_key?(:message)
49
+ end
50
+
51
+ def parse_message(message)
52
+ @json_message = message
53
+ response = JSONParser.parse(@json_message)
54
+ @msg_id = response['id']
55
+
56
+ parse_result(response)
57
+ parse_headers(response)
58
+ end
59
+
60
+ def parse_result(response)
61
+ @result = Result.new
62
+ @result.success = response.has_key?('result')
63
+ @result.failed = !@result.success
64
+
65
+ if @result.success
66
+ @result.result = response['result']
67
+
68
+ elsif response.has_key?('error')
69
+ @result.error_code = response['error']['code']
70
+ @result.error_msg = response['error']['message']
71
+
72
+ # TODO can we safely constantize this ?
73
+ @result.error_class = response['error']['class']
74
+ end
75
+
76
+ @result
77
+ end
78
+
79
+ def parse_headers(request)
80
+ request.keys.select { |k|
81
+ !['jsonrpc', 'id', 'method', 'result', 'error'].include?(k)
82
+ }.each { |k| @headers[k] = request[k] }
83
+ end
84
+
85
+ public
86
+
87
+ # Class helper to determine if the specified string is a valid json-rpc
88
+ # method response
89
+ # @param [String] message string message to check
90
+ # @return [true,false] indicating if message is response message
91
+ def self.is_response_message?(message)
92
+ begin
93
+ # FIXME log error
94
+ json = JSONParser.parse(message)
95
+ json.has_key?('result') || json.has_key?('error')
96
+ rescue Exception => e
97
+ puts e.to_s
98
+ false
99
+ end
100
+ end
101
+
102
+ def success_json
103
+ {'result' => @result.result}
104
+ end
105
+
106
+ def error_json
107
+ {'error' => {'code' => @result.error_code,
108
+ 'message' => @result.error_msg,
109
+ 'class' => @result.error_class}}
110
+ end
111
+
112
+ # Convert request message to json
113
+ def to_json(*a)
114
+ result_json = @result.success ? success_json : error_json
115
+
116
+ {'jsonrpc' => '2.0',
117
+ 'id' => @msg_id}.merge(@headers).
118
+ merge(result_json).to_json(*a)
119
+ end
120
+
121
+ # Convert request to string format
122
+ def to_s
123
+ to_json.to_s
124
+ end
125
+
126
+ end # class Response
127
+ end # module Messages
128
+ end # module RJR
@@ -1,15 +1,14 @@
1
1
  # RJR Base Node Interface
2
2
  #
3
- # Copyright (C) 2012-2013 Mohammed Morsi <mo@morsi.org>
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
4
  # Licensed under the Apache License, Version 2.0
5
5
 
6
- require 'thread'
7
6
  require 'socket'
8
- require 'rjr/common'
9
- require 'rjr/message'
7
+ require 'rjr/messages'
10
8
  require 'rjr/dispatcher'
11
- require 'rjr/em_adapter'
12
- require 'rjr/thread_pool'
9
+ require 'rjr/util/em_adapter'
10
+ require 'rjr/util/thread_pool'
11
+ require 'rjr/node_callback'
13
12
 
14
13
  module RJR
15
14
 
@@ -21,7 +20,8 @@ module RJR
21
20
  # Each subclass should define
22
21
  # * RJR_NODE_TYPE - unique id of the transport
23
22
  # * listen method - begin listening for new requests and return
24
- # * send_message(msg, connection) - send message using the specified connection (transport dependent)
23
+ # * send_message(msg, connection) - send message using the specified connection
24
+ # (transport dependent)
25
25
  # * invoke - establish connection, send message, and wait for / return result
26
26
  # * notify - establish connection, send message, and immediately return
27
27
  #
@@ -44,12 +44,21 @@ class Node
44
44
  # Dispatcher to use to satisfy requests
45
45
  attr_accessor :dispatcher
46
46
 
47
+ # Handlers for various connection events
48
+ attr_reader :connection_event_handlers
49
+
47
50
  class <<self
48
51
  # Bool indiciting if this node is persistent
49
52
  def persistent?
50
53
  self.const_defined?(:PERSISTENT_NODE) &&
51
54
  self.const_get(:PERSISTENT_NODE)
52
55
  end
56
+
57
+ # Bool indiciting if this node is indirect
58
+ def indirect?
59
+ self.const_defined?(:INDIRECT_NODE) &&
60
+ self.const_get(:INDIRECT_NODE)
61
+ end
53
62
  end
54
63
 
55
64
  # Bool indicating if this node class is persistent
@@ -57,20 +66,39 @@ class Node
57
66
  self.class.persistent?
58
67
  end
59
68
 
69
+ # Bool indicating if this node class is indirect
70
+ def indirect?
71
+ self.class.indirect?
72
+ end
73
+
60
74
  # alias of RJR_NODE_TYPE
61
75
  def node_type
62
- self.class::RJR_NODE_TYPE
76
+ self.class.const_defined?(:RJR_NODE_TYPE) ?
77
+ self.class.const_get(:RJR_NODE_TYPE) : nil
78
+ end
79
+
80
+ def self.em
81
+ defined?(@@em) ? @@em : nil
82
+ end
83
+
84
+ def em
85
+ self.class.em
63
86
  end
64
87
 
65
- # XXX used by debugging / stats interface
66
- def self.em ; defined?(@@em) ? @@em : nil end
67
- def self.tp ; defined?(@@tp) ? @@tp : nil end
88
+ def self.tp
89
+ defined?(@@tp) ? @@tp : nil
90
+ end
91
+
92
+ def tp
93
+ self.class.tp
94
+ end
68
95
 
69
96
  # RJR::Node initializer
70
97
  #
71
98
  # @param [Hash] args options to set on request
72
99
  # @option args [String] :node_id unique id of the node
73
- # @option args [Hash<String,String>] :headers optional headers to set on all json-rpc messages
100
+ # @option args [Hash<String,String>] :headers optional headers to set
101
+ # on all json-rpc messages
74
102
  # @option args [Dispatcher] :dispatcher dispatcher to assign to the node
75
103
  def initialize(args = {})
76
104
  clear_event_handlers
@@ -86,28 +114,29 @@ class Node
86
114
  @@em ||= EMAdapter.new
87
115
 
88
116
  # will do nothing if already started
89
- @@tp.start
90
- @@em.start
117
+ tp.start
118
+ em.start
91
119
  end
92
120
 
93
- # Block until the eventmachine reactor and thread pool have both completed running
121
+ # Block until the eventmachine reactor and thread pool have both
122
+ # completed running.
94
123
  #
95
124
  # @return self
96
125
  def join
97
- @@tp.join
98
- @@em.join
126
+ tp.join
127
+ em.join
99
128
  self
100
129
  end
101
130
 
102
131
  # Immediately terminate the node
103
132
  #
104
- # *Warning* this does what it says it does. All running threads, and reactor
105
- # jobs are immediately killed
133
+ # *Warning* this does what it says it does. All running threads,
134
+ # and reactor jobs are immediately killed
106
135
  #
107
136
  # @return self
108
137
  def halt
109
- @@em.stop_event_loop
110
- @@tp.stop
138
+ em.stop_event_loop
139
+ tp.stop
111
140
  self
112
141
  end
113
142
 
@@ -119,36 +148,48 @@ class Node
119
148
 
120
149
  # Register connection event handler
121
150
  # @param [:error, :close] event the event to register the handler for
122
- # @param [Callable] handler block param to be added to array of handlers that are called when event occurs
151
+ # @param [Callable] handler block param to be added to array of handlers
152
+ # that are called when event occurs
123
153
  # @yield [Node] self is passed to each registered handler when event occurs
124
154
  def on(event, &handler)
125
- if @connection_event_handlers.keys.include?(event)
126
- @connection_event_handlers[event] << handler
127
- end
155
+ return unless @connection_event_handlers.keys.include?(event)
156
+ @connection_event_handlers[event] << handler
128
157
  end
129
158
 
130
159
  private
131
160
 
132
161
  # Internal helper, run connection event handlers for specified event
133
162
  def connection_event(event)
134
- if @connection_event_handlers.keys.include?(event)
135
- @connection_event_handlers[event].each { |h|
136
- h.call self
137
- }
138
- end
163
+ return unless @connection_event_handlers.keys.include?(event)
164
+ @connection_event_handlers[event].each { |h|
165
+ h.call self
166
+ }
139
167
  end
140
168
 
141
169
  ##################################################################
142
170
 
171
+ # Internal helper, extract client info from connection
172
+ def client_for(connection)
173
+ # skip if an indirect node type or local
174
+ return nil, nil if self.indirect? || self.node_type == :local
175
+
176
+ begin
177
+ return Socket.unpack_sockaddr_in(connection.get_peername)
178
+ rescue Exception=>e
179
+ end
180
+
181
+ return nil, nil
182
+ end
183
+
143
184
  # Internal helper, handle message received
144
185
  def handle_message(msg, connection = {})
145
- if RequestMessage.is_request_message?(msg)
146
- @@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
186
+ if Messages::Request.is_request_message?(msg)
187
+ tp << ThreadPoolJob.new(msg) { |m| handle_request(m, false, connection) }
147
188
 
148
- elsif NotificationMessage.is_notification_message?(msg)
149
- @@tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
189
+ elsif Messages::Notification.is_notification_message?(msg)
190
+ tp << ThreadPoolJob.new(msg) { |m| handle_request(m, true, connection) }
150
191
 
151
- elsif ResponseMessage.is_response_message?(msg)
192
+ elsif Messages::Response.is_response_message?(msg)
152
193
  handle_response(msg)
153
194
 
154
195
  end
@@ -158,39 +199,32 @@ class Node
158
199
  def handle_request(data, notification=false, connection={})
159
200
  # get client for the specified connection
160
201
  # TODO should grap port/ip immediately on connection and use that
161
- client_port,client_ip = nil,nil
162
- begin
163
- # XXX skip if an 'indirect' node type or local
164
- unless [:amqp, :local].include?(self.class::RJR_NODE_TYPE)
165
- client_port, client_ip =
166
- Socket.unpack_sockaddr_in(connection.get_peername)
167
- end
168
- rescue Exception=>e
169
- end
202
+ client_port,client_ip = client_for(connection)
170
203
 
171
204
  msg = notification ?
172
- NotificationMessage.new(:message => data,
173
- :headers => @message_headers) :
174
- RequestMessage.new(:message => data,
175
- :headers => @message_headers)
176
-
177
- result =
178
- @dispatcher.dispatch(:rjr_method => msg.jr_method,
179
- :rjr_method_args => msg.jr_args,
180
- :rjr_headers => msg.headers,
181
- :rjr_client_ip => client_ip,
182
- :rjr_client_port => client_port,
183
- :rjr_node => self,
184
- :rjr_node_id => @node_id,
185
- :rjr_node_type => self.class::RJR_NODE_TYPE,
186
- :rjr_callback =>
187
- NodeCallback.new(:node => self,
188
- :connection => connection))
205
+ Messages::Notification.new(:message => data,
206
+ :headers => @message_headers) :
207
+ Messages::Request.new(:message => data,
208
+ :headers => @message_headers)
209
+
210
+ callback = NodeCallback.new(:node => self,
211
+ :connection => connection)
212
+
213
+ result = @dispatcher.dispatch(:rjr_method => msg.jr_method,
214
+ :rjr_method_args => msg.jr_args,
215
+ :rjr_headers => msg.headers,
216
+ :rjr_client_ip => client_ip,
217
+ :rjr_client_port => client_port,
218
+ :rjr_node => self,
219
+ :rjr_node_id => node_id,
220
+ :rjr_node_type => self.node_type,
221
+ :rjr_callback => callback)
189
222
 
190
223
  unless notification
191
- response = ResponseMessage.new(:id => msg.msg_id,
192
- :result => result,
193
- :headers => msg.headers)
224
+ response = Messages::Response.new(:id => msg.msg_id,
225
+ :result => result,
226
+ :headers => msg.headers,
227
+ :request => msg)
194
228
  self.send_msg(response.to_s, connection)
195
229
  return response
196
230
  end
@@ -200,7 +234,8 @@ class Node
200
234
 
201
235
  # Internal helper, handle response message received
202
236
  def handle_response(data)
203
- msg = ResponseMessage.new(:message => data, :headers => self.message_headers)
237
+ msg = Messages::Response.new(:message => data,
238
+ :headers => self.message_headers)
204
239
  res = err = nil
205
240
  begin
206
241
  res = @dispatcher.handle_response(msg.result)
@@ -243,37 +278,5 @@ class Node
243
278
  end
244
279
  return res
245
280
  end
246
-
247
281
  end # class Node
248
-
249
- # Node callback interface, used to invoke json-rpc methods
250
- # against a remote node via node connection previously established
251
- #
252
- # After a node sends a json-rpc request to another, the either node may send
253
- # additional requests to each other via the connection already established until
254
- # it is closed on either end
255
- class NodeCallback
256
-
257
- # NodeCallback initializer
258
- # @param [Hash] args the options to create the node callback with
259
- # @option args [node] :node node used to send messages
260
- # @option args [connection] :connection connection to be used in channel selection
261
- def initialize(args = {})
262
- @node = args[:node]
263
- @connection = args[:connection]
264
- end
265
-
266
- def notify(callback_method, *data)
267
- # XXX return if node type does not support
268
- # pesistent conntections (throw err instead?)
269
- return if @node.class::RJR_NODE_TYPE == :web
270
-
271
- msg = NotificationMessage.new :method => callback_method,
272
- :args => data, :headers => @node.message_headers
273
-
274
- # TODO surround w/ begin/rescue block incase of socket errors / raise RJR::ConnectionError
275
- @node.send_msg msg.to_s, @connection
276
- end
277
- end
278
-
279
282
  end # module RJR