rjr 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|