hot_bunnies 1.5.0-java → 2.0.0.pre1-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/lib/ext/rabbitmq-client.jar +0 -0
  2. data/lib/hot_bunnies/channel.rb +287 -9
  3. data/lib/hot_bunnies/consumers.rb +152 -0
  4. data/lib/hot_bunnies/exceptions.rb +151 -0
  5. data/lib/hot_bunnies/exchange.rb +22 -30
  6. data/lib/hot_bunnies/juc.rb +9 -0
  7. data/lib/hot_bunnies/metadata.rb +90 -0
  8. data/lib/hot_bunnies/queue.rb +52 -265
  9. data/lib/hot_bunnies/session.rb +170 -0
  10. data/lib/hot_bunnies/version.rb +1 -1
  11. data/lib/hot_bunnies.rb +5 -91
  12. metadata +15 -39
  13. data/.gitignore +0 -7
  14. data/.rvmrc +0 -1
  15. data/.travis.yml +0 -12
  16. data/ChangeLog.md +0 -46
  17. data/Gemfile +0 -10
  18. data/LICENSE +0 -20
  19. data/README.md +0 -58
  20. data/Rakefile +0 -6
  21. data/examples/blocking_subscription.rb +0 -36
  22. data/examples/non_blocking_subscription.rb +0 -32
  23. data/examples/non_blocking_subscription_with_executor.rb +0 -38
  24. data/hot_bunnies.gemspec +0 -24
  25. data/spec/integration/alternate_exchanges_spec.rb +0 -36
  26. data/spec/integration/basic_consume_spec.rb +0 -128
  27. data/spec/integration/connection_spec.rb +0 -26
  28. data/spec/integration/error_handling_by_consumers_spec.rb +0 -97
  29. data/spec/integration/exchange_bind_spec.rb +0 -35
  30. data/spec/integration/exchange_declare_spec.rb +0 -113
  31. data/spec/integration/message_metadata_access_spec.rb +0 -94
  32. data/spec/integration/publisher_confirmations_spec.rb +0 -51
  33. data/spec/integration/queue_bind_spec.rb +0 -56
  34. data/spec/integration/queue_declare_spec.rb +0 -44
  35. data/spec/integration/queue_delete_spec.rb +0 -23
  36. data/spec/integration/queue_purge_spec.rb +0 -38
  37. data/spec/integration/queue_unbind_spec.rb +0 -53
  38. data/spec/integration/sender_selected_distribution_spec.rb +0 -47
  39. data/spec/spec_helper.rb +0 -18
Binary file
@@ -1,19 +1,162 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module HotBunnies
4
- module Channel
5
- def queue(name, options={})
6
- Queue.new(self, name, options)
4
+ class Channel
5
+ attr_reader :session
6
+
7
+ def initialize(session, delegate)
8
+ @connection = session
9
+ @delegate = delegate
10
+
11
+ # we keep track of consumers to gracefully shut down their
12
+ # executors when the channel is closed. This frees library users
13
+ # from having to worry about this. MK.
14
+ @consumers = ConcurrentHashMap.new
15
+ end
16
+
17
+ def client
18
+ @connection
19
+ end
20
+
21
+ def id
22
+ @delegate.channel_number
23
+ end
24
+
25
+ def number
26
+ @delegate.channel_number
27
+ end
28
+
29
+ def channel_number
30
+ @delegate.channel_number
31
+ end
32
+
33
+ def close(code = 200, reason = "Goodbye")
34
+ v = @delegate.close(code, reason)
35
+
36
+ @consumers.each do |tag, consumer|
37
+ consumer.gracefully_shut_down
38
+ end
39
+
40
+ @connection.unregister_channel(self)
41
+
42
+ v
7
43
  end
8
44
 
45
+
46
+ # @group Exchanges
47
+
9
48
  def exchange(name, options={})
