stomp 1.1.5 → 1.1.6

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.
@@ -1,3 +1,7 @@
1
+ == ?
2
+
3
+ * Fixed multi-thread app hanging
4
+
1
5
  == 1.1.5 2010-17-03
2
6
 
3
7
  * Added publish method (send is now deprecated)
@@ -17,6 +17,7 @@ require 'stomp/ext/hash'
17
17
  require 'stomp/connection'
18
18
  require 'stomp/client'
19
19
  require 'stomp/message'
20
+ require 'stomp/version'
20
21
  require 'stomp/errors'
21
22
 
22
23
  module Stomp
@@ -286,15 +286,12 @@ module Stomp
286
286
 
287
287
  @listener_thread = Thread.start do
288
288
  while true
289
- message = @connection.poll
290
- case
291
- when message.nil?
292
- sleep 0.1
293
- when message.command == 'MESSAGE'
289
+ message = @connection.receive
290
+ if message.command == 'MESSAGE'
294
291
  if listener = @listeners[message.headers['destination']]
295
292
  listener.call(message)
296
293
  end
297
- when message.command == 'RECEIPT'
294
+ elsif message.command == 'RECEIPT'
298
295
  if listener = @receipt_listeners[message.headers['receipt-id']]
299
296
  listener.call(message)
300
297
  end
@@ -1,5 +1,4 @@
1
1
  require 'socket'
2
- require 'monitor'
3
2
  require 'timeout'
4
3
 
5
4
  module Stomp
@@ -71,8 +70,11 @@ module Stomp
71
70
  @parameters = nil
72
71
  end
73
72
 
73
+ # Use Mutexes: only one lock per each thread
74
+ # Revert to original implementation attempt
74
75
  @transmit_semaphore = Mutex.new
75
- @read_semaphore = Monitor.new
76
+ @read_semaphore = Mutex.new
77
+ @socket_semaphore = Mutex.new
76
78
 
77
79
  @subscriptions = {}
78
80
  @failure = nil
@@ -98,7 +100,7 @@ module Stomp
98
100
  end
99
101
 
100
102
  def socket
101
- @read_semaphore.synchronize do
103
+ @socket_semaphore.synchronize do
102
104
  used_socket = @socket
103
105
  used_socket = nil if closed?
104
106
 
@@ -154,7 +156,7 @@ module Stomp
154
156
  end
155
157
 
156
158
  def change_host
157
- @parameters[:hosts].shuffle! if @parameters[:randomize]
159
+ @parameters[:hosts] = @parameters[:hosts].sort_by { rand } if @parameters[:randomize]
158
160
 
159
161
  # Set first as master and send it to the end of array
160
162
  current_host = @parameters[:hosts].shift
@@ -169,7 +171,7 @@ module Stomp
169
171
  end
170
172
 
171
173
  def max_reconnect_attempts?
172
- !(@parameters.nil? || @parameters[:max_reconnect_attempts].nil?) && @parameters[:max_reconnect_attempts] != 0 && @connection_attempts > @parameters[:max_reconnect_attempts]
174
+ !(@parameters.nil? || @parameters[:max_reconnect_attempts].nil?) && @parameters[:max_reconnect_attempts] != 0 && @connection_attempts >= @parameters[:max_reconnect_attempts]
173
175
  end
174
176
 
175
177
  def increase_reconnect_delay
@@ -308,10 +310,10 @@ module Stomp
308
310
  # Return a pending message if one is available, otherwise
309
311
  # return nil
310
312
  def poll
311
- @read_semaphore.synchronize do
312
- return nil if @socket.nil? || !@socket.ready?
313
- receive
314
- end
313
+ # No need for a read lock here. The receive method eventually fullfills
314
+ # that requirement.
315
+ return nil if @socket.nil? || !@socket.ready?
316
+ receive
315
317
  end
316
318
 
317
319
  # Receive a frame, block until the frame is received
@@ -20,5 +20,5 @@ class ::Hash
20
20
  end
21
21
 
22
22
  symbolized
23
- end unless self.respond_to? :symbolize_keys
23
+ end unless self.method_defined?(:symbolize_keys)
24
24
  end
@@ -2,7 +2,7 @@ module Stomp
2
2
  module Version #:nodoc: all
3
3
  MAJOR = 1
4
4
  MINOR = 1
5
- PATCH = 5
5
+ PATCH = 6
6
6
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
7
  end
8
8
  end
@@ -334,11 +334,12 @@ describe Stomp::Connection do
334
334
  @parameters[:max_reconnect_attempts] = limit
335
335
  @connection = Stomp::Connection.new(@parameters)
336
336
 
337
- @connection.instance_variable_set(:@connection_attempts, limit)
337
+ @connection.instance_variable_set(:@connection_attempts, limit-1)
338
338
  @connection.max_reconnect_attempts?.should be_false
339
339
 
