amqp 1.1.0.pre1 → 1.1.0.pre2

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,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "amq/client/async/consumer"
3
+ require "amqp/consumer_tag_generator"
4
4
 
5
5
  module AMQP
6
6
  # AMQP consumers are entities that handle messages delivered to them ("push API" as opposed to "pull API") by AMQP broker.
@@ -11,7 +11,15 @@ module AMQP
11
11
  # @see AMQP::Queue
12
12
  # @see AMQP::Queue#subscribe
13
13
  # @see AMQP::Queue#cancel
14
- class Consumer < AMQ::Client::Async::Consumer
14
+ class Consumer
15
+
16
+ #
17
+ # Behaviours
18
+ #
19
+
20
+ include Callbacks
21
+ extend ProtocolMethodHandlers
22
+
15
23
 
16
24
  #
17
25
  # API
@@ -27,25 +35,38 @@ module AMQP
27
35
  attr_reader :arguments
28
36
 
29
37
 
30
- # @return [AMQ::Client::ConsumerTagGenerator] Consumer tag generator
38
+ # @return [AMQP::ConsumerTagGenerator] Consumer tag generator
31
39
  def self.tag_generator
32
- @tag_generator ||= AMQ::Client::ConsumerTagGenerator.new
40
+ @tag_generator ||= AMQP::ConsumerTagGenerator.new
33
41
  end # self.tag_generator
34
42
 
35
- # @param [AMQ::Client::ConsumerTagGenerator] Assigns consumer tag generator that will be used by consumer instances
36
- # @return [AMQ::Client::ConsumerTagGenerator] Provided argument
43
+ # @param [AMQP::ConsumerTagGenerator] Assigns consumer tag generator that will be used by consumer instances
44
+ # @return [AMQP::ConsumerTagGenerator] Provided argument
37
45
  def self.tag_generator=(generator)
38
46
  @tag_generator = generator
39
47
  end
40
48
 
41
49
 
42
- def initialize(channel, queue, consumer_tag = nil, exclusive = false, no_ack = false, arguments = {}, no_local = false)
43
- super(channel, queue, (consumer_tag || self.class.tag_generator.generate_for(queue)), exclusive, no_ack, arguments, no_local)
50
+ def initialize(channel, queue, consumer_tag = nil, exclusive = false, no_ack = false, arguments = {}, no_local = false, &block)
51
+ @callbacks = Hash.new
52
+
53
+ @channel = channel || raise(ArgumentError, "channel is nil")
54
+ @connection = channel.connection || raise(ArgumentError, "connection is nil")
55
+ @queue = queue || raise(ArgumentError, "queue is nil")
56
+ @consumer_tag = consumer_tag || self.class.tag_generator.generate_for(queue)
57
+ @exclusive = exclusive
58
+ @no_ack = no_ack
59
+ @arguments = arguments
60
+
61
+ @no_local = no_local
62
+
63
+ self.register_with_channel
64
+ self.register_with_queue
44
65
  end # initialize
45
66
 
46
67
  # @return [Boolean] true if this consumer is exclusive (other consumers for the same queue are not allowed)
47
68
  def exclusive?
48
- super
69
+ !!@exclusive
49
70
  end # exclusive?
50
71
 
51
72
 
@@ -55,7 +76,12 @@ module AMQP
55
76
  def consume(nowait = false, &block)
56
77
  @channel.once_open do
57
78
  @queue.once_declared do
58
- super(nowait, &block)
79
+ @connection.send_frame(AMQ::Protocol::Basic::Consume.encode(@channel.id, @queue.name, @consumer_tag, @no_local, @no_ack, @exclusive, nowait, @arguments))
80
+ self.redefine_callback(:consume, &block)
81
+
82
+ @channel.consumers_awaiting_consume_ok.push(self)
83
+
84
+ self
59
85
  end
60
86
  end
61
87
 
@@ -72,7 +98,10 @@ module AMQP
72
98
  @consumer_tag = self.class.tag_generator.generate_for(@queue)
73
99
  self.register_with_channel
74
100
 
75
- super(&block)
101
+ @connection.send_frame(AMQ::Protocol::Basic::Consume.encode(@channel.id, @queue.name, @consumer_tag, @no_local, @no_ack, @exclusive, block.nil?, @arguments))
102
+ self.redefine_callback(:consume, &block) if block
103
+
104
+ self
76
105
  end
