amqp 1.1.0.pre1 → 1.1.0.pre2

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.
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ module AMQP
4
+
5
+ # Manages the encoding of credentials for the EXTERNAL authentication
6
+ # mechanism.
7
+ class AuthMechanismAdapter::External < AuthMechanismAdapter
8
+
9
+ auth_mechanism "EXTERNAL"
10
+
11
+ # Encodes a username and password for the EXTERNAL mechanism. Since
12
+ # authentication is handled by an encapsulating protocol like SSL or
13
+ # UNIX domain sockets, EXTERNAL doesn't pass along any username or
14
+ # password information at all and this method always returns the
15
+ # empty string.
16
+ #
17
+ # @param [String] username The username to encode. This parameter is
18
+ # ignored.
19
+ # @param [String] password The password to encode. This parameter is
20
+ # ignored.
21
+ # @return [String] The username and password, encoded for the
22
+ # EXTERNAL mechanism. This is always the empty string.
23
+ def encode_credentials(username, password)
24
+ ""
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ module AMQP
4
+
5
+ # Manages the encoding of credentials for the PLAIN authentication
6
+ # mechanism.
7
+ class AuthMechanismAdapter::Plain < AuthMechanismAdapter
8
+
9
+ auth_mechanism "PLAIN"
10
+
11
+ # Encodes credentials for the given username and password. This
12
+ # involves sending the password across the wire in plaintext, so
13
+ # PLAIN authentication should only be used over a secure transport
14
+ # layer.
15
+ #
16
+ # @param [String] username The username to encode.
17
+ # @param [String] password The password to encode.
18
+ # @return [String] The username and password, encoded for the PLAIN
19
+ # mechanism.
20
+ def encode_credentials(username, password)
21
+ "\0#{username}\0#{password}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ module AMQP
2
+ module Callbacks
3
+
4
+ def redefine_callback(event, callable = nil, &block)
5
+ f = (callable || block)
6
+ # yes, re-assign!
7
+ @callbacks[event] = [f]
8
+
9
+ self
10
+ end
11
+
12
+ def define_callback(event, callable = nil, &block)
13
+ f = (callable || block)
14
+
15
+ @callbacks[event] ||= []
16
+ @callbacks[event] << f if f
17
+
18
+ self
19
+ end # define_callback(event, &block)
20
+ alias append_callback define_callback
21
+
22
+ def prepend_callback(event, &block)
23
+ @callbacks[event] ||= []
24
+ @callbacks[event].unshift(block)
25
+
26
+ self
27
+ end # prepend_callback(event, &block)
28
+
29
+ def clear_callbacks(event)
30
+ @callbacks[event].clear if @callbacks[event]
31
+ end # clear_callbacks(event)
32
+
33
+
34
+ def exec_callback(name, *args, &block)
35
+ list = Array(@callbacks[name])
36
+ if list.any?
37
+ list.each { |c| c.call(*args, &block) }
38
+ end
39
+ end
40
+
41
+ def exec_callback_once(name, *args, &block)
42
+ list = (@callbacks.delete(name) || Array.new)
43
+ if list.any?
44
+ list.each { |c| c.call(*args, &block) }
45
+ end
46
+ end
47
+
48
+ def exec_callback_yielding_self(name, *args, &block)
49
+ list = Array(@callbacks[name])
50
+ if list.any?
51
+ list.each { |c| c.call(self, *args, &block) }
52
+ end
53
+ end
54
+
55
+ def exec_callback_once_yielding_self(name, *args, &block)
56
+ list = (@callbacks.delete(name) || Array.new)
57
+
58
+ if list.any?
59
+ list.each { |c| c.call(self, *args, &block) }
60
+ end
61
+ end
62
+
63
+ def has_callback?(name)
64
+ @callbacks[name] && !@callbacks[name].empty?
65
+ end # has_callback?
66
+ end # Callbacks
67
+ end
@@ -141,7 +141,19 @@ module AMQP
141
141
  # Learn more in {file:docs/VendorSpecificExtensions.textile}
142
142
  #