10
- exchange = Exchange.new(self, name, options)
11
- exchange.declare!
12
- exchange
49
+ Exchange.new(self, name, options).tap do |x|
50
+ x.declare!
51
+ end
52
+ end
53
+
54
+ def fanout(name, opts = {})
55
+ Exchange.new(self, name, opts.merge(:type => "fanout")).tap do |x|
56
+ x.declare!
57
+ end
58
+ end
59
+
60
+ def direct(name, opts = {})
61
+ Exchange.new(self, name, opts.merge(:type => "direct")).tap do |x|
62
+ x.declare!
63
+ end
64
+ end
65
+
66
+ def topic(name, opts = {})
67
+ Exchange.new(self, name, opts.merge(:type => "topic")).tap do |x|
68
+ x.declare!
69
+ end
70
+ end
71
+
72
+ def headers(name, opts = {})
73
+ Exchange.new(self, name, opts.merge(:type => "headers")).tap do |x|
74
+ x.declare!
75
+ end
13
76
  end
14
77
 
15
78
  def default_exchange
16
- self.exchange("", :durable => true, :auto_delete => false, :type => "direct")
79
+ @default_exchange ||= self.exchange("", :durable => true, :auto_delete => false, :type => "direct")
80
+ end
81
+
82
+ def exchange_declare(name, type, durable = false, auto_delete = false, arguments = nil)
83
+ @delegate.exchange_declare(name, type, durable, auto_delete, arguments)
84
+ end
85
+
86
+ # @endgroup
87
+
88
+
89
+ # @group Queues
90
+
91
+ def queue(name, options={})
92
+ Queue.new(self, name, options).tap do |q|
93
+ q.declare!
94
+ end
95
+ end
96
+
97
+ def queue_declare(name, durable, exclusive, auto_delete, arguments = {})
98
+ converting_rjc_exceptions_to_ruby do
99
+ @delegate.queue_declare(name, durable, exclusive, auto_delete, arguments)
100
+ end
101
+ end
102
+
103
+ def queue_declare_passive(name)
104
+ converting_rjc_exceptions_to_ruby do
105
+ @delegate.queue_declare_passive(name)
106
+ end
107
+ end
108
+
109
+ def queue_delete(name, if_empty = false, if_unused = false)
110
+ converting_rjc_exceptions_to_ruby do
111
+ @delegate.queue_delete(name, if_empty, if_unused)
112
+ end
113
+ end
114
+
115
+ def queue_bind(queue, exchange, routing_key, arguments = nil)
116
+ converting_rjc_exceptions_to_ruby do
117
+ @delegate.queue_bind(queue, exchange, routing_key, arguments)
118
+ end
119
+ end
120
+
121
+ def queue_unbind(queue, exchange, routing_key, arguments = nil)
122
+ converting_rjc_exceptions_to_ruby do
123
+ @delegate.queue_unbind(queue, exchange, routing_key, arguments)
124
+ end
125
+ end
126
+
127
+ def queue_purge(name)
128
+ converting_rjc_exceptions_to_ruby do
129
+ @delegate.queue_purge(name)
130
+ end
131
+ end
132
+
133
+ # @endgroup
134
+
135
+
136
+ # @group basic.*
137
+
138
+ def basic_publish(exchange, routing_key, mandatory, properties, body)
139
+ converting_rjc_exceptions_to_ruby do
140
+ @delegate.basic_publish(exchange, routing_key, mandatory, false, BasicPropertiesBuilder.build_properties_from(properties || Hash.new), body)
141
+ end
142
+ end
143
+
144
+ def basic_get(queue, auto_ack)
145
+ converting_rjc_exceptions_to_ruby do
146
+ @delegate.basic_get(queue, auto_ack)
147
+ end
148
+ end
149
+
150
+ def basic_consume(queue, auto_ack, consumer)
151
+ converting_rjc_exceptions_to_ruby do
152
+ @delegate.basic_consume(queue, auto_ack, consumer)
153
+ end
154
+ end
155
+
156
+ def basic_qos(prefetch_count)
157
+ converting_rjc_exceptions_to_ruby do
158
+ @delegate.basic_qos(prefetch_count)
159
+ end
17
160
  end
18
161
 
19
162
  def qos(options={})
@@ -24,11 +167,146 @@ module HotBunnies
24
167
  end
25
168
 
26
169
  def prefetch=(n)