77
106
  end
78
107
 
@@ -83,7 +112,20 @@ module AMQP
83
112
  def cancel(nowait = false, &block)
84
113
  @channel.once_open do
85
114
  @queue.once_declared do
86
- super(nowait, &block)
115
+ @connection.send_frame(AMQ::Protocol::Basic::Cancel.encode(@channel.id, @consumer_tag, nowait))
116
+ self.clear_callbacks(:delivery)
117
+ self.clear_callbacks(:consume)
118
+ self.clear_callbacks(:scancel)
119
+
120
+ self.unregister_with_channel
121
+ self.unregister_with_queue
122
+
123
+ if !nowait
124
+ self.redefine_callback(:cancel, &block)
125
+ @channel.consumers_awaiting_cancel_ok.push(self)
126
+ end
127
+
128
+ self
87
129
  end
88
130
  end
89
131
 
@@ -129,30 +171,67 @@ module AMQP
129
171
  end
130
172
  }
131
173
 
132
- super(&delivery_shim)
174
+ self.append_callback(:delivery, &delivery_shim)
175
+
176
+ self
133
177
  end # on_delivery(&block)
134
178
 
135
179
 
180
+ # @return [String] Readable representation of relevant object state.
181
+ def inspect
182
+ "#<AMQP::Consumer:#{@consumer_tag}> queue=#{@queue.name} channel=#{@channel.id} callbacks=#{@callbacks.inspect}"
183
+ end # inspect
184
+
185
+
186
+ def on_cancel(&block)
187
+ self.append_callback(:scancel, &block)
188
+
189
+ self
190
+ end # on_cancel(&block)
191
+
192
+ def handle_cancel(basic_cancel)
193
+ self.exec_callback(:scancel, basic_cancel)
194
+ end # handle_cancel(basic_cancel)
195
+
196
+
197
+
136
198
  # @group Acknowledging & Rejecting Messages
137
199
 
138
200
  # Acknowledge a delivery tag.
139
201
  # @return [Consumer] self
140
202
  #
141
203
  # @api public
142
- # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
204
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
143
205
  def acknowledge(delivery_tag)
144
- super(delivery_tag)
206
+ @channel.acknowledge(delivery_tag)
207
+
208
+ self
145
209
  end # acknowledge(delivery_tag)
146
210
 
147
211
  #
148
212
  # @return [Consumer] self
149
213
  #
150
214
  # @api public
151
- # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
215
+ # @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
152
216
  def reject(delivery_tag, requeue = true)
153
- super(delivery_tag, requeue)
217
+ @channel.reject(delivery_tag, requeue)
218
+
219
+ self
154
220
  end # reject(delivery_tag, requeue = true)
155
221
 
222
+ # Defines a callback that will be executed when AMQP connection is recovered after a network failure..
223
+ # Only one callback can be defined (the one defined last replaces previously added ones).
224
+ #
225
+ # @api public
226
+ # Defines a callback that will be executed when AMQP connection is recovered after a network failure..
227
+ # Only one callback can be defined (the one defined last replaces previously added ones).
228
+ #
229
+ # @api public
230
+ def on_recovery(&block)
231
+ self.redefine_callback(:after_recovery, &block)
232
+ end # on_recovery(&block)
233
+ alias after_recovery on_recovery
234
+
156
235
  # @endgroup
157
236
 
158
237
 
@@ -163,10 +242,15 @@ module AMQP
163
242
  #
164
243
  # @api public
165
244
  def on_connection_interruption(&block)
166
- super(&block)
245
+ self.redefine_callback(:after_connection_interruption, &block)
167
246
  end # on_connection_interruption(&block)
168
247
  alias after_connection_interruption on_connection_interruption
169
248
 
249
+ # @private
250
+ def handle_connection_interruption(method = nil)
251
+ self.exec_callback_yielding_self(:after_connection_interruption)
252
+ end # handle_connection_interruption
253
+
170
254
 
171
255
  # Defines a callback that will be executed after TCP connection is recovered after a network failure
172
256
  # but before AMQP connection is re-opened.
@@ -174,17 +258,19 @@ module AMQP
174
258
  #
175
259
  # @api public
176
260
  def before_recovery(&block)
177
- super(&block)
261
+ self.redefine_callback(:before_recovery, &block)
178
262
  end # before_recovery(&block)
179
263
 
