rjr 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +144 -0
- data/Rakefile +5 -5
- data/bin/rjr-server +2 -1
- data/lib/rjr.rb +3 -0
- data/lib/rjr/amqp_node.rb +103 -52
- data/lib/rjr/common.rb +19 -3
- data/lib/rjr/dispatcher.rb +103 -7
- data/lib/rjr/errors.rb +5 -3
- data/lib/rjr/local_node.rb +38 -12
- data/lib/rjr/message.rb +56 -1
- data/lib/rjr/multi_node.rb +31 -4
- data/lib/rjr/node.rb +57 -8
- data/lib/rjr/tcp_node.rb +65 -11
- data/lib/rjr/thread_pool.rb +33 -9
- data/lib/rjr/web_node.rb +72 -20
- data/lib/rjr/ws_node.rb +54 -12
- metadata +35 -2
- data/README.rdoc +0 -73
data/lib/rjr/dispatcher.rb
CHANGED
@@ -1,25 +1,50 @@
|
|
1
1
|
# RJR Request / Response Dispatcher
|
2
2
|
#
|
3
|
+
# Representation of a json-rpc request, response and mechanisms which to
|
4
|
+
# register methods to handle requests and return responses
|
5
|
+
#
|
3
6
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
7
|
# Licensed under the Apache License, Version 2.0
|
5
8
|
|
6
|
-
# establish client connection w/ specified args and invoke block w/
|
7
|
-
# newly created client, returning it after block terminates
|
8
|
-
|
9
9
|
require 'rjr/common'
|
10
10
|
|
11
11
|
module RJR
|
12
12
|
|
13
|
+
# JSON-RPC request representation
|
13
14
|
class Request
|
15
|
+
# name of the method which request is for
|
14
16
|
attr_accessor :method
|
17
|
+
|
18
|
+
# array of arguments which to pass to the rpc method handler
|
15
19
|
attr_accessor :method_args
|
20
|
+
|
21
|
+
# hash of keys/values corresponding to optional headers received as part of of the request
|
16
22
|
attr_accessor :headers
|
23
|
+
|
24
|
+
# callback through which additional requests may be invoked
|
17
25
|
attr_accessor :rjr_callback
|
26
|
+
|
27
|
+
# type of the rjr node which request was received on
|
18
28
|
attr_accessor :rjr_node_type
|
29
|
+
|
30
|
+
# id of the rjr node which request was received on
|
19
31
|
attr_accessor :rjr_node_id
|
20
32
|
|
33
|
+
# callable object registered to the specified method which to invoke request on with arguments
|
21
34
|
attr_accessor :handler
|
22
35
|
|
36
|
+
# RJR request intializer
|
37
|
+
# @param [Hash] args options to set on request
|
38
|
+
# @option args [String] :method name of the method which request is for
|
39
|
+
# @option args [Array] :method_args array of arguments which to pass to the rpc method handler
|
40
|
+
# @option args [Hash] :headers hash of keys/values corresponding to optional headers received as part of of the request
|
41
|
+
# @option args [String] :client_ip ip address of client which invoked the request (if applicable)
|
42
|
+
# @option args [String] :client_port port of client which invoked the request (if applicable)
|
43
|
+
# @option args [RJR::Callback] :rjr_callback callback through which additional requests may be invoked
|
44
|
+
# @option args [RJR::Node] :rjr_node rjr node which request was received on
|
45
|
+
# @option args [String] :rjr_node_id id of the rjr node which request was received on
|
46
|
+
# @option args [Symbol] :rjr_node_type type of the rjr node which request was received on
|
47
|
+
# @option args [Callable] :handler callable object registered to the specified method which to invoke request on with arguments
|
23
48
|
def initialize(args = {})
|
24
49
|
@method = args[:method]
|
25
50
|
@method_args = args[:method_args]
|
@@ -33,6 +58,8 @@ class Request
|
|
33
58
|
@handler = args[:handler]
|
34
59
|
end
|
35
60
|
|
61
|
+
# Actually invoke the request by calling the registered handler with the specified
|
62
|
+
# method parameters in the local scope
|
36
63
|
def handle
|
37
64
|
RJR::Logger.info "Dispatching '#{@method}' request with parameters (#{@method_args.join(',')}) on #{@rjr_node_type}-node(#{@rjr_node_id})"
|
38
65
|
retval = instance_exec(*@method_args, &@handler)
|
@@ -41,14 +68,32 @@ class Request
|
|
41
68
|
end
|
42
69
|
end
|
43
70
|
|
71
|
+
# JSON-RPC result representation
|
44
72
|
class Result
|
73
|
+
# boolean indicating if request was successfully invoked
|
45
74
|
attr_accessor :success
|
75
|
+
|
76
|
+
# boolean indicating if request was not successfully invoked
|
46
77
|
attr_accessor :failed
|
78
|
+
|
79
|
+
# return value of the json-rpc call if successful
|
47
80
|
attr_accessor :result
|
81
|
+
|
82
|
+
# code corresponding to json-rpc error if problem occured during request invocation
|
48
83
|
attr_accessor :error_code
|
84
|
+
|
85
|
+
# message corresponding to json-rpc error if problem occured during request invocation
|
49
86
|
attr_accessor :error_msg
|
87
|
+
|
88
|
+
# class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
50
89
|
attr_accessor :error_class
|
51
90
|
|
91
|
+
# RJR result intializer
|
92
|
+
# @param [Hash] args options to set on result
|
93
|
+
# @option args [Object] :result result of json-rpc method handler if successfully returned
|
94
|
+
# @option args [Integer] :error_code code corresponding to json-rpc error if problem occured during request invocation
|
95
|
+
# @option args [String] :error_msg message corresponding to json-rpc error if problem occured during request invocation
|
96
|
+
# @option args [Class] :error_class class of error raised (if any) during request invocation (this is extra metadata beyond standard json-rpc)
|
52
97
|
def initialize(args = {})
|
53
98
|
@result = nil
|
54
99
|
@error_code = nil
|
@@ -70,6 +115,8 @@ class Result
|
|
70
115
|
end
|
71
116
|
end
|
72
117
|
|
118
|
+
# Compare Result against other result, returning true if both correspond
|
119
|
+
# to equivalent json-rpc results else false
|
73
120
|
def ==(other)
|
74
121
|
@success == other.success &&
|
75
122
|
@failed == other.failed &&
|
@@ -79,17 +126,20 @@ class Result
|
|
79
126
|
@error_class == other.error_class
|
80
127
|
end
|
81
128
|
|
129
|
+
# Convert Response to human consumable string
|
82
130
|
def to_s
|
83
131
|
"#{@success} #{@result} #{@error_code} #{@error_msg} #{@error_class}"
|
84
132
|
end
|
85
133
|
|
86
134
|
######### Specific request types
|
87
135
|
|
136
|
+
# JSON-RPC -32600 / Invalid Request
|
88
137
|
def self.invalid_request
|
89
138
|
return Result.new(:error_code => -32600,
|
90
139
|
:error_msg => ' Invalid Request')
|
91
140
|
end
|
92
141
|
|
142
|
+
# JSON-RPC -32602 / Method not found
|
93
143
|
def self.method_not_found(name)
|
94
144
|
return Result.new(:error_code => -32602,
|
95
145
|
:error_msg => "Method '#{name}' not found")
|
@@ -97,15 +147,33 @@ class Result
|
|
97
147
|
|
98
148
|
end
|
99
149
|
|
150
|
+
# Association between json-rpc method name and registered handler to
|
151
|
+
# be invoked on new requests.
|
152
|
+
#
|
153
|
+
# When invoked, creates new {RJR::Request} object with specified request
|
154
|
+
# params and uses it to invoke handler in its context. Formats and returns
|
155
|
+
# return of operation
|
100
156
|
class Handler
|
101
157
|
attr_accessor :method_name
|
102
158
|
attr_accessor :handler_proc
|
103
159
|
|
160
|
+
# RJR::Handler intializer
|
161
|
+
# @param [Hash] args options to set on handler
|
162
|
+
# @option args [String] :method name of json-rpc method to which handler is bound
|
163
|
+
# @option args [Callable] :handle callable object which to bind to method name
|
104
164
|
def initialize(args = {})
|
105
165
|
@method_name = args[:method]
|
106
166
|
@handler_proc = args[:handler]
|
107
167
|
end
|
108
168
|
|
169
|
+
# Handle new json-rpc request to registered method.
|
170
|
+
#
|
171
|
+
# Creates new {RJR::Request} with the local method name and handler and the
|
172
|
+
# arguments received as part of the request. Uses it to invoke handler and
|
173
|
+
# creates and returns new {RJR::Result} encapsulating the return value if
|
174
|
+
# successful or error code/message/class if not.
|
175
|
+
#
|
176
|
+
# If invalid method_name is specified returns a json-rpc 'Method not found'
|
109
177
|
def handle(args = {})
|
110
178
|
return Result.method_not_found(args[:missing_name]) if @method_name.nil?
|
111
179
|
|
@@ -126,13 +194,39 @@ class Handler
|
|
126
194
|
end
|
127
195
|
end
|
128
196
|
|
197
|
+
# Primary RJR JSON-RPC method dispatcher interface.
|
198
|
+
#
|
199
|
+
# Provides class methods which to register global handlers to json-rpc methods and
|
200
|
+
# to handle requests and responses.
|
129
201
|
class Dispatcher
|
130
|
-
#
|
202
|
+
# Clear all registered json-rpc handlers
|
131
203
|
def self.init_handlers
|
132
204
|
@@handlers = {}
|
133
205
|
end
|
134
206
|
|
135
|
-
#
|
207
|
+
# Register a handler for the specified method(s)
|
208
|
+
#
|
209
|
+
# *WARNING* Do not invoke 'return' in registered handlers as these are blocks and *not* lambdas
|
210
|
+
# (see {http://stackoverflow.com/questions/626/when-to-use-lambda-when-to-use-proc-new Ruby Lambdas vs Procs})
|
211
|
+
#
|
212
|
+
# If specifying a single method name pass in a string, else pass in an array of strings.
|
213
|
+
# The block argument will be used as the method handler and will be invoked when
|
214
|
+
# json-rpc requests are received corresponding to the method name(s)
|
215
|
+
# @param [String,Array<String>] method_names one or more string method names
|
216
|
+
# @param [Hash] args options to initialize handler with, current unused
|
217
|
+
# @param [Callable] handler block to invoke when json-rpc requests to method are received
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# RJR::Dispatcher.add_handler("hello_world") {
|
221
|
+
# "hello world"
|
222
|
+
# }
|
223
|
+
#
|
224
|
+
# RJR::Dispatcher.add_handler(["echo", "ECHO"]) { |val|
|
225
|
+
# val
|
226
|
+
# }
|
227
|
+
#
|
228
|
+
# # TODO define yard macros to construct documentation for a rjr based api
|
229
|
+
# from calls to add_handler
|
136
230
|
def self.add_handler(method_names, args = {}, &handler)
|
137
231
|
method_names = Array(method_names) unless method_names.is_a?(Array)
|
138
232
|
@@handlers ||= {}
|
@@ -142,7 +236,8 @@ class Dispatcher
|
|
142
236
|
}
|
143
237
|
end
|
144
238
|
|
145
|
-
# Helper to
|
239
|
+
# Helper used by RJR nodes to dispatch requests received via transports to
|
240
|
+
# registered handlers.
|
146
241
|
def self.dispatch_request(method_name, args = {})
|
147
242
|
@@handlers ||= {}
|
148
243
|
handler = @@handlers[method_name]
|
@@ -155,7 +250,8 @@ class Dispatcher
|
|
155
250
|
return handler.handle args
|
156
251
|
end
|
157
252
|
|
158
|
-
# Helper to handle
|
253
|
+
# Helper used by RJR nodes to handle responses received from rjr requests
|
254
|
+
# (returns return-value of method handler or raises error)
|
159
255
|
def self.handle_response(result)
|
160
256
|
unless result.success
|
161
257
|
#if result.error_class
|
data/lib/rjr/errors.rb
CHANGED
@@ -3,12 +3,14 @@
|
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
# establish client connection w/ specified args and invoke block w/
|
7
|
-
# newly created client, returning it after block terminates
|
8
|
-
|
9
6
|
module RJR
|
7
|
+
|
8
|
+
# The RJR::Errors module provides a mechanism to dynamically create errors
|
9
|
+
# on demand as they are needed. At some point this will go away / be replaced
|
10
|
+
# with a more rigidly / fixed defined error heirarchy.
|
10
11
|
module Errors
|
11
12
|
|
13
|
+
# Catches all Errors constants and define new RuntimeError subclass
|
12
14
|
def self.const_missing(error_name) # :nodoc:
|
13
15
|
if error_name.to_s =~ /Error\z/
|
14
16
|
const_set(error_name, Class.new(RuntimeError))
|
data/lib/rjr/local_node.rb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
1
|
# RJR Local Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests via local method calls
|
4
|
+
#
|
3
5
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
6
|
# Licensed under the Apache License, Version 2.0
|
5
7
|
|
6
|
-
# establish client connection w/ specified args and invoke block w/
|
7
|
-
# newly created client, returning it after block terminates
|
8
|
-
|
9
8
|
require 'rjr/node'
|
10
9
|
require 'rjr/message'
|
11
10
|
|
12
11
|
module RJR
|
13
12
|
|
14
|
-
# Local
|
15
|
-
# send data back to client via local handlers
|
13
|
+
# Local node callback interface, used to invoke local json-rpc method handlers
|
16
14
|
class LocalNodeCallback
|
15
|
+
# LocalNodeCallback initializer
|
16
|
+
# @param [Hash] args the options to create the local node callback with
|
17
|
+
# @option args [LocalNode] :node local node used to send/receive messages
|
17
18
|
def initialize(args = {})
|
18
19
|
@node = args[:node]
|
19
20
|
end
|
20
21
|
|
22
|
+
# Implementation of {RJR::NodeCallback#invoke}
|
21
23
|
def invoke(callback_method, *data)
|
22
24
|
# TODO any exceptions from handler will propagate here, surround w/ begin/rescue block
|
23
25
|
@node.invoke_request(callback_method, *data)
|
@@ -25,34 +27,58 @@ class LocalNodeCallback
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
# Local node definition,
|
29
|
-
# requests via local handlers
|
30
|
+
# Local node definition, implements the {RJR::Node} interface to
|
31
|
+
# listen for and invoke json-rpc requests via local handlers
|
32
|
+
#
|
33
|
+
# This is useful for situations in which you would like to invoke registered
|
34
|
+
# json-rpc handlers locally, enforcing the same constraints as
|
35
|
+
# you would on a json-rpc request coming in remotely.
|
36
|
+
#
|
37
|
+
# @example Listening for and dispatching json-rpc requests locally
|
38
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
39
|
+
# @rjr_node_type == :local ? "Hello superuser #{name}" : "Hello #{name}!"
|
40
|
+
# }
|
41
|
+
#
|
42
|
+
# # initialize node and invoke request
|
43
|
+
# node = RJR::LocalNode.new :node_id => 'node'
|
44
|
+
# node.invoke_request('hello', 'mo')
|
45
|
+
#
|
30
46
|
class LocalNode < RJR::Node
|
31
47
|
RJR_NODE_TYPE = :local
|
32
48
|
|
33
|
-
#
|
49
|
+
# allows clients to override the node type for the local node
|
34
50
|
attr_accessor :node_type
|
35
51
|
|
36
|
-
#
|
52
|
+
# LocalNode initializer
|
53
|
+
# @param [Hash] args the options to create the local node with
|
37
54
|
def initialize(args = {})
|
38
55
|
super(args)
|
39
56
|
@node_type = RJR_NODE_TYPE
|
40
57
|
end
|
41
58
|
|
42
59
|
# register connection event handler,
|
43
|
-
#
|
44
|
-
#
|
60
|
+
# *note* Until we support manual disconnections of the local node, we don't have to do anything here
|
61
|
+
#
|
62
|
+
# @param [:error, :close] event the event to register the handler for
|
63
|
+
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
64
|
+
# @yield [LocalNode] self is passed to each registered handler when event occurs
|
45
65
|
def on(event, &handler)
|
46
66
|
# TODO raise error (for the time being)?
|
47
67
|
end
|
48
68
|
|
49
69
|
# Instruct Node to start listening for and dispatching rpc requests
|
70
|
+
#
|
71
|
+
# Currently does nothing as method handlers can be invoked directly upon invoke_request
|
50
72
|
def listen
|
51
73
|
em_run do
|
52
74
|
end
|
53
75
|
end
|
54
76
|
|
55
|
-
# Instructs node to send rpc request, and wait for
|
77
|
+
# Instructs node to send rpc request, and wait for and return response
|
78
|
+
# @param [String] rpc_method json-rpc method to invoke on destination
|
79
|
+
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
80
|
+
# @return [Object] the json result retrieved from destination converted to a ruby object
|
81
|
+
# @raise [Exception] if the destination raises an exception, it will be converted to json and re-raised here
|
56
82
|
def invoke_request(rpc_method, *args)
|
57
83
|
0.upto(args.size).each { |i| args[i] = args[i].to_s if args[i].is_a?(Symbol) }
|
58
84
|
message = RequestMessage.new :method => rpc_method,
|
data/lib/rjr/message.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RJR Message
|
2
2
|
#
|
3
|
+
# Representations of json-rpc messages in accordance with the standard
|
4
|
+
#
|
3
5
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
6
|
# Licensed under the Apache License, Version 2.0
|
5
7
|
|
@@ -18,12 +20,34 @@ class RequestMessage
|
|
18
20
|
Array.new(16) {|x| rand(0xff) }
|
19
21
|
end
|
20
22
|
|
23
|
+
# Message string received from the source
|
21
24
|
attr_accessor :json_message
|
25
|
+
|
26
|
+
# Method source is invoking on the destination
|
22
27
|
attr_accessor :jr_method
|
28
|
+
|
29
|
+
# Arguments source is passing to destination method
|
23
30
|
attr_accessor :jr_args
|
31
|
+
|
32
|
+
# ID of the message in accordance w/ json-rpc specification
|
24
33
|
attr_accessor :msg_id
|
34
|
+
|
35
|
+
# Optional headers to add to json outside of standard json-rpc request
|
25
36
|
attr_accessor :headers
|
26
37
|
|
38
|
+
# RJR Request Message initializer
|
39
|
+
#
|
40
|
+
# This should be invoked with one of two argument sets. If creating a new message
|
41
|
+
# to send to the server, specify :method, :args, and :headers to include in the message
|
42
|
+
# (message id will be autogenerated). If handling an new request message sent from the
|
43
|
+
# client, simply specify :message and optionally any additional headers (they will
|
44
|
+
# be merged with the headers contained in the message)
|
45
|
+
#
|
46
|
+
# @param [Hash] args options to set on request
|
47
|
+
# @option args [String] :message json string received from sender
|
48
|
+
# @option args [Hash] :headers optional headers to set in request and subsequent messages
|
49
|
+
# @option args [String] :method method to invoke on server
|
50
|
+
# @option args [Array<Object>] :args to pass to server method, all must be convertable to/from json
|
27
51
|
def initialize(args = {})
|
28
52
|
if args.has_key?(:message)
|
29
53
|
begin
|
@@ -52,6 +76,10 @@ class RequestMessage
|
|
52
76
|
end
|
53
77
|
end
|
54
78
|
|
79
|
+
# Class helper to determine if the specified string is a valid json-rpc
|
80
|
+
# method request
|
81
|
+
# @param [String] message string message to check
|
82
|
+
# @return [true,false] indicating if message is request message
|
55
83
|
def self.is_request_message?(message)
|
56
84
|
begin
|
57
85
|
# TODO log error
|
@@ -61,6 +89,7 @@ class RequestMessage
|
|
61
89
|
end
|
62
90
|
end
|
63
91
|
|
92
|
+
# Convert request message to string json format
|
64
93
|
def to_s
|
65
94
|
request = { 'jsonrpc' => '2.0',
|
66
95
|
'method' => @jr_method,
|
@@ -72,13 +101,34 @@ class RequestMessage
|
|
72
101
|
|
73
102
|
end
|
74
103
|
|
75
|
-
# Message sent from server to client in response to request message
|
104
|
+
# Message sent from server to client in response to json-rpc request message
|
76
105
|
class ResponseMessage
|
106
|
+
# Message string received from the source
|
77
107
|
attr_accessor :json_message
|
108
|
+
|
109
|
+
# ID of the message in accordance w/ json-rpc specification
|
78
110
|
attr_accessor :msg_id
|
111
|
+
|
112
|
+
# Result encapsulated in the response message
|
113
|
+
# @see RJR::Result
|
79
114
|
attr_accessor :result
|
115
|
+
|
116
|
+
# Optional headers to add to json outside of standard json-rpc request
|
80
117
|
attr_accessor :headers
|
81
118
|
|
119
|
+
# ResponseMessage initializer
|
120
|
+
#
|
121
|
+
# This should be invoked with one of two argument sets. If creating a new message
|
122
|
+
# to send to the client, specify :id, :result, and :headers to include in the message.
|
123
|
+
# If handling an new request message sent from the client, simply specify :message
|
124
|
+
# and optionally any additional headers (they will be merged with the headers contained
|
125
|
+
# in the message)
|
126
|
+
#
|
127
|
+
# @param [Hash] args options to set on request
|
128
|
+
# @option args [String] :message json string received from sender
|
129
|
+
# @option args [Hash] :headers optional headers to set in request and subsequent messages
|
130
|
+
# @option args [String] :id id to set in response message, should be same as that in received message
|
131
|
+
# @option args [RJR::Result] :result result of json-rpc method invocation
|
82
132
|
def initialize(args = {})
|
83
133
|
if args.has_key?(:message)
|
84
134
|
response = JSON.parse(args[:message])
|
@@ -115,6 +165,10 @@ class ResponseMessage
|
|
115
165
|
|
116
166
|
end
|
117
167
|
|
168
|
+
# Class helper to determine if the specified string is a valid json-rpc
|
169
|
+
# method response
|
170
|
+
# @param [String] message string message to check
|
171
|
+
# @return [true,false] indicating if message is response message
|
118
172
|
def self.is_response_message?(message)
|
119
173
|
begin
|
120
174
|
json = JSON.parse(message)
|
@@ -125,6 +179,7 @@ class ResponseMessage
|
|
125
179
|
end
|
126
180
|
end
|
127
181
|
|
182
|
+
# Convert request message to string json format
|
128
183
|
def to_s
|
129
184
|
s = ''
|
130
185
|
if result.success
|
data/lib/rjr/multi_node.rb
CHANGED
@@ -1,30 +1,57 @@
|
|
1
1
|
# RJR MultiNode Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests over multiple protocols
|
4
|
+
#
|
3
5
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
6
|
# Licensed under the Apache License, Version 2.0
|
5
7
|
|
6
|
-
# establish client connection w/ specified args and invoke block w/
|
7
|
-
# newly created client, returning it after block terminates
|
8
|
-
|
9
8
|
require 'eventmachine'
|
10
9
|
require 'rjr/node'
|
11
10
|
require 'rjr/message'
|
12
11
|
|
13
12
|
module RJR
|
14
13
|
|
14
|
+
# Multiple node definition, allows a developer to easily multiplex transport
|
15
|
+
# mechanisms to serve JSON-RPC requests over.
|
16
|
+
#
|
17
|
+
# @example Listening for json-rpc requests over amqp, tcp, http, and websockets
|
18
|
+
# # register rjr dispatchers (see RJR::Dispatcher)
|
19
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
20
|
+
# # optionally use @rjr_node_type to handle different transport types
|
21
|
+
# "Hello #{name}!"
|
22
|
+
# }
|
23
|
+
#
|
24
|
+
# amqp_server = RJR::TCPNode.new :node_id => 'amqp_server', :broker => 'localhost'
|
25
|
+
# tcp_server = RJR::TCPNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '7777'
|
26
|
+
# web_server = RJR::WebNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '80'
|
27
|
+
# ws_server = RJR::WebNode.new :node_id => 'tcp_server', :host => 'localhost', :port => '8080'
|
28
|
+
#
|
29
|
+
# server = RJR::MultiNode.new :node_id => 'server',
|
30
|
+
# :nodes => [amqp_server, tcp_server, web_server, ws_server]
|
31
|
+
# server.listen
|
32
|
+
# server.join
|
33
|
+
#
|
34
|
+
# # invoke requests as you normally would via any protocol
|
35
|
+
#
|
15
36
|
class MultiNode < RJR::Node
|
16
|
-
#
|
37
|
+
# MultiNode initializer
|
38
|
+
# @param [Hash] args the options to create the tcp node with
|
39
|
+
# @option args [Array<RJR::Node>] :nodes array of nodes to use to listen to new requests on
|
17
40
|
def initialize(args = {})
|
18
41
|
super(args)
|
19
42
|
@nodes = args[:nodes]
|
20
43
|
end
|
21
44
|
|
45
|
+
# Add node to multinode
|
46
|
+
# @param [RJR::Node] node the node to add
|
22
47
|
def <<(node)
|
23
48
|
@nodes << node
|
24
49
|
end
|
25
50
|
|
26
51
|
|
27
52
|
# Instruct Node to start listening for and dispatching rpc requests
|
53
|
+
#
|
54
|
+
# Implementation of {RJR::Node#listen}
|
28
55
|
def listen
|
29
56
|
@nodes.each { |node|
|
30
57
|
node.listen
|