march_hare 2.0.0-java

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.
@@ -0,0 +1,9 @@
1
+ module MarchHare
2
+ module JavaConcurrent
3
+ java_import 'java.lang.Thread'
4
+ java_import 'java.lang.Runnable'
5
+ java_import 'java.lang.InterruptedException'
6
+ include_package 'java.util.concurrent'
7
+ include_package 'java.util.concurrent.atomic'
8
+ end
9
+ end
@@ -0,0 +1,93 @@
1
+ module MarchHare
2
+ class Headers
3
+ attr_reader :channel, :consumer_tag, :envelope, :properties
4
+
5
+ def initialize(channel, consumer_tag, envelope, properties)
6
+ @channel = channel
7
+ @consumer_tag = consumer_tag
8
+ @envelope = envelope
9
+ @properties = properties
10
+ end
11
+
12
+ def ack(options={})
13
+ @channel.basic_ack(delivery_tag, options.fetch(:multiple, false))
14
+ end
15
+
16
+ def reject(options={})
17
+ @channel.basic_reject(delivery_tag, options.fetch(:requeue, false))
18
+ end
19
+
20
+ begin :envelope_delegation
21
+ [
22
+ :routing_key,
23
+ :redeliver,
24
+ :exchange
25
+ ].each do |envelope_property|
26
+ define_method(envelope_property) { @envelope.__send__(envelope_property) }
27
+ end
28
+
29
+ alias_method :redelivered?, :redeliver
30
+ end
31
+
32
+ def delivery_tag
33
+ @delivery_tag ||= VersionedDeliveryTag.new(@envelope.delivery_tag, @channel.recoveries_counter.get)
34
+ end
35
+
36
+ begin :message_properties_delegation
37
+ [
38
+ :content_encoding,
39
+ :content_type,
40
+ :content_encoding,
41
+ :headers,
42
+ :delivery_mode,
43
+ :priority,
44
+ :correlation_id,
45
+ :reply_to,
46
+ :expiration,
47
+ :message_id,
48
+ :timestamp,
49
+ :type,
50
+ :user_id,
51
+ :app_id,
52
+ :cluster_id
53
+ ].each do |properties_property|
54
+ define_method(properties_property) { @properties.__send__(properties_property) }
55
+ end # each
56
+ end
57
+
58
+ def persistent?
59
+ delivery_mode == 2
60
+ end
61
+
62
+ def redelivered?
63
+ redeliver
64
+ end
65
+
66
+ def redelivery?
67
+ redeliver
68
+ end
69
+ end # Headers
70
+
71
+
72
+ class BasicPropertiesBuilder
73
+ def self.build_properties_from(props = {})
74
+ builder = AMQP::BasicProperties::Builder.new
75
+
76
+ builder.content_type(props[:content_type]).
77
+ content_encoding(props[:content_encoding]).
78
+ headers(props[:headers]).
79
+ delivery_mode(props[:persistent] ? 2 : 1).
80
+ priority(props[:priority]).
81
+ correlation_id(props[:correlation_id]).
82
+ reply_to(props[:reply_to]).
83
+ expiration(if props[:expiration] then props[:expiration].to_s end).
84
+ message_id(props[:message_id]).
85
+ timestamp(props[:timestamp]).
86
+ type(props[:type]).
87
+ user_id(props[:user_id]).
88
+ app_id(props[:app_id]).
89
+ cluster_id(props[:cluster_id]).
90
+ build
91
+ end
92
+ end
93
+ end # MarchHare
@@ -0,0 +1,265 @@
1
+ # encoding: utf-8
2
+
3
+ require "march_hare/juc"
4
+ require "march_hare/metadata"
5
+ require "march_hare/consumers"
6
+ require "set"
7
+
8
+ module MarchHare
9
+ # Represents AMQP 0.9.1 queue.
10
+ #
11
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
12
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions guide
13
+ class Queue
14
+ # @return [MarchHare::Channel] Channel this queue uses
15
+ attr_reader :channel
16
+ # @return [String] Queue name
17
+ attr_reader :name
18
+
19
+ # @param [MarchHare::Channel] channel_or_connection Channel this queue will use.
20
+ # @param [String] name Queue name. Pass an empty string to make RabbitMQ generate a unique one.
21
+ # @param [Hash] opts Queue properties
22
+ #
23
+ # @option opts [Boolean] :durable (false) Should this queue be durable?
24
+ # @option opts [Boolean] :auto_delete (false) Should this queue be automatically deleted when the last consumer disconnects?
25
+ # @option opts [Boolean] :exclusive (false) Should this queue be exclusive (only can be used by this connection, removed when the connection is closed)?
26
+ # @option opts [Boolean] :arguments ({}) Additional optional arguments (typically used by RabbitMQ extensions and plugins)
27
+ #
28
+ # @see MarchHare::Channel#queue
29
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
30
+ # @see http://rubymarchhare.info/articles/extensions.html RabbitMQ Extensions guide
31
+ def initialize(channel, name, options={})
32
+ @channel = channel
33
+ @name = name
34
+ @options = {:durable => false, :exclusive => false, :auto_delete => false, :passive => false, :arguments => Hash.new}.merge(options)
35
+
36
+ @durable = @options[:durable]
37
+ @exclusive = @options[:exclusive]
38
+ @server_named = @name.empty?
39
+ @auto_delete = @options[:auto_delete]
40
+ @arguments = @options[:arguments]
41
+
42
+ @bindings = Set.new
43
+ end
44
+
45
+
46
+ # @return [Boolean] true if this queue was declared as durable (will survive broker restart).
47
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
48
+ def durable?
49
+ @durable
50
+ end # durable?
51
+
52
+ # @return [Boolean] true if this queue was declared as exclusive (limited to just one consumer)
53
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
54
+ def exclusive?
55
+ @exclusive
56
+ end # exclusive?
57
+
58
+ # @return [Boolean] true if this queue was declared as automatically deleted (deleted as soon as last consumer unbinds).
59
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
60
+ def auto_delete?
61
+ @auto_delete
62
+ end # auto_delete?
63
+
64
+ # @return [Boolean] true if this queue was declared as server named.
65
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
66
+ def server_named?
67
+ @server_named
68
+ end # server_named?
69
+
70
+ # @return [Hash] Additional optional arguments (typically used by RabbitMQ extensions and plugins)
71
+ def arguments
72
+ @arguments
73
+ end
74
+
75
+
76
+
77
+ # Binds queue to an exchange
78
+ #
79
+ # @param [MarchHare::Exchange,String] exchange Exchange to bind to
80
+ # @param [Hash] options Binding properties
81
+ #
82
+ # @option options [String] :routing_key Routing key
83
+ # @option options [Hash] :arguments ({}) Additional optional binding arguments
84
+ #
85
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
86
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
87
+ def bind(exchange, options={})
88
+ exchange_name = if exchange.respond_to?(:name) then
89
+ exchange.name
90
+ else
91
+ exchange.to_s
92
+ end
93
+ @channel.queue_bind(@name, exchange_name, (options[:routing_key] || options[:key] || ""), options[:arguments])
94
+
95
+ # store bindings for automatic recovery. We need to be very careful to
96
+ # not cause an infinite rebinding loop here when we recover. MK.
97
+ binding = { :exchange => exchange_name, :routing_key => (options[:routing_key] || options[:key]), :arguments => options[:arguments] }
98
+ @bindings << binding unless @bindings.include?(binding)
99
+
100
+ self
101
+ end
102
+
103
+ # Unbinds queue from an exchange
104
+ #
105
+ # @param [MarchHare::Exchange,String] exchange Exchange to unbind from
106
+ # @param [Hash] options Binding properties
107
+ #
108
+ # @option options [String] :routing_key Routing key
109
+ # @option options [Hash] :arguments ({}) Additional optional binding arguments
110
+ #
111
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
112
+ # @see http://rubymarchhare.info/articles/bindings.html Bindings guide
113
+ def unbind(exchange, options={})
114
+ exchange_name = if exchange.respond_to?(:name) then
115
+ exchange.name
116
+ else
117
+ exchange.to_s
118
+ end
119
+ @channel.queue_unbind(@name, exchange_name, options.fetch(:routing_key, ''))
120
+
121
+ binding = { :exchange => exchange_name, :routing_key => (options[:routing_key] || options[:key] || ""), :arguments => options[:arguments] }
122
+ @bindings.delete(binding) unless @bindings.include?(binding)
123
+
124
+ self
125
+ end
126
+
127
+ # Deletes the queue
128
+ #
129
+ # @option [Boolean] if_unused (false) Should this queue be deleted only if it has no consumers?
130
+ # @option [Boolean] if_empty (false) Should this queue be deleted only if it has no messages?
131
+ #
132
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
133
+ def delete(if_unused = false, if_empty = false)
134
+ @channel.queue_delete(@name, if_unused, if_empty)
135
+ end
136
+
137
+ # Purges a queue (removes all messages from it)
138
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
139
+ # @api public
140
+ def purge
141
+ @channel.queue_purge(@name)
142
+ end
143
+
144
+ def get(options = {:block => false})
145
+ response = @channel.basic_get(@name, !options.fetch(:ack, false))
146
+
147
+ if response
148
+ [Headers.new(@channel, nil, response.envelope, response.props), String.from_java_bytes(response.body)]
149
+ else
150
+ nil
151
+ end
152
+ end
153
+ alias pop get
154
+
155
+ def build_consumer(opts = {}, &block)
156
+ if opts[:block] || opts[:blocking]
157
+ BlockingCallbackConsumer.new(@channel, self, opts[:buffer_size], opts, block)
158
+ else
159
+ CallbackConsumer.new(@channel, self, opts, block)
160
+ end
161
+ end
162
+
163
+ # Adds a consumer to the queue (subscribes for message deliveries).
164
+ #
165
+ # @param [Hash] opts Options
166
+ #
167
+ # @option opts [Boolean] :manual_ack (false) Will this consumer use manual acknowledgements?
168
+ # @option opts [Boolean] :exclusive (false) Should this consumer be exclusive for this queue?
169
+ # @option opts [Boolean] :block (false) Should the call block calling thread?
170
+ # @option opts [#call] :on_cancellation Block to execute when this consumer is cancelled remotely (e.g. via the RabbitMQ Management plugin)
171
+ # @option opts [String] :consumer_tag Unique consumer identifier. It is usually recommended to let MarchHare generate it for you.
172
+ # @option opts [Hash] :arguments ({}) Additional (optional) arguments, typically used by RabbitMQ extensions
173
+ #
174
+ # @see http://rubymarchhare.info/articles/queues.html Queues and Consumers guide
175
+ # @api public
176
+ def subscribe(opts = {}, &block)
177
+ subscribe_with(build_consumer(opts, &block), opts)
178
+ end
179
+
180
+ def subscribe_with(consumer, opts = {})
181
+ @consumer_tag = @channel.basic_consume(@name, !(opts[:ack] || opts[:manual_ack]), consumer)
182
+ consumer.consumer_tag = @consumer_tag
183
+
184
+ @default_consumer = consumer
185
+ @channel.register_consumer(@consumer_tag, consumer)
186
+
187
+ consumer.start
188
+ consumer
189
+ end
190
+
191
+ # @return [Array<Integer>] A pair with information about the number of queue messages and consumers
192
+ # @see #message_count
193
+ # @see #consumer_count
194
+ def status
195
+ response = @channel.queue_declare_passive(@name)
196
+ [response.message_count, response.consumer_count]
197
+ end
198
+
199
+ # @return [Integer] How many messages the queue has ready (e.g. not delivered but not unacknowledged)
200
+ def message_count
201
+ response = @channel.queue_declare_passive(@name)
202
+ response.message_count
203
+ end
204
+
205
+ # @return [Integer] How many active consumers the queue has
206
+ def consumer_count
207
+ response = @channel.queue_declare_passive(@name)
208
+ response.consumer_count
209
+ end
210
+
211
+ # Publishes a message to the queue via default exchange. Takes the same arguments
212
+ # as {MarchHare::Exchange#publish}
213
+ #
214
+ # @see MarchHare::Exchange#publish
215
+ # @see MarchHare::Channel#default_exchange
216
+ def publish(payload, opts = {})
217
+ @channel.default_exchange.publish(payload, opts.merge(:routing_key => @name))
218
+
219
+ self
220
+ end
221
+
222
+
223
+ #
224
+ # Implementation
225
+ #
226
+
227
+ # @return [Boolean] true if this queue is a pre-defined one (amq.direct, amq.fanout, amq.match and so on)
228
+ def predefined?
229
+ @name.start_with?("amq.")
230
+ end
231
+
232
+ # @private
233
+ def declare!
234
+ response = if @options[:passive]
235
+ then @channel.queue_declare_passive(@name)
236
+ else @channel.queue_declare(@name, @options[:durable], @options[:exclusive], @options[:auto_delete], @options[:arguments])
237
+ end
238
+ @name = response.queue
239
+ end
240
+
241
+ # @private
242
+ def recover_from_network_failure
243
+ if self.server_named?
244
+ old_name = @name.dup
245
+ @name = ""
246
+
247
+ @channel.deregister_queue_named(old_name)
248
+ end
249
+
250
+ declare! if !predefined?
251
+
252
+ @channel.register_queue(self)
253
+ recover_bindings
254
+ end
255
+
256
+ # @private
257
+ def recover_bindings
258
+ @bindings.each do |b|
259
+ # TODO: use a logger
260
+ # puts "Recovering binding #{b.inspect}"
261
+ self.bind(b[:exchange], b)
262
+ end
263
+ end
264
+ end # Queue
265
+ end # MarchHare
@@ -0,0 +1,440 @@
1
+ # encoding: utf-8
2
+ require "march_hare/shutdown_listener"
3
+ require "set"
4
+ require "march_hare/thread_pools"
5
+
6
+ module MarchHare
7
+ java_import com.rabbitmq.client.ConnectionFactory
8
+ java_import com.rabbitmq.client.Connection
9
+ java_import com.rabbitmq.client.BlockedListener
10
+
11
+ # Connection to a RabbitMQ node.
12
+ #
13
+ # Used to open and close connections and open (create) new channels.
14
+ #
15
+ # @see .connect
16
+ # @see #create_channel
17
+ # @see #close
18
+ # @see http://rubymarchhare.info/articles/getting_started.html Getting Started guide
19
+ # @see http://rubymarchhare.info/articles/connecting.html Connecting to RabbitMQ guide
20
+ class Session
21
+
22
+ #
23
+ # API
24
+ #
25
+
26
+ # Default reconnection interval for TCP connection failures
27
+ DEFAULT_NETWORK_RECOVERY_INTERVAL = 5.0
28
+
29
+ # Connects to a RabbitMQ node.
30
+ #
31
+ # @param [Hash] options Connection options
32
+ #
33
+ # @option options [String] :host ("127.0.0.1") Hostname or IP address to connect to
34
+ # @option options [Integer] :port (5672) Port RabbitMQ listens on
35
+ # @option options [String] :username ("guest") Username
36
+ # @option options [String] :password ("guest") Password
37
+ # @option options [String] :vhost ("/") Virtual host to use
38
+ # @option options [Integer] :heartbeat (600) Heartbeat interval. 0 means no heartbeat.
39
+ # @option options [Boolean] :tls (false) Set to true to use TLS/SSL connection. This will switch port to 5671 by default.
40
+ #
41
+ # @see http://rubymarchhare.info/articles/connecting.html Connecting to RabbitMQ guide
42
+ def self.connect(options={})
43
+ cf = ConnectionFactory.new
44
+
45
+ cf.uri = options[:uri] if options[:uri]
46
+ cf.host = hostname_from(options) if include_host?(options)
47
+ cf.port = options[:port].to_i if options[:port]
48
+ cf.virtual_host = vhost_from(options) if include_vhost?(options)
49
+ cf.connection_timeout = timeout_from(options) if include_timeout?(options)
50
+ cf.username = username_from(options) if include_username?(options)
51
+ cf.password = password_from(options) if include_password?(options)
52
+
53
+ cf.requested_heartbeat = heartbeat_from(options) if include_heartbeat?(options)
54
+ cf.connection_timeout = connection_timeout_from(options) if include_connection_timeout?(options)
55
+
56
+ tls = (options[:ssl] || options[:tls])
57
+ case tls
58
+ when true then
59
+ cf.use_ssl_protocol
60
+ when String then
61
+ if options[:trust_manager]
62
+ cf.use_ssl_protocol(tls, options[:trust_manager])
63
+ else
64
+ cf.use_ssl_protocol(tls)
65
+ end
66
+ end
67
+
68
+
69
+ new(cf, options)
70
+ end
71
+
72
+ # @return [Array<MarchHare::Channel>] Channels opened on this connection
73
+ attr_reader :channels
74
+
75
+
76
+ # @private
77
+ def initialize(connection_factory, opts = {})
78
+ @cf = connection_factory
79
+ # executors cannot be restarted after shutdown,
80
+ # so we really need a factory here. MK.
81
+ @executor_factory = opts[:executor_factory] || build_executor_factory_from(opts)
82
+ @connection = self.new_connection
83
+ @channels = JavaConcurrent::ConcurrentHashMap.new
84
+
85
+ # should automatic recovery from network failures be used?
86
+ @automatically_recover = if opts[:automatically_recover].nil? && opts[:automatic_recovery].nil?
87
+ true
88
+ else
89
+ opts[:automatically_recover] || opts[:automatic_recovery]
90
+ end
91
+ @network_recovery_interval = opts.fetch(:network_recovery_interval, DEFAULT_NETWORK_RECOVERY_INTERVAL)
92
+ @shutdown_hooks = Array.new
93
+
94
+ if @automatically_recover
95
+ self.add_automatic_recovery_hook
96
+ end
97
+ end
98
+
99
+ # Opens a new channel.
100
+ #
101
+ # @param [Integer] (nil): Channel number. Pass nil to let MarchHare allocate an available number
102
+ # in a safe way.
103
+ #
104
+ # @return [MarchHare::Channel] Newly created channel
105
+ # @see MarchHare::Channel
106
+ # @see http://rubymarchhare.info/articles/getting_started.html Getting Started guide
107
+ def create_channel(n = nil)
108
+ jc = if n
109
+ @connection.create_channel(n)
110
+ else
111
+ @connection.create_channel
112
+ end
113
+
114
+ ch = Channel.new(self, jc)
115
+ register_channel(ch)
116
+
117
+ ch
118
+ end
119
+
120
+ # Closes connection gracefully.
121
+ #
122
+ # This includes shutting down consumer work pool gracefully,
123
+ # waiting up to 5 seconds for all consumer deliveries to be
124
+ # processed.
125
+ def close
126
+ @channels.select { |_, ch| ch.open? }.each do |_, ch|
127
+ ch.close
128
+ end
129
+
130
+ @connection.close
131
+ end
132
+
133
+ # @return [Boolean] true if connection is open, false otherwise
134
+ def open?
135
+ @connection.open?
136
+ end
137
+ alias connected? open?
138
+
139
+ # @return [Boolean] true if this channel is closed
140
+ def closed?
141
+ !@connection.open?
142
+ end
143
+
144
+ # Defines a shutdown event callback. Shutdown events are
145
+ # broadcasted when a connection is closed, either explicitly
146
+ # or forcefully, or due to a network/peer failure.
147
+ def on_shutdown(&block)
148
+ sh = ShutdownListener.new(self, &block)
149
+ @shutdown_hooks << sh
150
+
151
+ @connection.add_shutdown_listener(sh)
152
+
153
+ sh
154
+ end
155
+
156
+ # Defines a connection.blocked handler
157
+ def on_blocked(&block)
158
+ self.add_blocked_listener(BlockBlockedUnblockedListener.for_blocked(block))
159
+ end
160
+
161
+ # Defines a connection.unblocked handler
162
+ def on_unblocked(&block)
163
+ self.add_blocked_listener(BlockBlockedUnblockedListener.for_unblocked(block))
164
+ end
165
+
166
+ # Clears all callbacks defined with #on_blocked and #on_unblocked.
167
+ def clear_blocked_connection_callbacks
168
+ @connection.clear_blocked_listeners
169
+ end
170
+
171
+
172
+ # @private
173
+ def add_automatic_recovery_hook
174
+ fn = Proc.new do |_, signal|
175
+ if !signal.initiated_by_application
176
+ self.automatically_recover
177
+ end
178
+ end
179
+
180
+ @automatic_recovery_hook = self.on_shutdown(&fn)
181
+ end
182
+
183
+ # @private
184
+ def disable_automatic_recovery
185
+ @connetion.remove_shutdown_listener(@automatic_recovery_hook) if @automatic_recovery_hook
186
+ end
187
+
188
+ # Begins automatic connection recovery (typically only used internally
189
+ # to recover from network failures)
190
+ def automatically_recover
191
+ ms = @network_recovery_interval * 1000
192
+ # recovering immediately makes little sense. Wait a bit first. MK.
193
+ java.lang.Thread.sleep(ms)
194
+
195
+ @connection = converting_rjc_exceptions_to_ruby do
196
+ reconnecting_on_network_failures(ms) do
197
+ self.new_connection
198
+ end
199
+ end
200
+ @thread_pool = ThreadPools.dynamically_growing
201
+ self.recover_shutdown_hooks
202
+
203
+ # sorting channels by id means that the cases like the following:
204
+ #
205
+ # ch1 = conn.create_channel
206
+ # ch2 = conn.create_channel
207
+ #
208
+ # x = ch1.topic("logs", :durable => false)
209
+ # q = ch2.queue("", :exclusive => true)
210
+ #
211
+ # q.bind(x)
212
+ #
213
+ # will recover correctly because exchanges and queues will be recovered
214
+ # in the order the user expects and before bindings.
215
+ @channels.sort_by {|id, _| id}.each do |id, ch|
216
+ begin
217
+ ch.automatically_recover(self, @connection)
218
+ rescue Exception, java.io.IOException => e
219
+ # TODO: logging
220
+ $stderr.puts e
221
+ end
222
+ end
223
+ end
224
+
225
+ # @private
226
+ def recover_shutdown_hooks
227
+ @shutdown_hooks.each do |sh|
228
+ @connection.add_shutdown_listener(sh)
229
+ end
230
+ end
231
+
232
+ # Flushes the socket used by this connection.
233
+ def flush
234
+ @connection.flush
235
+ end
236
+
237
+ # @private
238
+ def heartbeat=(n)
239
+ @connection.heartbeat = n
240
+ end
241
+
242
+ # No-op, exists for better API compatibility with Bunny.
243
+ def start
244
+ # no-op
245
+ #
246
+ # This method mimics Bunny::Session#start in Bunny 0.9.
247
+ # Without it, #method_missing will proxy the call to com.rabbitmq.client.AMQConnection,
248
+ # which happens to have a #start method which is not idempotent.
249
+ #
250
+ # So we stub out #start in case someone migrating from Bunny forgets to remove
251
+ # the call to #start. MK.
252
+ end
253
+
254
+ def method_missing(selector, *args)
255
+ @connection.__send__(selector, *args)
256
+ end
257
+
258
+ # @return [String]
259
+ def to_s
260
+ "#<#{self.class.name}:#{object_id} #{@cf.username}@#{@cf.host}:#{@cf.port}, vhost=#{@cf.virtual_host}>"
261
+ end
262
+
263
+
264
+ #
265
+ # Implementation
266
+ #
267
+
268
+ # @private
269
+ def register_channel(ch)
270
+ @channels[ch.channel_number] = ch
271
+ end
272
+
273
+ # @private
274
+ def unregister_channel(ch)
275
+ @channels.delete(ch.channel_number)
276
+ end
277
+
278
+ protected
279
+
280
+ # @private
281
+ def self.hostname_from(options)
282
+ options[:host] || options[:hostname] || ConnectionFactory::DEFAULT_HOST
283
+ end
284
+
285
+ # @private
286
+ def self.include_host?(options)
287
+ !!(options[:host] || options[:hostname])
288
+ end
289
+
290
+ # @private
291
+ def self.vhost_from(options)
292
+ options[:virtual_host] || options[:vhost] || ConnectionFactory::DEFAULT_VHOST
293
+ end
294
+
295
+ # @private
296
+ def self.include_vhost?(options)
297
+ !!(options[:virtual_host] || options[:vhost])
298
+ end
299
+
300
+ # @private
301
+ def self.timeout_from(options)
302
+ options[:connection_timeout] || options[:timeout]
303
+ end
304
+
305
+ # @private
306
+ def self.include_timeout?(options)
307
+ !!(options[:connection_timeout] || options[:timeout])
308
+ end
309
+
310
+ # @private
311
+ def self.username_from(options)
312
+ options[:username] || options[:user] || ConnectionFactory::DEFAULT_USER
313
+ end
314
+
315
+ # @private
316
+ def self.heartbeat_from(options)
317
+ options[:heartbeat_interval] || options[:requested_heartbeat] || ConnectionFactory::DEFAULT_HEARTBEAT
318
+ end
319
+
320
+ # @private
321
+ def self.connection_timeout_from(options)
322
+ options[:connection_timeout_interval] || options[:connection_timeout] || ConnectionFactory::DEFAULT_CONNECTION_TIMEOUT
323
+ end
324
+
325
+ # @private
326
+ def self.include_username?(options)
327
+ !!(options[:username] || options[:user])
328
+ end
329
+
330
+ # @private
331
+ def self.password_from(options)
332
+ options[:password] || options[:pass] || ConnectionFactory::DEFAULT_PASS
333
+ end
334
+
335
+ # @private
336
+ def self.include_password?(options)
337
+ !!(options[:password] || options[:pass])
338
+ end
339
+
340
+ # @private
341
+ def self.include_heartbeat?(options)
342
+ !!(options[:heartbeat_interval] || options[:requested_heartbeat] || options[:heartbeat])
343
+ end
344
+
345
+ # @private
346
+ def self.include_connection_timeout?(options)
347
+ !!(options[:connection_timeout_interval] || options[:connection_timeout])
348
+ end
349
+
350
+ # Executes a block, catching Java exceptions RabbitMQ Java client throws and
351
+ # transforms them to Ruby exceptions that are then re-raised.
352
+ #
353
+ # @private
354
+ def converting_rjc_exceptions_to_ruby(&block)
355
+ begin
356
+ block.call
357
+ rescue java.net.ConnectException => e
358
+ raise ConnectionRefused.new("Connection to #{@cf.host}:#{@cf.port} refused")
359
+ rescue java.net.UnknownHostException => e
360
+ raise ConnectionRefused.new("Connection to #{@cf.host}:#{@cf.port} refused: host unknown")
361
+ rescue com.rabbitmq.client.AuthenticationFailureException => e
362
+ raise AuthenticationFailureError.new(@cf.username, @cf.virtual_host, @cf.password.bytesize)
363
+ rescue com.rabbitmq.client.PossibleAuthenticationFailureException => e
364
+ raise PossibleAuthenticationFailureError.new(@cf.username, @cf.virtual_host, @cf.password.bytesize)
365
+ end
366
+ end
367
+
368
+ # @private
369
+ def reconnecting_on_network_failures(interval_in_ms, &fn)
370
+ begin
371
+ fn.call
372
+ rescue IOError, MarchHare::ConnectionRefused, java.io.IOException => e
373
+ java.lang.Thread.sleep(interval_in_ms)
374
+
375
+ retry
376
+ end
377
+ end
378
+
379
+ # @private
380
+ def new_connection
381
+ converting_rjc_exceptions_to_ruby do
382
+ if @executor_factory
383
+ @cf.new_connection(@executor_factory.call)
384
+ else
385
+ @cf.new_connection
386
+ end
387
+ end
388
+ end
389
+
390
+ # Makes it easier to construct executor factories.
391
+ # @private
392
+ def build_executor_factory_from(opts)
393
+ # if we are given a thread pool size, construct
394
+ # a callable that creates a fixed size executor
395
+ # of that size. MK.
396
+ if n = opts[:thread_pool_size]
397
+ return Proc.new { MarchHare::ThreadPools.fixed_of_size(n) }
398
+ end
399
+ end
400
+
401
+ # Ruby blocks-based BlockedListener that handles
402
+ # connection.blocked and connection.unblocked.
403
+ # @private
404
+ class BlockBlockedUnblockedListener
405
+ include com.rabbitmq.client.BlockedListener
406
+
407
+ def self.for_blocked(block)
408
+ new(block, noop_fn1)
409
+ end
410
+
411
+ def self.for_unblocked(block)
412
+ new(noop_fn0, block)
413
+ end
414
+
415
+ # Returns a no-op function of arity 0.
416
+ def self.noop_fn0
417
+ Proc.new {}
418
+ end
419
+
420
+ # Returns a no-op function of arity 1.
421
+ def self.noop_fn1
422
+ Proc.new { |_| }
423
+ end
424
+
425
+ def initialize(on_blocked, on_unblocked)
426
+ @blocked = on_blocked
427
+ @unblocked = on_unblocked
428
+ end
429
+
430
+ def handle_blocked(reason)
431
+ @blocked.call(reason)
432
+ end
433
+
434
+ def handle_unblocked()
435
+ @unblocked.call()
436
+ end
437
+ end
438
+
439
+ end
440
+ end