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,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