180
- # Defines a callback that will be executed when AMQP connection is recovered after a network failure..
181
- # Only one callback can be defined (the one defined last replaces previously added ones).
182
- #
183
- # @api public
184
- def on_recovery(&block)
185
- super(&block)
186
- end # on_recovery(&block)
187
- alias after_recovery on_recovery
264
+ # @private
265
+ def run_before_recovery_callbacks
266
+ self.exec_callback_yielding_self(:before_recovery)
267
+ end
268
+
269
+ # @private
270
+ def run_after_recovery_callbacks
271
+ self.exec_callback_yielding_self(:after_recovery)
272
+ end
273
+
188
274
 
189
275
 
190
276
  # Called by associated connection object when AMQP connection has been re-established
@@ -192,17 +278,99 @@ module AMQP
192
278
  #
193
279
  # @api plugin
194
280
  def auto_recover
195
- super
281
+ self.exec_callback_yielding_self(:before_recovery)
282
+ self.resubscribe
283
+ self.exec_callback_yielding_self(:after_recovery)
196
284
  end # auto_recover
197
285
 
198
286
  # @endgroup
199
287
 
200
288
 
201
- # @return [String] Readable representation of relevant object state.
202
- def inspect
203
- "#<AMQP::Consumer:#{@consumer_tag}> queue=#{@queue.name} channel=#{@channel.id} callbacks=#{@callbacks.inspect}"
204
- end # inspect
289
+ def to_s
290
+ "#<#{self.class.name} @consumer_tag=#{@consumer_tag} @queue=#{@queue.name} @channel=#{@channel.id}>"
291
+ end
292
+
293
+
294
+ #
295
+ # Implementation
296
+ #
297
+
298
+ def handle_delivery(basic_deliver, metadata, payload)
299
+ self.exec_callback(:delivery, basic_deliver, metadata, payload)
300
+ end # handle_delivery(basic_deliver, metadata, payload)
301
+
302
+ def handle_consume_ok(consume_ok)
303
+ self.exec_callback_once(:consume, consume_ok)
304
+ end # handle_consume_ok(consume_ok)
305
+
306
+ def handle_cancel_ok(cancel_ok)
307
+ @consumer_tag = nil
205
308
 
309
+ # detach from object graph so that this object will be garbage-collected
310
+ @queue = nil
311
+ @channel = nil
312
+ @connection = nil
206
313
 
314
+ self.exec_callback_once(:cancel, cancel_ok)
315
+ end # handle_cancel_ok(method)
316
+
317
+
318
+
319
+ self.handle(AMQ::Protocol::Basic::ConsumeOk) do |connection, frame|
320
+ channel = connection.channels[frame.channel]
321
+ consumer = channel.consumers_awaiting_consume_ok.shift
322
+
323
+ consumer.handle_consume_ok(frame.decode_payload)
324
+ end
325
+
326
+
327
+ self.handle(AMQ::Protocol::Basic::CancelOk) do |connection, frame|
328
+ channel = connection.channels[frame.channel]
329
+ consumer = channel.consumers_awaiting_cancel_ok.shift
330
+
331
+ consumer.handle_cancel_ok(frame.decode_payload)
332
+ end
333
+
334
+
335
+ self.handle(AMQ::Protocol::Basic::Deliver) do |connection, method_frame, content_frames|
336
+ channel = connection.channels[method_frame.channel]
337
+ basic_deliver = method_frame.decode_payload
338
+ consumer = channel.consumers[basic_deliver.consumer_tag]
339
+
340
+ metadata = content_frames.shift
341
+ payload = content_frames.map { |frame| frame.payload }.join
342
+
343
+ # Handle the delivery only if the consumer still exists.
344
+ # The broker has been known to deliver a few messages after the consumer has been shut down.
345
+ consumer.handle_delivery(basic_deliver, metadata, payload) if consumer
346
+ end
347
+
348
+
349
+ protected
350
+
351
+ def register_with_channel
352
+ @channel.consumers[@consumer_tag] = self
353
+ end # register_with_channel
354
+
355
+ def register_with_queue
356
+ @queue.consumers[@consumer_tag] = self
357
+ end # register_with_queue
358
+
359
+ def unregister_with_channel
360
+ @channel.consumers.delete(@consumer_tag)
361
+ end # register_with_channel
362
+
363
+ def unregister_with_queue
364
+ @queue.consumers.delete(@consumer_tag)
365
+ end # register_with_queue
366
+
367
+ handle(AMQ::Protocol::Basic::Cancel) do |connection, method_frame|
368
+ channel = connection.channels[method_frame.channel]
369
+ basic_cancel = method_frame.decode_payload
370
+ consumer = channel.consumers[basic_cancel.consumer_tag]
371
+
372
+ # Handle the delivery only if the consumer still exists.
373
+ consumer.handle_cancel(basic_cancel) if consumer
374
+ end
207
375
  end # Consumer
