rjr 0.18.2 → 0.19.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 (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,43 @@
1
+ # RJR Node Callback
2
+ #
3
+ # Copyright (C) 2012-2014 Mohammed Morsi <mo@morsi.org>
4
+ # Licensed under the Apache License, Version 2.0
5
+
6
+ module RJR
7
+
8
+ # Node callback interface, used to invoke json-rpc
9
+ # methods against a remote node via node connection
10
+ # previously established
11
+ #
12
+ # After a node sends a json-rpc request to another,
13
+ # the either node may send additional requests to
14
+ # each other via the connection already established
15
+ # until it is closed on either end
16
+ class NodeCallback
17
+ attr_reader :node
18
+ attr_reader :connection
19
+
20
+ # NodeCallback initializer
21
+ # @param [Hash] args the options to create the node callback with
22
+ # @option args [node] :node node used to send messages
23
+ # @option args [connection] :connection connection to be used in
24
+ # channel selection
25
+ def initialize(args = {})
26
+ @node = args[:node]
27
+ @connection = args[:connection]
28
+ end
29
+
30
+ def notify(callback_method, *data)
31
+ # TODO throw error here ?
32
+ return unless node.persistent?
33
+
34
+ msg = Messages::Notification.new :method => callback_method,
35
+ :args => data,
36
+ :headers => @node.message_headers
37
+
38
+ # TODO surround w/ begin/rescue block,
39
+ # raise RJR::ConnectionError on socket errors
40
+ @node.send_msg msg.to_s, @connection
41
+ end
42
+ end # class NodeCallback
43
+ end # module RJR
@@ -20,7 +20,7 @@ RJR::Nodes::AMQP = RJR::Nodes::Missing
20
20
  else
21
21
  require 'thread'
22
22
  require 'rjr/node'
23
- require 'rjr/message'
23
+ require 'rjr/messages'
24
24
 
25
25
  module RJR
26
26
  module Nodes
@@ -52,6 +52,7 @@ module Nodes
52
52
  class AMQP < RJR::Node
53
53
  RJR_NODE_TYPE = :amqp
54
54
  PERSISTENT_NODE = true
55
+ INDIRECT_NODE = true
55
56
 
56
57
  private
57
58
 
@@ -126,7 +127,7 @@ class AMQP < RJR::Node
126
127
 
127
128
  # Publish a message using the amqp exchange
128
129
  #
129
- # Implementation of {RJR::Node#send_msg}
130
+ # Implementation of RJR::Node#send_msg
130
131
  def send_msg(msg, metadata, &on_publish)
131
132
  @amqp_lock.synchronize {
132
133
  #raise RJR::Errors::ConnectionError.new("client unreachable") if @disconnected
@@ -143,7 +144,7 @@ class AMQP < RJR::Node
143
144
 
144
145
  # Instruct Node to start listening for and dispatching rpc requests.
145
146
  #
146
- # Implementation of {RJR::Node#listen}
147
+ # Implementation of RJR::Node#listen
147
148
  def listen
148
149
  @@em.schedule do
149
150
  init_node {
@@ -155,7 +156,7 @@ class AMQP < RJR::Node
155
156
 
156
157
  # Instructs node to send rpc request, and wait for and return response.
157
158
  #
158
- # Implementation of {RJR::Node#invoke}
159
+ # Implementation of RJR::Node#invoke
159
160
  #
160
161
  # Do not invoke directly from em event loop or callback as will block the message
161
162
  # subscription used to receive responses
@@ -166,9 +167,9 @@ class AMQP < RJR::Node
166
167
  # @return [Object] the json result retrieved from destination converted to a ruby object
167
168
  # @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
168
169
  def invoke(routing_key, rpc_method, *args)
169
- message = RequestMessage.new :method => rpc_method,
170
- :args => args,
171
- :headers => @message_headers
170
+ message = Messages::Request.new :method => rpc_method,
171
+ :args => args,
172
+ :headers => @message_headers
172
173
  @@em.schedule do
173
174
  init_node {
174
175
  subscribe # begin listening for result
@@ -191,7 +192,7 @@ class AMQP < RJR::Node
191
192
 
192
193
  # Instructs node to send rpc notification (immadiately returns / no response is generated)
193
194
  #
194
- # Implementation of {RJR::Node#notify}
195
+ # Implementation of RJR::Node#notif}
195
196
  #
196
197
  # @param [String] routing_key destination queue to send request to
197
198
  # @param [String] rpc_method json-rpc method to invoke on destination
@@ -202,9 +203,9 @@ class AMQP < RJR::Node
202
203
  published_c = ConditionVariable.new
203
204
 
204
205
  invoked = false
205
- message = NotificationMessage.new :method => rpc_method,
206
- :args => args,
207
- :headers => @message_headers
206
+ message = Messages::Notification.new :method => rpc_method,
207
+ :args => args,
208
+ :headers => @message_headers
208
209
  @@em.schedule do
209
210
  init_node {
210
211
  send_msg(message.to_s, :routing_key => routing_key, :reply_to => @queue_name){
@@ -116,21 +116,21 @@ class Easy < RJR::Node
116
116
 
117
117
  # Send data using specified connection
118
118
  #
119
- # Implementation of {RJR::Node#send_msg}
119
+ # Implementation of RJR::Node#send_msg
120
120
  def send_msg(data, connection)
121
121
  # TODO
122
122
  end
123
123
 
124
124
  # Instruct Nodes to start listening for and dispatching rpc requests
125
125
  #
126
- # Implementation of {RJR::Node#listen}
126
+ # Implementation of RJR::Node#listen
127
127
  def listen
128
128
  @multi_node.listen
129
129
  end
130
130
 
131
131
  # Instructs node to send rpc request, and wait for and return response.
132
132
  #
133
- # Implementation of {RJR::Node#invoke}
133
+ # Implementation of RJR::Node#invoke
134
134
  #
135
135
  # @param [String] dst destination send request to
136
136
  # @param [String] rpc_method json-rpc method to invoke on destination
@@ -145,7 +145,7 @@ class Easy < RJR::Node
145
145
 
146
146
  # Instructs node to send rpc notification (immadiately returns / no response is generated)
147
147
  #
148
- # Implementation of {RJR::Node#notify}
148
+ # Implementation of RJR::Node#notify
149
149
  #
150
150
  # @param [String] dst destination to send notification to
151
151
  # @param [String] rpc_method json-rpc method to invoke on destination
@@ -6,7 +6,7 @@
6
6
  # Licensed under the Apache License, Version 2.0
7
7
 
8
8
  require 'rjr/node'
9
- require 'rjr/message'
9
+ require 'rjr/messages'
10
10
 
11
11
  module RJR
12
12
  module Nodes
@@ -38,6 +38,7 @@ module Nodes
38
38
  class Local < RJR::Node
39
39
  RJR_NODE_TYPE = :local
40
40
  PERSISTENT_NODE = true
41
+ INDIRECT_NODE = false
41
42
 
42
43
  # allows clients to override the node type for the local node
43
44
  attr_accessor :node_type
@@ -57,17 +58,17 @@ class Local < RJR::Node
57
58
  #
58
59
  # Simply dispatch local notification.
59
60
  #
60
- # Implementation of {RJR::Node#send_msg}
61
+ # Implementation of RJR::Node#send_msg
61
62
  def send_msg(msg, connection)
62
63
  # ignore response message
63
- unless ResponseMessage.is_response_message?(msg)
64
+ unless Messages::Response.is_response_message?(msg)
64
65
  launch_request(msg, true) # .join?
65
66
  end
66
67
  end
67
68
 
68
69
  # Instruct Nodes to start listening for and dispatching rpc requests
69
70
  #
70
- # Implementation of {RJR::Node#listen}
71
+ # Implementation of RJR::Node#listen
71
72
  def listen
72
73
  # do nothing
73
74
  self
@@ -87,7 +88,7 @@ class Local < RJR::Node
87
88
 
88
89
  # Instructs node to send rpc request, and wait for and return response
89
90
  #
90
- # Implementation of {RJR::Node#invoke}
91
+ # Implementation of RJR::Node#invoke
91
92
  #
92
93
  # If strictly confirming to other nodes, this would use event machine to launch
93
94
  # a thread pool job to dispatch request and block on result.
@@ -100,9 +101,9 @@ class Local < RJR::Node
100
101
  # @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
101
102
  def invoke(rpc_method, *args)
102
103
  0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
103
- message = RequestMessage.new(:method => rpc_method,
104
- :args => args,
105
- :headers => @message_headers)
104
+ message = Messages::Request.new(:method => rpc_method,
105
+ :args => args,
106
+ :headers => @message_headers)
106
107
  launch_request(message.to_s, false)
107
108
 
108
109
  # TODO optional timeout for response ?
@@ -116,7 +117,7 @@ class Local < RJR::Node
116
117
 
117
118
  # Instructs node to send rpc notification (immediately returns / no response is generated)
118
119
  #
119
- # Implementation of {RJR::Node#notify}
120
+ # Implementation of RJR::Node#notify
120
121
  #
121
122
  # Same performance comment as invoke_request above
122
123
  #
@@ -124,9 +125,9 @@ class Local < RJR::Node
124
125
  # @param [Array] args array of arguments to convert to json and invoke remote method wtih
125
126
  def notify(rpc_method, *args)
126
127
  0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
127
- message = NotificationMessage.new(:method => rpc_method,
128
- :args => args,
129
- :headers => @message_headers)
128
+ message = Messages::Notification.new(:method => rpc_method,
129
+ :args => args,
130
+ :headers => @message_headers)
130
131
  launch_request(message.to_s, true) #.join ?
131
132
  nil
132
133
  end
@@ -70,7 +70,7 @@ class Multi < RJR::Node
70
70
 
71
71
  # Instruct Node to start listening for and dispatching rpc requests
72
72
  #
73
- # Implementation of {RJR::Node#listen}
73
+ # Implementation of RJR::Node#listen
74
74
  def listen
75
75
  @nodes.each { |node|
76
76
  node.listen
@@ -10,7 +10,8 @@ require 'thread'
10
10
  require 'eventmachine'
11
11
 
12
12
  require 'rjr/node'
13
- require 'rjr/message'
13
+ require 'rjr/messages'
14
+ require 'rjr/util/json_parser'
14
15
 
15
16
  module RJR
16
17
  module Nodes
@@ -34,13 +35,13 @@ class TCPConnection < EventMachine::Connection
34
35
  @data = ""
35
36
  end
36
37
 
37
- # {EventMachine::Connection#receive_data} callback, handle request / response messages
38
+ # EventMachine::Connection#receive_data callback, handle request / response messages
38
39
  def receive_data(data)
39
40
  # a large json-rpc message may be split over multiple packets
40
41
  # (invocations of receive_data)
41
42
  # and multiple messages may be concatinated into one packet
42
43
  @data += data
43
- while extracted = MessageUtil.retrieve_json(@data)
44
+ while extracted = JSONParser.extract_json_from(@data)
44
45
  msg, @data = *extracted
45
46
  @rjr_node.send(:handle_message, msg, self) # XXX private method
46
47
  end
@@ -80,6 +81,7 @@ end
80
81
  class TCP < RJR::Node
81
82
  RJR_NODE_TYPE = :tcp
82
83
  PERSISTENT_NODE = true
84
+ INDIRECT_NODE = false
83
85
 
84
86
  attr_accessor :connections
85
87
 
@@ -123,14 +125,14 @@ class TCP < RJR::Node
123
125
 
124
126
  # Send data using specified connection
125
127
  #
126
- # Implementation of {RJR::Node#send_msg}
128
+ # Implementation of RJR::Node#send_msg
127
129
  def send_msg(data, connection)
128
130
  connection.send_msg(data)
129
131
  end
130
132
 
131
133
  # Instruct Node to start listening for and dispatching rpc requests
132
134
  #
133
- # Implementation of {RJR::Node#listen}
135
+ # Implementation of RJR::Node#listen
134
136
  def listen
135
137
  @@em.schedule {
136
138
  @@em.start_server @host, @port, TCPConnection, { :rjr_node => self }
@@ -140,7 +142,7 @@ class TCP < RJR::Node
140
142
 
141
143
  # Instructs node to send rpc request, and wait for / return response.
142
144
  #
143
- # Implementation of {RJR::Node#invoke}
145
+ # Implementation of RJR::Node#invoke
144
146
  #
145
147
  # Do not invoke directly from em event loop or callback as will block the message
146
148
  # subscription used to receive responses
@@ -153,9 +155,9 @@ class TCP < RJR::Node
153
155
  uri = URI.parse(uri)
154
156
  host,port = uri.host, uri.port
155
157
 
156
- message = RequestMessage.new :method => rpc_method,
157
- :args => args,
158
- :headers => @message_headers
158
+ message = Messages::Request.new :method => rpc_method,
159
+ :args => args,
160
+ :headers => @message_headers
159
161
  connection = nil
160
162
  @@em.schedule {
161
163
  init_client(:host => host, :port => port,
@@ -176,7 +178,7 @@ class TCP < RJR::Node
176
178
 
177
179
  # Instructs node to send rpc notification (immadiately returns / no response is generated)
178
180
  #
179
- # Implementation of {RJR::Node#notify}
181
+ # Implementation of RJR::Node#notify
180
182
  #
181
183
  # @param [String] uri location of node to send notification to, should be
182
184
  # in format of jsonrpc://hostname:port
@@ -192,9 +194,9 @@ class TCP < RJR::Node
192
194
 
193
195
  invoked = false
194
196
  conn = nil
195
- message = NotificationMessage.new :method => rpc_method,
196
- :args => args,
197
- :headers => @message_headers
197
+ message = Messages::Notification.new :method => rpc_method,
198
+ :args => args,
199
+ :headers => @message_headers
198
200
  @@em.schedule {
199
201
  init_client(:host => host, :port => port,
200
202
  :rjr_node => self) { |c|
@@ -26,14 +26,14 @@ class Template < RJR::Node
26
26
 
27
27
  # Send data using specified connection
28
28
  #
29
- # Implementation of {RJR::Node#send_msg}
29
+ # Implementation of RJR::Node#send_msg
30
30
  def send_msg(data, connection)
31
31
  # TODO
32
32
  end
33
33
 
34
34
  # Instruct Node to start listening for and dispatching rpc requests
35
35
  #
36
- # Implementation of {RJR::Node#listen}
36
+ # Implementation of RJR::Node#listen
37
37
  def listen
38
38
  # TODO
39
39
  self
@@ -41,7 +41,7 @@ class Template < RJR::Node
41
41
 
42
42
  # Instructs node to send rpc request, and wait for / return response.
43
43
  #
44
- # Implementation of {RJR::Node#invoke}
44
+ # Implementation of RJR::Node#invoke
45
45
  # @param [String] optional_destination if the transport requires it, param
46
46
  # to specify the target of this request, if not remove this param
47
47
  # @param [String] rpc_method json-rpc method to invoke on destination
@@ -52,7 +52,7 @@ class Template < RJR::Node
52
52
 
53
53
  # Instructs node to send rpc notification (immadiately returns / no response is generated)
54
54
  #
55
- # Implementation of {RJR::Node#notify}
55
+ # Implementation of RJR::Node#notify
56
56
  # @param [String] optional_destination if the transport requires it, param
57
57
  # to specify the target of this request, if not remove this param
58
58
  # @param [String] rpc_method json-rpc method to invoke on destination
@@ -13,7 +13,8 @@ require 'thread'
13
13
  require 'eventmachine'
14
14
 
15
15
  require 'rjr/node'
16
- require 'rjr/message'
16
+ require 'rjr/messages'
17
+ require 'rjr/util/json_parser'
17
18
 
18
19
  module RJR
19
20
  module Nodes
@@ -35,13 +36,13 @@ class UnixConnection < EventMachine::Connection
35
36
  @data = ""
36
37
  end
37
38
 
38
- # {EventMachine::Connection#receive_data} callback, handle request / response messages
39
+ # EventMachine::Connection#receive_data callback, handle request / response messages
39
40
  def receive_data(data)
40
41
  # a large json-rpc message may be split over multiple packets
41
42
  # (invocations of receive_data)
42
43
  # and multiple messages may be concatinated into one packet
43
44
  @data += data
44
- while extracted = MessageUtil.retrieve_json(@data)
45
+ while extracted = JSONParser.extract_json_from(@data)
45
46
  msg, @data = *extracted
46
47
  @rjr_node.send(:handle_message, msg, self) # XXX private method
47
48
  end
@@ -65,6 +66,7 @@ end
65
66
  class Unix < RJR::Node
66
67
  RJR_NODE_TYPE = :unix
67
68
  PERSISTENT_NODE = true
69
+ INDIRECT_NODE = false
68
70
 
69
71
  attr_accessor :connections
70
72
 
@@ -106,14 +108,14 @@ class Unix < RJR::Node
106
108
 
107
109
  # Send data using specified connection
108
110
  #
109
- # Implementation of {RJR::Node#send_msg}
111
+ # Implementation of RJR::Node#send_msg
110
112
  def send_msg(data, connection)
111
113
  connection.send_msg(data)
112
114
  end
113
115
 
114
116
  # Instruct Node to start listening for and dispatching rpc requests
115
117
  #
116
- # Implementation of {RJR::Node#listen}
118
+ # Implementation of RJR::Node#listen
117
119
  def listen
118
120
  @@em.schedule {
119
121
  @@em.start_unix_domain_server @socketname, nil, UnixConnection, { :rjr_node => self }
@@ -123,7 +125,7 @@ class Unix < RJR::Node
123
125
 
124
126
  # Instructs node to send rpc request, and wait for / return response.
125
127
  #
126
- # Implementation of {RJR::Node#invoke}
128
+ # Implementation of RJR::Node#invoke
127
129
  #
128
130
  # Do not invoke directly from em event loop or callback as will block the message
129
131
  # subscription used to receive responses
@@ -133,9 +135,9 @@ class Unix < RJR::Node
133
135
  # @param [String] rpc_method json-rpc method to invoke on destination
134
136
  # @param [Array] args array of arguments to convert to json and invoke remote method wtih
135
137
  def invoke(socketname, rpc_method, *args)
136
- message = RequestMessage.new :method => rpc_method,
137
- :args => args,
138
- :headers => @message_headers
138
+ message = Messages::Request.new :method => rpc_method,
139
+ :args => args,
140
+ :headers => @message_headers
139
141
  connection = nil
140
142
  @@em.schedule {
141
143
  init_client(:socketname => socketname,
@@ -156,7 +158,7 @@ class Unix < RJR::Node
156
158
 
157
159
  # Instructs node to send rpc notification (immadiately returns / no response is generated)
158
160
  #
159
- # Implementation of {RJR::Node#notify}
161
+ # Implementation of RJR::Node#notify
160
162
  #
161
163
  # @param [String] socketname name of socket which
162
164
  # destination node is listening on
@@ -169,9 +171,9 @@ class Unix < RJR::Node
169
171
 
170
172
  invoked = false
171
173
  conn = nil
172
- message = NotificationMessage.new :method => rpc_method,
173
- :args => args,
174
- :headers => @message_headers
174
+ message = Messages::Notification.new :method => rpc_method,
175
+ :args => args,
176
+ :headers => @message_headers
175
177
  @@em.schedule {
176
178
  init_client(:socketname => socketname,
177
179
  :rjr_node => self) { |c|