march_hare 2.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be4969a5a66b26f56f7b8bc7eefcd1f4e1627a5c
4
+ data.tar.gz: 5fde2978b7a8a1fd08b11734a046eb2200839635
5
+ SHA512:
6
+ metadata.gz: ca66334d7d382f363a5ac1cf2f77e619e99125e48b616819f6b3e5a1dd48df4571c55e6e838e88fa6488a1dcd619eeec800f5953f96602487e86050503888f81
7
+ data.tar.gz: a4ef1bd5756dac9a0ee79a9d619c4b57a2e0b19e0332ea3383335b4974780d93c11ff80fabfa9af9f41f6feb2c44fecff56e6904291ba0004330357c7c44c57e
Binary file
@@ -0,0 +1,2 @@
1
+ # Backwards compatibility
2
+ require "march_hare"
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require 'java'
4
+ require 'ext/commons-io'
5
+ require 'ext/rabbitmq-client'
6
+
7
+ require 'march_hare/version'
8
+ require 'march_hare/exceptions'
9
+ require 'march_hare/session'
10
+
11
+ # MarchHare is a JRuby client for RabbitMQ built on top of the official Java client.
12
+ #
13
+ # @see MarchHare.connect
14
+ # @see MarchHare::Session
15
+ # @see MarchHare::Channel
16
+ module MarchHare
17
+ # Delegates to {MarchHare::Session.connect}
18
+ # @see MarchHare::Session.connect
19
+ def self.connect(*args)
20
+ Session.connect(*args)
21
+ end
22
+ end
23
+
24
+ # Backwards compatibility
25
+ # @private
26
+ Hotbunnies = MarchHare
27
+ # Backwards compatibility
28
+ # @private
29
+ HotBunnies = MarchHare
30
+
31
+ require 'march_hare/channel'
32
+ require 'march_hare/queue'
33
+ require 'march_hare/exchange'
@@ -0,0 +1,952 @@
1
+ # encoding: utf-8
2
+ require "march_hare/shutdown_listener"
3
+ require "march_hare/juc"
4
+
5
+ module MarchHare
6
+ # ## Channels in RabbitMQ
7
+ #
8
+ # To quote {http://www.rabbitmq.com/resources/specs/amqp0-9-1.pdf AMQP 0.9.1 specification}:
9
+ #
10
+ # AMQP 0.9.1 is a multi-channelled protocol. Channels provide a way to multiplex
11
+ # a heavyweight TCP/IP connection into several light weight connections.
12
+ # This makes the protocol more “firewall friendly” since port usage is predictable.
13
+ # It also means that traffic shaping and other network QoS features can be easily employed.
14
+ # Channels are independent of each other and can perform different functions simultaneously
15
+ # with other channels, the available bandwidth being shared between the concurrent activities.
16
+ #
17
+ #
18
+ # ## Opening Channels
19
+ #
20
+ # Channels can be opened either via `MarchHare::Session#create_channel` (sufficient in the majority
21
+ # of cases) or by instantiating `MarchHare::Channel` directly:
22
+ #
23
+ # @example Using {MarchHare::Session#create_channel}:
24
+ # conn = MarchHare.new
25
+ # conn.start
26
+ #
27
+ # ch = conn.create_channel
28
+ #
29
+ # This will automatically allocate a channel id.
30
+ #
31
+ # ## Closing Channels
32
+ #
33
+ # Channels are closed via {MarchHare::Channel#close}. Channels that get a channel-level exception are
34
+ # closed, too. Closed channels can no longer be used. Attempts to use them will raise
35
+ # {MarchHare::ChannelAlreadyClosed}.
36
+ #
37
+ # @example
38
+ #
39
+ # ch = conn.create_channel
40
+ # ch.close
41
+ #
42
+ # ## Higher-level API
43
+ #
44
+ # MarchHare offers two sets of methods on {MarchHare::Channel}: known as higher-level and lower-level
45
+ # APIs, respectively. Higher-level API mimics {http://rubyamqp.info amqp gem} API where
46
+ # exchanges and queues are objects (instance of {MarchHare::Exchange} and {MarchHare::Queue}, respectively).
47
+ # Lower-level API is built around AMQP 0.9.1 methods (commands), where queues and exchanges are
48
+ # passed as strings (à la RabbitMQ Java client, {http://clojurerabbitmq.info Langohr} and Pika).
49
+ #
50
+ # ### Queue Operations In Higher-level API
51
+ #
52
+ # * {MarchHare::Channel#queue} is used to declare queues. The rest of the API is in {MarchHare::Queue}.
53
+ #
54
+ #
55
+ # ### Exchange Operations In Higher-level API
56
+ #
57
+ # * {MarchHare::Channel#topic} declares a topic exchange. The rest of the API is in {MarchHare::Exchange}.
58
+ # * {MarchHare::Channel#direct} declares a direct exchange.
59
+ # * {MarchHare::Channel#fanout} declares a fanout exchange.
60
+ # * {MarchHare::Channel#headers} declares a headers exchange.
61
+ # * {MarchHare::Channel#default_exchange}
62
+ # * {MarchHare::Channel#exchange} is used to declare exchanges with type specified as a symbol or string.
63
+ #
64
+ #
65
+ # ## Channel Qos (Prefetch Level)
66
+ #
67
+ # It is possible to control how many messages at most a consumer will be given (before it acknowledges
68
+ # or rejects previously consumed ones). This setting is per channel and controlled via {MarchHare::Channel#prefetch}.
69
+ #
70
+ #
71
+ # ## Channel IDs
72
+ #
73
+ # Channels are identified by their ids which are integers. MarchHare takes care of allocating and
74
+ # releasing them as channels are opened and closed. It is almost never necessary to specify
75
+ # channel ids explicitly.
76
+ #
77
+ # There is a limit on the maximum number of channels per connection, usually 65536. Note
78
+ # that allocating channels is very cheap on both client and server so having tens, hundreds
79
+ # or even thousands of channels is possible.
80
+ #
81
+ # ## Channels and Error Handling
82
+ #
83
+ # Channel-level exceptions are more common than connection-level ones and often indicate
84
+ # issues applications can recover from (such as consuming from or trying to delete
85
+ # a queue that does not exist).
86
+ #
87
+ # With MarchHare, channel-level exceptions are raised as Ruby exceptions, for example,
88
+ # {MarchHare::NotFound}, that provide access to the underlying `channel.close` method
89
+ # information.
90
+ #
91
+ # @example Handling 404 NOT_FOUND
92
+ # begin
93
+ # ch.queue_delete("queue_that_should_not_exist#{rand}")
94
+ # rescue MarchHare::NotFound => e
95
+ # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
96
+ # end
97
+ #
98
+ # @example Handling 406 PRECONDITION_FAILED
99
+ # begin
100
+ # ch2 = conn.create_channel
101
+ # q = "rubymarchhare.examples.recovery.q#{rand}"
102
+ #
103
+ # ch2.queue_declare(q, :durable => false)
104
+ # ch2.queue_declare(q, :durable => true)
105
+ # rescue MarchHare::PreconditionFailed => e
106
+ # puts "Channel-level exception! Code: #{e.channel_close.reply_code}, message: #{e.channel_close.reply_text}"
107
+ # ensure
108
+ # conn.create_channel.queue_delete(q)
109
+ # end
110
+ #
111
+ # @see MarchHare::Session#create_channel
112
+ # @see http://www.rabbitmq.com/tutorials/amqp-concepts.html AMQP 0.9.1 Model Concepts Guide
113
+ # @see http://rubymarchhare.info/articles/getting_started.html Getting Started with RabbitMQ Using MarchHare
114
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers
115
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing
116
+ class Channel
117
+ # @return [Array<MarchHare::Consumer>] Consumers on this channel
118
+ attr_reader :consumers
119
+
120
+ # @private
121
+ def initialize(session, delegate)
122
+ @connection = session
123
+ @delegate = delegate
124
+
125
+ @exchanges = JavaConcurrent::ConcurrentHashMap.new
126
+ @queues = JavaConcurrent::ConcurrentHashMap.new
127
+ # we keep track of consumers in part to gracefully shut down their
128
+ # executors when the channel is closed. This frees library users
129
+ # from having to worry about this. MK.
130
+ @consumers = JavaConcurrent::ConcurrentHashMap.new
131
+ @shutdown_hooks = Array.new
132
+ @recoveries_counter = JavaConcurrent::AtomicInteger.new(0)
133
+
134
+ on_shutdown do |ch, cause|
135
+ ch.gracefully_shut_down_consumers
136
+ end
137
+ end
138
+
139
+ # @return [MarchHare::Session] Connection this channel is on
140
+ def session
141
+ @connection
142
+ end
143
+ alias client session
144
+ alias connection session
145
+
146
+ # @return [Integer] Channel id
147
+ def channel_number
148
+ @delegate.channel_number
149
+ end
150
+ alias id channel_number
151
+ alias number channel_number
152
+
153
+ # Closes the channel.
154
+ #
155
+ # Closed channels can no longer be used. Closed channel id is
156
+ # returned back to the pool of available ids and may be used by
157
+ # a different channel opened later.
158
+ def close(code = 200, reason = "Goodbye")
159
+ v = @delegate.close(code, reason)
160
+
161
+ @consumers.each do |tag, consumer|
162
+ consumer.gracefully_shut_down
163
+ end
164
+
165
+ @connection.unregister_channel(self)
166
+
167
+ v
168
+ end
169
+
170
+ # Defines a shutdown event callback. Shutdown events are
171
+ # broadcasted when a channel is closed, either explicitly
172
+ # or forcefully, or due to a network/peer failure.
173
+ def on_shutdown(&block)
174
+ sh = ShutdownListener.new(self, &block)
175
+
176
+ @shutdown_hooks << sh
177
+ @delegate.add_shutdown_listener(sh)
178
+
179
+ sh
180
+ end
181
+
182
+ # @private
183
+ def automatically_recover(session, java_connection)
184
+ jch = java_connection.create_channel(id)
185
+
186
+ self.revive_with(jch)
187
+ self.recover_shutdown_hooks
188
+
189
+ self.recover_prefetch_setting
190
+ self.recover_exchanges
191
+ # # this includes bindings recovery
192
+ self.recover_queues
193
+ self.recover_consumers
194
+ self.increment_recoveries_counter
195
+ end
196
+
197
+ # @private
198
+ def revive_with(java_ch)
199
+ @delegate = java_ch
200
+ end
201
+
202
+ # @private
203
+ def recover_shutdown_hooks
204
+ @shutdown_hooks.each do |sh|
205
+ @delegate.add_shutdown_listener(sh)
206
+ end
207
+ end
208
+
209
+ # Recovers basic.qos setting. Used by the Automatic Network Failure
210
+ # Recovery feature.
211
+ #
212
+ def recover_prefetch_setting
213
+ basic_qos(@prefetch_count) if @prefetch_count
214
+ end
215
+
216
+ # Recovers exchanges. Used by the Automatic Network Failure
217
+ # Recovery feature.
218
+ #
219
+ def recover_exchanges
220
+ @exchanges.values.each do |x|
221
+ begin
222
+ x.recover_from_network_failure
223
+ rescue Exception => e
224
+ # TODO: logger
225
+ $stderr.puts "Caught exception when recovering exchange #{x.name}"
226
+ end
227
+ end
228
+ end
229
+
230
+ # Recovers queues and bindings. Used by the Automatic Network Failure
231
+ # Recovery feature.
232
+ def recover_queues
233
+ @queues.values.each do |q|
234
+ begin
235
+ q.recover_from_network_failure
236
+ rescue Exception => e
237
+ # TODO: logger
238
+ $stderr.puts "Caught exception when recovering queue #{q.name}"
239
+ end
240
+ end
241
+ end
242
+
243
+ # Recovers consumers. Used by the Automatic Network Failure
244
+ # Recovery feature.
245
+ def recover_consumers
246
+ @consumers.values.each do |c|
247
+ begin
248
+ self.unregister_consumer(c)
249
+ c.recover_from_network_failure
250
+ rescue Exception => e
251
+ # TODO: logger
252
+ $stderr.puts "Caught exception when recovering consumer #{c.consumer_tag}"
253
+ end
254
+ end
255
+ end
256
+
257
+ # @private
258
+ def increment_recoveries_counter
259
+ @recoveries_counter.increment_and_get
260
+ end
261
+
262
+ attr_reader :recoveries_counter
263
+
264
+ # @group Exchanges
265
+
266
+ # Declares a headers exchange or looks it up in the cache of previously
267
+ # declared exchanges.
268
+ #
269
+ # @param [String] name Exchange name
270
+ # @param [Hash] opts Exchange parameters
271
+ #
272
+ # @option options [String,Symbol] :type (:direct) Exchange type, e.g. :fanout or "x-consistent-hash"
273
+ # @option options [Boolean] :durable (false) Should the exchange be durable?
274
+ # @option options [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
275
+ # @option options [Hash] :arguments ({}) Optional exchange arguments
276
+ #
277
+ # @return [MarchHare::Exchange] Exchange instance
278
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
279
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
280
+ def exchange(name, options={})
281
+ dx = Exchange.new(self, name, options).tap do |x|
282
+ x.declare!
283
+ end
284
+
285
+ self.register_exchange(dx)
286
+ end
287
+
288
+ # Declares a fanout exchange or looks it up in the cache of previously
289
+ # declared exchanges.
290
+ #
291
+ # @param [String] name Exchange name
292
+ # @param [Hash] opts Exchange parameters
293
+ #
294
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
295
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
296
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
297
+ #
298
+ # @return [MarchHare::Exchange] Exchange instance
299
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
300
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
301
+ def fanout(name, opts = {})
302
+ dx = Exchange.new(self, name, opts.merge(:type => "fanout")).tap do |x|
303
+ x.declare!
304
+ end
305
+
306
+ self.register_exchange(dx)
307
+ end
308
+
309
+ # Declares a direct exchange or looks it up in the cache of previously
310
+ # declared exchanges.
311
+ #
312
+ # @param [String] name Exchange name
313
+ # @param [Hash] opts Exchange parameters
314
+ #
315
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
316
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
317
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
318
+ #
319
+ # @return [MarchHare::Exchange] Exchange instance
320
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
321
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
322
+ def direct(name, opts = {})
323
+ dx = Exchange.new(self, name, opts.merge(:type => "direct")).tap do |x|
324
+ x.declare!
325
+ end
326
+
327
+ self.register_exchange(dx)
328
+ end
329
+
330
+ # Declares a topic exchange or looks it up in the cache of previously
331
+ # declared exchanges.
332
+ #
333
+ # @param [String] name Exchange name
334
+ # @param [Hash] opts Exchange parameters
335
+ #
336
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
337
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
338
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments (used by RabbitMQ extensions)
339
+ #
340
+ # @return [MarchHare::Exchange] Exchange instance
341
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
342
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
343
+ def topic(name, opts = {})
344
+ dx = Exchange.new(self, name, opts.merge(:type => "topic")).tap do |x|
345
+ x.declare!
346
+ end
347
+
348
+ self.register_exchange(dx)
349
+ end
350
+
351
+ # Declares a headers exchange or looks it up in the cache of previously
352
+ # declared exchanges.
353
+ #
354
+ # @param [String] name Exchange name
355
+ # @param [Hash] opts Exchange parameters
356
+ #
357
+ # @option opts [Boolean] :durable (false) Should the exchange be durable?
358
+ # @option opts [Boolean] :auto_delete (false) Should the exchange be automatically deleted when no longer in use?
359
+ # @option opts [Hash] :arguments ({}) Optional exchange arguments
360
+ #
361
+ # @return [MarchHare::Exchange] Exchange instance
362
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
363
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions to AMQP 0.9.1 guide
364
+ def headers(name, opts = {})
365
+ dx = Exchange.new(self, name, opts.merge(:type => "headers")).tap do |x|
366
+ x.declare!
367
+ end
368
+
369
+ self.register_exchange(dx)
370
+ end
371
+
372
+ # Provides access to the default exchange
373
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
374
+ def default_exchange
375
+ @default_exchange ||= self.exchange("", :durable => true, :auto_delete => false, :type => "direct")
376
+ end
377
+
378
+ # Declares a echange using echange.declare AMQP 0.9.1 method.
379
+ #
380
+ # @param [String] name Exchange name
381
+ # @param [Boolean] durable (false) Should information about this echange be persisted to disk so that it
382
+ # can survive broker restarts? Typically set to true for long-lived exchanges.
383
+ # @param [Boolean] auto_delete (false) Should this echange be deleted when it is no longer used?
384
+ # @param [Boolean] passive (false) If true, exchange will be checked for existence. If it does not
385
+ # exist, {MarchHare::NotFound} will be raised.
386
+ #
387
+ # @return RabbitMQ response
388
+ # @see http://rubymarchhare.info/articles/echanges.html Exchanges and Publishing guide
389
+ def exchange_declare(name, type, durable = false, auto_delete = false, arguments = nil)
390
+ @delegate.exchange_declare(name, type, durable, auto_delete, arguments)
391
+ end
392
+
393
+ # Binds an exchange to another exchange using exchange.bind method (RabbitMQ extension)
394
+ #
395
+ # @param [String] desitnation Destination exchange name
396
+ # @param [String] source Source exchange name
397
+ #
398
+ # @param [String] routing_key Routing key used for binding
399
+ # @param [Hash] arguments (nil) Optional arguments
400
+ #
401
+ # @return RabbitMQ response
402
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ extensions guide
403
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
404
+ def exchange_bind(destination, source, routing_key, arguments = nil)
405
+ @delegate.exchange_bind(destination, source, routing_key, arguments)
406
+ end
407
+
408
+ # Unbinds an exchange from another exchange using exchange.unbind method (RabbitMQ extension)
409
+ #
410
+ # @param [String] destination Destination exchange name
411
+ # @param [String] source Source exchange name
412
+ #
413
+ # @param [String] routing_key Routing key used for binding
414
+ # @param [Hash] arguments ({}) Optional arguments
415
+ #
416
+ # @return RabbitMQ response
417
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ extensions guide
418
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
419
+ def exchange_unbind(destination, source, routing_key, arguments = nil)
420
+ @delegate.exchange_unbind(destination, source, routing_key, arguments)
421
+ end
422
+
423
+ # @endgroup
424
+
425
+
426
+ # @group Queues
427
+
428
+ # Declares a queue or looks it up in the per-channel cache.
429
+ #
430
+ # @param [String] name Queue name. Pass an empty string to declare a server-named queue (make RabbitMQ generate a unique name).
431
+ # @param [Hash] options Queue properties and other options
432
+ #
433
+ # @option options [Boolean] :durable (false) Should this queue be durable?
434
+ # @option options [Boolean] :auto-delete (false) Should this queue be automatically deleted when the last consumer disconnects?
435
+ # @option options [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
436
+ # @option options [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
437
+ #
438
+ # @return [MarchHare::Queue] Queue that was declared or looked up in the cache
439
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
440
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions guide
441
+ def queue(name, options={})
442
+ dq = Queue.new(self, name, options).tap do |q|
443
+ q.declare!
444
+ end
445
+
446
+ self.register_queue(dq)
447
+ end
448
+
449
+ # Declares a queue using queue.declare AMQP 0.9.1 method.
450
+ #
451
+ # @param [String] name Queue name
452
+ #
453
+ # @param [Boolean] durable (false) Should information about this queue be persisted to disk so that it
454
+ # can survive broker restarts? Typically set to true for long-lived queues.
455
+ # @param [Boolean] auto_delete (false) Should this queue be deleted when the last consumer is cancelled?
456
+ # @param [Boolean] exclusive (false) Should only this connection be able to use this queue?
457
+ # If true, the queue will be automatically deleted when this
458
+ # connection is closed
459
+ # @param [Boolean] passive (false) If true, queue will be checked for existence. If it does not
460
+ # exist, {MarchHare::NotFound} will be raised.
461
+ #
462
+ # @return RabbitMQ response
463
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
464
+ def queue_declare(name, durable, exclusive, auto_delete, arguments = {})
465
+ converting_rjc_exceptions_to_ruby do
466
+ @delegate.queue_declare(name, durable, exclusive, auto_delete, arguments)
467
+ end
468
+ end
469
+
470
+ # Checks if a queue exists using queue.declare AMQP 0.9.1 method.
471
+ # If it does not, a channel exception will be raised.
472
+ #
473
+ # @param [String] name Queue name
474
+ #
475
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
476
+ def queue_declare_passive(name)
477
+ converting_rjc_exceptions_to_ruby do
478
+ @delegate.queue_declare_passive(name)
479
+ end
480
+ end
481
+
482
+ # Deletes a queue using queue.delete AMQP 0.9.1 method
483
+ #
484
+ # @param [String] name Queue name
485
+ #
486
+ # @param [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
487
+ # @param [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
488
+ #
489
+ # @return RabbitMQ response
490
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
491
+ def queue_delete(name, if_empty = false, if_unused = false)
492
+ converting_rjc_exceptions_to_ruby do
493
+ @delegate.queue_delete(name, if_empty, if_unused)
494
+ end
495
+ end
496
+
497
+ # Binds a queue to an exchange using queue.bind AMQP 0.9.1 method
498
+ #
499
+ # @param [String] name Queue name
500
+ # @param [String] exchange Exchange name
501
+ #
502
+ # @param [String] routing_key Routing key used for binding
503
+ # @param [Hash] arguments (nil) Optional arguments
504
+ #
505
+ # @return RabbitMQ response
506
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
507
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
508
+ def queue_bind(queue, exchange, routing_key, arguments = nil)
509
+ converting_rjc_exceptions_to_ruby do
510
+ @delegate.queue_bind(queue, exchange, routing_key, arguments)
511
+ end
512
+ end
513
+
514
+ # Unbinds a queue from an exchange using queue.unbind AMQP 0.9.1 method
515
+ #
516
+ # @param [String] name Queue name
517
+ # @param [String] exchange Exchange name
518
+ #
519
+ # @param [String] routing_key Routing key used for binding
520
+ # @param [Hash] arguments ({}) Optional arguments
521
+ #
522
+ # @return RabbitMQ response
523
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
524
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
525
+ def queue_unbind(queue, exchange, routing_key, arguments = nil)
526
+ converting_rjc_exceptions_to_ruby do
527
+ @delegate.queue_unbind(queue, exchange, routing_key, arguments)
528
+ end
529
+ end
530
+
531
+ # Purges a queue (removes all messages from it) using queue.purge AMQP 0.9.1 method.
532
+ #
533
+ # @param [String] name Queue name
534
+ #
535
+ # @return RabbitMQ response
536
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
537
+ def queue_purge(name)
538
+ converting_rjc_exceptions_to_ruby do
539
+ @delegate.queue_purge(name)
540
+ end
541
+ end
542
+
543
+ # @endgroup
544
+
545
+
546
+ # @group basic.*
547
+
548
+ # Publishes a message using basic.publish AMQP 0.9.1 method.
549
+ #
550
+ # @param [String] exchange Exchange to publish to
551
+ # @param [String] routing_key Routing key
552
+ # @param [String] body Message payload. It will never be modified by MarchHare or RabbitMQ in any way.
553
+ # @option opts [Boolean] :mandatory Should the message be returned if it cannot be routed to any queue?
554
+ #
555
+ # @param [Hash] properties Message properties
556
+ #
557
+ # @option properties [Boolean] :persistent Should the message be persisted to disk?
558
+ # @option properties [Integer] :timestamp A timestamp associated with this message
559
+ # @option properties [Integer] :expiration Expiration time after which the message will be deleted
560
+ # @option properties [String] :type Message type, e.g. what type of event or command this message represents. Can be any string
561
+ # @option properties [String] :reply_to Queue name other apps should send the response to
562
+ # @option properties [String] :content_type Message content type (e.g. application/json)
563
+ # @option properties [String] :content_encoding Message content encoding (e.g. gzip)
564
+ # @option properties [String] :correlation_id Message correlated to this one, e.g. what request this message is a reply for
565
+ # @option properties [Integer] :priority Message priority, 0 to 9. Not used by RabbitMQ, only applications
566
+ # @option properties [String] :message_id Any message identifier
567
+ # @option properties [String] :user_id Optional user ID. Verified by RabbitMQ against the actual connection username
568
+ # @option properties [String] :app_id Optional application ID
569
+ #
570
+ # @return [MarchHare::Channel] Self
571
+ def basic_publish(exchange, routing_key, mandatory, properties, body)
572
+ converting_rjc_exceptions_to_ruby do
573
+ @delegate.basic_publish(exchange, routing_key, mandatory, false, BasicPropertiesBuilder.build_properties_from(properties || Hash.new), body)
574
+ end
575
+ end
576
+
577
+ def basic_get(queue, auto_ack)
578
+ converting_rjc_exceptions_to_ruby do
579
+ @delegate.basic_get(queue, auto_ack)
580
+ end
581
+ end
582
+
583
+ def basic_consume(queue, auto_ack, consumer)
584
+ consumer.auto_ack = auto_ack
585
+ tag = converting_rjc_exceptions_to_ruby do
586
+ @delegate.basic_consume(queue, auto_ack, consumer)
587
+ end
588
+ self.register_consumer(tag, consumer)
589
+
590
+ tag
591
+ end
592
+
593
+ def basic_qos(prefetch_count)
594
+ r = converting_rjc_exceptions_to_ruby do
595
+ @delegate.basic_qos(prefetch_count)
596
+ end
597
+ @prefetch_count = prefetch_count
598
+
599
+ r
600
+ end
601
+
602
+ def qos(options={})
603
+ if options.size == 1 && options[:prefetch_count]
604
+ then basic_qos(options[:prefetch_count])
605
+ else basic_qos(options.fetch(:prefetch_size, 0), options.fetch(:prefetch_count, 0), options.fetch(:global, false))
606
+ end
607
+ end
608
+
609
+ # Sets how many messages will be given to consumers on this channel before they
610
+ # have to acknowledge or reject one of the previously consumed messages
611
+ #
612
+ # @param [Integer] prefetch_count Prefetch (QoS setting) for this channel
613
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishing guide
614
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
615
+ def prefetch=(n)
616
+ basic_qos(n)
617
+ end
618
+
619
+ # Acknowledges a message. Acknowledged messages are completely removed from the queue.
620
+ #
621
+ # @param [Integer] delivery_tag Delivery tag to acknowledge
622
+ # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be acknowledged as well?
623
+ # @see #nack
624
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
625
+ def ack(delivery_tag, multiple = false)
626
+ guarding_against_stale_delivery_tags(delivery_tag) do
627
+ basic_ack(delivery_tag.to_i, multiple)
628
+ end
629
+ end
630
+ alias acknowledge ack
631
+
632
+ # Rejects a message. A rejected message can be requeued or
633
+ # dropped by RabbitMQ.
634
+ #
635
+ # @param [Integer] delivery_tag Delivery tag to reject
636
+ # @param [Boolean] requeue Should this message be requeued instead of dropping it?
637
+ # @see #ack
638
+ # @see #nack
639
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
640
+ def reject(delivery_tag, requeue = false)
641
+ guarding_against_stale_delivery_tags(delivery_tag) do
642
+ basic_reject(delivery_tag.to_i, requeue)
643
+ end
644
+ end
645
+
646
+ # Rejects a message. A rejected message can be requeued or
647
+ # dropped by RabbitMQ. This method is similar to {MarchHare::Channel#reject} but
648
+ # supports rejecting multiple messages at once, and is usually preferred.
649
+ #
650
+ # @param [Integer] delivery_tag Delivery tag to reject
651
+ # @param [Boolean] multiple (false) Should all unacknowledged messages up to this be rejected as well?
652
+ # @param [Boolean] requeue (false) Should this message be requeued instead of dropping it?
653
+ # @see #ack
654
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
655
+ def nack(delivery_tag, multiple = false, requeue = false)
656
+ guarding_against_stale_delivery_tags(delivery_tag) do
657
+ basic_nack(delivery_tag.to_i, multiple, requeue)
658
+ end
659
+ end
660
+
661
+ # Rejects or requeues a message.
662
+ #
663
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
664
+ # @param [Boolean] requeue Should the message be requeued?
665
+ # @return [NilClass] nil
666
+ #
667
+ # @example Requeue a message
668
+ # conn = MarchHare.new
669
+ # conn.start
670
+ #
671
+ # ch = conn.create_channel
672
+ # q.subscribe do |delivery_info, properties, payload|
673
+ # # requeue the message
674
+ # ch.basic_reject(delivery_info.delivery_tag, true)
675
+ # end
676
+ #
677
+ # @example Reject a message
678
+ # conn = MarchHare.new
679
+ # conn.start
680
+ #
681
+ # ch = conn.create_channel
682
+ # q.subscribe do |delivery_info, properties, payload|
683
+ # # requeue the message
684
+ # ch.basic_reject(delivery_info.delivery_tag, false)
685
+ # end
686
+ #
687
+ # @example Requeue a message fetched via basic.get
688
+ # conn = MarchHare.new
689
+ # conn.start
690
+ #
691
+ # ch = conn.create_channel
692
+ # # we assume the queue exists and has messages
693
+ # delivery_info, properties, payload = ch.basic_get("bunny.examples.queue3", :ack => true)
694
+ # ch.basic_reject(delivery_info.delivery_tag, true)
695
+ #
696
+ # @see #basic_nack
697
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
698
+ def basic_reject(delivery_tag, requeue)
699
+ converting_rjc_exceptions_to_ruby do
700
+ @delegate.basic_reject(delivery_tag.to_i, requeue)
701
+ end
702
+ end
703
+
704
+ # Acknowledges one or more messages (deliveries).
705
+ #
706
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
707
+ # @param [Boolean] multiple Should all deliveries up to this one be acknowledged?
708
+ # @return [NilClass] nil
709
+ #
710
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
711
+ def basic_ack(delivery_tag, multiple)
712
+ converting_rjc_exceptions_to_ruby do
713
+ @delegate.basic_ack(delivery_tag.to_i, multiple)
714
+ end
715
+ end
716
+
717
+ # Rejects or requeues messages just like {MarchHare::Channel#basic_reject} but can do so
718
+ # with multiple messages at once.
719
+ #
720
+ # @param [Integer] delivery_tag Delivery tag obtained from delivery info
721
+ # @param [Boolean] requeue Should the message be requeued?
722
+ # @param [Boolean] multiple Should all deliveries up to this one be rejected/requeued?
723
+ # @return [NilClass] nil
724
+ #
725
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
726
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions guide
727
+ def basic_nack(delivery_tag, multiple = false, requeue = false)
728
+ converting_rjc_exceptions_to_ruby do
729
+ @delegate.basic_nack(delivery_tag.to_i, multiple, requeue)
730
+ end
731
+ end
732
+
733
+ # Redeliver unacknowledged messages
734
+ #
735
+ # @param [Boolean] requeue Should messages be requeued?
736
+ # @return RabbitMQ response
737
+ def basic_recover(requeue = true)
738
+ converting_rjc_exceptions_to_ruby do
739
+ @delegate.basic_recover(requeue)
740
+ end
741
+ end
742
+
743
+ # Redeliver unacknowledged messages
744
+ #
745
+ # @param [Boolean] requeue Should messages be requeued?
746
+ # @return RabbitMQ response
747
+ def basic_recover_async(requeue = true)
748
+ converting_rjc_exceptions_to_ruby do
749
+ @delegate.basic_recover_async(requeue)
750
+ end
751
+ end
752
+
753
+ # @endgroup
754
+
755
+ # Enables publisher confirms on the channel.
756
+ # @return [NilClass] nil
757
+ #
758
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishers guide
759
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions guide
760
+ def confirm_select
761
+ converting_rjc_exceptions_to_ruby do
762
+ @delegate.confirm_select
763
+ end
764
+ end
765
+
766
+ # Waits until all outstanding publisher confirms arrive.
767
+ #
768
+ # Takes an optional timeout in milliseconds. Will raise
769
+ # an exception in timeout has occured.
770
+ #
771
+ # @param [Integer] timeout Timeout in milliseconds
772
+ # @return [Boolean] true if all confirms were positive,
773
+ # false if some were negative
774
+ def wait_for_confirms(timeout = nil)
775
+ if timeout
776
+ converting_rjc_exceptions_to_ruby do
777
+ @delegate.wait_for_confirms(timeout)
778
+ end
779
+ else
780
+ @delegate.wait_for_confirms
781
+ end
782
+ end
783
+
784
+ def next_publisher_seq_no
785
+ @delegate.next_publisher_seq_no
786
+ end
787
+
788
+ # Enables transactions on the channel
789
+ def tx_select
790
+ converting_rjc_exceptions_to_ruby do
791
+ @delegate.tx_select
792
+ end
793
+ end
794
+
795
+ # Commits a transaction
796
+ def tx_commit
797
+ converting_rjc_exceptions_to_ruby do
798
+ @delegate.tx_commit
799
+ end
800
+ end
801
+
802
+ # Rolls back a transaction
803
+ def tx_rollback
804
+ converting_rjc_exceptions_to_ruby do
805
+ @delegate.tx_rollback
806
+ end
807
+ end
808
+
809
+ # Enables or disables channel flow. This feature id deprecated
810
+ # in RabbitMQ.
811
+ def channel_flow(active)
812
+ converting_rjc_exceptions_to_ruby do
813
+ @delegate.channel_flow(active)
814
+ end
815
+ end
816
+
817
+ # Defines a returned message handler.
818
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishers guide
819
+ def on_return(&block)
820
+ self.add_return_listener(BlockReturnListener.from(block))
821
+ end
822
+
823
+ # Defines a publisher confirm handler
824
+ # @see http://rubymarchhare.info/articles/exchanges.html Exchanges and Publishers guide
825
+ def on_confirm(&block)
826
+ self.add_confirm_listener(BlockConfirmListener.from(block))
827
+ end
828
+
829
+ def method_missing(selector, *args)
830
+ @delegate.__send__(selector, *args)
831
+ end
832
+
833
+
834
+ #
835
+ # Implementation
836
+ #
837
+
838
+ # @private
839
+ class BlockConfirmListener
840
+ include com.rabbitmq.client.ConfirmListener
841
+
842
+ def self.from(block)
843
+ new(block)
844
+ end
845
+
846
+ def initialize(block)
847
+ @block = block
848
+ end
849
+
850
+ def handleAck(delivery_tag, multiple)
851
+ @block.call(:ack, delivery_tag, multiple)
852
+ end
853
+
854
+ def handleNack(delivery_tag, multiple)
855
+ @block.call(:nack, delivery_tag, multiple)
856
+ end
857
+ end
858
+
859
+ # @private
860
+ class BlockReturnListener
861
+ include com.rabbitmq.client.ReturnListener
862
+
863
+ def self.from(block)
864
+ new(block)
865
+ end
866
+
867
+ def initialize(block)
868
+ @block = block
869
+ end
870
+
871
+ def handleReturn(reply_code, reply_text, exchange, routing_key, basic_properties, payload)
872
+ # TODO: convert properties to a Ruby hash
873
+ @block.call(reply_code, reply_text, exchange, routing_key, basic_properties, String.from_java_bytes(payload))
874
+ end
875
+ end
876
+
877
+ # @private
878
+ def deregister_queue(queue)
879
+ @queues.delete(queue.name)
880
+ end
881
+
882
+ # @private
883
+ def deregister_queue_named(name)
884
+ @queues.delete(name)
885
+ end
886
+
887
+ # @private
888
+ def register_queue(queue)
889
+ @queues[queue.name] = queue
890
+ end
891
+
892
+ # @private
893
+ def find_queue(name)
894
+ @queues[name]
895
+ end
896
+
897
+ # @private
898
+ def deregister_exchange(exchange)
899
+ @exchanges.delete(exchange.name)
900
+ end
901
+
902
+ # @private
903
+ def register_exchange(exchange)
904
+ @exchanges[exchange.name] = exchange
905
+ end
906
+
907
+ # @private
908
+ def register_consumer(consumer_tag, consumer)
909
+ @consumers[consumer_tag] = consumer
910
+ end
911
+
912
+ # @private
913
+ def unregister_consumer(consumer_tag)
914
+ @consumers.delete(consumer_tag)
915
+ end
916
+
917
+ # @private
918
+ def gracefully_shut_down_consumers
919
+ @consumers.each do |tag, consumer|
920
+ consumer.gracefully_shut_down
921
+ end
922
+ end
923
+
924
+ # Executes a block, catching Java exceptions RabbitMQ Java client throws and
925
+ # transforms them to Ruby exceptions that are then re-raised.
926
+ #
927
+ # @private
928
+ def converting_rjc_exceptions_to_ruby(&block)
929
+ begin
930
+ block.call
931
+ rescue Exception, java.lang.Throwable => e
932
+ Exceptions.convert_and_reraise(e)
933
+ end
934
+ end
935
+
936
+ # @private
937
+ def guarding_against_stale_delivery_tags(tag, &block)
938
+ case tag
939
+ # if a fixnum was passed, execute unconditionally. MK.
940
+ when Fixnum then
941
+ block.call
942
+ # versioned delivery tags should be checked to avoid
943
+ # sending out stale (invalid) tags after channel was reopened
944
+ # during network failure recovery. MK.
945
+ when VersionedDeliveryTag then
946
+ if !tag.stale?(@recoveries_counter.get)
947
+ block.call
948
+ end
949
+ end
950
+ end
951
+ end
952
+ end