rjr 0.7.0 → 0.8.0
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.
- 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/node.rb
CHANGED
@@ -11,19 +11,51 @@ require 'rjr/thread_pool'
|
|
11
11
|
|
12
12
|
module RJR
|
13
13
|
|
14
|
-
#
|
15
|
-
#
|
14
|
+
# Base RJR Node interface. Nodes are the central transport mechanism of rjr,
|
15
|
+
# this class provides the core methods common among all transport types and
|
16
|
+
# mechanisms to start and run the eventmachine reactor which drives all requests.
|
17
|
+
#
|
18
|
+
# A subclass of RJR::Node should be defined for each transport that is supported,
|
19
|
+
# implementing the 'listen' operation to listen for new requests and 'invoke_request'
|
20
|
+
# to issue them.
|
16
21
|
class Node
|
17
|
-
|
22
|
+
class << self
|
23
|
+
# @!group Config options
|
24
|
+
|
25
|
+
# Default number of threads to instantiate in local worker pool
|
26
|
+
attr_accessor :default_threads
|
27
|
+
|
28
|
+
# Default timeout after which worker threads are killed
|
29
|
+
attr_accessor :default_timeout
|
30
|
+
|
31
|
+
# @!endgroup
|
32
|
+
end
|
33
|
+
|
34
|
+
# Unique string identifier of the node
|
18
35
|
attr_reader :node_id
|
19
36
|
|
20
|
-
#
|
37
|
+
# Attitional header fields to set on all
|
38
|
+
# requests and responses received and sent by node
|
21
39
|
attr_accessor :message_headers
|
22
40
|
|
41
|
+
# Nodes use internal thread pools to handle requests and free
|
42
|
+
# up the eventmachine reactor to continue processing requests
|
43
|
+
# @see ThreadPool
|
23
44
|
attr_reader :thread_pool
|
24
45
|
|
46
|
+
# RJR::Node initializer
|
47
|
+
# @param [Hash] args options to set on request
|
48
|
+
# @option args [String] :node_id unique id of the node *required*!!!
|
49
|
+
# @option args [Hash<String,String>] :headers optional headers to set on all json-rpc messages
|
50
|
+
# @option args [Integer] :threads number of handler to threads to instantiate in local worker pool
|
51
|
+
# @option args [Integer] :timeout timeout after which worker thread being run is killed
|
25
52
|
def initialize(args = {})
|
26
|
-
|
53
|
+
RJR::Node.default_threads ||= 15
|
54
|
+
RJR::Node.default_timeout ||= 5
|
55
|
+
|
56
|
+
@node_id = args[:node_id]
|
57
|
+
@num_threads = args[:threads] || RJR::Node.default_threads
|
58
|
+
@timeout = args[:timeout] || RJR::Node.default_timeout
|
27
59
|
|
28
60
|
@message_headers = {}
|
29
61
|
@message_headers.merge!(args[:headers]) if args.has_key?(:headers)
|
@@ -31,11 +63,23 @@ class Node
|
|
31
63
|
ObjectSpace.define_finalizer(self, self.class.finalize(self))
|
32
64
|
end
|
33
65
|
|
66
|
+
# Ruby ObjectSpace finalizer to ensure that node terminates all
|
67
|
+
# operations when object is destroyed
|
34
68
|
def self.finalize(node)
|
35
69
|
proc { node.halt ; node.join }
|
36
70
|
end
|
37
71
|
|
38
|
-
#
|
72
|
+
# Run a job in event machine.
|
73
|
+
#
|
74
|
+
# This will start the eventmachine reactor and thread pool if not already
|
75
|
+
# running, schedule the specified block to be run and immediately return.
|
76
|
+
#
|
77
|
+
# For use by subclasses to start listening and sending operations within
|
78
|
+
# the context of event machine.
|
79
|
+
#
|
80
|
+
# Keeps track of an internal counter of how many times this was invoked so
|
81
|
+
# a specific node can be shutdown / started up without affecting the
|
82
|
+
# eventmachine reactor (@see #stop)
|
39
83
|
def em_run(&bl)
|
40
84
|
@@em_jobs ||= 0
|
41
85
|
@@em_jobs += 1
|
@@ -44,8 +88,7 @@ class Node
|
|
44
88
|
|
45
89
|
unless !@thread_pool.nil? && @thread_pool.running?
|
46
90
|
# threads pool to handle incoming requests
|
47
|
-
|
48
|
-
@thread_pool = ThreadPool.new(10, :timeout => 5)
|
91
|
+
@thread_pool = ThreadPool.new(@num_threads, :timeout => @timeout)
|
49
92
|
end
|
50
93
|
|
51
94
|
if @@em_thread.nil?
|
@@ -63,10 +106,12 @@ class Node
|
|
63
106
|
EventMachine.schedule bl
|
64
107
|
end
|
65
108
|
|
109
|
+
# Returns boolean indicating if this node is still running or not
|
66
110
|
def em_running?
|
67
111
|
@@em_jobs > 0 && EventMachine.reactor_running?
|
68
112
|
end
|
69
113
|
|
114
|
+
# Block until the eventmachine reactor and thread pool have both completed running
|
70
115
|
def join
|
71
116
|
@@em_thread.join if @@em_thread
|
72
117
|
@@em_thread = nil
|
@@ -74,6 +119,8 @@ class Node
|
|
74
119
|
@thread_pool = nil
|
75
120
|
end
|
76
121
|
|
122
|
+
# Decrement the event machine job counter and if equal to zero,
|
123
|
+
# immediately terminate the node
|
77
124
|
def stop
|
78
125
|
@@em_jobs -= 1
|
79
126
|
if @@em_jobs == 0
|
@@ -82,6 +129,8 @@ class Node
|
|
82
129
|
end
|
83
130
|
end
|
84
131
|
|
132
|
+
# Immediately terminate the node, halting the eventmachine reactor and
|
133
|
+
# terminating the thread pool
|
85
134
|
def halt
|
86
135
|
@@em_jobs = 0
|
87
136
|
EventMachine.stop
|
data/lib/rjr/tcp_node.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# RJR TCP Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests over the TCP protocol
|
4
|
+
#
|
3
5
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
6
|
# Licensed under the Apache License, Version 2.0
|
5
7
|
|
@@ -11,14 +13,24 @@ require 'rjr/message'
|
|
11
13
|
|
12
14
|
module RJR
|
13
15
|
|
14
|
-
# TCP
|
15
|
-
#
|
16
|
+
# TCP node callback interface, used to invoke json-rpc methods
|
17
|
+
# against a remote node via a tcp socket connection previously opened
|
18
|
+
#
|
19
|
+
# After a node sends a json-rpc request to another, the either node may send
|
20
|
+
# additional requests to each other via the socket already established until
|
21
|
+
# it is closed on either end
|
16
22
|
class TCPNodeCallback
|
23
|
+
|
24
|
+
# TCPNodeCallback initializer
|
25
|
+
# @param [Hash] args the options to create the tcp node callback with
|
26
|
+
# @option args [TCPNodeEndpoint] :endpoint tcp node endpoint used to send/receive messages
|
27
|
+
# @option args [Hash] :headers hash of rjr message headers present in client request when callback is established
|
17
28
|
def initialize(args = {})
|
18
29
|
@endpoint = args[:endpoint]
|
19
30
|
@message_headers = args[:headers]
|
20
31
|
end
|
21
32
|
|
33
|
+
# Implementation of {RJR::NodeCallback#invoke}
|
22
34
|
def invoke(callback_method, *data)
|
23
35
|
msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
|
24
36
|
# TODO surround w/ begin/rescue block incase of socket errors
|
@@ -26,9 +38,13 @@ class TCPNodeCallback
|
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
|
-
#
|
30
|
-
#
|
41
|
+
# @private
|
42
|
+
# Helper class intialized by eventmachine encapsulating a socket connection
|
31
43
|
class TCPNodeEndpoint < EventMachine::Connection
|
44
|
+
|
45
|
+
# TCPNodeEndpoint intializer
|
46
|
+
#
|
47
|
+
# specify the TCPNode establishing the connection and an optional first message to send
|
32
48
|
def initialize(args = {})
|
33
49
|
@rjr_node = args[:rjr_node]
|
34
50
|
|
@@ -36,6 +52,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
36
52
|
@send_message = args[:init_message]
|
37
53
|
end
|
38
54
|
|
55
|
+
# {EventMachine::Connection#post_init} callback, sends first message if specified
|
39
56
|
def post_init
|
40
57
|
unless @send_message.nil?
|
41
58
|
send_data @send_message.to_s
|
@@ -43,6 +60,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
43
60
|
end
|
44
61
|
end
|
45
62
|
|
63
|
+
# {EventMachine::Connection#receive_data} callback, handle request / response messages
|
46
64
|
def receive_data(data)
|
47
65
|
if RequestMessage.is_request_message?(data)
|
48
66
|
@rjr_node.thread_pool << ThreadPoolJob.new { handle_request(data) }
|
@@ -54,6 +72,9 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
54
72
|
end
|
55
73
|
|
56
74
|
|
75
|
+
private
|
76
|
+
|
77
|
+
# Internal helper, handle request message received
|
57
78
|
def handle_request(data)
|
58
79
|
client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
|
59
80
|
msg = RequestMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
@@ -73,6 +94,7 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
73
94
|
send_data(response.to_s)
|
74
95
|
end
|
75
96
|
|
97
|
+
# Internal helper, handle response message received
|
76
98
|
def handle_response(data)
|
77
99
|
msg = ResponseMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
78
100
|
res = err = nil
|
@@ -90,7 +112,26 @@ class TCPNodeEndpoint < EventMachine::Connection
|
|
90
112
|
end
|
91
113
|
end
|
92
114
|
|
93
|
-
# TCP node definition, listen for and invoke json-rpc requests via
|
115
|
+
# TCP node definition, listen for and invoke json-rpc requests via TCP sockets
|
116
|
+
#
|
117
|
+
# Clients should specify the hostname / port when listening for requests and
|
118
|
+
# when invoking them.
|
119
|
+
#
|
120
|
+
# @example Listening for json-rpc requests over tcp
|
121
|
+
# # register rjr dispatchers (see RJR::Dispatcher)
|
122
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
123
|
+
# "Hello #{name}!"
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# # initialize node, listen, and block
|
127
|
+
# server = RJR::TCPNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
|
128
|
+
# server.listen
|
129
|
+
# server.join
|
130
|
+
#
|
131
|
+
# @example Invoking json-rpc requests over tcp
|
132
|
+
# client = RJR::TCPNode.new :node_id => 'client', :host => 'localhost', :port => '8888'
|
133
|
+
# puts client.invoke_request('jsonrpc://localhost:7777', 'hello', 'mo')
|
134
|
+
#
|
94
135
|
class TCPNode < RJR::Node
|
95
136
|
RJR_NODE_TYPE = :tcp
|
96
137
|
|
@@ -98,8 +139,16 @@ class TCPNode < RJR::Node
|
|
98
139
|
attr_accessor :response_cv
|
99
140
|
attr_accessor :responses
|
100
141
|
|
142
|
+
private
|
143
|
+
# Initialize the tcp subsystem
|
144
|
+
def init_node
|
145
|
+
end
|
146
|
+
|
101
147
|
public
|
102
|
-
#
|
148
|
+
# TCPNode initializer
|
149
|
+
# @param [Hash] args the options to create the tcp node with
|
150
|
+
# @option args [String] :host the hostname/ip which to listen on
|
151
|
+
# @option args [Integer] :port the port which to listen on
|
103
152
|
def initialize(args = {})
|
104
153
|
super(args)
|
105
154
|
@host = args[:host]
|
@@ -114,18 +163,19 @@ class TCPNode < RJR::Node
|
|
114
163
|
@connection_event_handlers = {:closed => [], :error => []}
|
115
164
|
end
|
116
165
|
|
117
|
-
#
|
166
|
+
# Register connection event handler
|
167
|
+
# @param [:error, :close] event the event to register the handler for
|
168
|
+
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
169
|
+
# @yield [TCPNode] self is passed to each registered handler when event occurs
|
118
170
|
def on(event, &handler)
|
119
171
|
if @connection_event_handlers.keys.include?(event)
|
120
172
|
@connection_event_handlers[event] << handler
|
121
173
|
end
|
122
174
|
end
|
123
175
|
|
124
|
-
# Initialize the tcp subsystem
|
125
|
-
def init_node
|
126
|
-
end
|
127
|
-
|
128
176
|
# Instruct Node to start listening for and dispatching rpc requests
|
177
|
+
#
|
178
|
+
# Implementation of {RJR::Node#listen}
|
129
179
|
def listen
|
130
180
|
em_run {
|
131
181
|
init_node
|
@@ -134,6 +184,10 @@ class TCPNode < RJR::Node
|
|
134
184
|
end
|
135
185
|
|
136
186
|
# Instructs node to send rpc request, and wait for / return response
|
187
|
+
# @param [String] uri location of node to send request to, should be
|
188
|
+
# in format of jsonrpc://hostname:port
|
189
|
+
# @param [String] rpc_method json-rpc method to invoke on destination
|
190
|
+
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
137
191
|
def invoke_request(uri, rpc_method, *args)
|
138
192
|
uri = URI.parse(uri)
|
139
193
|
host,port = uri.host, uri.port
|
data/lib/rjr/thread_pool.rb
CHANGED
@@ -3,11 +3,14 @@
|
|
3
3
|
# Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
|
4
4
|
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
|
-
# Work item to be executed in thread pool
|
6
|
+
# Work item to be executed in a thread launched by pool
|
7
7
|
class ThreadPoolJob
|
8
8
|
attr_accessor :handler
|
9
9
|
attr_accessor :params
|
10
10
|
|
11
|
+
# ThreadPoolJob initializer
|
12
|
+
# @param [Array] params arguments to pass to the job when it is invoked
|
13
|
+
# @param [Callable] block handle to callable object corresponding to job to invoke
|
11
14
|
def initialize(*params, &block)
|
12
15
|
@params = params
|
13
16
|
@handler = block
|
@@ -15,11 +18,15 @@ class ThreadPoolJob
|
|
15
18
|
end
|
16
19
|
|
17
20
|
|
18
|
-
#
|
19
|
-
# assigning work to them as it arrives
|
21
|
+
# Utility to launches a specified number of threads on instantiation,
|
22
|
+
# assigning work to them in order as it arrives.
|
23
|
+
#
|
24
|
+
# Supports optional timeout which allows the developer to kill and restart
|
25
|
+
# threads if a job is taking too long to run.
|
20
26
|
class ThreadPool
|
21
27
|
|
22
|
-
#
|
28
|
+
# @private
|
29
|
+
# Helper class to encapsulate each thread pool thread
|
23
30
|
class ThreadPoolJobRunner
|
24
31
|
attr_accessor :time_started
|
25
32
|
|
@@ -29,6 +36,10 @@ class ThreadPool
|
|
29
36
|
@thread_lock = Mutex.new
|
30
37
|
end
|
31
38
|
|
39
|
+
# Start thread and pull a work items off the thread pool work queue and execute them.
|
40
|
+
#
|
41
|
+
# This method will return immediately after the worker thread is started but the
|
42
|
+
# thread launched will persist until {#stop} is invoked
|
32
43
|
def run
|
33
44
|
@thread_lock.synchronize {
|
34
45
|
@thread = Thread.new {
|
@@ -49,6 +60,7 @@ class ThreadPool
|
|
49
60
|
}
|
50
61
|
end
|
51
62
|
|
63
|
+
# Return boolean indicating if worker thread is running or not
|
52
64
|
def running?
|
53
65
|
res = nil
|
54
66
|
@thread_lock.synchronize{
|
@@ -57,7 +69,8 @@ class ThreadPool
|
|
57
69
|
res
|
58
70
|
end
|
59
71
|
|
60
|
-
#
|
72
|
+
# Return boolean indicating if worker thread run time has exceeded timeout
|
73
|
+
# Should not invoke after stop is called
|
61
74
|
def check_timeout(timeout)
|
62
75
|
@timeout_lock.synchronize {
|
63
76
|
if !@time_started.nil? && Time.now - @time_started > timeout
|
@@ -67,6 +80,7 @@ class ThreadPool
|
|
67
80
|
}
|
68
81
|
end
|
69
82
|
|
83
|
+
# Stop the worker thread being executed
|
70
84
|
def stop
|
71
85
|
@thread_lock.synchronize {
|
72
86
|
if @thread.alive?
|
@@ -77,6 +91,7 @@ class ThreadPool
|
|
77
91
|
}
|
78
92
|
end
|
79
93
|
|
94
|
+
# Block until the worker thread is finished
|
80
95
|
def join
|
81
96
|
@thread_lock.synchronize {
|
82
97
|
@thread.join unless @thread.nil?
|
@@ -85,6 +100,9 @@ class ThreadPool
|
|
85
100
|
end
|
86
101
|
|
87
102
|
# Create a thread pool with a specified number of threads
|
103
|
+
# @param [Integer] num_threads the number of worker threads to create
|
104
|
+
# @param [Hash] args optional arguments to initialize thread pool with
|
105
|
+
# @option args [Integer] :timeout optional timeout to use to kill long running worker jobs
|
88
106
|
def initialize(num_threads, args = {})
|
89
107
|
@num_threads = num_threads
|
90
108
|
@timeout = args[:timeout]
|
@@ -116,32 +134,37 @@ class ThreadPool
|
|
116
134
|
end
|
117
135
|
end
|
118
136
|
|
137
|
+
# Return boolean indicated if thread pool is running.
|
138
|
+
#
|
139
|
+
# If at least one worker thread isn't terminated, the pool is still considered running
|
119
140
|
def running?
|
120
141
|
!terminate && (@timeout.nil? || (!@timeout_thread.nil? && @timeout_thread.status)) &&
|
121
142
|
@job_runners.all? { |r| r.running? }
|
122
143
|
end
|
123
144
|
|
124
|
-
#
|
145
|
+
# Return boolean indicating if the thread pool should be terminated
|
125
146
|
def terminate
|
126
147
|
@terminate_lock.synchronize { @terminate }
|
127
148
|
end
|
128
149
|
|
129
|
-
# terminate
|
150
|
+
# Instruct thread pool to terminate
|
151
|
+
# @param [Boolean] val true/false indicating if thread pool should terminate
|
130
152
|
def terminate=(val)
|
131
153
|
@terminate_lock.synchronize { @terminate = val }
|
132
154
|
end
|
133
155
|
|
134
156
|
# Add work to the pool
|
157
|
+
# @param [ThreadPoolJob] work job to execute in first available thread
|
135
158
|
def <<(work)
|
136
159
|
@work_queue.push work
|
137
160
|
end
|
138
161
|
|
139
|
-
# Return the next job queued up
|
162
|
+
# Return the next job queued up and remove it from the queue
|
140
163
|
def next_job
|
141
164
|
@work_queue.pop
|
142
165
|
end
|
143
166
|
|
144
|
-
# Terminate the thread pool
|
167
|
+
# Terminate the thread pool, stopping all worker threads
|
145
168
|
def stop
|
146
169
|
terminate = true
|
147
170
|
unless @timout_thread.nil?
|
@@ -153,6 +176,7 @@ class ThreadPool
|
|
153
176
|
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.stop } }
|
154
177
|
end
|
155
178
|
|
179
|
+
# Block until all worker threads have finished executing
|
156
180
|
def join
|
157
181
|
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.join } }
|
158
182
|
end
|
data/lib/rjr/web_node.rb
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# RJR WWW Endpoint
|
2
2
|
#
|
3
|
+
# Implements the RJR::Node interface to satisty JSON-RPC requests over the HTTP protocol
|
4
|
+
#
|
5
|
+
# The web node does not support callbacks at the moment, though at some point we may
|
6
|
+
# allow a client to specify an optional webserver to send callback requests to. (TODO)
|
7
|
+
#
|
3
8
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
9
|
# Licensed under the Apache License, Version 2.0
|
5
10
|
|
@@ -16,8 +21,8 @@ require 'rjr/message'
|
|
16
21
|
|
17
22
|
module RJR
|
18
23
|
|
19
|
-
# Web
|
20
|
-
#
|
24
|
+
# Web node callback interface, *note* callbacks are not supported on the web
|
25
|
+
# node and thus this currently does nothing
|
21
26
|
class WebNodeCallback
|
22
27
|
def initialize()
|
23
28
|
end
|
@@ -26,16 +31,31 @@ class WebNodeCallback
|
|
26
31
|
end
|
27
32
|
end
|
28
33
|
|
29
|
-
#
|
34
|
+
# @private
|
35
|
+
# Helper class intialized by eventmachine encapsulating a http connection
|
30
36
|
class WebRequestHandler < EventMachine::Connection
|
31
37
|
include EventMachine::HttpServer
|
32
38
|
|
33
39
|
RJR_NODE_TYPE = :web
|
34
40
|
|
41
|
+
# WebRequestHandler initializer.
|
42
|
+
#
|
43
|
+
# specify the WebNode establishing the connection
|
35
44
|
def initialize(*args)
|
36
45
|
@web_node = args[0]
|
37
46
|
end
|
38
47
|
|
48
|
+
# {EventMachine::Connection#process_http_request} callback, handle request messages
|
49
|
+
def process_http_request
|
50
|
+
# TODO support http protocols other than POST
|
51
|
+
msg = @http_post_content.nil? ? '' : @http_post_content
|
52
|
+
#@thread_pool << ThreadPoolJob.new { handle_request(msg) }
|
53
|
+
handle_request(msg)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Internal helper, handle request message received
|
39
59
|
def handle_request(message)
|
40
60
|
msg = nil
|
41
61
|
result = nil
|
@@ -66,38 +86,66 @@ class WebRequestHandler < EventMachine::Connection
|
|
66
86
|
resp.content_type "application/json"
|
67
87
|
resp.send_response
|
68
88
|
end
|
89
|
+
end
|
69
90
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
91
|
+
# Web node definition, listen for and invoke json-rpc requests via web requests
|
92
|
+
#
|
93
|
+
# Clients should specify the hostname / port when listening for requests and
|
94
|
+
# when invoking them.
|
95
|
+
#
|
96
|
+
# *note* the RJR javascript client also supports sending / receiving json-rpc
|
97
|
+
# messages over http
|
98
|
+
#
|
99
|
+
# @example Listening for json-rpc requests over tcp
|
100
|
+
# # register rjr dispatchers (see RJR::Dispatcher)
|
101
|
+
# RJR::Dispatcher.add_handler('hello') { |name|
|
102
|
+
# "Hello #{name}!"
|
103
|
+
# }
|
104
|
+
#
|
105
|
+
# # initialize node, listen, and block
|
106
|
+
# server = RJR::WebNode.new :node_id => 'server', :host => 'localhost', :port => '7777'
|
107
|
+
# server.listen
|
108
|
+
# server.join
|
109
|
+
#
|
110
|
+
# @example Invoking json-rpc requests over http using rjr
|
111
|
+
# client = RJR::WebNode.new :node_id => 'client'
|
112
|
+
# puts client.invoke_request('http://localhost:7777', 'hello', 'mo')
|
113
|
+
#
|
114
|
+
# @example Invoking json-rpc requests over http using curl
|
115
|
+
# $ curl -X POST http://localhost:7777 -d '{"jsonrpc":"2.0","method":"hello","params":["mo"],"id":"123"}'
|
116
|
+
# > {"jsonrpc":"2.0","id":"123","result":"Hello mo!"}
|
117
|
+
#
|
118
|
+
class WebNode < RJR::Node
|
119
|
+
private
|
120
|
+
# Initialize the web subsystem
|
121
|
+
def init_node
|
75
122
|
end
|
76
123
|
|
77
|
-
|
78
|
-
# puts "~~~~ #{data}"
|
79
|
-
#end
|
80
|
-
end
|
124
|
+
public
|
81
125
|
|
82
|
-
|
83
|
-
#
|
126
|
+
# TCPNode initializer
|
127
|
+
# @param [Hash] args the options to create the tcp node with
|
128
|
+
# @option args [String] :host the hostname/ip which to listen on
|
129
|
+
# @option args [Integer] :port the port which to listen on
|
84
130
|
def initialize(args = {})
|
85
131
|
super(args)
|
86
132
|
@host = args[:host]
|
87
133
|
@port = args[:port]
|
88
134
|
end
|
89
135
|
|
90
|
-
#
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
#
|
95
|
-
#
|
136
|
+
# Register connection event handler,
|
137
|
+
#
|
138
|
+
# *note* Since web node connections aren't persistant, we don't do anything here.
|
139
|
+
# @param [:error, :close] event the event to register the handler for
|
140
|
+
# @param [Callable] handler block param to be added to array of handlers that are called when event occurs
|
141
|
+
# @yield [LocalNode] self is passed to each registered handler when event occurs
|
96
142
|
def on(event, &handler)
|
97
143
|
# TODO raise error?
|
98
144
|
end
|
99
145
|
|
100
146
|
# Instruct Node to start listening for and dispatching rpc requests
|
147
|
+
#
|
148
|
+
# Implementation of {RJR::Node#listen}
|
101
149
|
def listen
|
102
150
|
em_run do
|
103
151
|
init_node
|
@@ -106,6 +154,10 @@ class WebNode < RJR::Node
|
|
106
154
|
end
|
107
155
|
|
108
156
|
# Instructs node to send rpc request, and wait for / return response
|
157
|
+
# @param [String] uri location of node to send request to, should be
|
158
|
+
# in format of http://hostname:port
|
159
|
+
# @param [String] rpc_method json-rpc method to invoke on destination
|
160
|
+
# @param [Array] args array of arguments to convert to json and invoke remote method wtih
|
109
161
|
def invoke_request(uri, rpc_method, *args)
|
110
162
|
init_node
|
111
163
|
message = RequestMessage.new :method => rpc_method,
|