143
143
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Specification.pdf AMQP 0.9.1 specification (Section 2.2.5)
144
- class Channel < AMQ::Client::Channel
144
+ class Channel
145
+
146
+ #
147
+ # Behaviours
148
+ #
149
+
150
+ extend RegisterEntityMixin
151
+ include Entity
152
+ extend ProtocolMethodHandlers
153
+
154
+ register_entity :queue, AMQP::Queue
155
+ register_entity :exchange, AMQP::Exchange
156
+
145
157
 
146
158
  #
147
159
  # API
@@ -156,6 +168,24 @@ module AMQP
156
168
  # @return [Symbol]
157
169
  attr_reader :status
158
170
 
171
+ DEFAULT_REPLY_TEXT = "Goodbye".freeze
172
+
173
+ attr_reader :id
174
+
175
+ attr_reader :exchanges_awaiting_declare_ok, :exchanges_awaiting_delete_ok
176
+ attr_reader :queues_awaiting_declare_ok, :queues_awaiting_delete_ok, :queues_awaiting_bind_ok, :queues_awaiting_unbind_ok, :queues_awaiting_purge_ok, :queues_awaiting_get_response
177
+ attr_reader :consumers_awaiting_consume_ok, :consumers_awaiting_cancel_ok
178
+
179
+ attr_accessor :flow_is_active
180
+
181
+ # Change publisher index. Publisher index is incremented
182
+ # by 1 after each Basic.Publish starting at 1. This is done
183
+ # on both client and server, hence this acknowledged messages
184
+ # can be matched via its delivery-tag.
185
+ #
186
+ # @api private
187
+ attr_writer :publisher_index
188
+
159
189
 
160
190
  # @param [AMQP::Session] connection Connection to open this channel on. If not given, default AMQP
161
191
  # connection (accessible via {AMQP.connection}) will be used.
@@ -211,7 +241,32 @@ module AMQP
211
241
  id = self.class.next_channel_id
212
242
  end
213
243
 
214
- super(@connection, id, options)
244
+ super(@connection)
245
+
246
+ @id = id
247
+ @exchanges = Hash.new
248
+ @queues = Hash.new
249
+ @consumers = Hash.new
250
+ @options = { :auto_recovery => @connection.auto_recovering? }.merge(options)
251
+ @auto_recovery = (!!@options[:auto_recovery])
252
+
253
+ # we must synchronize frameset delivery. MK.
254
+ @mutex = Mutex.new
255
+
256
+ reset_state!
257
+
258
+ # 65536 is here for cases when channel is opened without passing a callback in,
259
+ # otherwise channel_mix would be nil and it causes a lot of needless headaches.
260
+ # lets just have this default. MK.
261
+ channel_max = if @connection.open?
262
+ @connection.channel_max || 65536
263
+ else
264
+ 65536
265
+ end
266
+
267
+ if channel_max != 0 && !(0..channel_max).include?(id)
268
+ raise ArgumentError.new("Max channel for the connection is #{channel_max}, given: #{id}")
269
+ end
215
270
 
216
271
  # we need this deferrable to mimic what AMQP gem 0.7 does to enable
217
272
  # the following (pseudo-synchronous) style of programming some people use in their
@@ -224,7 +279,7 @@ module AMQP
224
279
  # ...
225
280
  #
226
281
  # Read more about EM::Deferrable#callback behavior in EventMachine documentation. MK.
227
- @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
282
+ @channel_is_open_deferrable = AMQP::Deferrable.new
228
283
 
229
284
  @parameter_checks = {:queue => [:durable, :exclusive, :auto_delete, :arguments], :exchange => [:type, :durable, :arguments]}
230
285
 
@@ -263,7 +318,7 @@ module AMQP
263
318
  return unless auto_recovering?
264
319
 
265
320
  @channel_is_open_deferrable.fail
266
- @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
321
+ @channel_is_open_deferrable = AMQP::Deferrable.new
267
322
 
268
323
  self.open do
269
324
  @channel_is_open_deferrable.succeed
@@ -292,7 +347,7 @@ module AMQP
292
347
  self.class.release_channel_id(old_id)
293
348
 
294
349
  @channel_is_open_deferrable.fail
295
- @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
350
+ @channel_is_open_deferrable = AMQP::Deferrable.new
296
351
 
297
352
  self.open do
298
353
  @channel_is_open_deferrable.succeed
@@ -820,13 +875,13 @@ module AMQP
820
875
  Queue.new(self, name, opts)
821
876
  else