340
- @connection.instance_variable_set(:@connection_attempts, limit+1)
340
+ @connection.instance_variable_set(:@connection_attempts, limit)
341
341
  @connection.max_reconnect_attempts?.should be_true
342
+
342
343
  end
343
344
  end
344
345
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{stomp}
8
- s.version = "1.1.5"
8
+ s.version = "1.1.6"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian McCallister", "Marius Mathiesen", "Thiago Morello"]
12
- s.date = %q{2010-03-17}
12
+ s.date = %q{2010-06-10}
13
13
  s.description = %q{Ruby client for the Stomp messaging protocol}
14
14
  s.email = ["brianm@apache.org", "marius@stones.com", "morellon@gmail.com"]
15
15
  s.executables = ["catstomp", "stompcat"]
@@ -7,6 +7,9 @@ class TestClient < Test::Unit::TestCase
7
7
 
8
8
  def setup
9
9
  @client = Stomp::Client.new(user, passcode, host, port)
10
+ # Multi_thread test data
11
+ @max_threads = 20
12
+ @max_msgs = 50
10
13
  end
11
14
 
12
15
  def teardown
@@ -36,7 +39,6 @@ class TestClient < Test::Unit::TestCase
36
39
  assert_equal message_text, received.body
37
40
  end
38
41
 
39
- # BROKEN
40
42
  def test_noack
41
43
  @client.publish destination, message_text
42
44
 
@@ -49,11 +51,13 @@ class TestClient < Test::Unit::TestCase
49
51
  # was never acked so should be resent to next client
50
52
 
51
53
  @client = Stomp::Client.new(user, passcode, host, port)
52
- received = nil
53
- @client.subscribe(destination) {|msg| received = msg}
54
- sleep 0.01 until received
54
+ received2 = nil
55
+ @client.subscribe(destination) {|msg| received2 = msg}
56
+ sleep 0.01 until received2
55
57
 
56
- assert_equal message_text, received.body
58
+ assert_equal message_text, received2.body
59
+ assert_equal received.body, received2.body
60
+ assert_equal received.headers['message-id'], received2.headers['message-id']
57
61
  end
58
62
 
59
63
  def test_receipts
@@ -178,13 +182,82 @@ class TestClient < Test::Unit::TestCase
178
182
  def test_unsubscribe
179
183
  message = nil
180
184
  client = Stomp::Client.new(user, passcode, host, port, true)
181
- client.subscribe(destination, :ack => 'client') { |m| message = m }
182
- @client.publish destination, message_text
183
- Timeout::timeout(4) do
184
- sleep 0.01 until message
185
+ assert_nothing_raised {
186
+ client.subscribe(destination, :ack => 'client') { |m| message = m }
187
+ @client.publish destination, message_text
188
+ Timeout::timeout(4) do
189
+ sleep 0.01 until message
190
+ end
191
+ }
192
+ assert_equal message_text, message.body
193
+ assert_nothing_raised {
194
+ client.unsubscribe destination # was throwing exception on unsub at one point
195
+ client.close
196
+ }
197
+ # Same message should remain on the queue. Receive it again with ack=>auto.
198
+ message_copy = nil
199
+ client = Stomp::Client.new(user, passcode, host, port, true)
200
+ assert_nothing_raised {
201
+ client.subscribe(destination, :ack => 'auto') { |m| message_copy = m }
202
+ Timeout::timeout(4) do
203
+ sleep 0.01 until message_copy
204
+ end
205
+ }
206
+ assert_equal message_text, message_copy.body
207
+ assert_equal message.headers['message-id'], message_copy.headers['message-id']
208
+ end
209
+
210
+ def test_thread_one_subscribe
211
+ msg = nil
212
+ Thread.new(@client) do |acli|
213
+ assert_nothing_raised {
214
+ acli.subscribe(destination) { |m| msg = m }
215
+ Timeout::timeout(4) do
216
+ sleep 0.01 until msg
217
+ end
218
+ }
185
219
  end
186
- client.unsubscribe destination # was throwing exception on unsub at one point
220
+ #
221
+ @client.publish(destination, message_text)
222
+ sleep 1
223
+ assert_not_nil msg
224
+ end
187
225
 
226
+ def test_thread_multi_subscribe
227
+ #
228
+ lock = Mutex.new
229
+ msg_ctr = 0
230
+ 1.upto(@max_threads) do |tnum|
231
+ # Threads within threads .....
232
+ Thread.new(@client) do |acli|
233
+ assert_nothing_raised {
234
+ acli.subscribe(destination) { |m|
235
+ msg = m
236
+ lock.synchronize do
237
+ msg_ctr += 1
238
+ end
239
+ # Simulate message processing
240
+ sleep 0.05
241
+ }
242
+ }
243
+ end
244
+ end
245
+ #
246
+ 1.upto(@max_msgs) do |mnum|
247
+ msg = Time.now.to_s + " #{mnum}"
248
+ @client.publish(destination, message_text)
249
+ end
250
+ #
251
+ max_sleep=5
252
+ sleep_incr = 0.10
253
+ total_slept = 0
254
+ while true
255
+ break if @max_msgs == msg_ctr
256
+ total_slept += sleep_incr
257
+ break if total_slept > max_sleep
258
+ sleep sleep_incr
259
+ end
260
+ assert_equal @max_msgs, msg_ctr
188
261
  end
