rjr 0.6.1 → 0.7.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/LICENSE +202 -661
- data/Rakefile +2 -2
- data/bin/rjr-server +2 -2
- data/lib/rjr/amqp_node.rb +42 -22
- data/lib/rjr/common.rb +1 -1
- data/lib/rjr/dispatcher.rb +1 -1
- data/lib/rjr/errors.rb +1 -1
- data/lib/rjr/local_node.rb +2 -1
- data/lib/rjr/message.rb +1 -1
- data/lib/rjr/multi_node.rb +1 -1
- data/lib/rjr/node.rb +15 -6
- data/lib/rjr/tcp_node.rb +174 -1
- data/lib/rjr/thread_pool.rb +20 -25
- data/lib/rjr/web_node.rb +1 -1
- data/lib/rjr/ws_node.rb +2 -1
- data/lib/rjr.rb +4 -3
- data/specs/tcp_node_spec.rb +32 -0
- metadata +3 -2
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# rjr project Rakefile
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010 Mohammed Morsi <
|
4
|
-
# Licensed under the
|
3
|
+
# Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
require 'rdoc/task'
|
7
7
|
require "rspec/core/rake_task"
|
data/bin/rjr-server
CHANGED
@@ -5,8 +5,8 @@
|
|
5
5
|
# Flags:
|
6
6
|
# -h --help
|
7
7
|
#
|
8
|
-
# Copyright (C) 2011 Mohammed Morsi <mo@morsi.org>
|
9
|
-
# Licensed under the
|
8
|
+
# Copyright (C) 2011-2012 Mohammed Morsi <mo@morsi.org>
|
9
|
+
# Licensed under the Apache License, Version 2.0
|
10
10
|
|
11
11
|
require 'rubygems'
|
12
12
|
require 'optparse'
|
data/lib/rjr/amqp_node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR AMQP Endpoint
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
@@ -57,7 +57,6 @@ class AMQPNode < RJR::Node
|
|
57
57
|
:rjr_callback =>
|
58
58
|
AMQPNodeCallback.new(:node => self,
|
59
59
|
:exchange => @exchange,
|
60
|
-
:amqp_lock => @amqp_lock,
|
61
60
|
:destination => reply_to,
|
62
61
|
:headers => headers))
|
63
62
|
response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
|
@@ -74,8 +73,9 @@ class AMQPNode < RJR::Node
|
|
74
73
|
end
|
75
74
|
|
76
75
|
@response_lock.synchronize{
|
77
|
-
|
78
|
-
|
76
|
+
result = [msg.msg_id, res]
|
77
|
+
result << err if !err.nil?
|
78
|
+
@responses << result
|
79
79
|
@response_cv.signal
|
80
80
|
}
|
81
81
|
end
|
@@ -88,7 +88,10 @@ class AMQPNode < RJR::Node
|
|
88
88
|
@broker = args[:broker]
|
89
89
|
@connection_event_handlers = {:closed => [], :error => []}
|
90
90
|
@response_lock = Mutex.new
|
91
|
-
@response_cv
|
91
|
+
@response_cv = ConditionVariable.new
|
92
|
+
@response_check_cv = ConditionVariable.new
|
93
|
+
@responses = []
|
94
|
+
@amqp_lock = Mutex.new
|
92
95
|
end
|
93
96
|
|
94
97
|
# Initialize the amqp subsystem
|
@@ -106,11 +109,11 @@ class AMQPNode < RJR::Node
|
|
106
109
|
@exchange = @channel.default_exchange
|
107
110
|
|
108
111
|
@listening = false
|
109
|
-
|
112
|
+
#@disconnected = false
|
110
113
|
|
111
114
|
@exchange.on_return do |basic_return, metadata, payload|
|
112
115
|
puts "#{payload} was returned! reply_code = #{basic_return.reply_code}, reply_text = #{basic_return.reply_text}"
|
113
|
-
|
116
|
+
#@disconnected = true # FIXME member will be set on wrong class
|
114
117
|
connection_event(:error)
|
115
118
|
connection_event(:closed)
|
116
119
|
end
|
@@ -118,25 +121,41 @@ class AMQPNode < RJR::Node
|
|
118
121
|
|
119
122
|
# publish a message using the amqp exchange
|
120
123
|
def publish(*args)
|
121
|
-
|
122
|
-
|
124
|
+
@amqp_lock.synchronize {
|
125
|
+
#raise RJR::Errors::ConnectionError.new("client unreachable") if @disconnected
|
126
|
+
@exchange.publish *args
|
127
|
+
}
|
128
|
+
nil
|
123
129
|
end
|
124
130
|
|
125
131
|
# subscribe to messages using the amqp queue
|
126
132
|
def subscribe(*args, &bl)
|
127
133
|
return if @listening
|
128
|
-
@
|
129
|
-
|
130
|
-
|
131
|
-
|
134
|
+
@amqp_lock.synchronize {
|
135
|
+
@listening = true
|
136
|
+
@queue.subscribe do |metadata, msg|
|
137
|
+
bl.call metadata, msg
|
138
|
+
end
|
139
|
+
}
|
140
|
+
nil
|
132
141
|
end
|
133
142
|
|
134
143
|
def wait_for_result(message)
|
135
144
|
res = nil
|
136
|
-
|
137
|
-
@
|
138
|
-
|
139
|
-
|
145
|
+
while res.nil?
|
146
|
+
@response_lock.synchronize{
|
147
|
+
@response_cv.wait @response_lock
|
148
|
+
# FIXME throw err if more than 1 match found
|
149
|
+
res = @responses.select { |response| message.msg_id == response.first }.first
|
150
|
+
unless res.nil?
|
151
|
+
@responses.delete(res)
|
152
|
+
else
|
153
|
+
@response_cv.signal
|
154
|
+
@response_check_cv.wait @response_lock
|
155
|
+
end
|
156
|
+
@response_check_cv.signal
|
157
|
+
}
|
158
|
+
end
|
140
159
|
return res
|
141
160
|
end
|
142
161
|
|
@@ -185,14 +204,15 @@ class AMQPNode < RJR::Node
|
|
185
204
|
publish message.to_s, :routing_key => routing_key, :reply_to => @queue_name
|
186
205
|
end
|
187
206
|
|
207
|
+
# TODO optional timeout for response ?
|
188
208
|
result = wait_for_result(message)
|
189
|
-
self.stop
|
190
|
-
self.join unless self.em_running?
|
209
|
+
#self.stop
|
210
|
+
#self.join unless self.em_running?
|
191
211
|
|
192
|
-
if result.size >
|
193
|
-
raise result[
|
212
|
+
if result.size > 2
|
213
|
+
raise result[2]
|
194
214
|
end
|
195
|
-
return result
|
215
|
+
return result[1]
|
196
216
|
end
|
197
217
|
|
198
218
|
end
|
data/lib/rjr/common.rb
CHANGED
data/lib/rjr/dispatcher.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR Request / Response Dispatcher
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
data/lib/rjr/errors.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR Errors
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
data/lib/rjr/local_node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR Local Endpoint
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
@@ -19,6 +19,7 @@ class LocalNodeCallback
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def invoke(callback_method, *data)
|
22
|
+
# TODO any exceptions from handler will propagate here, surround w/ begin/rescue block
|
22
23
|
@node.invoke_request(callback_method, *data)
|
23
24
|
# TODO support local_node 'disconnections'
|
24
25
|
end
|
data/lib/rjr/message.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR Message
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
data/lib/rjr/multi_node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR MultiNode Endpoint
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
data/lib/rjr/node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR Node
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
@@ -20,11 +20,19 @@ class Node
|
|
20
20
|
# attitional parameters to set on messages
|
21
21
|
attr_accessor :message_headers
|
22
22
|
|
23
|
+
attr_reader :thread_pool
|
24
|
+
|
23
25
|
def initialize(args = {})
|
24
26
|
@node_id = args[:node_id]
|
25
27
|
|
26
28
|
@message_headers = {}
|
27
29
|
@message_headers.merge!(args[:headers]) if args.has_key?(:headers)
|
30
|
+
|
31
|
+
ObjectSpace.define_finalizer(self, self.class.finalize(self))
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.finalize(node)
|
35
|
+
proc { node.halt ; node.join }
|
28
36
|
end
|
29
37
|
|
30
38
|
# run job in event machine
|
@@ -46,7 +54,7 @@ class Node
|
|
46
54
|
begin
|
47
55
|
EventMachine.run
|
48
56
|
rescue Exception => e
|
49
|
-
puts "Critical exception #{e}"
|
57
|
+
puts "Critical exception #{e}\n#{e.backtrace.join("\n")}"
|
50
58
|
ensure
|
51
59
|
end
|
52
60
|
}
|
@@ -60,10 +68,10 @@ class Node
|
|
60
68
|
end
|
61
69
|
|
62
70
|
def join
|
63
|
-
if @@em_thread
|
64
|
-
|
65
|
-
|
66
|
-
|
71
|
+
@@em_thread.join if @@em_thread
|
72
|
+
@@em_thread = nil
|
73
|
+
@thread_pool.join if @thread_pool
|
74
|
+
@thread_pool = nil
|
67
75
|
end
|
68
76
|
|
69
77
|
def stop
|
@@ -77,6 +85,7 @@ class Node
|
|
77
85
|
def halt
|
78
86
|
@@em_jobs = 0
|
79
87
|
EventMachine.stop
|
88
|
+
@thread_pool.stop unless @thread_pool.nil?
|
80
89
|
end
|
81
90
|
|
82
91
|
end
|
data/lib/rjr/tcp_node.rb
CHANGED
@@ -1 +1,174 @@
|
|
1
|
-
#
|
1
|
+
# RJR TCP Endpoint
|
2
|
+
#
|
3
|
+
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
|
+
|
6
|
+
require 'uri'
|
7
|
+
require 'eventmachine'
|
8
|
+
|
9
|
+
require 'rjr/node'
|
10
|
+
require 'rjr/message'
|
11
|
+
|
12
|
+
module RJR
|
13
|
+
|
14
|
+
# TCP client node callback interface,
|
15
|
+
# send data back to client via established tcp socket.
|
16
|
+
class TCPNodeCallback
|
17
|
+
def initialize(args = {})
|
18
|
+
@endpoint = args[:endpoint]
|
19
|
+
@message_headers = args[:headers]
|
20
|
+
end
|
21
|
+
|
22
|
+
def invoke(callback_method, *data)
|
23
|
+
msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
|
24
|
+
# TODO surround w/ begin/rescue block incase of socket errors
|
25
|
+
@endpoint.send_data msg.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# helper class intialized by event machine corresponding to
|
30
|
+
# a client or server socket connection
|
31
|
+
class TCPNodeEndpoint < EventMachine::Connection
|
32
|
+
def initialize(args = {})
|
33
|
+
@rjr_node = args[:rjr_node]
|
34
|
+
|
35
|
+
# these params should be set for clients
|
36
|
+
@send_message = args[:init_message]
|
37
|
+
end
|
38
|
+
|
39
|
+
def post_init
|
40
|
+
unless @send_message.nil?
|
41
|
+
send_data @send_message.to_s
|
42
|
+
@send_message = nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def receive_data(data)
|
47
|
+
if RequestMessage.is_request_message?(data)
|
48
|
+
@rjr_node.thread_pool << ThreadPoolJob.new { handle_request(data) }
|
49
|
+
|
50
|
+
elsif ResponseMessage.is_response_message?(data)
|
51
|
+
handle_response(data)
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
def handle_request(data)
|
58
|
+
client_port, client_ip = Socket.unpack_sockaddr_in(get_peername)
|
59
|
+
msg = RequestMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
60
|
+
headers = @rjr_node.message_headers.merge(msg.headers)
|
61
|
+
result = Dispatcher.dispatch_request(msg.jr_method,
|
62
|
+
:method_args => msg.jr_args,
|
63
|
+
:headers => headers,
|
64
|
+
:client_ip => client_ip,
|
65
|
+
:client_port => client_port,
|
66
|
+
:rjr_node => @rjr_node,
|
67
|
+
:rjr_node_id => @rjr_node.node_id,
|
68
|
+
:rjr_node_type => TCPNode::RJR_NODE_TYPE,
|
69
|
+
:rjr_callback =>
|
70
|
+
TCPNodeCallback.new(:endpoint => self,
|
71
|
+
:headers => headers))
|
72
|
+
response = ResponseMessage.new(:id => msg.msg_id, :result => result, :headers => headers)
|
73
|
+
send_data(response.to_s)
|
74
|
+
end
|
75
|
+
|
76
|
+
def handle_response(data)
|
77
|
+
msg = ResponseMessage.new(:message => data, :headers => @rjr_node.message_headers)
|
78
|
+
res = err = nil
|
79
|
+
begin
|
80
|
+
res = Dispatcher.handle_response(msg.result)
|
81
|
+
rescue Exception => e
|
82
|
+
err = e
|
83
|
+
end
|
84
|
+
|
85
|
+
@rjr_node.response_lock.synchronize {
|
86
|
+
@rjr_node.responses << [msg.msg_id, res]
|
87
|
+
@rjr_node.responses.last << err unless err.nil?
|
88
|
+
@rjr_node.response_cv.signal
|
89
|
+
}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# TCP node definition, listen for and invoke json-rpc requests via tcp sockets
|
94
|
+
class TCPNode < RJR::Node
|
95
|
+
RJR_NODE_TYPE = :tcp
|
96
|
+
|
97
|
+
attr_accessor :response_lock
|
98
|
+
attr_accessor :response_cv
|
99
|
+
attr_accessor :responses
|
100
|
+
|
101
|
+
public
|
102
|
+
# initialize the node w/ the specified params
|
103
|
+
def initialize(args = {})
|
104
|
+
super(args)
|
105
|
+
@host = args[:host]
|
106
|
+
@port = args[:port]
|
107
|
+
|
108
|
+
@response_lock = Mutex.new
|
109
|
+
@response_cv = ConditionVariable.new
|
110
|
+
@response_cv = ConditionVariable.new
|
111
|
+
@response_check_cv = ConditionVariable.new
|
112
|
+
@responses = []
|
113
|
+
|
114
|
+
@connection_event_handlers = {:closed => [], :error => []}
|
115
|
+
end
|
116
|
+
|
117
|
+
# register connection event handler
|
118
|
+
def on(event, &handler)
|
119
|
+
if @connection_event_handlers.keys.include?(event)
|
120
|
+
@connection_event_handlers[event] << handler
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Initialize the tcp subsystem
|
125
|
+
def init_node
|
126
|
+
end
|
127
|
+
|
128
|
+
# Instruct Node to start listening for and dispatching rpc requests
|
129
|
+
def listen
|
130
|
+
em_run {
|
131
|
+
init_node
|
132
|
+
EventMachine::start_server @host, @port, TCPNodeEndpoint, { :rjr_node => self }
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Instructs node to send rpc request, and wait for / return response
|
137
|
+
def invoke_request(uri, rpc_method, *args)
|
138
|
+
uri = URI.parse(uri)
|
139
|
+
host,port = uri.host, uri.port
|
140
|
+
|
141
|
+
message = RequestMessage.new :method => rpc_method,
|
142
|
+
:args => args,
|
143
|
+
:headers => @message_headers
|
144
|
+
em_run{
|
145
|
+
init_node
|
146
|
+
EventMachine::connect host, port, TCPNodeEndpoint, { :rjr_node => self,
|
147
|
+
:init_message => message }
|
148
|
+
}
|
149
|
+
|
150
|
+
# wait for matching response
|
151
|
+
res = nil
|
152
|
+
while res.nil?
|
153
|
+
@response_lock.synchronize {
|
154
|
+
@response_cv.wait response_lock
|
155
|
+
res = @responses.select { |response| message.msg_id == response.first }.first
|
156
|
+
unless res.nil?
|
157
|
+
@responses.delete(res)
|
158
|
+
else
|
159
|
+
@response_cv.signal
|
160
|
+
@response_check_cv.wait @response_lock
|
161
|
+
end
|
162
|
+
@response_check_cv.signal
|
163
|
+
}
|
164
|
+
end
|
165
|
+
|
166
|
+
# raise error or return result
|
167
|
+
if res.size > 2
|
168
|
+
raise res[2]
|
169
|
+
end
|
170
|
+
return res[1]
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
end # module RJR
|
data/lib/rjr/thread_pool.rb
CHANGED
@@ -1,27 +1,7 @@
|
|
1
1
|
# Thread Pool
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010 Mohammed Morsi <
|
4
|
-
#
|
5
|
-
# Permission is hereby granted, free of charge, to any person
|
6
|
-
# obtaining a copy of this software and associated documentation
|
7
|
-
# files (the "Software"), to deal in the Software without
|
8
|
-
# restriction, including without limitation the rights to use,
|
9
|
-
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
-
# copies of the Software, and to permit persons to whom the
|
11
|
-
# Software is furnished to do so, subject to the following
|
12
|
-
# conditions:
|
13
|
-
#
|
14
|
-
# The above copyright notice and this permission notice shall be
|
15
|
-
# included in all copies or substantial portions of the Software.
|
16
|
-
#
|
17
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
-
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
-
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
-
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
-
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
-
# OTHER DEALINGS IN THE SOFTWARE.
|
3
|
+
# Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
25
5
|
|
26
6
|
# Work item to be executed in thread pool
|
27
7
|
class ThreadPoolJob
|
@@ -72,7 +52,7 @@ class ThreadPool
|
|
72
52
|
def running?
|
73
53
|
res = nil
|
74
54
|
@thread_lock.synchronize{
|
75
|
-
res = (@thread.status != false)
|
55
|
+
res = (!@thread.nil? && (@thread.status != false))
|
76
56
|
}
|
77
57
|
res
|
78
58
|
end
|
@@ -93,6 +73,13 @@ class ThreadPool
|
|
93
73
|
@thread.kill
|
94
74
|
@thread.join
|
95
75
|
end
|
76
|
+
@thread = nil
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def join
|
81
|
+
@thread_lock.synchronize {
|
82
|
+
@thread.join unless @thread.nil?
|
96
83
|
}
|
97
84
|
end
|
98
85
|
end
|
@@ -130,7 +117,7 @@ class ThreadPool
|
|
130
117
|
end
|
131
118
|
|
132
119
|
def running?
|
133
|
-
!terminate && (@timeout.nil? || @timeout_thread.status) &&
|
120
|
+
!terminate && (@timeout.nil? || (!@timeout_thread.nil? && @timeout_thread.status)) &&
|
134
121
|
@job_runners.all? { |r| r.running? }
|
135
122
|
end
|
136
123
|
|
@@ -157,9 +144,17 @@ class ThreadPool
|
|
157
144
|
# Terminate the thread pool
|
158
145
|
def stop
|
159
146
|
terminate = true
|
160
|
-
|
147
|
+
unless @timout_thread.nil?
|
148
|
+
@timeout_thread.join
|
149
|
+
@timeout_thread.terminate
|
150
|
+
end
|
151
|
+
@timeout_thread = nil
|
161
152
|
@work_queue.clear
|
162
153
|
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.stop } }
|
163
154
|
end
|
155
|
+
|
156
|
+
def join
|
157
|
+
@job_runners_lock.synchronize { @job_runners.each { |jr| jr.join } }
|
158
|
+
end
|
164
159
|
end
|
165
160
|
|
data/lib/rjr/web_node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR WWW Endpoint
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
data/lib/rjr/ws_node.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# RJR WebSockets Endpoint
|
2
2
|
#
|
3
3
|
# Copyright (C) 2012 Mohammed Morsi <mo@morsi.org>
|
4
|
-
# Licensed under the
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
# establish client connection w/ specified args and invoke block w/
|
7
7
|
# newly created client, returning it after block terminates
|
@@ -26,6 +26,7 @@ class WSNodeCallback
|
|
26
26
|
#msg = CallbackMessage.new(:data => data)
|
27
27
|
msg = RequestMessage.new :method => callback_method, :args => data, :headers => @message_headers
|
28
28
|
raise RJR::Errors::ConnectionError.new("websocket closed") if @socket.state == :closed
|
29
|
+
# TODO surround w/ begin/rescue block incase of other socket errors?
|
29
30
|
@socket.send(msg.to_s)
|
30
31
|
end
|
31
32
|
end
|
data/lib/rjr.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# rjr - Ruby Json Rpc
|
2
2
|
#
|
3
|
-
# Copyright (C) 2010 Mohammed Morsi <
|
4
|
-
# Licensed under the
|
3
|
+
# Copyright (C) 2010-2012 Mohammed Morsi <mo@morsi.org>
|
4
|
+
# Licensed under the Apache License, Version 2.0
|
5
5
|
|
6
6
|
require 'rjr/common'
|
7
7
|
require 'rjr/errors'
|
@@ -14,4 +14,5 @@ require 'rjr/local_node'
|
|
14
14
|
require 'rjr/amqp_node'
|
15
15
|
require 'rjr/ws_node'
|
16
16
|
require 'rjr/web_node'
|
17
|
+
require 'rjr/tcp_node'
|
17
18
|
require 'rjr/multi_node'
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rjr/tcp_node'
|
2
|
+
require 'rjr/dispatcher'
|
3
|
+
|
4
|
+
describe RJR::TCPNode do
|
5
|
+
it "should invoke and satisfy tcp requests" do
|
6
|
+
server = RJR::TCPNode.new :node_id => 'tcp', :host => 'localhost', :port => 9987
|
7
|
+
client = RJR::TCPNode.new
|
8
|
+
|
9
|
+
foobar_invoked = false
|
10
|
+
RJR::Dispatcher.init_handlers
|
11
|
+
RJR::Dispatcher.add_handler('foobar') { |param|
|
12
|
+
@client_ip.should == "127.0.0.1"
|
13
|
+
#@client_port.should == 9987
|
14
|
+
@rjr_node.should == server
|
15
|
+
@rjr_node_id.should == 'tcp'
|
16
|
+
@rjr_node_type.should == :tcp
|
17
|
+
param.should == 'myparam'
|
18
|
+
foobar_invoked = true
|
19
|
+
'retval'
|
20
|
+
}
|
21
|
+
|
22
|
+
server.listen
|
23
|
+
sleep 1
|
24
|
+
res = client.invoke_request 'jsonrpc://localhost:9987', 'foobar', 'myparam'
|
25
|
+
res.should == 'retval'
|
26
|
+
server.halt
|
27
|
+
server.join
|
28
|
+
foobar_invoked.should == true
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO ensure closed / error event handlers are invoked
|
32
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rjr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-08-26 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- specs/dispatcher_spec.rb
|
54
54
|
- specs/node_spec.rb
|
55
55
|
- specs/amqp_node_spec.rb
|
56
|
+
- specs/tcp_node_spec.rb
|
56
57
|
- specs/multi_node_spec.rb
|
57
58
|
- specs/local_node_spec.rb
|
58
59
|
- specs/web_node_spec.rb
|