bunny 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = %q{bunny}
3
- s.version = "0.5.1"
3
+ s.version = "0.5.2"
4
4
  s.authors = ["Chris Duncan"]
5
- s.date = %q{2009-08-08}
5
+ s.date = %q{2009-08-17}
6
6
  s.description = %q{Another synchronous Ruby AMQP client}
7
7
  s.email = %q{celldee@gmail.com}
8
8
  s.rubyforge_project = %q{bunny-amqp}
@@ -1,4 +1,4 @@
1
- # simple.rb
1
+ # simple_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple.rb
1
+ # simple_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_ack.rb
1
+ # simple_ack_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_ack.rb
1
+ # simple_ack_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # consumer.rb
1
+ # simple_consumer_08.rb
2
2
 
3
3
  # N.B. To be used in conjunction with simple_publisher.rb
4
4
 
@@ -13,15 +13,18 @@
13
13
  #
14
14
  # Open up two console windows start this program in one of them by typing -
15
15
  #
16
- # ruby simple_consumer.rb
16
+ # ruby simple_consumer_08.rb
17
17
  #
18
18
  # Then switch to the other console window and type -
19
19
  #
20
- # ruby simple_publisher.rb
20
+ # ruby simple_publisher_08.rb
21
21
  #
22
22
  # A message will be printed out by the simple_consumer and it will wait for the next message
23
+ # until the timeout interval is reached.
23
24
  #
24
- # Run simple_publisher 3 more times. After the last run simple_consumer will stop.
25
+ # Run simple_publisher as many times as you like. When you want the program to stop just stop
26
+ # sending messages and the subscribe loop will timeout after 30 seconds, the program will
27
+ # unsubscribe from the queue and close the connection to the server.
25
28
 
26
29
  $:.unshift File.dirname(__FILE__) + '/../lib'
27
30
 
@@ -45,11 +48,14 @@ q.bind(exch, :key => 'fred')
45
48
  i = 1
46
49
 
47
50
  # subscribe to queue
48
- q.subscribe(:consumer_tag => 'testtag1') do |msg|
49
- puts i.to_s + ': ' + msg
51
+ ret = q.subscribe(:consumer_tag => 'testtag1', :timeout => 30) do |msg|
52
+ puts "#{i.to_s}: #{msg}"
50
53
  i+=1
51
- q.unsubscribe(:consumer_tag => 'testtag1') if i == 5
52
54
  end
53
55
 
54
- # close the connection
55
- b.stop
56
+ if ret == :timed_out
57
+ puts '==== simple_consumer_08.rb timed out - closing down ===='
58
+ q.unsubscribe(:consumer_tag => 'testtag1')
59
+ # close the connection
60
+ b.stop
61
+ end
@@ -1,4 +1,4 @@
1
- # consumer.rb
1
+ # simple_consumer_09.rb
2
2
 
3
3
  # N.B. To be used in conjunction with simple_publisher.rb
4
4
 
@@ -13,15 +13,18 @@
13
13
  #
14
14
  # Open up two console windows start this program in one of them by typing -
15
15
  #
16
- # ruby simple_consumer.rb
16
+ # ruby simple_consumer_09.rb
17
17
  #
18
18
  # Then switch to the other console window and type -
19
19
  #
20
- # ruby simple_publisher.rb
20
+ # ruby simple_publisher_09.rb
21
21
  #
22
22
  # A message will be printed out by the simple_consumer and it will wait for the next message
23
+ # until the timeout interval is reached.
23
24
  #
24
- # Run simple_publisher 3 more times. After the last run simple_consumer will stop.
25
+ # Run simple_publisher as many times as you like. When you want the program to stop just stop
26
+ # sending messages and the subscribe loop will timeout after 30 seconds, the program will
27
+ # unsubscribe from the queue and close the connection to the server.
25
28
 
26
29
  $:.unshift File.dirname(__FILE__) + '/../lib'
27
30
 
@@ -45,11 +48,14 @@ q.bind(exch, :key => 'fred')
45
48
  i = 1
46
49
 
47
50
  # subscribe to queue