208
376
  end # AMQP
@@ -0,0 +1,20 @@
1
+ module AMQP
2
+ class ConsumerTagGenerator
3
+
4
+ #
5
+ # API
6
+ #
7
+
8
+ # @return [String] Generated consumer tag
9
+ def generate
10
+ "#{Kernel.rand}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
11
+ end # generate
12
+
13
+ # @return [String] Generated consumer tag
14
+ def generate_for(queue)
15
+ raise ArgumentError, "argument must respond to :name" unless queue.respond_to?(:name)
16
+
17
+ "#{queue.name}-#{Time.now.to_i * 1000}-#{Kernel.rand(999_999_999_999)}"
18
+ end # generate_for(queue)
19
+ end # ConsumerTagGenerator
20
+ end
@@ -0,0 +1,5 @@
1
+ require "eventmachine"
2
+
3
+ module AMQP
4
+ Deferrable = EventMachine::DefaultDeferrable
5
+ end
@@ -0,0 +1,71 @@
1
+ require "amqp/openable"
2
+ require "amqp/callbacks"
3
+
4
+ module AMQP
5
+ module RegisterEntityMixin
6
+ # @example Registering Channel implementation
7
+ # Adapter.register_entity(:channel, Channel)
8
+ # # ... so then I can do:
9
+ # channel = client.channel(1)
10
+ # # instead of:
11
+ # channel = Channel.new(client, 1)
12
+ def register_entity(name, klass)
13
+ define_method(name) do |*args, &block|
14
+ klass.new(self, *args, &block)
15
+ end # define_method
16
+ end # register_entity
17
+ end # RegisterEntityMixin
18
+
19
+ module ProtocolMethodHandlers
20
+ def handle(klass, &block)
21
+ HandlersRegistry.register(klass, &block)
22
+ end
23
+
24
+ def handlers
25
+ HandlersRegistry.handlers
26
+ end
27
+ end # ProtocolMethodHandlers
28
+
29
+
30
+ # AMQ entities, as implemented by AMQP, have callbacks and can run them
31
+ # when necessary.
32
+ #
33
+ # @note Exchanges and queues implementation is based on this class.
34
+ #
35
+ # @abstract
36
+ module Entity
37
+
38
+ #
39
+ # Behaviors
40
+ #
41
+
42
+ include Openable
43
+ include Callbacks
44
+
45
+ #
46
+ # API
47
+ #
48
+
49
+ # @return [Array<#call>]
50
+ attr_reader :callbacks
51
+
52
+
53
+ def initialize(connection)
54
+ @connection = connection
55
+ # Be careful with default values for #ruby hashes: h = Hash.new(Array.new); h[:key] ||= 1
56
+ # won't assign anything to :key. MK.
57
+ @callbacks = Hash.new
58
+ end # initialize
59
+ end # Entity
60
+
61
+ # Common behavior of AMQ entities that can be either client or server-named, for example, exchanges and queues.
62
+ module ServerNamedEntity
63
+
64
+ # @return [Boolean] true if this entity is anonymous (server-named)
65
+ def server_named?
66
+ @server_named || @name.nil? || @name.empty?
67
+ end
68
+ # backwards compabitility. MK.
69
+ alias anonymous? server_named?
70
+ end # ServerNamedEntity
71
+ end
@@ -1,7 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- require "amq/client/exchange"
4
-
5
3
  module AMQP
6
4
 
7
5
  # h2. What are AMQP exchanges?
@@ -137,13 +135,23 @@ module AMQP
137
135
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.1.1)
138
136
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.1.5)
139
137
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 3.1.3)
140
- class Exchange < AMQ::Client::Exchange
138
+ class Exchange
139
+
140
+
141
+ #
142
+ # Behaviours
143
+ #
144
+
145
+ include Entity
146
+ extend ProtocolMethodHandlers
147
+
141
148
 