27
- qos(:prefetch_count => n)
170
+ basic_qos(n)
171
+ end
172
+
173
+ def ack(delivery_tag, multiple = false)
174
+ converting_rjc_exceptions_to_ruby do
175
+ basic_ack(delivery_tag, multiple)
176
+ end
177
+ end
178
+ alias acknowledge ack
179
+
180
+ def reject(delivery_tag, requeue = false)
181
+ converting_rjc_exceptions_to_ruby do
182
+ basic_reject(delivery_tag, requeue)
183
+ end
184
+ end
185
+
186
+ def nack(delivery_tag, multiple = false, requeue = false)
187
+ converting_rjc_exceptions_to_ruby do
188
+ basic_nack(delivery_tag, multiple, requeue)
189
+ end
190
+ end
191
+
192
+ def basic_recover(requeue = true)
193
+ converting_rjc_exceptions_to_ruby do
194
+ @delegate.basic_recover(requeue)
195
+ end
196
+ end
197
+
198
+ def basic_recover_async(requeue = true)
199
+ converting_rjc_exceptions_to_ruby do
200
+ @delegate.basic_recover_async(requeue)
201
+ end
202
+ end
203
+
204
+ # @endgroup
205
+
206
+
207
+ def confirm_select
208
+ converting_rjc_exceptions_to_ruby do
209
+ @delegate.confirm_select
210
+ end
211
+ end
212
+
213
+ # Waits until all outstanding publisher confirms arrive.
214
+ #
215
+ # Takes an optional timeout in milliseconds. Will raise
216
+ # an exception in timeout has occured.
217
+ #
218
+ # @param [Integer] timeout Timeout in milliseconds
219
+ # @return [Boolean] true if all confirms were positive,
220
+ # false if some were negative
221
+ def wait_for_confirms(timeout = nil)
222
+ if timeout
223
+ converting_rjc_exceptions_to_ruby do
224
+ @delegate.wait_for_confirms(timeout)
225
+ end
226
+ else
227
+ @delegate.wait_for_confirms
228
+ end
229
+ end
230
+
231
+ def next_publisher_seq_no
232
+ @delegate.next_publisher_seq_no
28
233
  end
29
234
 
235
+ def tx_select
236
+ converting_rjc_exceptions_to_ruby do
237
+ @delegate.tx_select
238
+ end
239
+ end
240
+
241
+ def tx_commit
242
+ converting_rjc_exceptions_to_ruby do
243
+ @delegate.tx_commit
244
+ end
245
+ end
246
+
247
+ def tx_rollback
248
+ converting_rjc_exceptions_to_ruby do
249
+ @delegate.tx_rollback
250
+ end
251
+ end
252
+
253
+ def channel_flow(active)
254
+ converting_rjc_exceptions_to_ruby do
255
+ @delegate.channel_flow(active)
256
+ end
257
+ end
258
+
259
+
30
260
  def on_return(&block)
31
- self.set_return_listener(block)
261
+ self.add_return_listener(BlockReturnListener.from(block))
262
+ end
263
+
264
+ def method_missing(selector, *args)
265
+ @delegate.__send__(selector, *args)
266
+ end
267
+
268
+
269
+ #
270
+ # Implementation
271
+ #
272
+
273
+ class BlockReturnListener
274
+ include com.rabbitmq.client.ReturnListener
275
+
276
+ def self.from(block)
277
+ new(block)
278
+ end
279
+
280
+ def initialize(block)
281
+ @block = block
282
+ end
283
+
284
+ def handleReturn(reply_code, reply_text, exchange, routing_key, basic_properties, payload)
285
+ # TODO: convert properties to a Ruby hash
286
+ @block.call(reply_code, reply_text, exchange, routing_key, basic_properties, String.from_java_bytes(payload))
287
+ end
288
+ end
289
+
290
+ # @private
291
+ def register_consumer(consumer_tag, consumer)
292
+ @consumers[consumer_tag] = consumer
293
+ end
294
+
295
+ # @private
296
+ def unregister_consumer(consumer_tag)
297
+ @consumers.delete(consumer_tag)
298
+ end
299
+
300
+ # Executes a block, catching Java exceptions RabbitMQ Java client throws and
301
+ # transforms them to Ruby exceptions that are then re-raised.
302
+ #
303
+ # @private
304
+ def converting_rjc_exceptions_to_ruby(&block)
305
+ begin
306
+ block.call
307
+ rescue Exception, java.lang.Throwable => e
308
+ Exceptions.convert_and_reraise(e)
309
+ end
32
310
  end
33
311
  end
34
312
  end