48
- q.subscribe(:consumer_tag => 'testtag1') do |msg|
49
- puts i.to_s + ': ' + msg
51
+ ret = q.subscribe(:consumer_tag => 'testtag1', :timeout => 30) do |msg|
52
+ puts "#{i.to_s}: #{msg}"
50
53
  i+=1
51
- q.unsubscribe(:consumer_tag => 'testtag1') if i == 5
52
54
  end
53
55
 
54
- # close the connection
55
- b.stop
56
+ if ret == :timed_out
57
+ puts '==== simple_consumer_09.rb timed out - closing down ===='
58
+ q.unsubscribe(:consumer_tag => 'testtag1')
59
+ # close the connection
60
+ b.stop
61
+ end
@@ -1,4 +1,4 @@
1
- # fanout.rb
1
+ # simple_fanout_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # fanout.rb
1
+ # simple_fanout_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_headers.rb
1
+ # simple_headers_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_headers.rb
1
+ # simple_headers_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,6 +1,6 @@
1
- # simple_publisher.rb
1
+ # simple_publisher_08.rb
2
2
 
3
- # N.B. To be used in conjunction with simple_consumer.rb. See simple_consumer.rb for explanation.
3
+ # N.B. To be used in conjunction with simple_consumer_08.rb. See simple_consumer.rb for explanation.
4
4
 
5
5
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
6
6
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_topic.rb
1
+ # simple_topic_08.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -1,4 +1,4 @@
1
- # simple_topic.rb
1
+ # simple_topic_09.rb
2
2
 
3
3
  # Assumes that target message broker/server has a user called 'guest' with a password 'guest'
4
4
  # and that it is running on 'localhost'.
@@ -12,7 +12,7 @@ module Bunny
12
12
  class ConnectionError < StandardError; end
13
13
  class MessageError < StandardError; end
14
14
 
15
- VERSION = '0.5.1'
15
+ VERSION = '0.5.2'
16
16
 
17
17
  # Returns the Bunny version number
18
18
 
@@ -34,6 +34,9 @@ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_
34
34
  servers, the server may respond to a Connection::Open method with a Connection::Redirect. The insist
35
35
  option, if set to _true_, tells the server that the client is insisting on a connection to the
36
36
  specified server.
37
+ * <tt>:frame_max => maximum frame size in bytes (default = 131072)</tt>
38
+ * <tt>:channel_max => maximum number of channels (default = 0 no maximum)</tt>
39
+ * <tt>:heartbeat => number of seconds (default = 0 no heartbeat)</tt>
37
40
 
38
41
  =end
39
42
 
@@ -50,10 +53,16 @@ Sets up a Bunny::Client object ready for connection to a broker/server. _Client_
50
53
  @status = :not_connected
51
54
  @frame_max = opts[:frame_max] || 131072
52
55
  @channel_max = opts[:channel_max] || 0
56
+ @heartbeat = opts[:heartbeat] || 0
53
57
  @logger = nil
54
58
  create_logger if @logging
59
+ @channels = []
55
60
  # Create channel 0
56
61
  @channel = Bunny::Channel.new(self, true)
62
+ @exchanges = {}
63
+ @queues = {}
64
+ @heartbeat_in = false
65
+ @connecting = false
57
66
  end
58
67
 
59
68
  =begin rdoc
@@ -91,18 +100,6 @@ Exchange
91
100
 
92
101
  === DESCRIPTION:
93
102
 
94
- Returns hash of exchanges declared by Bunny.
95
-
96
- =end
97
-
98
- def exchanges
99
- @exchanges ||= {}
100
- end
101
-
102
- =begin rdoc
103
-
104
- === DESCRIPTION:
105
-
106
103
  Declares a queue to the broker/server. If the queue does not exist, a new one is created
107
104
  using the arguments passed in. If the queue already exists, a reference to it is created, provided
108
105
  that the arguments passed in do not conflict with the existing attributes of the queue. If an error
@@ -141,19 +138,16 @@ Queue
141
138
 
142
139
  return queues[name] if queues.has_key?(name)
143
140
 
144
- queue = Bunny::Queue.new(self, name, opts)
141
+ Bunny::Queue.new(self, name, opts)
145
142
  end
146
-
147
- =begin rdoc
148
-
149
- === DESCRIPTION:
150
-
151
- Returns hash of queues declared by Bunny.
152
-
153
- =end
154
143
 