142
149
  #
143
150
  # API
144
151
  #
145
152
 
146
153
  DEFAULT_CONTENT_TYPE = "application/octet-stream".freeze
154
+ BUILTIN_TYPES = [:fanout, :direct, :topic, :headers].freeze
147
155
 
148
156
 
149
157
  # The default exchange. Default exchange is a direct exchange that is predefined.
@@ -191,6 +199,13 @@ module AMQP
191
199
  # from the broker arrives.
192
200
  attr_accessor :on_declare
193
201
 
202
+ # Channel this exchange belongs to.
203
+ attr_reader :channel
204
+
205
+ # @return [Hash] Additional arguments given on queue declaration. Typically used by AMQP extensions.
206
+ attr_reader :arguments
207
+
208
+
194
209
  # @return [String]
195
210
  attr_reader :default_routing_key
196
211
  alias key default_routing_key
@@ -301,18 +316,31 @@ module AMQP
301
316
 
302
317
  @status = :unknown
303
318
  @default_publish_options = (opts.delete(:default_publish_options) || {
304
- :routing_key => @default_routing_key,
305
- :mandatory => false,
306
- :immediate => false
307
- }).freeze
319
+ :routing_key => @default_routing_key,
320
+ :mandatory => false,
321
+ :immediate => false
322
+ }).freeze
308
323
 
309
324
  @default_headers = (opts.delete(:default_headers) || {
310
- :content_type => DEFAULT_CONTENT_TYPE,
311
- :persistent => false,
312
- :priority => 0
313
- }).freeze
325
+ :content_type => DEFAULT_CONTENT_TYPE,
326
+ :persistent => false,
327
+ :priority => 0
328
+ }).freeze
329
+
330
+ if !(BUILTIN_TYPES.include?(type.to_sym) || type.to_s =~ /^x-.+/i)
331
+ raise UnknownExchangeTypeError.new(BUILTIN_TYPES, type)
332
+ end
333
+
334
+ @channel = channel
335
+ @name = name
336
+ @type = type
337
+
338
+ # register pre-declared exchanges
339
+ if @name == AMQ::Protocol::EMPTY_STRING || @name =~ /^amq\.(direct|fanout|topic|match|headers)/
340
+ @channel.register_exchange(self)
341
+ end
314
342
 
315
- super(channel.connection, channel, name, type)
343
+ super(channel.connection)
316
344
 
317
345
  # The AMQP 0.8 specification (as well as 0.9.1) in 1.1.4.2 mentiones
318
346
  # that Exchange.Declare-Ok confirms the name of the exchange (because
@@ -338,9 +366,9 @@ module AMQP
338
366
  end
339
367
  end
340
368
 
341
- self.declare(passive = @opts[:passive], durable = @opts[:durable], auto_delete = @opts[:auto_delete], nowait = @opts[:nowait], @opts[:arguments], &shim)
369
+ self.exchange_declare(passive = @opts[:passive], durable = @opts[:durable], auto_delete = @opts[:auto_delete], nowait = @opts[:nowait], @opts[:arguments], &shim)
342
370
  else
343
- self.declare(passive = @opts[:passive], durable = @opts[:durable], auto_delete = @opts[:auto_delete], nowait = @opts[:nowait], @opts[:arguments])
371
+ self.exchange_declare(passive = @opts[:passive], durable = @opts[:durable], auto_delete = @opts[:auto_delete], nowait = @opts[:nowait], @opts[:arguments])
344
372
  end
345
373
  end
346
374
  end
@@ -360,6 +388,42 @@ module AMQP
360
388
  @channel
361
389
  end
362
390
 
391
+ # @return [Boolean] true if this exchange is of type `fanout`
392
+ # @api public
393
+ def fanout?
394
+ @type == :fanout
395
+ end
396
+
397
+ # @return [Boolean] true if this exchange is of type `direct`
398
+ # @api public
399
+ def direct?
400
+ @type == :direct
401
+ end
402
+
403
+ # @return [Boolean] true if this exchange is of type `topic`
404
+ # @api public
405
+ def topic?
406
+ @type == :topic
407
+ end
408
+
409
+ # @return [Boolean] true if this exchange is of type `headers`
410
+ # @api public
411
+ def headers?
412
+ @type == :headers
413
+ end
414
+
415
+ # @return [Boolean] true if this exchange is of a custom type (begins with x-)
416
+ # @api public
417
+ def custom_type?
418
+ @type.to_s =~ /^x-.+/i
419
+ end # custom_type?
420
+
421
+ # @return [Boolean] true if this exchange is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
422
+ def predefined?
423
+ @name && ((@name == AMQ::Protocol::EMPTY_STRING) || !!(@name =~ /^amq\.(direct|fanout|topic|headers|match)/i))
424
+ end # predefined?
425
+
426
+
363
427
  # Publishes message to the exchange. The message will be routed to queues by the exchange