822
877
  shim = Proc.new { |q, method|
823
- if block.arity == 1
824
- block.call(q)
825
- else
826
- queue = find_queue(method.queue)
827
- block.call(queue, method.consumer_count, method.message_count)
828
- end
829
- }
878
+ if block.arity == 1
879
+ block.call(q)
880
+ else
881
+ queue = find_queue(method.queue)
882
+ block.call(queue, method.consumer_count, method.message_count)
883
+ end
884
+ }
830
885
  Queue.new(self, name, opts, &shim)
831
886
  end
832
887
 
@@ -852,8 +907,14 @@ module AMQP
852
907
  # @note Instantiated channels are opened by default. This method should only be used for error recovery after network connection loss.
853
908
  # @api public
854
909
  def open(&block)
855
- super(&block)
910
+ @connection.send_frame(AMQ::Protocol::Channel::Open.encode(@id, AMQ::Protocol::EMPTY_STRING))
911
+ @connection.channels[@id] = self
912
+ self.status = :opening
913
+
914
+ self.redefine_callback :open, &block
856
915
  end
916
+ alias reopen open
917
+
857
918
 
858
919
  # @return [Boolean] true if channel is not closed.
859
920
  # @api public
@@ -889,9 +950,9 @@ module AMQP
889
950
  # @api public
890
951
  def close(reply_code = 200, reply_text = DEFAULT_REPLY_TEXT, class_id = 0, method_id = 0, &block)
891
952
  self.status = :closing
892
- r = super(reply_code, reply_text, class_id, method_id, &block)
953
+ @connection.send_frame(AMQ::Protocol::Channel::Close.encode(@id, reply_code, reply_text, class_id, method_id))
893
954
 
894
- r
955
+ self.redefine_callback :close, &block
895
956
  end
896
957
 
897
958
  # @endgroup
@@ -912,7 +973,10 @@ module AMQP
912
973
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.5.2.3.)
913
974
  # @api public
914
975
  def flow(active = false, &block)
915
- super(active, &block)
976
+ @connection.send_frame(AMQ::Protocol::Channel::Flow.encode(@id, active))
977
+
978
+ self.redefine_callback :flow, &block
979
+ self
916
980
  end
917
981
 
918
982
  # @return [Boolean] True if flow in this channel is active (messages will be delivered to consumers that use this channel).
@@ -952,7 +1016,9 @@ module AMQP
952
1016
  # @see #recover
953
1017
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.13.)
954
1018
  def acknowledge(delivery_tag, multiple = false)
955
- super(delivery_tag, multiple)
1019
+ @connection.send_frame(AMQ::Protocol::Basic::Ack.encode(self.id, delivery_tag, multiple))
1020
+
1021
+ self
956
1022
  end # acknowledge(delivery_tag, multiple = false)
957
1023
 
958
1024
  # Reject a message with given delivery tag.
@@ -961,8 +1027,14 @@ module AMQP
961
1027
  # @see #acknowledge
962
1028
  # @see #recover
963
1029
  # @see http://files.travis-ci.org/docs/amqp/0.9.1/AMQP091Reference.pdf AMQP 0.9.1 protocol documentation (Section 1.8.3.14.)
964
- def reject(delivery_tag, requeue = true)
965
- super(delivery_tag, requeue)
1030
+ def reject(delivery_tag, requeue = true, multi = false)
1031
+ if multi
1032
+ @connection.send_frame(AMQ::Protocol::Basic::Nack.encode(self.id, delivery_tag, multi, requeue))
1033
+ else
1034
+ @connection.send_frame(AMQ::Protocol::Basic::Reject.encode(self.id, delivery_tag, requeue))
1035
+ end
1036
+
1037
+ self
966
1038
  end # reject(delivery_tag, requeue = true)
967
1039
 
968
1040
  # Notifies AMQ broker that consumer has recovered and unacknowledged messages need
@@ -975,7 +1047,10 @@ module AMQP
975
1047
  # @see #acknowledge
976
1048
  # @api public
977
1049
  def recover(requeue = true, &block)
978
- super(requeue, &block)
1050
+ @connection.send_frame(AMQ::Protocol::Basic::Recover.encode(@id, requeue))
1051
+
1052
+ self.redefine_callback :recover, &block
1053
+ self
979
1054
  end # recover(requeue = false, &block)