155
- def queues
156
- @queues ||= {}
144
+ def send_heartbeat
145
+ # Create a new heartbeat frame
146
+ hb = Qrack::Transport::Heartbeat.new('')
147
+ # Channel 0 must be used
148
+ switch_channel(0) if @channel.number > 0
149
+ # Send the heartbeat to server
150
+ send_frame(hb)
157
151
  end
158
152
 
159
153
  def send_frame(*args)
@@ -169,18 +163,72 @@ Returns hash of queues declared by Bunny.
169
163
  nil
170
164
  end
171
165
 
172
- def next_frame
173
- frame = Qrack::Transport::Frame.parse(buffer)
174
- @logger.info("received") { frame } if @logging
175
- frame
166
+ def next_frame(opts = {})
167
+ secs = opts[:timeout] || 0
168
+
169
+ begin
170
+ Timeout::timeout(secs) do
171
+ @frame = Qrack::Transport::Frame.parse(buffer)
172
+ end
173
+ rescue Timeout::Error
174
+ return :timed_out
175
+ end
176
+
177
+ @logger.info("received") { @frame } if @logging
178
+
179
+ raise Bunny::ConnectionError, 'No connection to server' if (@frame.nil? and !connecting?)
180
+
181
+ if @frame.is_a?(Qrack::Transport::Heartbeat)
182
+ @heartbeat_in = true
183
+ next_frame
184
+ end
185
+
186
+ @frame
176
187
  end
177
188
 
178
189
  def next_payload
179
- frame = next_frame
190
+ frame = next_frame
180
191
  frame.payload
181
192
  end
182
193
 
183
194
  alias next_method next_payload
195
+
196
+ =begin rdoc
197
+
198
+ === DESCRIPTION:
199
+
200
+ Checks to see whether or not an undeliverable message has been returned as a result of a publish
201
+ with the <tt>:immediate</tt> or <tt>:mandatory</tt> options.
202
+
203
+ ==== OPTIONS:
204
+
205
+ * <tt>:timeout => number of seconds (default = 0.1) - The method will wait for a return
206
+ message until this timeout interval is reached.
207
+
208
+ ==== RETURNS:
209
+
210
+ <tt>:no_return</tt> if message was not returned before timeout .
211
+ <tt>{:header, :return_details, :payload}</tt> if message is returned. <tt>:return_details</tt> is
212
+ a hash <tt>{:reply_code, :reply_text, :exchange, :routing_key}</tt>.
213
+
214
+ =end
215
+
216
+ def returned_message(opts = {})
217
+ secs = opts[:timeout] || 0.1
218
+ frame = next_frame(:timeout => secs)
219
+
220
+ if frame.is_a?(Symbol)
221
+ return :no_return if frame == :timed_out
222
+ end
223
+
224
+ method = frame.payload
225
+ header = next_payload
226
+ msg = next_payload
227
+ raise Bunny::MessageError, 'unexpected length' if msg.length < header.size
228
+
229
+ # Return the message and related info
230
+ {:header => header, :payload => msg, :return_details => method.arguments}
231
+ end
184
232
 
185
233
  =begin rdoc
186
234
 
@@ -197,12 +245,9 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
197
245
 
198
246
  def close
199
247
  # Close all active channels
200
- channels.each_value do |c|
248
+ channels.each do |c|
201
249
  c.close if c.open?
202
250
  end
203
-
204
- # Set client channel to zero
205
- self.channel = channels[0]
206
251
 
207
252
  # Close connection to AMQP server
208
253
  close_connection
@@ -235,6 +280,8 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
235
280
  =end
236
281
 
237
282
  def start_session
283
+ @connecting = true
284
+
238
285
  loop do
239
286
  # Create/get socket
240
287
  socket
@@ -246,15 +293,17 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
246
293
  break if open_connection == :ok
247
294
  end
248
295
 
249
- # Open a channel
250
- self.channel = get_channel
251
- channel.open
296
+ # Open another channel because channel zero is used for specific purposes
297
+ c = create_channel()
298
+ c.open
252
299
 
253
300
  # Get access ticket
254
301
  request_access
255
302
 
303
+ @connecting = false
304
+
256
305
  # return status
