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,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|