980
1055
 
981
1056
  # @endgroup
@@ -990,21 +1065,30 @@ module AMQP
990
1065
  #
991
1066
  # @api public
992
1067
  def tx_select(&block)
993
- super(&block)
1068
+ @connection.send_frame(AMQ::Protocol::Tx::Select.encode(@id))
1069
+
1070
+ self.redefine_callback :tx_select, &block
1071
+ self
994
1072
  end # tx_select(&block)
995
1073
 
996
1074
  # Commits AMQP transaction.
997
1075
  #
998
1076
  # @api public
999
1077
  def tx_commit(&block)
1000
- super(&block)
1078
+ @connection.send_frame(AMQ::Protocol::Tx::Commit.encode(@id))
1079
+
1080
+ self.redefine_callback :tx_commit, &block
1081
+ self
1001
1082
  end # tx_commit(&block)
1002
1083
 
1003
1084
  # Rolls AMQP transaction back.
1004
1085
  #
1005
1086
  # @api public
1006
1087
  def tx_rollback(&block)
1007
- super(&block)
1088
+ @connection.send_frame(AMQ::Protocol::Tx::Rollback.encode(@id))
1089
+
1090
+ self.redefine_callback :tx_rollback, &block
1091
+ self
1008
1092
  end # tx_rollback(&block)
1009
1093
 
1010
1094
 
@@ -1021,7 +1105,7 @@ module AMQP
1021
1105
  #
1022
1106
  # @api public
1023
1107
  def on_error(&block)
1024
- super(&block)
1108
+ self.define_callback(:error, &block)
1025
1109
  end
1026
1110
 
1027
1111
  # @endgroup
@@ -1031,56 +1115,30 @@ module AMQP
1031
1115
 
1032
1116
  def confirm_select(nowait = false, &block)
1033
1117
  self.once_open do
1034
- super(nowait, &block)
1118
+ if nowait && block
1119
+ raise ArgumentError, "confirm.select with nowait = true and a callback makes no sense"
1035
1120
  end
1036
- end
1037
1121
 
1038
- # @endgroup
1039
-
1040
-
1041
- #
1042
- # Implementation
1043
- #
1122
+ @uses_publisher_confirmations = true
1123
+ reset_publisher_index!
1044
1124
 
1125
+ self.redefine_callback(:confirm_select, &block) unless nowait
1126
+ self.redefine_callback(:after_publish) do
1127
+ increment_publisher_index!
1128
+ end
1129
+ @connection.send_frame(AMQ::Protocol::Confirm::Select.encode(@id, nowait))
1045
1130
 
1046
- # Defines a global callback to be run on channel-level exception across
1047
- # all channels. Consider using Channel#on_error instead. This method is here for sake
1048
- # of backwards compatibility with 0.6.x and 0.7.x releases.
1049
- #
1050
- # @param [String] msg Error message that passed to previously defined handler
1051
- #
1052
- # @deprecated
1053
- # @api public
1054
- # @private
1055
- def self.error(msg = nil, &block)
1056
- if block
1057
- @global_error_handler = block
1058
- else
1059
- @global_error_handler.call(msg) if @global_error_handler && msg
1131
+ self
1060
1132
  end
1061
1133
  end
1062
1134
 
1135
+ # @endgroup
1063
1136
 
1064
- # Overrides AMQ::Client::Channel version to also call global callback
1065
- # (if defined) for backwards compatibility.
1066
- #
1067
- # @private
1068
- # @api private
1069
- def handle_close(method)
1070
- super(method)
1071
-
1072
- self.class.error(method.reply_text)
1073
- self.class.release_channel_id(@id)
1074
- end
1075
1137
 
1076
- # Overrides AMQ::Client::Channel version to also release the channel id
1077
1138
  #
1078
- # @private
1079
- # @api private
1080
- def handle_close_ok(method)
1081
- super(method)
1082
- self.class.release_channel_id(@id)
1083
- end
1139
+ # Implementation
1140
+ #
1141
+
1084
1142
  # Resets channel state (for example, list of registered queue objects and so on).
1085
1143
  #
1086
1144
  # Most of the time, this method is not
@@ -1089,11 +1147,11 @@ module AMQP
1089
1147
  # @private
1090
1148
  # @api plugin