257
- status
306
+ @status = :connected
258
307
  end
259
308
 
260
309
  alias start start_session
@@ -263,7 +312,7 @@ _Bunny_::_ProtocolError_ is raised. If successful, _Client_._status_ is set to <
263
312
 
264
313
  === DESCRIPTION:
265
314
 
266
- Asks the broker to redeliver all unacknowledged messages on a specifieid channel. Zero or
315
+ Asks the broker to redeliver all unacknowledged messages on a specified channel. Zero or
267
316
  more messages may be redelivered.
268
317
 
269
318
  ==== Options:
@@ -388,23 +437,36 @@ after a rollback.
388
437
  @logging = bool
389
438
  create_logger if @logging
390
439
  end
391
-
392
- def channels
393
- @channels ||= {}
394
- end
395
440
 
396
- def get_channel
397
- channels.each_value do |c|
441
+ def create_channel
442
+ channels.each do |c|
398
443
  return c if (!c.open? and c.number != 0)
399
444
  end
400
445
  # If no channel to re-use instantiate new one
401
446
  Bunny::Channel.new(self)
402
447
  end
403
448
 
449
+ def switch_channel(chann)
450
+ if (0...channels.size).include? chann
451
+ @channel = channels[chann]
452
+ chann
453
+ else
454
+ raise RuntimeError, "Invalid channel number - #{chann}"
455
+ end
456
+ end
457
+
458
+ def connecting?
459
+ connecting
460
+ end
461
+
404
462
  def init_connection
405
463
  write(Qrack::Protocol::HEADER)
406
464
  write([1, 1, Qrack::Protocol::VERSION_MAJOR, Qrack::Protocol::VERSION_MINOR].pack('C4'))
407
- raise Bunny::ProtocolError, 'Connection initiation failed' unless next_method.is_a?(Qrack::Protocol::Connection::Start)
465
+
466
+ frame = next_frame
467
+ if frame.nil? or !frame.payload.is_a?(Qrack::Protocol::Connection::Start)
468
+ raise Bunny::ProtocolError, 'Connection initiation failed'
469
+ end
408
470
  end
409
471
 
410
472
  def open_connection
@@ -417,12 +479,14 @@ after a rollback.
417
479
  )
418
480
  )
419
481
 
420
- method = next_method
421
- raise Bunny::ProtocolError, "Connection failed - user: #{@user}, pass: #{@pass}" if method.nil?
482
+ frame = next_frame
483
+ raise Bunny::ProtocolError, "Connection failed - user: #{@user}, pass: #{@pass}" if frame.nil?
484
+
485
+ method = frame.payload
422
486
 
423
487
  if method.is_a?(Qrack::Protocol::Connection::Tune)
424
488
  send_frame(
425
- Qrack::Protocol::Connection::TuneOk.new( :channel_max => @channel_max, :frame_max => @frame_max, :heartbeat => 0)
489
+ Qrack::Protocol::Connection::TuneOk.new( :channel_max => @channel_max, :frame_max => @frame_max, :heartbeat => @heartbeat)
426
490
  )
427
491
  end
428
492
 
@@ -444,6 +508,9 @@ after a rollback.
444
508
  end
445
509
 
446
510
  def close_connection
511
+ # Set client channel to zero
512
+ switch_channel(0)
513
+
447
514
  send_frame(
448
515
  Qrack::Protocol::Connection::Close.new(:reply_code => 200, :reply_text => 'Goodbye', :class_id => 0, :method_id => 0)
449
516
  )
@@ -477,9 +544,7 @@ after a rollback.
477
544
  def socket
478
545
  return @socket if @socket and (@status == :connected) and not @socket.closed?
479
546
 
480
- begin
481
- @status = :not_connected
482
-
547
+ begin
483
548
  # Attempt to connect.
484
549
  @socket = timeout(CONNECT_TIMEOUT) do
485
550
  TCPSocket.new(host, port)
@@ -488,7 +553,6 @@ after a rollback.
488
553
  if Socket.constants.include? 'TCP_NODELAY'
489
554
  @socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
490
555
  end
491
- @status = :connected
492
556
  rescue => e
493
557
  @status = :not_connected
494
558
  raise Bunny::ServerDownError, e.message