message-driver 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.coveralls.yml +2 -0
- data/.rubocop.yml +26 -2
- data/.rubocop_todo.yml +15 -123
- data/.travis.yml +2 -1
- data/CHANGELOG.md +10 -1
- data/Gemfile +15 -6
- data/Rakefile +23 -12
- data/ci/reset_vhost +8 -3
- data/ci/travis_setup +0 -3
- data/features/.nav +6 -1
- data/features/CHANGELOG.md +10 -1
- data/features/amqp_specific_features/binding_amqp_destinations.feature +1 -0
- data/features/amqp_specific_features/declaring_amqp_exchanges.feature +1 -0
- data/features/amqp_specific_features/server_named_destinations.feature +1 -0
- data/features/destination_metadata.feature +26 -0
- data/features/logging.feature +1 -1
- data/features/middleware/middleware_basics.feature +91 -0
- data/features/middleware/middleware_ordering.feature +60 -0
- data/features/middleware/middleware_parameters.feature +43 -0
- data/features/middleware/middleware_with_blocks.feature +85 -0
- data/features/step_definitions/dynamic_destinations_steps.rb +1 -1
- data/features/step_definitions/message_consumers_steps.rb +5 -0
- data/features/step_definitions/middleware_steps.rb +10 -0
- data/features/step_definitions/steps.rb +10 -2
- data/features/support/env.rb +4 -3
- data/features/support/firewall_helper.rb +1 -1
- data/features/support/message_table_matcher.rb +3 -2
- data/features/support/no_error_matcher.rb +2 -2
- data/features/support/test_runner.rb +11 -57
- data/features/support/transforms.rb +12 -10
- data/lib/message_driver.rb +3 -1
- data/lib/message_driver/adapters/base.rb +11 -11
- data/lib/message_driver/adapters/bunny_adapter.rb +189 -132
- data/lib/message_driver/adapters/in_memory_adapter.rb +51 -34
- data/lib/message_driver/adapters/stomp_adapter.rb +22 -23
- data/lib/message_driver/broker.rb +21 -16
- data/lib/message_driver/client.rb +15 -16
- data/lib/message_driver/destination.rb +26 -8
- data/lib/message_driver/message.rb +5 -4
- data/lib/message_driver/middleware.rb +8 -0
- data/lib/message_driver/middleware/base.rb +19 -0
- data/lib/message_driver/middleware/block_middleware.rb +33 -0
- data/lib/message_driver/middleware/middleware_stack.rb +61 -0
- data/lib/message_driver/subscription.rb +2 -2
- data/lib/message_driver/version.rb +1 -1
- data/message-driver.gemspec +3 -4
- data/spec/integration/bunny/amqp_integration_spec.rb +21 -82
- data/spec/integration/bunny/bunny_adapter_spec.rb +288 -268
- data/spec/integration/in_memory/in_memory_adapter_spec.rb +93 -90
- data/spec/integration/stomp/stomp_adapter_spec.rb +126 -93
- data/spec/spec_helper.rb +11 -9
- data/spec/support/shared/adapter_examples.rb +1 -1
- data/spec/support/shared/client_ack_examples.rb +4 -4
- data/spec/support/shared/context_examples.rb +6 -4
- data/spec/support/shared/destination_examples.rb +54 -14
- data/spec/support/shared/subscription_examples.rb +33 -26
- data/spec/support/shared/transaction_examples.rb +12 -12
- data/spec/support/utils.rb +1 -1
- data/spec/units/message_driver/adapters/base_spec.rb +42 -40
- data/spec/units/message_driver/broker_spec.rb +38 -38
- data/spec/units/message_driver/client_spec.rb +87 -87
- data/spec/units/message_driver/destination_spec.rb +16 -11
- data/spec/units/message_driver/message_spec.rb +96 -70
- data/spec/units/message_driver/middleware/base_spec.rb +30 -0
- data/spec/units/message_driver/middleware/block_middleware_spec.rb +82 -0
- data/spec/units/message_driver/middleware/middleware_stack_spec.rb +165 -0
- data/spec/units/message_driver/subscription_spec.rb +18 -16
- data/test_lib/broker_config.rb +21 -5
- data/test_lib/coverage.rb +11 -0
- data/test_lib/provider/base.rb +59 -0
- data/test_lib/provider/in_memory.rb +6 -0
- data/test_lib/provider/rabbitmq.rb +67 -0
- metadata +46 -35
@@ -3,11 +3,11 @@ RSpec::Matchers.define :have_no_errors do
|
|
3
3
|
test_runner.raised_error.nil?
|
4
4
|
end
|
5
5
|
|
6
|
-
|
6
|
+
failure_message do |test_runner|
|
7
7
|
err = test_runner.raised_error
|
8
8
|
filtered = (err.backtrace || []).reject do |line|
|
9
9
|
Cucumber::Ast::StepInvocation::BACKTRACE_FILTER_PATTERNS.find { |p| line =~ p }
|
10
10
|
end
|
11
|
-
(["#{err.class}: #{err}"]+filtered).join("\n ")
|
11
|
+
(["#{err.class}: #{err}"] + filtered).join("\n ")
|
12
12
|
end
|
13
13
|
end
|
@@ -1,78 +1,32 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
class TestRunner
|
2
4
|
include MessageDriver::Client
|
3
5
|
include RSpec::Matchers
|
6
|
+
extend Forwardable
|
4
7
|
|
5
8
|
attr_accessor :raised_error
|
6
9
|
attr_accessor :current_feature_file
|
7
|
-
attr_accessor :broker_name
|
8
10
|
|
9
|
-
def
|
10
|
-
@
|
11
|
+
def provider
|
12
|
+
@provider ||= Provider.new
|
11
13
|
end
|
12
14
|
|
15
|
+
def_delegators :provider, :broker_name, :broker_name=, :fetch_messages, :fetch_destination, :fetch_current_adapter_context, :purge_destination, :pause_if_needed
|
16
|
+
|
13
17
|
def run_config_code(src)
|
14
18
|
instance_eval(src, current_feature_file)
|
15
19
|
end
|
16
20
|
|
17
21
|
def run_test_code(src)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@raised_error = e
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def fetch_messages(destination_name)
|
26
|
-
destination = fetch_destination(destination_name)
|
27
|
-
pause_if_needed
|
28
|
-
result = []
|
29
|
-
loop do
|
30
|
-
msg = destination.pop_message
|
31
|
-
if msg.nil?
|
32
|
-
break
|
33
|
-
else
|
34
|
-
result << msg
|
35
|
-
end
|
36
|
-
end
|
37
|
-
result
|
38
|
-
end
|
39
|
-
|
40
|
-
def purge_destination(destination_name)
|
41
|
-
destination = fetch_destination(destination_name)
|
42
|
-
if destination.respond_to? :purge
|
43
|
-
destination.purge
|
44
|
-
else
|
45
|
-
fetch_messages(destination)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def fetch_destination(destination)
|
50
|
-
case destination
|
51
|
-
when String, Symbol
|
52
|
-
MessageDriver::Client[self.broker_name].find_destination(destination)
|
53
|
-
when MessageDriver::Destination::Base
|
54
|
-
destination
|
55
|
-
else
|
56
|
-
raise "didn't understand destination #{destination.inspect}"
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def fetch_current_adapter_context
|
61
|
-
MessageDriver::Client[self.broker_name].current_adapter_context
|
22
|
+
instance_eval(src, current_feature_file)
|
23
|
+
rescue => e
|
24
|
+
@raised_error = e
|
62
25
|
end
|
63
26
|
|
64
27
|
def publish_table_to_destination(destination, table)
|
65
28
|
table.hashes.each do |msg|
|
66
|
-
destination.publish(msg[:body], msg[:headers]||{}, msg[:properties]||{})
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def pause_if_needed(seconds=0.1)
|
71
|
-
seconds *= 10 if ENV['CI'] == 'true'
|
72
|
-
case BrokerConfig.current_adapter
|
73
|
-
when :in_memory
|
74
|
-
else
|
75
|
-
sleep seconds
|
29
|
+
destination.publish(msg[:body], msg[:headers] || {}, msg[:properties] || {})
|
76
30
|
end
|
77
31
|
end
|
78
32
|
end
|
@@ -1,17 +1,19 @@
|
|
1
|
-
|
2
|
-
case
|
3
|
-
when
|
4
|
-
|
1
|
+
STRING_OR_SYM = Transform(/^:?[A-Za-z]\w*$/) do |str|
|
2
|
+
case str
|
3
|
+
when /^:/
|
4
|
+
str.slice(1, str.length - 1).to_sym
|
5
5
|
else
|
6
|
-
|
6
|
+
str
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
case
|
12
|
-
when
|
13
|
-
|
10
|
+
NUMBER = Transform(/^(?:\d+|a|an|no)$/) do |num|
|
11
|
+
case num
|
12
|
+
when 'no'
|
13
|
+
0
|
14
|
+
when 'a', 'an'
|
15
|
+
1
|
14
16
|
else
|
15
|
-
|
17
|
+
Integer(num)
|
16
18
|
end
|
17
19
|
end
|
data/lib/message_driver.rb
CHANGED
@@ -5,6 +5,7 @@ require 'message_driver/logging'
|
|
5
5
|
require 'message_driver/errors'
|
6
6
|
require 'message_driver/broker'
|
7
7
|
require 'message_driver/message'
|
8
|
+
require 'message_driver/middleware'
|
8
9
|
require 'message_driver/destination'
|
9
10
|
require 'message_driver/subscription'
|
10
11
|
require 'message_driver/adapters/base'
|
@@ -12,12 +13,13 @@ require 'message_driver/client'
|
|
12
13
|
|
13
14
|
module MessageDriver
|
14
15
|
module_function
|
16
|
+
|
15
17
|
def configure(broker_name = Broker::DEFAULT_BROKER_NAME, options)
|
16
18
|
Broker.configure(broker_name, options)
|
17
19
|
end
|
18
20
|
|
19
21
|
def logger
|
20
|
-
@__logger ||= Logger.new(STDOUT).tap{|l| l.level = Logger::INFO}
|
22
|
+
@__logger ||= Logger.new(STDOUT).tap { |l| l.level = Logger::INFO }
|
21
23
|
end
|
22
24
|
|
23
25
|
def logger=(logger)
|
@@ -10,7 +10,7 @@ module MessageDriver
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def initialize(_broker, _configuration)
|
13
|
-
|
13
|
+
fail 'Must be implemented in subclass'
|
14
14
|
end
|
15
15
|
|
16
16
|
def new_context
|
@@ -20,11 +20,11 @@ module MessageDriver
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def build_context
|
23
|
-
|
23
|
+
fail 'Must be implemented in subclass'
|
24
24
|
end
|
25
25
|
|
26
26
|
def reset_after_tests
|
27
|
-
#does nothing, can be overridden by adapters that want to support testing scenarios
|
27
|
+
# does nothing, can be overridden by adapters that want to support testing scenarios
|
28
28
|
end
|
29
29
|
|
30
30
|
def stop
|
@@ -47,20 +47,20 @@ module MessageDriver
|
|
47
47
|
@valid = true
|
48
48
|
end
|
49
49
|
|
50
|
-
def publish(_destination, _body, _headers={}, _properties={})
|
51
|
-
|
50
|
+
def publish(_destination, _body, _headers = {}, _properties = {})
|
51
|
+
fail 'Must be implemented in subclass'
|
52
52
|
end
|
53
53
|
|
54
|
-
def pop_message(_destination, _options={})
|
55
|
-
|
54
|
+
def pop_message(_destination, _options = {})
|
55
|
+
fail 'Must be implemented in subclass'
|
56
56
|
end
|
57
57
|
|
58
|
-
def subscribe(_destination, _options={}, &_consumer)
|
59
|
-
|
58
|
+
def subscribe(_destination, _options = {}, &_consumer)
|
59
|
+
fail "#subscribe is not supported by #{adapter.class}"
|
60
60
|
end
|
61
61
|
|
62
|
-
def create_destination(_name, _dest_options={}, _message_props={})
|
63
|
-
|
62
|
+
def create_destination(_name, _dest_options = {}, _message_props = {})
|
63
|
+
fail 'Must be implemented in subclass'
|
64
64
|
end
|
65
65
|
|
66
66
|
def valid?
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'bunny'
|
2
|
+
require 'forwardable'
|
2
3
|
|
3
4
|
module MessageDriver
|
4
5
|
class Broker
|
@@ -9,13 +10,22 @@ module MessageDriver
|
|
9
10
|
|
10
11
|
module Adapters
|
11
12
|
class BunnyAdapter < Base
|
12
|
-
NETWORK_ERRORS = [Bunny::TCPConnectionFailed,
|
13
|
+
NETWORK_ERRORS = [Bunny::TCPConnectionFailed,
|
14
|
+
Bunny::ConnectionClosedError,
|
15
|
+
Bunny::ConnectionLevelException,
|
16
|
+
Bunny::NetworkErrorWrapper,
|
17
|
+
Bunny::NetworkFailure,
|
18
|
+
IOError].freeze
|
13
19
|
|
14
20
|
class Message < MessageDriver::Message::Base
|
15
21
|
attr_reader :delivery_info
|
16
22
|
|
17
|
-
def initialize(ctx, delivery_info, properties, payload)
|
18
|
-
|
23
|
+
def initialize(ctx, delivery_info, properties, payload, destination)
|
24
|
+
raw_body = payload
|
25
|
+
raw_headers = properties[:headers]
|
26
|
+
raw_headers = {} if raw_headers.nil?
|
27
|
+
b, h, p = destination.middleware.on_consume(payload, raw_headers, properties)
|
28
|
+
super(ctx, b, h, p, raw_body)
|
19
29
|
@delivery_info = delivery_info
|
20
30
|
end
|
21
31
|
|
@@ -29,10 +39,11 @@ module MessageDriver
|
|
29
39
|
end
|
30
40
|
|
31
41
|
class Destination < MessageDriver::Destination::Base
|
32
|
-
def publish_params(headers, properties)
|
33
|
-
|
34
|
-
props
|
35
|
-
[
|
42
|
+
def publish_params(body, headers, properties)
|
43
|
+
b, h, p = middleware.on_publish(body, headers, properties)
|
44
|
+
props = @message_props.merge(p)
|
45
|
+
props[:headers] = h if h
|
46
|
+
[b, exchange_name, routing_key(properties), props]
|
36
47
|
end
|
37
48
|
|
38
49
|
def exchange_name
|
@@ -47,27 +58,35 @@ module MessageDriver
|
|
47
58
|
class QueueDestination < Destination
|
48
59
|
def after_initialize(adapter_context)
|
49
60
|
if @dest_options[:no_declare]
|
50
|
-
|
51
|
-
|
61
|
+
if @name.empty?
|
62
|
+
fail MessageDriver::Error, 'server-named queues must be declared, but you provided :no_declare => true'
|
63
|
+
end
|
64
|
+
if @dest_options[:bindings]
|
65
|
+
fail MessageDriver::Error, 'queues with bindings must be declared, but you provided :no_declare => true'
|
66
|
+
end
|
52
67
|
else
|
53
68
|
adapter_context.with_channel(false) do |ch|
|
54
|
-
bunny_queue(ch, true)
|
69
|
+
bunny_queue(ch, init: true)
|
55
70
|
end
|
56
71
|
end
|
57
72
|
end
|
58
73
|
|
59
|
-
def bunny_queue(channel,
|
60
|
-
|
61
|
-
if
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
74
|
+
def bunny_queue(channel, options = {})
|
75
|
+
opts = @dest_options.dup
|
76
|
+
opts.merge!(passive: options[:passive]) if options.key? :passive
|
77
|
+
queue = channel.queue(@name, opts)
|
78
|
+
handle_queue_init(queue) if options.fetch(:init, false)
|
79
|
+
queue
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_queue_init(queue)
|
83
|
+
@name = queue.name
|
84
|
+
if (bindings = @dest_options[:bindings])
|
85
|
+
bindings.each do |bnd|
|
86
|
+
fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
|
87
|
+
queue.bind(bnd[:source], bnd[:args] || {})
|
68
88
|
end
|
69
89
|
end
|
70
|
-
queue
|
71
90
|
end
|
72
91
|
|
73
92
|
def exchange_name
|
@@ -80,7 +99,17 @@ module MessageDriver
|
|
80
99
|
|
81
100
|
def message_count
|
82
101
|
adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
|
83
|
-
ch
|
102
|
+
bunny_queue(ch, passive: true).message_count
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def subscribe(options = {}, &consumer)
|
107
|
+
adapter.broker.client.current_adapter_context.subscribe(self, options, &consumer)
|
108
|
+
end
|
109
|
+
|
110
|
+
def consumer_count
|
111
|
+
adapter.broker.client.current_adapter_context.with_channel(false) do |ch|
|
112
|
+
bunny_queue(ch, passive: true).consumer_count
|
84
113
|
end
|
85
114
|
end
|
86
115
|
|
@@ -96,15 +125,15 @@ module MessageDriver
|
|
96
125
|
if (declare = @dest_options[:declare])
|
97
126
|
adapter_context.with_channel(false) do |ch|
|
98
127
|
type = declare.delete(:type)
|
99
|
-
|
128
|
+
fail MessageDriver::Error, 'you must provide a valid exchange type' unless type
|
100
129
|
ch.exchange_declare(@name, type, declare)
|
101
130
|
end
|
102
131
|
end
|
103
132
|
if (bindings = @dest_options[:bindings])
|
104
133
|
adapter_context.with_channel(false) do |ch|
|
105
134
|
bindings.each do |bnd|
|
106
|
-
|
107
|
-
ch.exchange_bind(bnd[:source], @name, bnd[:args]||{})
|
135
|
+
fail MessageDriver::Error, "binding #{bnd.inspect} must provide a source!" unless bnd[:source]
|
136
|
+
ch.exchange_bind(bnd[:source], @name, bnd[:args] || {})
|
108
137
|
end
|
109
138
|
end
|
110
139
|
end
|
@@ -112,20 +141,25 @@ module MessageDriver
|
|
112
141
|
end
|
113
142
|
|
114
143
|
class Subscription < Subscription::Base
|
144
|
+
attr_reader :sub_ctx, :error_handler
|
145
|
+
|
115
146
|
def start
|
116
|
-
|
147
|
+
unless destination.is_a? QueueDestination
|
148
|
+
fail MessageDriver::Error,
|
149
|
+
'subscriptions are only supported with QueueDestinations'
|
150
|
+
end
|
117
151
|
@sub_ctx = adapter.new_subscription_context(self)
|
118
152
|
@error_handler = options[:error_handler]
|
119
|
-
@
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
153
|
+
@message_handler = case options[:ack]
|
154
|
+
when :auto, nil
|
155
|
+
AutoAckHandler.new(self)
|
156
|
+
when :manual
|
157
|
+
ManualAckHandler.new(self)
|
158
|
+
when :transactional
|
159
|
+
TransactionalAckHandler.new(self)
|
160
|
+
else
|
161
|
+
fail MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
|
162
|
+
end
|
129
163
|
start_subscription
|
130
164
|
end
|
131
165
|
|
@@ -142,50 +176,74 @@ module MessageDriver
|
|
142
176
|
|
143
177
|
private
|
144
178
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
179
|
+
class MessageHandler
|
180
|
+
extend Forwardable
|
181
|
+
include Logging
|
182
|
+
|
183
|
+
attr_accessor :subscription
|
184
|
+
def_delegators :subscription, :adapter, :sub_ctx, :consumer, :error_handler, :options
|
185
|
+
|
186
|
+
def initialize(subscription)
|
187
|
+
@subscription = subscription
|
188
|
+
end
|
189
|
+
|
190
|
+
def call(message)
|
191
|
+
consumer.call(message)
|
192
|
+
rescue => e
|
193
|
+
error_handler.call(e, message) unless error_handler.nil?
|
194
|
+
end
|
195
|
+
|
196
|
+
def nack_message(e, message)
|
197
|
+
requeue = true
|
198
|
+
if e.is_a?(DontRequeue) || (options[:retry_redelivered] == false && message.redelivered?)
|
199
|
+
requeue = false
|
150
200
|
end
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
201
|
+
if sub_ctx.valid?
|
202
|
+
begin
|
203
|
+
sub_ctx.nack_message(message, requeue: requeue)
|
204
|
+
rescue => e
|
205
|
+
logger.error exception_to_str(e)
|
155
206
|
end
|
156
207
|
end
|
157
208
|
end
|
158
209
|
end
|
159
210
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
211
|
+
class ManualAckHandler < MessageHandler
|
212
|
+
# all functionality implemented in super class
|
213
|
+
end
|
214
|
+
|
215
|
+
class AutoAckHandler < MessageHandler
|
216
|
+
def call(message)
|
217
|
+
consumer.call(message)
|
218
|
+
sub_ctx.ack_message(message)
|
219
|
+
rescue => e
|
220
|
+
nack_message(e, message)
|
221
|
+
error_handler.call(e, message) unless error_handler.nil?
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
class TransactionalAckHandler < MessageHandler
|
226
|
+
def call(message)
|
227
|
+
adapter.broker.client.with_message_transaction do
|
167
228
|
consumer.call(message)
|
168
|
-
|
169
|
-
adapter.broker.client.with_message_transaction do
|
170
|
-
consumer.call(message)
|
171
|
-
@sub_ctx.ack_message(message)
|
172
|
-
end
|
229
|
+
sub_ctx.ack_message(message)
|
173
230
|
end
|
174
231
|
rescue => e
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
232
|
+
nack_message(e, message)
|
233
|
+
error_handler.call(e, message) unless error_handler.nil?
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def start_subscription
|
238
|
+
@sub_ctx.with_channel do |ch|
|
239
|
+
queue = destination.bunny_queue(@sub_ctx.channel)
|
240
|
+
ch.prefetch(options[:prefetch_size]) if options.key? :prefetch_size
|
241
|
+
@bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
|
242
|
+
adapter.broker.client.with_adapter_context(@sub_ctx) do
|
243
|
+
message = @sub_ctx.args_to_message(delivery_info, properties, payload, destination)
|
244
|
+
@message_handler.call(message)
|
186
245
|
end
|
187
246
|
end
|
188
|
-
@error_handler.call(e, message) unless @error_handler.nil?
|
189
247
|
end
|
190
248
|
end
|
191
249
|
end
|
@@ -196,7 +254,7 @@ module MessageDriver
|
|
196
254
|
@config = config
|
197
255
|
end
|
198
256
|
|
199
|
-
def connection(ensure_started=true)
|
257
|
+
def connection(ensure_started = true)
|
200
258
|
if ensure_started
|
201
259
|
begin
|
202
260
|
@connection ||= Bunny::Session.new(@config)
|
@@ -212,14 +270,12 @@ module MessageDriver
|
|
212
270
|
end
|
213
271
|
|
214
272
|
def stop
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
@connection = nil
|
222
|
-
end
|
273
|
+
super
|
274
|
+
@connection.close unless @connection.nil?
|
275
|
+
rescue => e
|
276
|
+
logger.error "error while attempting connection close\n#{exception_to_str(e)}"
|
277
|
+
ensure
|
278
|
+
@connection = nil
|
223
279
|
end
|
224
280
|
|
225
281
|
def build_context
|
@@ -244,15 +300,15 @@ module MessageDriver
|
|
244
300
|
@in_transaction = false
|
245
301
|
end
|
246
302
|
|
247
|
-
def create_destination(name, dest_options={}, message_props={})
|
248
|
-
dest =
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
303
|
+
def create_destination(name, dest_options = {}, message_props = {})
|
304
|
+
dest = case type = dest_options.delete(:type)
|
305
|
+
when :exchange
|
306
|
+
ExchangeDestination.new(adapter, name, dest_options, message_props)
|
307
|
+
when :queue, nil
|
308
|
+
QueueDestination.new(adapter, name, dest_options, message_props)
|
309
|
+
else
|
310
|
+
fail MessageDriver::Error, "invalid destination type #{type}"
|
311
|
+
end
|
256
312
|
dest.after_initialize(self)
|
257
313
|
dest
|
258
314
|
end
|
@@ -261,14 +317,20 @@ module MessageDriver
|
|
261
317
|
true
|
262
318
|
end
|
263
319
|
|
264
|
-
def begin_transaction(options={})
|
265
|
-
|
320
|
+
def begin_transaction(options = {})
|
321
|
+
if in_transaction?
|
322
|
+
fail MessageDriver::TransactionError,
|
323
|
+
"you can't begin another transaction, you are already in one!"
|
324
|
+
end
|
266
325
|
@in_transaction = true
|
267
326
|
@in_confirms_transaction = true if options[:type] == :confirm_and_wait
|
268
327
|
end
|
269
328
|
|
270
|
-
def commit_transaction(channel_commit=false)
|
271
|
-
|
329
|
+
def commit_transaction(channel_commit = false)
|
330
|
+
if !in_transaction? && !channel_commit
|
331
|
+
fail MessageDriver::TransactionError,
|
332
|
+
"you can't finish the transaction unless you already in one!"
|
333
|
+
end
|
272
334
|
begin
|
273
335
|
if @in_confirms_transaction
|
274
336
|
wait_for_confirms(@channel) unless @rollback_only
|
@@ -291,9 +353,8 @@ module MessageDriver
|
|
291
353
|
end
|
292
354
|
|
293
355
|
def wait_for_confirms(channel)
|
294
|
-
|
295
|
-
|
296
|
-
end
|
356
|
+
# FIXME: make the thread-safety of this better once https://github.com/ruby-amqp/bunny/issues/227 is fixed
|
357
|
+
channel.wait_for_confirms until channel.unconfirmed_set.empty?
|
297
358
|
end
|
298
359
|
private :wait_for_confirms
|
299
360
|
|
@@ -311,8 +372,8 @@ module MessageDriver
|
|
311
372
|
@in_transaction
|
312
373
|
end
|
313
374
|
|
314
|
-
def publish(destination, body, headers={}, properties={})
|
315
|
-
exchange, routing_key, props = *destination.publish_params(headers, properties)
|
375
|
+
def publish(destination, body, headers = {}, properties = {})
|
376
|
+
body, exchange, routing_key, props = *destination.publish_params(body, headers, properties)
|
316
377
|
confirm = props.delete(:confirm)
|
317
378
|
confirm = false if confirm.nil?
|
318
379
|
with_channel(true) do |ch|
|
@@ -324,8 +385,8 @@ module MessageDriver
|
|
324
385
|
end
|
325
386
|
end
|
326
387
|
|
327
|
-
def pop_message(destination, options={})
|
328
|
-
|
388
|
+
def pop_message(destination, options = {})
|
389
|
+
fail MessageDriver::Error, "You can't pop a message off an exchange" if destination.is_a? ExchangeDestination
|
329
390
|
|
330
391
|
with_channel(false) do |ch|
|
331
392
|
queue = ch.queue(destination.name, passive: true)
|
@@ -334,7 +395,7 @@ module MessageDriver
|
|
334
395
|
if message.nil? || message[0].nil?
|
335
396
|
nil
|
336
397
|
else
|
337
|
-
args_to_message(*message)
|
398
|
+
args_to_message(*message, destination)
|
338
399
|
end
|
339
400
|
end
|
340
401
|
end
|
@@ -343,13 +404,13 @@ module MessageDriver
|
|
343
404
|
true
|
344
405
|
end
|
345
406
|
|
346
|
-
def ack_message(message, _options={})
|
407
|
+
def ack_message(message, _options = {})
|
347
408
|
with_channel(true) do |ch|
|
348
409
|
ch.ack(message.delivery_tag)
|
349
410
|
end
|
350
411
|
end
|
351
412
|
|
352
|
-
def nack_message(message, options={})
|
413
|
+
def nack_message(message, options = {})
|
353
414
|
requeue = options[:requeue].is_a?(FalseClass) ? false : true
|
354
415
|
with_channel(true) do |ch|
|
355
416
|
ch.reject(message.delivery_tag, requeue)
|
@@ -360,13 +421,13 @@ module MessageDriver
|
|
360
421
|
true
|
361
422
|
end
|
362
423
|
|
363
|
-
def subscribe(destination, options={}, &consumer)
|
424
|
+
def subscribe(destination, options = {}, &consumer)
|
364
425
|
sub = Subscription.new(adapter, destination, consumer, options)
|
365
426
|
sub.start
|
366
427
|
sub
|
367
428
|
end
|
368
429
|
|
369
|
-
def invalidate(in_unsubscribe=false)
|
430
|
+
def invalidate(in_unsubscribe = false)
|
370
431
|
super()
|
371
432
|
unless @subscription.nil? || in_unsubscribe
|
372
433
|
begin
|
@@ -387,30 +448,28 @@ module MessageDriver
|
|
387
448
|
end
|
388
449
|
|
389
450
|
def handle_errors
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
else
|
398
|
-
raise MessageDriver::WrappedError.new(e.to_s, e)
|
399
|
-
end
|
400
|
-
rescue Bunny::ChannelAlreadyClosed => e
|
401
|
-
@need_channel_reset = true
|
402
|
-
@rollback_only = true if in_transaction?
|
451
|
+
yield
|
452
|
+
rescue Bunny::ChannelLevelException => e
|
453
|
+
@need_channel_reset = true
|
454
|
+
@rollback_only = true if in_transaction?
|
455
|
+
if e.is_a? Bunny::NotFound
|
456
|
+
raise MessageDriver::QueueNotFound.new(e.to_s, e)
|
457
|
+
else
|
403
458
|
raise MessageDriver::WrappedError.new(e.to_s, e)
|
404
|
-
rescue *NETWORK_ERRORS => e
|
405
|
-
@need_channel_reset = true
|
406
|
-
@rollback_only = true if in_transaction?
|
407
|
-
raise MessageDriver::ConnectionError.new(e.to_s, e)
|
408
459
|
end
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
raise MessageDriver::
|
413
|
-
|
460
|
+
rescue Bunny::ChannelAlreadyClosed => e
|
461
|
+
@need_channel_reset = true
|
462
|
+
@rollback_only = true if in_transaction?
|
463
|
+
raise MessageDriver::WrappedError.new(e.to_s, e)
|
464
|
+
rescue *NETWORK_ERRORS => e
|
465
|
+
@need_channel_reset = true
|
466
|
+
@rollback_only = true if in_transaction?
|
467
|
+
raise MessageDriver::ConnectionError.new(e.to_s, e)
|
468
|
+
end
|
469
|
+
|
470
|
+
def with_channel(require_commit = true)
|
471
|
+
fail MessageDriver::TransactionRollbackOnly if @rollback_only
|
472
|
+
fail MessageDriver::Error, 'this adapter context is not valid!' unless valid?
|
414
473
|
@channel = adapter.connection.create_channel if @channel.nil?
|
415
474
|
reset_channel if @need_channel_reset
|
416
475
|
if in_transaction?
|
@@ -430,8 +489,8 @@ module MessageDriver
|
|
430
489
|
end
|
431
490
|
end
|
432
491
|
|
433
|
-
def args_to_message(delivery_info, properties, payload)
|
434
|
-
Message.new(self, delivery_info, properties, payload)
|
492
|
+
def args_to_message(delivery_info, properties, payload, destination)
|
493
|
+
Message.new(self, delivery_info, properties, payload, destination)
|
435
494
|
end
|
436
495
|
|
437
496
|
private
|
@@ -449,18 +508,16 @@ module MessageDriver
|
|
449
508
|
private
|
450
509
|
|
451
510
|
def log_errors
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
logger.error exception_to_str(e)
|
456
|
-
end
|
511
|
+
yield
|
512
|
+
rescue => e
|
513
|
+
logger.error exception_to_str(e)
|
457
514
|
end
|
458
515
|
|
459
516
|
def validate_bunny_version
|
460
517
|
required = Gem::Requirement.create('>= 1.2.2')
|
461
518
|
current = Gem::Version.create(Bunny::VERSION)
|
462
519
|
unless required.satisfied_by? current
|
463
|
-
|
520
|
+
fail MessageDriver::Error, 'bunny 1.2.2 or later is required for the bunny adapter'
|
464
521
|
end
|
465
522
|
end
|
466
523
|
end
|