@@ -0,0 +1,152 @@
1
+ module HotBunnies
2
+ import com.rabbitmq.client.DefaultConsumer
3
+
4
+ class BaseConsumer < DefaultConsumer
5
+ attr_accessor :consumer_tag
6
+
7
+ def initialize(channel)
8
+ super(channel)
9
+ @channel = channel
10
+
11
+ @cancelling = JavaConcurrent::AtomicBoolean.new
12
+ @cancelled = JavaConcurrent::AtomicBoolean.new
13
+ end
14
+
15
+ def handleDelivery(consumer_tag, envelope, properties, body)
16
+ body = String.from_java_bytes(body)
17
+ headers = Headers.new(channel, consumer_tag, envelope, properties)
18
+
19
+ deliver(headers, body)
20
+ end
21
+
22
+ def handleCancel(consumer_tag)
23
+ @cancelled.set(true)
24
+ end
25
+
26
+ def handleCancelOk(consumer_tag)
27
+ @cancelled.set(true)
28
+ @channel.unregister_consumer(consumer_tag)
29
+ end
30
+
31
+ def start
32
+ end
33
+
34
+ def deliver(headers, message)
35
+ raise NotImplementedError, 'To be implemented by a subclass'
36
+ end
37
+
38
+ def cancel
39
+ @cancelling.set(true)
40
+ response = channel.basic_cancel(consumer_tag)
41
+ @cancelled.set(true)
42
+
43
+ response
44
+ end
45
+
46
+ def cancelled?
47
+ @cancelling.get || @cancelled.get
48
+ end
49
+
50
+ def active?
51
+ !cancelled?
52
+ end
53
+ end
54
+
55
+
56
+ class CallbackConsumer < BaseConsumer
57
+ def initialize(channel, callback)
58
+ super(channel)
59
+ @callback = callback
60
+ @callback_arity = @callback.arity
61
+ end
62
+
63
+ def callback(headers, message)
64
+ if @callback_arity == 2
65
+ @callback.call(headers, message)
66
+ else
67
+ @callback.call(message)
68
+ end
69
+ end
70
+ end
71
+
72
+ class AsyncCallbackConsumer < CallbackConsumer
73
+ def initialize(channel, callback, executor)
74
+ super(channel, callback)
75
+ @executor = executor
76
+ end
77
+
78
+ def deliver(headers, message)
79
+ unless @executor.shutdown?
80
+ @executor.submit do
81
+ begin
82
+ callback(headers, message)
83
+ rescue Exception => e
84
+ $stderr.puts "Unhandled exception in consumer #{@consumer_tag}: #{e.message}"
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ def cancel
91
+ super
92
+
93
+ maybe_shut_down_executor
94
+ end
95
+
96
+ def shutdown!
97
+ @executor.shutdown_now if @executor
98
+ end
99
+ alias shut_down! shutdown!
100
+
101
+ def gracefully_shut_down
102
+ unless @executor.await_termination(1, JavaConcurrent::TimeUnit::SECONDS)
103
+ @executor.shutdown_now
104
+ end
105
+ end
106
+ alias maybe_shut_down_executor gracefully_shut_down
107
+ alias gracefully_shutdown gracefully_shut_down
108
+ end
109
+
110
+ class BlockingCallbackConsumer < CallbackConsumer
111
+ include JavaConcurrent
112
+
113
+ def initialize(channel, buffer_size, callback)
114
+ super(channel, callback)
115
+ if buffer_size
116
+ @internal_queue = ArrayBlockingQueue.new(buffer_size)
117
+ else
118
+ @internal_queue = LinkedBlockingQueue.new
119
+ end
120
+ end
121
+
122
+ def start
123
+ interrupted = false
124
+ until (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
125
+ begin
126
+ pair = @internal_queue.take
127
+ callback(*pair) if pair
128
+ rescue InterruptedException => e
129
+ interrupted = true
130
+ end
131
+ end
132
+ while (pair = @internal_queue.poll)
133
+ callback(*pair)
134
+ end
135
+ if interrupted
136
+ JavaConcurrent::Thread.current_thread.interrupt
137
+ end
138
+ end
139
+
140
+ def deliver(*pair)
141
+ if (@cancelling.get || @cancelled.get) || JavaConcurrent::Thread.current_thread.interrupted?
142
+ @internal_queue.offer(pair)
143
+ else
144
+ begin
145
+ @internal_queue.put(pair)
146
+ rescue InterruptedException => e
147
+ JavaConcurrent::Thread.current_thread.interrupt
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,151 @@
1
+ module HotBunnies
2
+ class Exception < ::StandardError
3
+ end
4
+
5
+
6
+ class ChannelLevelException < Exception
7
+ attr_reader :channel_close
8
+
9
+ def initialize(message, channel_close)
10
+ super(message)
11
+
12
+ @channel_close = channel_close
13
+ end
14
+ end
15
+
16
+ class ConnectionLevelException < Exception
17
+ attr_reader :connection_close
18
+
19
+ def initialize(message, connection_close)
20
+ super(message)
21
+
22
+ @connection_close = connection_close
23
+ end
24
+ end
25
+
26
+
27
+ class PossibleAuthenticationFailureError < Exception
28
+
29
+ #
30
+ # API
31
+ #
32
+
33
+ attr_reader :username, :vhost
34
+
35
+ def initialize(username, vhost, password_length)
36
+ @username = username
37
+ @vhost = vhost
38
+
39
+ super("RabbitMQ closed TCP connection before authentication succeeded: this usually means authentication failure due to misconfiguration or that RabbitMQ version does not support AMQP 0.9.1. Please check your configuration. Username: #{username}, vhost: #{vhost}, password length: #{password_length}")
40
+ end # initialize(settings)
41
+ end # PossibleAuthenticationFailureError
42
+
43
+
44
+ class PreconditionFailed < ChannelLevelException
45
+ end
46
+
47
+ class NotFound < ChannelLevelException
48
+ end
49
+
50
+ class ResourceLocked < ChannelLevelException
51
+ end
52
+
53
+ class AccessRefused < ChannelLevelException
54
+ end
55
+
56
+ class ChannelError < ConnectionLevelException
57
+ end
58
+
59
+ class InvalidCommand < ConnectionLevelException
60
+ end
61
+
62
+ class FrameError < ConnectionLevelException
63
+ end
64
+
65
+ class UnexpectedFrame < ConnectionLevelException
66
+ end
67
+
68
+
69
+
70
+ # Converts RabbitMQ Java client exceptions
71
+ # @private
72
+ class Exceptions
73
+ def self.convert(e, unwrap_io_exception = true)
74
+ case e
75
+ when java.io.IOException then
76
+ c = e.cause
77
+
78
+ if unwrap_io_exception
79
+ convert(c, false)
80
+ else
81
+ c
82
+ end
83
+ when com.rabbitmq.client.AlreadyClosedException then
84
+ cmd = e.reason
85
+
86
+ puts cmd.method.class.inspect
87
+ when com.rabbitmq.client.ShutdownSignalException then
88
+ cmd = e.reason
89
+
90
+ exception_for_protocol_method(cmd.method)
91
+ else
92
+ e
93
+ end
94
+ end
95
+
96
+ def self.convert_and_reraise(e)
97
+ raise convert(e)
98
+ end
99
+
100
+ def self.exception_for_protocol_method(m)
101
+ case m
102
+ # com.rabbitmq.client.AMQP.Connection.Close does not resolve the inner
103
+ # class correctly. Looks like a JRuby bug we work around by using Rubyesque
104
+ # class name. MK.
105
+ when Java::ComRabbitmqClient::AMQP::Connection::Close then
106
+ exception_for_connection_close(m)
107
+ when Java::ComRabbitmqClient::AMQP::Channel::Close then
108
+ exception_for_channel_close(m)
109
+ else
110
+ NotImplementedError.new("Exception convertion for protocol method #{m.inspect} is not implemented!")
111
+ end
112
+ end # def self
113
+
114
+
115
+ def self.exception_for_connection_close(m)
116
+ klass = case m.reply_code
117
+ when 320 then
118
+ ConnectionForced
119
+ when 501 then
120
+ FrameError
121
+ when 503 then
122
+ InvalidCommand
123
+ when 504 then
124
+ ChannelError
125
+ when 505 then
126
+ UnexpectedFrame
127
+ else
128
+ raise "Unknown reply code: #{m.reply_code}, text: #{m.reply_text}"
129
+ end
130
+
131
+ klass.new("Connection-level error: #{m.reply_text}", m)
132
+ end
133
+
134
+ def self.exception_for_channel_close(m)
135
+ klass = case m.reply_code
136
+ when 403 then
137
+ AccessRefused
138
+ when 404 then
139
+ NotFound
140
+ when 405 then
141
+ ResourceLocked
142
+ when 406 then
143
+ PreconditionFailed
144
+ else
145
+ ChannelLevelException
146
+ end
147
+
148
+ klass.new(m.reply_text, m)
149
+ end
150
+ end # Exceptions
151
+ end # HotBunnies
@@ -1,18 +1,29 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module HotBunnies
4
+ import com.rabbitmq.client.AMQP
5
+
4
6
  class Exchange
5
7
  attr_reader :name, :channel
6
8
 
7
- def initialize(channel, name, options={})
9
+ def initialize(channel, name, options = {})
10
+ raise ArgumentError, "exchange channel cannot be nil" if channel.nil?
11
+ raise ArgumentError, "exchange name cannot be nil" if name.nil?
12
+ raise ArgumentError, "exchange :type must be specified as an option" if options[:type].nil?
13
+
8
14
  @channel = channel
9
- @name = name
15
+ @name = name
16
+ @type = options[:type]
10
17
  @options = {:type => :fanout, :durable => false, :auto_delete => false, :internal => false, :passive => false}.merge(options)
11
18
  end
12
19
 
13
- def publish(body, options={})
14
- options = {:routing_key => '', :mandatory => false, :immediate => false}.merge(options)
15
- @channel.basic_publish(@name, options[:routing_key], options[:mandatory], options[:immediate], build_properties_from(options.fetch(:properties, Hash.new)), body.to_java_bytes)
20
+ def publish(body, opts = {})
21
+ options = {:routing_key => '', :mandatory => false}.merge(opts)
22
+ @channel.basic_publish(@name,
23
+ options[:routing_key],
24
+ options[:mandatory],
25
+ options.fetch(:properties, Hash.new),
26
+ body.to_java_bytes)
16
27
  end
17
28
 
18
29
  def delete(options={})
@@ -28,37 +39,18 @@ module HotBunnies
28
39
  @name.empty? || @name.start_with?("amq.")
29
40
  end
30
41
 
42
+ #
43
+ # Implementation
44
+ #
45
+
46
+ # @api private
31
47
  def declare!
32
48
  unless predefined?
33
49
  if @options[:passive]
34
50
  then @channel.exchange_declare_passive(@name)
35
- else @channel.exchange_declare(@name, @options[:type].to_s, @options[:durable], @options[:auto_delete], @options[:internal], @options[:arguments])
51
+ else @channel.exchange_declare(@name, @options[:type].to_s, @options[:durable], @options[:auto_delete], @options[:arguments])
36
52
  end
37
53
  end
38
54
  end
39
-
40
-
41
- protected
42
-
43
- def build_properties_from(props = {})
44
- builder = AMQP::BasicProperties::Builder.new
45
-
46
- builder.content_type(props[:content_type]).
47
- content_encoding(props[:content_encoding]).
48
- headers(props[:headers]).
49
- delivery_mode(props[:persistent] ? 2 : 1).
50
- priority(props[:priority]).
51
- correlation_id(props[:correlation_id]).
52
- reply_to(props[:reply_to]).
53
- expiration(props[:expiration]).
54
- message_id(props[:message_id]).
55
- timestamp(props[:timestamp]).
56
- type(props[:type]).
57
- user_id(props[:user_id]).
58
- app_id(props[:app_id]).
59
- cluster_id(props[:cluster_id]).
60
- build
61
- end
62
-
63
55
  end
64
56
  end
@@ -0,0 +1,9 @@
1
+ module JavaConcurrent
2
+ java_import 'java.lang.Thread'
3
+ java_import 'java.lang.InterruptedException'
4
+ java_import 'java.util.concurrent.Executors'
5
+ java_import 'java.util.concurrent.LinkedBlockingQueue'
6
+ java_import 'java.util.concurrent.ArrayBlockingQueue'
7
+ java_import 'java.util.concurrent.TimeUnit'
8
+ java_import 'java.util.concurrent.atomic.AtomicBoolean'
9
+ end