1091
1149
  def reset(&block)
1092
- # See AMQ::Client::Channel
1150
+ # See AMQP::Channel
1093
1151
  self.reset_state!
1094
1152
 
1095
1153
  # there is no way to reset a deferrable; we have to use a new instance. MK.
1096
- @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
1154
+ @channel_is_open_deferrable = AMQP::Deferrable.new
1097
1155
  @channel_is_open_deferrable.callback(&block)
1098
1156
 
1099
1157
  @connection.on_connection do
@@ -1103,22 +1161,19 @@ module AMQP
1103
1161
  end
1104
1162
  end
1105
1163
 
1106
- # @private
1107
- # @api plugin
1108
- def reset_state!
1109
- super
1110
- end # reset_state!
1111
-
1112
-
1113
1164
  # Overrides superclass method to also re-create @channel_is_open_deferrable
1114
1165
  #
1115
1166
  # @api plugin
1116
1167
  # @private
1117
- def handle_connection_interruption(reason = nil)
1118
- super(reason)
1168
+ def handle_connection_interruption(method = nil)
1169
+ @queues.each { |name, q| q.handle_connection_interruption(method) }
1170
+ @exchanges.each { |name, e| e.handle_connection_interruption(method) }
1171
+
1172
+ self.exec_callback_yielding_self(:after_connection_interruption)
1173
+ self.reset_state!
1119
1174
 
1120
1175
  self.class.release_channel_id(@id) unless auto_recovering?
1121
- @channel_is_open_deferrable = AMQ::Client::EventMachineClient::Deferrable.new
1176
+ @channel_is_open_deferrable = AMQP::Deferrable.new
1122
1177
  end
1123
1178
 
1124
1179
 
@@ -1178,6 +1233,392 @@ module AMQP
1178
1233
  end # self.initialize_channel_id_allocator
1179
1234
 
1180
1235
 
