// Copyright (c) 2007-Present Pivotal Software, Inc. All rights reserved. // // This software, the RabbitMQ Java client library, is triple-licensed under the // Mozilla Public License 1.1 ("MPL"), the GNU General Public License version 2 // ("GPL") and the Apache License version 2 ("ASL"). For the MPL, please see // LICENSE-MPL-RabbitMQ. For the GPL, please see LICENSE-GPL2. For the ASL, // please see LICENSE-APACHE2. // // This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, // either express or implied. See the LICENSE file for specific language governing // rights and limitations of this software. // // If you have any questions regarding licensing, please contact us at // info@rabbitmq.com. package com.rabbitmq.client.impl.recovery; import com.rabbitmq.client.Command; import com.rabbitmq.client.NoOpMetricsCollector; import com.rabbitmq.client.MetricsCollector; import com.rabbitmq.client.impl.AMQConnection; import com.rabbitmq.client.impl.AMQImpl; import com.rabbitmq.client.impl.ChannelN; import com.rabbitmq.client.impl.ConsumerWorkService; import java.io.IOException; /** * {@link com.rabbitmq.client.impl.ChannelN} modification that keeps track of delivery * tags and avoids sending <pre>basic.ack</pre>, <pre>basic.nack</pre>, and <pre>basic.reject</pre> * for stale tags. * * @since 3.3.0 */ public class RecoveryAwareChannelN extends ChannelN { private long maxSeenDeliveryTag = 0; private long activeDeliveryTagOffset = 0; /** * Construct a new channel on the given connection with the given * channel number. Usually not called directly - call * Connection.createChannel instead. * * @param connection The connection associated with this channel * @param channelNumber The channel number to be associated with this channel * @param workService service for managing this channel's consumer callbacks */ public RecoveryAwareChannelN(AMQConnection connection, int channelNumber, ConsumerWorkService workService) { this(connection, channelNumber, workService, new NoOpMetricsCollector()); } /** * Construct a new channel on the given connection with the given * channel number. Usually not called directly - call * Connection.createChannel instead. * * @param connection The connection associated with this channel * @param channelNumber The channel number to be associated with this channel * @param workService service for managing this channel's consumer callbacks * @param metricsCollector service for managing metrics */ public RecoveryAwareChannelN(AMQConnection connection, int channelNumber, ConsumerWorkService workService, MetricsCollector metricsCollector) { super(connection, channelNumber, workService, metricsCollector); } @Override protected void processDelivery(Command command, AMQImpl.Basic.Deliver method) { long tag = method.getDeliveryTag(); if(tag > maxSeenDeliveryTag) { maxSeenDeliveryTag = tag; } super.processDelivery(command, offsetDeliveryTag(method)); } private AMQImpl.Basic.Deliver offsetDeliveryTag(AMQImpl.Basic.Deliver method) { return new AMQImpl.Basic.Deliver(method.getConsumerTag(), method.getDeliveryTag() + activeDeliveryTagOffset, method.getRedelivered(), method.getExchange(), method.getRoutingKey()); } @Override public void basicAck(long deliveryTag, boolean multiple) throws IOException { // FIXME no check if deliveryTag = 0 (ack all) long realTag = deliveryTag - activeDeliveryTagOffset; // 0 tag means ack all if (realTag >= 0) { super.basicAck(realTag, multiple); } } @Override public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException { // FIXME no check if deliveryTag = 0 (nack all) long realTag = deliveryTag - activeDeliveryTagOffset; // 0 tag means nack all if (realTag >= 0) { super.basicNack(realTag, multiple, requeue); } } @Override public void basicReject(long deliveryTag, boolean requeue) throws IOException { long realTag = deliveryTag - activeDeliveryTagOffset; if (realTag > 0) { super.basicReject(realTag, requeue); } } void inheritOffsetFrom(RecoveryAwareChannelN other) { activeDeliveryTagOffset = other.getActiveDeliveryTagOffset() + other.getMaxSeenDeliveryTag(); maxSeenDeliveryTag = 0; } public long getMaxSeenDeliveryTag() { return maxSeenDeliveryTag; } public long getActiveDeliveryTagOffset() { return activeDeliveryTagOffset; } }