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.
- checksums.yaml +4 -4
- data/ChangeLog.md +5 -0
- data/Gemfile +0 -2
- data/README.md +30 -24
- data/amqp.gemspec +0 -1
- data/lib/amq/protocol/get_response.rb +55 -0
- data/lib/amqp.rb +237 -3
- data/lib/amqp/auth_mechanism_adapter.rb +69 -0
- data/lib/amqp/auth_mechanism_adapter/external.rb +27 -0
- data/lib/amqp/auth_mechanism_adapter/plain.rb +24 -0
- data/lib/amqp/callbacks.rb +67 -0
- data/lib/amqp/channel.rb +517 -76
- data/lib/amqp/consumer.rb +200 -32
- data/lib/amqp/consumer_tag_generator.rb +20 -0
- data/lib/amqp/deferrable.rb +5 -0
- data/lib/amqp/entity.rb +71 -0
- data/lib/amqp/exchange.rb +266 -17
- data/lib/amqp/framing/string/frame.rb +36 -0
- data/lib/amqp/handlers_registry.rb +28 -0
- data/lib/amqp/openable.rb +58 -0
- data/lib/amqp/queue.rb +526 -19
- data/lib/amqp/session.rb +943 -70
- data/lib/amqp/settings.rb +157 -0
- data/lib/amqp/version.rb +1 -1
- data/spec/integration/authentication_spec.rb +1 -1
- data/spec/integration/extensions/rabbitmq/publisher_confirmations_spec.rb +0 -15
- data/spec/integration/store_and_forward_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/amqp/client_spec.rb +3 -1
- data/spec/unit/amqp/connection_spec.rb +10 -41
- metadata +15 -19
- data/lib/amqp/client.rb +0 -100
- data/lib/amqp/connection.rb +0 -223
data/lib/amqp/consumer.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "
|
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
|
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 [
|
38
|
+
# @return [AMQP::ConsumerTagGenerator] Consumer tag generator
|
31
39
|
def self.tag_generator
|
32
|
-
@tag_generator ||=
|
40
|
+
@tag_generator ||= AMQP::ConsumerTagGenerator.new
|
33
41
|
end # self.tag_generator
|
34
42
|
|
35
|
-
# @param [
|
36
|
-
# @return [
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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://
|
204
|
+
# @see http://bit.ly/htCzCX AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
|
143
205
|
def acknowledge(delivery_tag)
|
144
|
-
|
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://
|
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
|
-
|
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
|
-
|
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
|
-
|
261
|
+
self.redefine_callback(:before_recovery, &block)
|
178
262
|
end # before_recovery(&block)
|
179
263
|
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
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
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
data/lib/amqp/entity.rb
ADDED
@@ -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
|
data/lib/amqp/exchange.rb
CHANGED
@@ -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
|
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
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
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
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
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
|
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.
|
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.
|
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
|
-
|
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
|
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
|
-
|
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
|