1236
+ # @return [Boolean] true if this channel uses automatic recovery mode
1237
+ def auto_recovering?
1238
+ @auto_recovery
1239
+ end # auto_recovering?
1240
+
1241
+
1242
+ # @return [Hash<String, Consumer>]
1243
+ def consumers
1244
+ @consumers
1245
+ end # consumers
1246
+
1247
+ # @return [Array<Queue>] Collection of queues that were declared on this channel.
1248
+ def queues
1249
+ @queues.values
1250
+ end
1251
+
1252
+ # @return [Array<Exchange>] Collection of exchanges that were declared on this channel.
1253
+ def exchanges
1254
+ @exchanges.values
1255
+ end
1256
+
1257
+
1258
+ # AMQP connection this channel belongs to.
1259
+ #
1260
+ # @return [AMQP::Connection] Connection this channel belongs to.
1261
+ def connection
1262
+ @connection
1263
+ end # connection
1264
+
1265
+ # Synchronizes given block using this channel's mutex.
1266
+ # @api public
1267
+ def synchronize(&block)
1268
+ @mutex.synchronize(&block)
1269
+ end
1270
+
1271
+
1272
+
1273
+ # @group QoS and flow handling
1274
+
1275
+ # Requests a specific quality of service. The QoS can be specified for the current channel
1276
+ # or for all channels on the connection.
1277
+ #
1278
+ # @note RabbitMQ as of 2.3.1 does not support prefetch_size.
1279
+ # @api public
1280
+ def qos(prefetch_size = 0, prefetch_count = 32, global = false, &block)
1281
+ @connection.send_frame(AMQ::Protocol::Basic::Qos.encode(@id, prefetch_size, prefetch_count, global))
1282
+
1283
+ self.redefine_callback :qos, &block
1284
+ self
1285
+ end # qos
1286
+
1287
+ # @endgroup
1288
+
1289
+
1290
+
1291
+ # @group Error handling
1292
+
1293
+
1294
+ # Defines a callback that will be executed after TCP connection is interrupted (typically because of a network failure).
1295
+ # Only one callback can be defined (the one defined last replaces previously added ones).
1296
+ #
1297
+ # @api public
1298
+ def on_connection_interruption(&block)
1299
+ self.redefine_callback(:after_connection_interruption, &block)
1300
+ end # on_connection_interruption(&block)
1301
+ alias after_connection_interruption on_connection_interruption
1302
+
1303
+
1304
+ # Defines a callback that will be executed after TCP connection has recovered after a network failure
1305
+ # but before AMQP connection is re-opened.
1306
+ # Only one callback can be defined (the one defined last replaces previously added ones).
1307
+ #
1308
+ # @api public
1309
+ def before_recovery(&block)
1310
+ self.redefine_callback(:before_recovery, &block)
1311
+ end # before_recovery(&block)
1312
+
1313
+ # @private
1314
+ def run_before_recovery_callbacks
1315
+ self.exec_callback_yielding_self(:before_recovery)
1316
+
1317
+ @queues.each { |name, q| q.run_before_recovery_callbacks }
1318
+ @exchanges.each { |name, e| e.run_before_recovery_callbacks }
1319
+ end
1320
+
1321
+
1322
+
1323
+ # Defines a callback that will be executed after AMQP connection has recovered after a network failure.
1324
+ # Only one callback can be defined (the one defined last replaces previously added ones).
1325
+ #
1326
+ # @api public
1327
+ def on_recovery(&block)
1328
+ self.redefine_callback(:after_recovery, &block)
1329
+ end # on_recovery(&block)
1330
+ alias after_recovery on_recovery
1331
+
1332
+ # @private
1333
+ def run_after_recovery_callbacks
1334
+ self.exec_callback_yielding_self(:after_recovery)
1335
+
1336
+ @queues.each { |name, q| q.run_after_recovery_callbacks }
1337
+ @exchanges.each { |name, e| e.run_after_recovery_callbacks }
1338
+ end
1339
+
1340
+
1341
+ # Called by associated connection object when AMQP connection has been re-established
1342
+ # (for example, after a network failure).
1343
+ #
1344
+ # @api plugin
1345
+ def auto_recover
1346
+ return unless auto_recovering?
1347
+
1348
+ self.open do
1349
+ # exchanges must be recovered first because queue recovery includes recovery of bindings. MK.
1350
+ @exchanges.each { |name, e| e.auto_recover }
1351
+ @queues.each { |name, q| q.auto_recover }
1352
+ end
1353
+ end # auto_recover
1354
+
1355
+ # @endgroup
1356
+
1357
+
1358
+ # Publisher index is an index of the last message since
1359
+ # the confirmations were activated, started with 0. It's
1360
+ # incremented by 1 every time a message is published.
1361
+ # This is done on both client and server, hence this
1362
+ # acknowledged messages can be matched via its delivery-tag.
1363
+ #
1364
+ # @return [Integer] Current publisher index.
1365
+ # @api public
1366
+ def publisher_index
1367
+ @publisher_index ||= 0
1368
+ end
1369
+
1370
+ # Resets publisher index to 0
1371
+ #
1372
+ # @api plugin
1373
+ def reset_publisher_index!
1374
+ @publisher_index = 0
1375
+ end
1376
+
1377
+
1378
+ # This method is executed after publishing of each message via {Exchage#publish}.
1379
+ # Currently it just increments publisher index by 1, so messages
1380
+ # can be actually matched.
1381
+ #
1382
+ # @api plugin
1383
+ def increment_publisher_index!
1384
+ @publisher_index += 1
1385
+ end
1386
+
1387
+ # @return [Boolean]
1388
+ def uses_publisher_confirmations?
1389
+ @uses_publisher_confirmations
1390
+ end # uses_publisher_confirmations?
1391
+
1392
+
1393
+ # Turn on confirmations for this channel and, if given,
1394
+ # register callback for basic.ack from the broker.
1395
+ #
1396
+ # @raise [RuntimeError] Occurs when confirmations are already activated.
1397
+ # @raise [RuntimeError] Occurs when nowait is true and block is given.
1398
+ # @param [Boolean] nowait Whether we expect Confirm.Select-Ok to be returned by the broker or not.
1399
+ #
1400
+ # @yield [basick_ack] Callback which will be executed every time we receive Basic.Ack from the broker.
1401
+ # @yieldparam [AMQ::Protocol::Basic::Ack] basick_ack Protocol method class instance.
1402
+ #
1403
+ # @return [self] self.
1404
+ def on_ack(nowait = false, &block)
1405
+ self.define_callback(:ack, &block) if block
1406
+
1407
+ self
1408
+ end
1409
+
1410
+
1411
+ # Register error callback for Basic.Nack. It's called
1412
+ # when message(s) is rejected.
1413
+ #
1414
+ # @return [self] self
1415
+ def on_nack(&block)
1416
+ self.define_callback(:nack, &block) if block
1417
+
1418
+ self
1419
+ end
1420
+
1421
+
1422
+
1423
+
1424
+ # Handler for Confirm.Select-Ok. By default, it just
1425
+ # executes hook specified via the #confirmations method
1426
+ # with a single argument, a protocol method class
1427
+ # instance (an instance of AMQ::Protocol::Confirm::SelectOk)
1428
+ # and then it deletes the callback, since Confirm.Select
1429
+ # is supposed to be sent just once.
1430
+ #
1431
+ # @api plugin
1432
+ def handle_select_ok(method)
1433
+ self.exec_callback_once(:confirm_select, method)
1434
+ end
1435
+
1436
+ # Handler for Basic.Ack. By default, it just
1437
+ # executes hook specified via the #confirm method
1438
+ # with a single argument, a protocol method class
1439
+ # instance (an instance of AMQ::Protocol::Basic::Ack).
1440
+ #
1441
+ # @api plugin
1442
+ def handle_basic_ack(method)
1443
+ self.exec_callback(:ack, method)
1444
+ end
1445
+
1446
+
1447
+ # Handler for Basic.Nack. By default, it just
1448
+ # executes hook specified via the #confirm_failed method
1449
+ # with a single argument, a protocol method class
1450
+ # instance (an instance of AMQ::Protocol::Basic::Nack).
1451
+ #
1452
+ # @api plugin
1453
+ def handle_basic_nack(method)
1454
+ self.exec_callback(:nack, method)
1455
+ end
1456
+
1457
+
1458
+
1459
+ #
1460
+ # Implementation
1461
+ #
1462
+
1463
+ def register_exchange(exchange)
1464
+ raise ArgumentError, "argument is nil!" if exchange.nil?
1465
+
1466
+ @exchanges[exchange.name] = exchange
1467
+ end # register_exchange(exchange)
1468
+
1469
+ # Finds exchange in the exchanges cache on this channel by name. Exchange only exists in the cache if
1470
+ # it was previously instantiated on this channel.
1471
+ #
1472
+ # @param [String] name Exchange name
1473
+ # @return [AMQP::Exchange] Exchange (if found)
1474
+ # @api plugin
1475
+ def find_exchange(name)
1476
+ @exchanges[name]
1477
+ end
1478
+
1479
+ # @api plugin
1480
+ # @private
1481
+ def register_queue(queue)
1482
+ raise ArgumentError, "argument is nil!" if queue.nil?
1483
+
1484
+ @queues[queue.name] = queue
1485
+ end # register_queue(queue)
1486
+
1487
+ # @api plugin
1488
+ # @private
1489
+ def find_queue(name)
1490
+ @queues[name]
1491
+ end
1492
+
1493
+
1494
+ RECOVERY_EVENTS = [:after_connection_interruption, :before_recovery, :after_recovery].freeze
1495
+
1496
+
1497
+ # @api plugin
1498
+ # @private
1499
+ def reset_state!
1500
+ @flow_is_active = true
1501
+
1502
+ @queues_awaiting_declare_ok = Array.new
1503
+ @exchanges_awaiting_declare_ok = Array.new
1504
+
1505
+ @queues_awaiting_delete_ok = Array.new
1506
+
1507
+ @exchanges_awaiting_delete_ok = Array.new
1508
+ @queues_awaiting_purge_ok = Array.new
1509
+ @queues_awaiting_bind_ok = Array.new
1510
+ @queues_awaiting_unbind_ok = Array.new
1511
+ @consumers_awaiting_consume_ok = Array.new
1512
+ @consumers_awaiting_cancel_ok = Array.new
1513
+
1514
+ @queues_awaiting_get_response = Array.new
1515
+
1516
+ @callbacks = @callbacks.delete_if { |k, v| !RECOVERY_EVENTS.include?(k) }
1517
+ @uses_publisher_confirmations = false
1518
+ end # reset_state!
1519
+
1520
+
1521
+ # @api plugin
1522
+ # @private
1523
+ def handle_open_ok(open_ok)
1524
+ self.status = :opened
1525
+ self.exec_callback_once_yielding_self(:open, open_ok)
1526
+ end
1527
+
1528
+ # @api plugin
1529
+ # @private
1530
+ def handle_close_ok(close_ok)
1531
+ self.status = :closed
1532
+ self.connection.clear_frames_on(self.id)
1533
+ self.exec_callback_once_yielding_self(:close, close_ok)
1534
+
1535
+ self.class.release_channel_id(@id)
1536
+ end
1537
+
1538
+ # @api plugin
1539
+ # @private
1540
+ def handle_close(channel_close)
1541
+ self.status = :closed
1542
+ self.connection.clear_frames_on(self.id)
1543
+
1544
+ self.exec_callback_yielding_self(:error, channel_close)
1545
+ end
1546
+
1547
+
1548
+
1549
+ self.handle(AMQ::Protocol::Channel::OpenOk) do |connection, frame|
1550
+ channel = connection.channels[frame.channel]
1551
+ channel.handle_open_ok(frame.decode_payload)
1552
+ end
1553
+
1554
+ self.handle(AMQ::Protocol::Channel::CloseOk) do |connection, frame|
1555
+ method = frame.decode_payload
1556
+ channels = connection.channels
1557
+
1558
+ channel = channels[frame.channel]
1559
+ channels.delete(channel)
1560
+ channel.handle_close_ok(method)
1561
+ end
1562
+
1563
+ self.handle(AMQ::Protocol::Channel::Close) do |connection, frame|
1564
+ method = frame.decode_payload
1565
+ channels = connection.channels
1566
+ channel = channels[frame.channel]
1567
+ connection.send_frame(AMQ::Protocol::Channel::CloseOk.encode(frame.channel))
1568
+ channel.handle_close(method)
1569
+ end
1570
+
1571
+ self.handle(AMQ::Protocol::Basic::QosOk) do |connection, frame|
1572
+ channel = connection.channels[frame.channel]
1573
+ channel.exec_callback(:qos, frame.decode_payload)
1574
+ end
1575
+
1576
+ self.handle(AMQ::Protocol::Basic::RecoverOk) do |connection, frame|
1577
+ channel = connection.channels[frame.channel]
1578
+ channel.exec_callback(:recover, frame.decode_payload)
1579
+ end
1580
+
1581
+ self.handle(AMQ::Protocol::Channel::FlowOk) do |connection, frame|
1582
+ channel = connection.channels[frame.channel]
1583
+ method = frame.decode_payload
1584
+
1585
+ channel.flow_is_active = method.active
1586
+ channel.exec_callback(:flow, method)
1587
+ end
1588
+
1589
+ self.handle(AMQ::Protocol::Tx::SelectOk) do |connection, frame|
1590
+ channel = connection.channels[frame.channel]
1591
+ channel.exec_callback(:tx_select, frame.decode_payload)
1592
+ end
1593
+
1594
+ self.handle(AMQ::Protocol::Tx::CommitOk) do |connection, frame|
1595
+ channel = connection.channels[frame.channel]
1596
+ channel.exec_callback(:tx_commit, frame.decode_payload)
1597
+ end
1598
+
1599
+ self.handle(AMQ::Protocol::Tx::RollbackOk) do |connection, frame|
1600
+ channel = connection.channels[frame.channel]
1601
+ channel.exec_callback(:tx_rollback, frame.decode_payload)
1602
+ end
1603
+
1604
+ self.handle(AMQ::Protocol::Confirm::SelectOk) do |connection, frame|
1605
+ method = frame.decode_payload
1606
+ channel = connection.channels[frame.channel]
1607
+ channel.handle_select_ok(method)
1608
+ end
1609
+
1610
+ self.handle(AMQ::Protocol::Basic::Ack) do |connection, frame|
1611
+ method = frame.decode_payload
1612
+ channel = connection.channels[frame.channel]
1613
+ channel.handle_basic_ack(method)
1614
+ end
1615
+
1616
+ self.handle(AMQ::Protocol::Basic::Nack) do |connection, frame|
1617
+ method = frame.decode_payload
1618
+ channel = connection.channels[frame.channel]
1619
+ channel.handle_basic_nack(method)
1620
+ end
1621
+
1181
1622
  protected
1182
1623
 
1183
1624
  @private