364
428
  # and distributed to any active consumers. Routing logic is determined by exchange type and
365
429
  # configuration as well as message attributes (like :routing_key or message headers).
@@ -475,9 +539,9 @@ module AMQP
475
539
  @channel.once_open do
476
540
  properties = @default_headers.merge(options)
477
541
  properties[:delivery_mode] = properties.delete(:persistent) ? 2 : 1
478
- super(payload.to_s, opts[:key] || opts[:routing_key] || @default_routing_key, properties, opts[:mandatory], opts[:immediate])
542
+ basic_publish(payload.to_s, opts[:key] || opts[:routing_key] || @default_routing_key, properties, opts[:mandatory], opts[:immediate])
479
543
 
480
- # don't pass block to AMQ::Client::Exchange#publish because it will be executed
544
+ # don't pass block to AMQP::Exchange#publish because it will be executed
481
545
  # immediately and we want to do it later. See ruby-amqp/amqp/#67 MK.
482
546
  EventMachine.next_tick(&block) if block
483
547
  end
@@ -506,7 +570,7 @@ module AMQP
506
570
  # @api public
507
571
  def delete(opts = {}, &block)
508
572
  @channel.once_open do
509
- super(opts.fetch(:if_unused, false), opts.fetch(:nowait, false), &block)
573
+ exchange_delete(opts.fetch(:if_unused, false), opts.fetch(:nowait, false), &block)
510
574
  end
511
575
 
512
576
  # backwards compatibility
@@ -544,6 +608,191 @@ module AMQP
544
608
  end
545
609
 
546
610
 