189
262
 
190
263
  private
@@ -7,6 +7,9 @@ class TestStomp < Test::Unit::TestCase
7
7
 
8
8
  def setup
9
9
  @conn = Stomp::Connection.open(user, passcode, host, port)
10
+ # Data for multi_thread tests
11
+ @max_threads = 20
12
+ @max_msgs = 100
10
13
  end
11
14
 
12
15
  def teardown
@@ -107,6 +110,117 @@ class TestStomp < Test::Unit::TestCase
107
110
  assert_equal "b\0", msg_b.body
108
111
  end
109
112
 
113
+ def test_thread_hang_one
114
+ received = nil
115
+ Thread.new(@conn) do |amq|
116
+ while true
117
+ received = amq.receive
118
+ end
119
+ end
120
+ #
121
+ @conn.subscribe( make_destination )
122
+ message = Time.now.to_s
123
+ @conn.publish(make_destination, message)
124
+ sleep 1
125
+ assert_not_nil received
126
+ assert_equal message, received.body
127
+ end
128
+
129
+ def test_thread_poll_one
130
+ received = nil
131
+ Thread.new(@conn) do |amq|
132
+ while true
133
+ received = amq.poll
134
+ # One message is needed
135
+ Thread.exit if received
136
+ sleep 0.1
137
+ end
138
+ end
139
+ #
140
+ @conn.subscribe( make_destination )
141
+ message = Time.now.to_s
142
+ @conn.publish(make_destination, message)
143
+ sleep 1
144
+ assert_not_nil received
145
+ assert_equal message, received.body
146
+ end
147
+
148
+ def test_multi_thread_receive
149
+ lock = Mutex.new
150
+ msg_ctr = 0
151
+ #
152
+ 1.upto(@max_threads) do |tnum|
153
+ Thread.new(@conn) do |amq|
154
+ while true
155
+ received = amq.receive
156
+ lock.synchronize do
157
+ msg_ctr += 1
158
+ end
159
+ # Simulate message processing
160
+ sleep 0.05
161
+ end
162
+ end
163
+ end
164
+ #
165
+ @conn.subscribe( make_destination )
166
+ 1.upto(@max_msgs) do |mnum|
167
+ msg = Time.now.to_s + " #{mnum}"
168
+ @conn.publish(make_destination, msg)
169
+ end
170
+ #
171
+ max_sleep=5
172
+ sleep_incr = 0.10
173
+ total_slept = 0
174
+ while true
175
+ break if @max_msgs == msg_ctr
176
+ total_slept += sleep_incr
177
+ break if total_slept > max_sleep
178
+ sleep sleep_incr
179
+ end
180
+ assert_equal @max_msgs, msg_ctr
181
+ end
182
+
183
+ def test_multi_thread_poll
184
+ #
185
+ lock = Mutex.new
186
+ msg_ctr = 0
187
+ #
188
+ 1.upto(@max_threads) do |tnum|
189
+ Thread.new(@conn) do |amq|
190
+ while true
191
+ received = amq.poll
192
+ if received
193
+ lock.synchronize do
194
+ msg_ctr += 1
195
+ end
196
+ # Simulate message processing
197
+ sleep 0.05
198
+ else
199
+ # Wait a bit for more work
200
+ sleep 0.05
201
+ end
202
+ end
203
+ end
204
+ end
205
+ #
206
+ @conn.subscribe( make_destination )
207
+ 1.upto(@max_msgs) do |mnum|
208
+ msg = Time.now.to_s + " #{mnum}"
209
+ @conn.publish(make_destination, msg)
210
+ end
211
+ #
212
+ max_sleep=5
213
+ sleep_incr = 0.10
214
+ total_slept = 0
215
+ while true
216
+ break if @max_msgs == msg_ctr
217
+ total_slept += sleep_incr
218
+ break if total_slept > max_sleep
219
+ sleep sleep_incr
220
+ end
221
+ assert_equal @max_msgs, msg_ctr
222
+ end
223
+
110
224
  private
111
225
  def make_destination
112
226
  "/queue/test/ruby/stomp/" + name
@@ -133,3 +247,4 @@ class TestStomp < Test::Unit::TestCase
133
247
  assert_equal "txn message", msg.body
134
248
  end
135
249
  end
250
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stomp
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian McCallister
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2010-03-17 00:00:00 -03:00
14
+ date: 2010-06-10 00:00:00 -03:00
15
15
  default_executable:
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency