hot_bunnies 1.5.0-java → 2.0.0.pre1-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.
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