611
+ # @group Declaration
612
+
613
+ # @api public
614
+ def exchange_declare(passive = false, durable = false, auto_delete = false, nowait = false, arguments = nil, &block)
615
+ # for re-declaration
616
+ @passive = passive
617
+ @durable = durable
618
+ @auto_delete = auto_delete
619
+ @arguments = arguments
620
+
621
+ @connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, passive, durable, auto_delete, false, nowait, arguments))
622
+
623
+ unless nowait
624
+ self.define_callback(:declare, &block)
625
+ @channel.exchanges_awaiting_declare_ok.push(self)
626
+ end
627
+
628
+ self
629
+ end
630
+
631
+
632
+ # @api public
633
+ def redeclare(&block)
634
+ nowait = block.nil?
635
+ @connection.send_frame(AMQ::Protocol::Exchange::Declare.encode(@channel.id, @name, @type.to_s, @passive, @durable, @auto_delete, false, nowait, @arguments))
636
+
637
+ unless nowait
638
+ self.define_callback(:declare, &block)
639
+ @channel.exchanges_awaiting_declare_ok.push(self)
640
+ end
641
+
642
+ self
643
+ end # redeclare(&block)
644
+
645
+ # @endgroup
646
+
647
+
648
+ # @api public
649
+ def exchange_delete(if_unused = false, nowait = false, &block)
650
+ @connection.send_frame(AMQ::Protocol::Exchange::Delete.encode(@channel.id, @name, if_unused, nowait))
651
+
652
+ unless nowait
653
+ self.define_callback(:delete, &block)
654
+
655
+ # TODO: delete itself from exchanges cache
656
+ @channel.exchanges_awaiting_delete_ok.push(self)
657
+ end
658
+
659
+ self
660
+ end # delete(if_unused = false, nowait = false)
661
+
662
+
663
+
664
+ # @group Publishing Messages
665
+
666
+ # @api public
667
+ def basic_publish(payload, routing_key = AMQ::Protocol::EMPTY_STRING, user_headers = {}, mandatory = false, immediate = false, frame_size = nil)
668
+ headers = { :priority => 0, :delivery_mode => 2, :content_type => "application/octet-stream" }.merge(user_headers)
669
+ @connection.send_frameset(AMQ::Protocol::Basic::Publish.encode(@channel.id, payload, headers, @name, routing_key, mandatory, immediate, (frame_size || @connection.frame_max)), @channel)
670
+
671
+ # publisher confirms support. MK.
672
+ @channel.exec_callback(:after_publish)
673
+ self
674
+ end
675
+
676
+
677
+ # @api public
678
+ def on_return(&block)
679
+ self.redefine_callback(:return, &block)
680
+
681
+ self
682
+ end # on_return(&block)
683
+
684
+ # @endgroup
685
+
686
+
687
+
688
+ # @group Error Handling and Recovery
689
+
690
+
691
+ # Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
692
+ # Only one callback can be defined (the one defined last replaces previously added ones).
693
+ #
694
+ # @api public
695
+ def on_connection_interruption(&block)
696
+ self.redefine_callback(:after_connection_interruption, &block)
697
+ end # on_connection_interruption(&block)
698
+ alias after_connection_interruption on_connection_interruption
699
+
700
+ # @private
701
+ def handle_connection_interruption(method = nil)
702
+ self.exec_callback_yielding_self(:after_connection_interruption)
703
+ end # handle_connection_interruption
704
+
705
+
706
+
707
+ # Defines a callback that will be executed after TCP connection is recovered after a network failure
708
+ # but before AMQP connection is re-opened.
709
+ # Only one callback can be defined (the one defined last replaces previously added ones).
710
+ #
711
+ # @api public
712
+ def before_recovery(&block)
713
+ self.redefine_callback(:before_recovery, &block)
714
+ end # before_recovery(&block)
715
+
716
+ # @private
717
+ def run_before_recovery_callbacks
718
+ self.exec_callback_yielding_self(:before_recovery)
719
+ end
720
+
721
+
722
+ # Defines a callback that will be executed when AMQP connection is recovered after a network failure..
723
+ # Only one callback can be defined (the one defined last replaces previously added ones).
724
+ #
725
+ # @api public
726
+ def on_recovery(&block)
727
+ self.redefine_callback(:after_recovery, &block)
728
+ end # on_recovery(&block)
729
+ alias after_recovery on_recovery
730
+
731
+ # @private
732
+ def run_after_recovery_callbacks
733
+ self.exec_callback_yielding_self(:after_recovery)
734
+ end
735
+
736
+
737
+ # Called by associated connection object when AMQP connection has been re-established
738
+ # (for example, after a network failure).
739
+ #
740
+ # @api plugin
741
+ def auto_recover
742
+ self.redeclare unless predefined?
743
+ end # auto_recover
744
+
745
+ # @endgroup
746
+
747
+
748
+
749
+ #
750
+ # Implementation
751
+ #
752
+
753
+
754
+ def handle_declare_ok(method)
755
+ @name = method.exchange if self.anonymous?
756
+ @channel.register_exchange(self)
757
+
758
+ self.exec_callback_once_yielding_self(:declare, method)
759
+ end
760
+
761
+ def handle_delete_ok(method)
762
+ self.exec_callback_once(:delete, method)
763
+ end # handle_delete_ok(method)
764
+
765
+
766
+
767
+ self.handle(AMQ::Protocol::Exchange::DeclareOk) do |connection, frame|
768
+ method = frame.decode_payload
769
+ channel = connection.channels[frame.channel]
770
+ exchange = channel.exchanges_awaiting_declare_ok.shift
771
+
772
+ exchange.handle_declare_ok(method)
773
+ end # handle
774
+
775
+
776
+ self.handle(AMQ::Protocol::Exchange::DeleteOk) do |connection, frame|
777
+ channel = connection.channels[frame.channel]
778
+ exchange = channel.exchanges_awaiting_delete_ok.shift
779
+ exchange.handle_delete_ok(frame.decode_payload)
780
+ end # handle
781
+
782
+
783
+ self.handle(AMQ::Protocol::Basic::Return) do |connection, frame, content_frames|
784
+ channel = connection.channels[frame.channel]
785
+ method = frame.decode_payload
786
+ exchange = channel.find_exchange(method.exchange)
787
+
788
+ header = content_frames.shift
789
+ body = content_frames.map { |frame| frame.payload }.join
790
+
791
+ exchange.exec_callback(:return, method, header, body)
792
+ end
793
+
794
+
795
+
547
796
  protected
548
797
 
549
798
  # @private