message-driver 0.4.0 → 0.5.0
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.
- 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
|