All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.rabbitmq.client.impl.ChannelN Maven / Gradle / Ivy

Go to download

The RabbitMQ Java client library allows Java applications to interface with RabbitMQ.

The newest version!
// Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
//
// This software, the RabbitMQ Java client library, is triple-licensed under the
// Mozilla Public License 2.0 ("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
// [email protected].

package com.rabbitmq.client.impl;

import com.rabbitmq.client.*;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Method;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.impl.AMQImpl.Channel;
import com.rabbitmq.client.impl.AMQImpl.Queue;
import com.rabbitmq.client.impl.AMQImpl.*;
import com.rabbitmq.client.observation.ObservationCollector;
import com.rabbitmq.utility.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;

/**
 * Main interface to AMQP protocol functionality. Public API -
 * Implementation of all AMQChannels except channel zero.
 * 

* To open a channel, *

 * {@link Connection} conn = ...;
 * {@link ChannelN} ch1 = conn.{@link Connection#createChannel createChannel}();
 * 
*/ public class ChannelN extends AMQChannel implements com.rabbitmq.client.Channel { private static final int MAX_UNSIGNED_SHORT = 65535; private static final String UNSPECIFIED_OUT_OF_BAND = ""; private static final Logger LOGGER = LoggerFactory.getLogger(ChannelN.class); /** Map from consumer tag to {@link Consumer} instance. *

* Note that, in general, this map should ONLY ever be accessed * from the connection's reader thread. We go to some pains to * ensure this is the case - see the use of * BlockingRpcContinuation to inject code into the reader thread * in basicConsume and basicCancel. */ private final Map _consumers = Collections.synchronizedMap(new HashMap()); /* All listeners collections are in CopyOnWriteArrayList objects */ /** The ReturnListener collection. */ private final Collection returnListeners = new CopyOnWriteArrayList(); /** The ConfirmListener collection. */ private final Collection confirmListeners = new CopyOnWriteArrayList(); /** Sequence number of next published message requiring confirmation.*/ private long nextPublishSeqNo = 0L; /** The current default consumer, or null if there is none. */ private volatile Consumer defaultConsumer = null; /** Dispatcher of consumer work for this channel */ private final ConsumerDispatcher dispatcher; /** Future boolean for shutting down */ private volatile CountDownLatch finishedShutdownFlag = null; /** Set of currently unconfirmed messages (i.e. messages that have * not been ack'd or nack'd by the server yet. */ private final SortedSet unconfirmedSet = Collections.synchronizedSortedSet(new TreeSet()); /** Whether the confirm select method has been successfully activated */ private boolean confirmSelectActivated = false; /** Whether any nacks have been received since the last waitForConfirms(). */ private volatile boolean onlyAcksReceived = true; protected final MetricsCollector metricsCollector; private final ObservationCollector observationCollector; /** * Construct a new channel on the given connection with the given * channel number. Usually not called directly - call * Connection.createChannel instead. * @see Connection#createChannel * @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 ChannelN(AMQConnection connection, int channelNumber, ConsumerWorkService workService) { this(connection, channelNumber, workService, new NoOpMetricsCollector(), ObservationCollector.NO_OP); } /** * Construct a new channel on the given connection with the given * channel number. Usually not called directly - call * Connection.createChannel instead. * @see Connection#createChannel * @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 ChannelN(AMQConnection connection, int channelNumber, ConsumerWorkService workService, MetricsCollector metricsCollector, ObservationCollector observationCollector) { super(connection, channelNumber); this.dispatcher = new ConsumerDispatcher(connection, this, workService); this.metricsCollector = metricsCollector; this.observationCollector = observationCollector; } /** * Package method: open the channel. * This is only called from {@link ChannelManager}. * @throws IOException if any problem is encountered */ public void open() throws IOException { // wait for the Channel.OpenOk response, and ignore it exnWrappingRpc(new Channel.Open(UNSPECIFIED_OUT_OF_BAND)); } @Override public void addReturnListener(ReturnListener listener) { returnListeners.add(listener); } @Override public ReturnListener addReturnListener(ReturnCallback returnCallback) { ReturnListener returnListener = (replyCode, replyText, exchange, routingKey, properties, body) -> returnCallback.handle(new Return( replyCode, replyText, exchange, routingKey, properties, body )); this.addReturnListener(returnListener); return returnListener; } @Override public boolean removeReturnListener(ReturnListener listener) { return returnListeners.remove(listener); } @Override public void clearReturnListeners() { returnListeners.clear(); } @Override public void addConfirmListener(ConfirmListener listener) { confirmListeners.add(listener); } @Override public ConfirmListener addConfirmListener(ConfirmCallback ackCallback, ConfirmCallback nackCallback) { ConfirmListener confirmListener = new ConfirmListener() { @Override public void handleAck(long deliveryTag, boolean multiple) throws IOException { ackCallback.handle(deliveryTag, multiple); } @Override public void handleNack(long deliveryTag, boolean multiple) throws IOException { nackCallback.handle(deliveryTag, multiple); } }; this.addConfirmListener(confirmListener); return confirmListener; } @Override public boolean removeConfirmListener(ConfirmListener listener) { return confirmListeners.remove(listener); } @Override public void clearConfirmListeners() { confirmListeners.clear(); } /** {@inheritDoc} */ @Override public boolean waitForConfirms() throws InterruptedException { boolean confirms = false; try { confirms = waitForConfirms(0L); } catch (TimeoutException e) { } return confirms; } /** {@inheritDoc} */ @Override public boolean waitForConfirms(long timeout) throws InterruptedException, TimeoutException { if (nextPublishSeqNo == 0L) throw new IllegalStateException("Confirms not selected"); long startTime = System.currentTimeMillis(); synchronized (unconfirmedSet) { while (true) { if (getCloseReason() != null) { throw Utility.fixStackTrace(getCloseReason()); } if (unconfirmedSet.isEmpty()) { boolean aux = onlyAcksReceived; onlyAcksReceived = true; return aux; } if (timeout == 0L) { unconfirmedSet.wait(); } else { long elapsed = System.currentTimeMillis() - startTime; if (timeout > elapsed) { unconfirmedSet.wait(timeout - elapsed); } else { throw new TimeoutException(); } } } } } /** {@inheritDoc} */ @Override public void waitForConfirmsOrDie() throws IOException, InterruptedException { try { waitForConfirmsOrDie(0L); } catch (TimeoutException e) { } } /** {@inheritDoc} */ @Override public void waitForConfirmsOrDie(long timeout) throws IOException, InterruptedException, TimeoutException { try { if (!waitForConfirms(timeout)) { close(AMQP.REPLY_SUCCESS, "NACKS RECEIVED", true, null, false); throw new IOException("nacks received"); } } catch (TimeoutException e) { close(AMQP.PRECONDITION_FAILED, "TIMEOUT WAITING FOR ACK"); throw(e); } } /** Returns the current default consumer. */ @Override public Consumer getDefaultConsumer() { return defaultConsumer; } /** * Sets the current default consumer. * A null argument is interpreted to mean "do not use a default consumer". */ @Override public void setDefaultConsumer(Consumer consumer) { defaultConsumer = consumer; } /** * Sends a ShutdownSignal to all active consumers. * Idempotent. * @param signal an exception signalling channel shutdown */ private void broadcastShutdownSignal(ShutdownSignalException signal) { this.finishedShutdownFlag = this.dispatcher.handleShutdownSignal(Utility.copy(_consumers), signal); } /** * Start to shutdown -- defer rest of processing until ready */ private void startProcessShutdownSignal(ShutdownSignalException signal, boolean ignoreClosed, boolean notifyRpc) { super.processShutdownSignal(signal, ignoreClosed, notifyRpc); } /** * Finish shutdown processing -- idempotent */ private void finishProcessShutdownSignal() { this.dispatcher.quiesce(); broadcastShutdownSignal(getCloseReason()); synchronized (unconfirmedSet) { unconfirmedSet.notifyAll(); } } /** * Protected API - overridden to quiesce consumer work and broadcast the signal * to all consumers after calling the superclass's method. */ @Override public void processShutdownSignal(ShutdownSignalException signal, boolean ignoreClosed, boolean notifyRpc) { startProcessShutdownSignal(signal, ignoreClosed, notifyRpc); finishProcessShutdownSignal(); } CountDownLatch getShutdownLatch() { return this.finishedShutdownFlag; } private void releaseChannel() { getConnection().disconnectChannel(this); } /** * Protected API - Filters the inbound command stream, processing * Basic.Deliver, Basic.Return and Channel.Close specially. If * we're in quiescing mode, all inbound commands are ignored, * except for Channel.Close and Channel.CloseOk. */ @Override public boolean processAsync(Command command) throws IOException { // If we are isOpen(), then we process commands normally. // // If we are not, however, then we are in a quiescing, or // shutting-down state as the result of an application // decision to close this channel, and we are to discard all // incoming commands except for a close and close-ok. Method method = command.getMethod(); // we deal with channel.close in the same way, regardless if (method instanceof Channel.Close) { asyncShutdown(command); return true; } if (isOpen()) { // We're in normal running mode. if (method instanceof Basic.Deliver) { processDelivery(command, (Basic.Deliver) method); return true; } else if (method instanceof Basic.Return) { callReturnListeners(command, (Basic.Return) method); return true; } else if (method instanceof Channel.Flow) { Channel.Flow channelFlow = (Channel.Flow) method; _channelLock.lock(); try { _blockContent = !channelFlow.getActive(); transmit(new Channel.FlowOk(!_blockContent)); _channelLockCondition.signalAll(); } finally { _channelLock.unlock(); } return true; } else if (method instanceof Basic.Ack) { Basic.Ack ack = (Basic.Ack) method; callConfirmListeners(command, ack); handleAckNack(ack.getDeliveryTag(), ack.getMultiple(), false); return true; } else if (method instanceof Basic.Nack) { Basic.Nack nack = (Basic.Nack) method; callConfirmListeners(command, nack); handleAckNack(nack.getDeliveryTag(), nack.getMultiple(), true); return true; } else if (method instanceof Basic.RecoverOk) { for (Map.Entry entry : Utility.copy(_consumers).entrySet()) { this.dispatcher.handleRecoverOk(entry.getValue(), entry.getKey()); } // Unlike all the other cases we still want this RecoverOk to // be handled by whichever RPC continuation invoked Recover, // so return false return false; } else if (method instanceof Basic.Cancel) { Basic.Cancel m = (Basic.Cancel)method; String consumerTag = m.getConsumerTag(); Consumer callback = _consumers.remove(consumerTag); // Not finding any matching consumer isn't necessarily an indication of an issue anywhere. // Sometimes there's a natural race condition between consumer management on the server and client ends. // E.g. Channel#basicCancel called just before a basic.cancel for the same consumer tag is received. // See https://github.com/rabbitmq/rabbitmq-java-client/issues/525 if (callback == null) { callback = defaultConsumer; } if (callback != null) { try { this.dispatcher.handleCancel(callback, consumerTag); } catch (WorkPoolFullException e) { // couldn't enqueue in work pool, propagating throw e; } catch (Throwable ex) { getConnection().getExceptionHandler().handleConsumerException(this, ex, callback, consumerTag, "handleCancel"); } } else { LOGGER.warn("Could not cancel consumer with unknown tag {}", consumerTag); } return true; } else { return false; } } else { // We're in quiescing mode == !isOpen() if (method instanceof Channel.CloseOk) { // We're quiescing, and we see a channel.close-ok: // this is our signal to leave quiescing mode and // finally shut down for good. Let it be handled as an // RPC reply one final time by returning false. return false; } else { // We're quiescing, and this inbound command should be // discarded as per spec. "Consume" it by returning // true. return true; } } } protected void processDelivery(Command command, Basic.Deliver method) { Basic.Deliver m = method; Consumer callback = _consumers.get(m.getConsumerTag()); if (callback == null) { if (defaultConsumer == null) { // No handler set. We should blow up as this message // needs acking, just dropping it is not enough. See bug // 22587 for discussion. throw new IllegalStateException("Unsolicited delivery -" + " see Channel.setDefaultConsumer to handle this" + " case."); } else { callback = defaultConsumer; } } Envelope envelope = new Envelope(m.getDeliveryTag(), m.getRedelivered(), m.getExchange(), m.getRoutingKey()); try { // call metricsCollector before the dispatching (which is async anyway) // this way, the message is inside the stats before it is handled // in case a manual ack in the callback, the stats will be able to record the ack metricsCollector.consumedMessage(this, m.getDeliveryTag(), m.getConsumerTag()); this.dispatcher.handleDelivery(callback, m.getConsumerTag(), envelope, (BasicProperties) command.getContentHeader(), command.getContentBody()); } catch (WorkPoolFullException e) { // couldn't enqueue in work pool, propagating throw e; } catch (Throwable ex) { getConnection().getExceptionHandler().handleConsumerException(this, ex, callback, m.getConsumerTag(), "handleDelivery"); } } private void callReturnListeners(Command command, Basic.Return basicReturn) { try { for (ReturnListener l : this.returnListeners) { l.handleReturn(basicReturn.getReplyCode(), basicReturn.getReplyText(), basicReturn.getExchange(), basicReturn.getRoutingKey(), (BasicProperties) command.getContentHeader(), command.getContentBody()); } } catch (Throwable ex) { getConnection().getExceptionHandler().handleReturnListenerException(this, ex); } finally { metricsCollector.basicPublishUnrouted(this); } } private void callConfirmListeners(@SuppressWarnings("unused") Command command, Basic.Ack ack) { try { for (ConfirmListener l : this.confirmListeners) { l.handleAck(ack.getDeliveryTag(), ack.getMultiple()); } } catch (Throwable ex) { getConnection().getExceptionHandler().handleConfirmListenerException(this, ex); } finally { metricsCollector.basicPublishAck(this, ack.getDeliveryTag(), ack.getMultiple()); } } private void callConfirmListeners(@SuppressWarnings("unused") Command command, Basic.Nack nack) { try { for (ConfirmListener l : this.confirmListeners) { l.handleNack(nack.getDeliveryTag(), nack.getMultiple()); } } catch (Throwable ex) { getConnection().getExceptionHandler().handleConfirmListenerException(this, ex); } finally { metricsCollector.basicPublishNack(this, nack.getDeliveryTag(), nack.getMultiple()); } } private void asyncShutdown(Command command) throws IOException { ShutdownSignalException signal = new ShutdownSignalException(false, false, command.getMethod(), this); _channelLock.lock(); try { try { processShutdownSignal(signal, true, false); quiescingTransmit(new Channel.CloseOk()); } finally { releaseChannel(); notifyOutstandingRpc(signal); } } finally { _channelLock.unlock(); } notifyListeners(); } /** Public API - {@inheritDoc} */ @Override public void close() throws IOException, TimeoutException { close(AMQP.REPLY_SUCCESS, "OK"); } /** Public API - {@inheritDoc} */ @Override public void close(int closeCode, String closeMessage) throws IOException, TimeoutException { close(closeCode, closeMessage, true, null, false); } /** Public API - {@inheritDoc} */ @Override public void abort() throws IOException { abort(AMQP.REPLY_SUCCESS, "OK"); } /** Public API - {@inheritDoc} */ @Override public void abort(int closeCode, String closeMessage) throws IOException { try { close(closeCode, closeMessage, true, null, true); } catch (IOException _e) { /* ignored */ } catch (TimeoutException _e) { /* ignored */ } } /** * Protected API - Close channel with code and message, indicating * the source of the closure and a causing exception (null if * none). * @param closeCode the close code (See under "Reply Codes" in the AMQP specification) * @param closeMessage a message indicating the reason for closing the connection * @param initiatedByApplication true if this comes from an API call, false otherwise * @param cause exception triggering close * @param abort true if we should close and ignore errors * @throws IOException if an error is encountered */ protected void close(int closeCode, String closeMessage, boolean initiatedByApplication, Throwable cause, boolean abort) throws IOException, TimeoutException { // First, notify all our dependents that we are shutting down. // This clears isOpen(), so no further work from the // application side will be accepted, and any inbound commands // will be discarded (unless they're channel.close-oks). Channel.Close reason = new Channel.Close(closeCode, closeMessage, 0, 0); ShutdownSignalException signal = new ShutdownSignalException(false, initiatedByApplication, reason, this); if (cause != null) { signal.initCause(cause); } BlockingRpcContinuation k = new BlockingRpcContinuation(){ @Override public AMQCommand transformReply(AMQCommand command) { ChannelN.this.finishProcessShutdownSignal(); return command; }}; boolean notify = false; try { // Synchronize the block below to avoid race conditions in case // connection wants to send Connection-CloseOK _channelLock.lock(); try { startProcessShutdownSignal(signal, !initiatedByApplication, true); quiescingRpc(reason, k); } finally { _channelLock.unlock(); } // Now that we're in quiescing state, channel.close was sent and // we wait for the reply. We ignore the result. // (It's NOT always close-ok.) notify = true; // do not wait indefinitely k.getReply(10000); } catch (TimeoutException ise) { if (!abort) throw ise; } catch (ShutdownSignalException sse) { if (!abort) throw sse; } catch (IOException ioe) { if (!abort) throw ioe; } finally { if (abort || notify) { // Now we know everything's been cleaned up and there should // be no more surprises arriving on the wire. Release the // channel number, and dissociate this ChannelN instance from // our connection so that any further frames inbound on this // channel can be caught as the errors they are. releaseChannel(); notifyListeners(); } } } /** Public API - {@inheritDoc} */ @Override public void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException { int unsignedShortPrefetchCount = ConnectionFactory.ensureUnsignedShort(prefetchCount); if (unsignedShortPrefetchCount != prefetchCount) { LOGGER.warn("Prefetch count must be between 0 and {}, value has been set to {} instead of {}", MAX_UNSIGNED_SHORT, unsignedShortPrefetchCount, prefetchCount); } exnWrappingRpc(new Basic.Qos(prefetchSize, unsignedShortPrefetchCount, global)); } /** Public API - {@inheritDoc} */ @Override public void basicQos(int prefetchCount, boolean global) throws IOException { basicQos(0, prefetchCount, global); } /** Public API - {@inheritDoc} */ @Override public void basicQos(int prefetchCount) throws IOException { basicQos(0, prefetchCount, false); } /** Public API - {@inheritDoc} */ @Override public void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException { basicPublish(exchange, routingKey, false, props, body); } /** Public API - {@inheritDoc} */ @Override public void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body) throws IOException { basicPublish(exchange, routingKey, mandatory, false, props, body); } /** Public API - {@inheritDoc} */ @Override public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) throws IOException { if (nextPublishSeqNo > 0) { unconfirmedSet.add(getNextPublishSeqNo()); nextPublishSeqNo++; } if (props == null) { props = MessageProperties.MINIMAL_BASIC; } AMQP.Basic.Publish publish = new Basic.Publish.Builder() .exchange(exchange) .routingKey(routingKey) .mandatory(mandatory) .immediate(immediate) .build(); try { ObservationCollector.PublishCall publishCall = properties -> { AMQCommand command = new AMQCommand(publish, properties, body); transmit(command); }; observationCollector.publish(publishCall, publish, props, body, this.connectionInfo()); } catch (IOException | AlreadyClosedException e) { metricsCollector.basicPublishFailure(this, e); throw e; } metricsCollector.basicPublish(this); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete, Map arguments) throws IOException { return exchangeDeclare(exchange, type, durable, autoDelete, false, arguments); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, Map arguments) throws IOException { return exchangeDeclare(exchange, type.getType(), durable, autoDelete, arguments); } @Override public void exchangeDeclareNoWait(String exchange, String type, boolean durable, boolean autoDelete, boolean internal, Map arguments) throws IOException { transmit(new AMQCommand(new Exchange.Declare.Builder() .exchange(exchange) .type(type) .durable(durable) .autoDelete(autoDelete) .internal(internal) .arguments(arguments) .passive(false) .nowait(true) .build())); } @Override public void exchangeDeclareNoWait(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map arguments) throws IOException { exchangeDeclareNoWait(exchange, type.getType(), durable, autoDelete, internal, arguments); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete, boolean internal, Map arguments) throws IOException { return (Exchange.DeclareOk) exnWrappingRpc(new Exchange.Declare.Builder() .exchange(exchange) .type(type) .durable(durable) .autoDelete(autoDelete) .internal(internal) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable, boolean autoDelete, boolean internal, Map arguments) throws IOException { return exchangeDeclare(exchange, type.getType(), durable, autoDelete, internal, arguments); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException { return exchangeDeclare(exchange, type, durable, false, null); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type, boolean durable) throws IOException { return exchangeDeclare(exchange, type.getType(), durable); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException { return exchangeDeclare(exchange, type, false, false, null); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclare(String exchange, BuiltinExchangeType type) throws IOException { return exchangeDeclare(exchange, type.getType()); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeclareOk exchangeDeclarePassive(String exchange) throws IOException { return (Exchange.DeclareOk) exnWrappingRpc(new Exchange.Declare.Builder() .exchange(exchange) .type("") .passive() .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeleteOk exchangeDelete(String exchange, boolean ifUnused) throws IOException { return (Exchange.DeleteOk) exnWrappingRpc(new Exchange.Delete.Builder() .exchange(exchange) .ifUnused(ifUnused) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public void exchangeDeleteNoWait(String exchange, boolean ifUnused) throws IOException { transmit(new AMQCommand(new Exchange.Delete.Builder() .exchange(exchange) .ifUnused(ifUnused) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Exchange.DeleteOk exchangeDelete(String exchange) throws IOException { return exchangeDelete(exchange, false); } /** Public API - {@inheritDoc} */ @Override public Exchange.BindOk exchangeBind(String destination, String source, String routingKey, Map arguments) throws IOException { return (Exchange.BindOk) exnWrappingRpc(new Exchange.Bind.Builder() .destination(destination) .source(source) .routingKey(routingKey) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public void exchangeBindNoWait(String destination, String source, String routingKey, Map arguments) throws IOException { transmit(new AMQCommand(new Exchange.Bind.Builder() .destination(destination) .source(source) .routingKey(routingKey) .arguments(arguments) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Exchange.BindOk exchangeBind(String destination, String source, String routingKey) throws IOException { return exchangeBind(destination, source, routingKey, null); } /** Public API - {@inheritDoc} */ @Override public Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey, Map arguments) throws IOException { return (Exchange.UnbindOk) exnWrappingRpc(new Exchange.Unbind.Builder() .destination(destination) .source(source) .routingKey(routingKey) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey) throws IOException { return exchangeUnbind(destination, source, routingKey, null); } /** Public API - {@inheritDoc} */ @Override public void exchangeUnbindNoWait(String destination, String source, String routingKey, Map arguments) throws IOException { transmit(new AMQCommand(new Exchange.Unbind.Builder() .destination(destination) .source(source) .routingKey(routingKey) .arguments(arguments) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments) throws IOException { validateQueueNameLength(queue); return (Queue.DeclareOk) exnWrappingRpc(new Queue.Declare.Builder() .queue(queue) .durable(durable) .exclusive(exclusive) .autoDelete(autoDelete) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclare() throws IOException { return queueDeclare("", false, true, true, null); } /** Public API - {@inheritDoc} */ @Override public void queueDeclareNoWait(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments) throws IOException { validateQueueNameLength(queue); transmit(new AMQCommand(new Queue.Declare.Builder() .queue(queue) .durable(durable) .exclusive(exclusive) .autoDelete(autoDelete) .arguments(arguments) .passive(false) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Queue.DeclareOk queueDeclarePassive(String queue) throws IOException { validateQueueNameLength(queue); return (Queue.DeclareOk) exnWrappingRpc(new Queue.Declare.Builder() .queue(queue) .passive() .exclusive() .autoDelete() .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public long messageCount(String queue) throws IOException { Queue.DeclareOk ok = queueDeclarePassive(queue); return ok.getMessageCount(); } /** Public API - {@inheritDoc} */ @Override public long consumerCount(String queue) throws IOException { Queue.DeclareOk ok = queueDeclarePassive(queue); return ok.getConsumerCount(); } /** Public API - {@inheritDoc} */ @Override public Queue.DeleteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty) throws IOException { validateQueueNameLength(queue); return (Queue.DeleteOk) exnWrappingRpc(new Queue.Delete.Builder() .queue(queue) .ifUnused(ifUnused) .ifEmpty(ifEmpty) .build()) .getMethod(); } @Override public void queueDeleteNoWait(String queue, boolean ifUnused, boolean ifEmpty) throws IOException { validateQueueNameLength(queue); transmit(new AMQCommand(new Queue.Delete.Builder() .queue(queue) .ifUnused(ifUnused) .ifEmpty(ifEmpty) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Queue.DeleteOk queueDelete(String queue) throws IOException { return queueDelete(queue, false, false); } /** Public API - {@inheritDoc} */ @Override public Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map arguments) throws IOException { validateQueueNameLength(queue); return (Queue.BindOk) exnWrappingRpc(new Queue.Bind.Builder() .queue(queue) .exchange(exchange) .routingKey(routingKey) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException { return queueBind(queue, exchange, routingKey, null); } /** Public API - {@inheritDoc} */ @Override public void queueBindNoWait(String queue, String exchange, String routingKey, Map arguments) throws IOException { validateQueueNameLength(queue); transmit(new AMQCommand(new Queue.Bind.Builder() .queue(queue) .exchange(exchange) .routingKey(routingKey) .arguments(arguments) .nowait(true) .build())); } /** Public API - {@inheritDoc} */ @Override public Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey, Map arguments) throws IOException { validateQueueNameLength(queue); return (Queue.UnbindOk) exnWrappingRpc(new Queue.Unbind.Builder() .queue(queue) .exchange(exchange) .routingKey(routingKey) .arguments(arguments) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Queue.PurgeOk queuePurge(String queue) throws IOException { validateQueueNameLength(queue); return (Queue.PurgeOk) exnWrappingRpc(new Queue.Purge.Builder() .queue(queue) .build()) .getMethod(); } /** Public API - {@inheritDoc} */ @Override public Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey) throws IOException { return queueUnbind(queue, exchange, routingKey, null); } /** Public API - {@inheritDoc} */ @Override public GetResponse basicGet(String queue, boolean autoAck) throws IOException { validateQueueNameLength(queue); AMQCommand replyCommand = exnWrappingRpc(new Basic.Get.Builder() .queue(queue) .noAck(autoAck) .build()); return this.observationCollector.basicGet(() -> { Method method = replyCommand.getMethod(); if (method instanceof Basic.GetOk) { Basic.GetOk getOk = (Basic.GetOk)method; Envelope envelope = new Envelope(getOk.getDeliveryTag(), getOk.getRedelivered(), getOk.getExchange(), getOk.getRoutingKey()); BasicProperties props = (BasicProperties)replyCommand.getContentHeader(); byte[] body = replyCommand.getContentBody(); int messageCount = getOk.getMessageCount(); metricsCollector.consumedMessage(this, getOk.getDeliveryTag(), autoAck); return new GetResponse(envelope, props, body, messageCount); } else if (method instanceof Basic.GetEmpty) { return null; } else { throw new UnexpectedMethodError(method); } }, queue); } /** Public API - {@inheritDoc} */ @Override public void basicAck(long deliveryTag, boolean multiple) throws IOException { transmit(new Basic.Ack(deliveryTag, multiple)); metricsCollector.basicAck(this, deliveryTag, multiple); } /** Public API - {@inheritDoc} */ @Override public void basicNack(long deliveryTag, boolean multiple, boolean requeue) throws IOException { transmit(new Basic.Nack(deliveryTag, multiple, requeue)); metricsCollector.basicNack(this, deliveryTag); } /** Public API - {@inheritDoc} */ @Override public void basicReject(long deliveryTag, boolean requeue) throws IOException { transmit(new Basic.Reject(deliveryTag, requeue)); metricsCollector.basicReject(this, deliveryTag); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, Consumer callback) throws IOException { return basicConsume(queue, false, callback); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException { return basicConsume(queue, consumerFromDeliverCancelCallbacks(deliverCallback, cancelCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, consumerFromDeliverShutdownCallbacks(deliverCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, consumerFromDeliverCancelShutdownCallbacks(deliverCallback, cancelCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException { return basicConsume(queue, autoAck, "", callback); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, "", consumerFromDeliverShutdownCallbacks(deliverCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException { return basicConsume(queue, autoAck, "", consumerFromDeliverCancelCallbacks(deliverCallback, cancelCallback)); } @Override public String basicConsume(String queue, boolean autoAck, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, "", consumerFromDeliverCancelShutdownCallbacks(deliverCallback, cancelCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, Map arguments, Consumer callback) throws IOException { return basicConsume(queue, autoAck, "", false, false, arguments, callback); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, Map arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException { return basicConsume(queue, autoAck, "", false, false, arguments, consumerFromDeliverCancelCallbacks(deliverCallback, cancelCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, Map arguments, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, "", false, false, arguments, consumerFromDeliverShutdownCallbacks(deliverCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, Map arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, "", false, false, arguments, consumerFromDeliverCancelShutdownCallbacks(deliverCallback, cancelCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, Consumer callback) throws IOException { return basicConsume(queue, autoAck, consumerTag, false, false, null, callback); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag, false, false, null, consumerFromDeliverCancelCallbacks(deliverCallback, cancelCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag, false, false, null, consumerFromDeliverShutdownCallbacks(deliverCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag , false, false, null, consumerFromDeliverCancelShutdownCallbacks(deliverCallback, cancelCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumerFromDeliverCancelCallbacks(deliverCallback, cancelCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, DeliverCallback deliverCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumerFromDeliverShutdownCallbacks(deliverCallback, shutdownSignalCallback)); } @Override public String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, DeliverCallback deliverCallback, CancelCallback cancelCallback, ConsumerShutdownSignalCallback shutdownSignalCallback) throws IOException { return basicConsume(queue, autoAck, consumerTag, noLocal, exclusive, arguments, consumerFromDeliverCancelShutdownCallbacks(deliverCallback, cancelCallback, shutdownSignalCallback)); } /** Public API - {@inheritDoc} */ @Override public String basicConsume(String queue, final boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, final Consumer callback) throws IOException { final Method m = new Basic.Consume.Builder() .queue(queue) .consumerTag(consumerTag) .noLocal(noLocal) .noAck(autoAck) .exclusive(exclusive) .arguments(arguments) .build(); BlockingRpcContinuation k = new BlockingRpcContinuation(m) { @Override public String transformReply(AMQCommand replyCommand) { String actualConsumerTag = ((Basic.ConsumeOk) replyCommand.getMethod()).getConsumerTag(); Consumer wrappedCallback = observationCollector.basicConsume(queue, consumerTag, callback); _consumers.put(actualConsumerTag, wrappedCallback); // need to register consumer in stats before it actually starts consuming metricsCollector.basicConsume(ChannelN.this, actualConsumerTag, autoAck); dispatcher.handleConsumeOk(wrappedCallback, actualConsumerTag); return actualConsumerTag; } }; rpc(m, k); try { if(_rpcTimeout == NO_RPC_TIMEOUT) { return k.getReply(); } else { try { return k.getReply(_rpcTimeout); } catch (TimeoutException e) { throw wrapTimeoutException(m, e); } } } catch(ShutdownSignalException ex) { throw wrap(ex); } } private Consumer consumerFromDeliverCancelCallbacks(final DeliverCallback deliverCallback, final CancelCallback cancelCallback) { return new Consumer() { @Override public void handleConsumeOk(String consumerTag) { } @Override public void handleCancelOk(String consumerTag) { } @Override public void handleCancel(String consumerTag) throws IOException { cancelCallback.handle(consumerTag); } @Override public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) { } @Override public void handleRecoverOk(String consumerTag) { } @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { deliverCallback.handle(consumerTag, new Delivery(envelope, properties, body)); } }; } private Consumer consumerFromDeliverShutdownCallbacks(final DeliverCallback deliverCallback, final ConsumerShutdownSignalCallback shutdownSignalCallback) { return new Consumer() { @Override public void handleConsumeOk(String consumerTag) { } @Override public void handleCancelOk(String consumerTag) { } @Override public void handleCancel(String consumerTag) throws IOException { } @Override public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) { shutdownSignalCallback.handleShutdownSignal(consumerTag, sig); } @Override public void handleRecoverOk(String consumerTag) { } @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { deliverCallback.handle(consumerTag, new Delivery(envelope, properties, body)); } }; } private Consumer consumerFromDeliverCancelShutdownCallbacks(final DeliverCallback deliverCallback, final CancelCallback cancelCallback, final ConsumerShutdownSignalCallback shutdownSignalCallback) { return new Consumer() { @Override public void handleConsumeOk(String consumerTag) { } @Override public void handleCancelOk(String consumerTag) { } @Override public void handleCancel(String consumerTag) throws IOException { cancelCallback.handle(consumerTag); } @Override public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) { shutdownSignalCallback.handleShutdownSignal(consumerTag, sig); } @Override public void handleRecoverOk(String consumerTag) { } @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { deliverCallback.handle(consumerTag, new Delivery(envelope, properties, body)); } }; } /** Public API - {@inheritDoc} */ @Override public void basicCancel(final String consumerTag) throws IOException { final Consumer originalConsumer = _consumers.get(consumerTag); if (originalConsumer == null) throw new IOException("Unknown consumerTag"); final Method m = new Basic.Cancel(consumerTag, false); BlockingRpcContinuation k = new BlockingRpcContinuation(m) { @Override public Consumer transformReply(AMQCommand replyCommand) { if (!(replyCommand.getMethod() instanceof Basic.CancelOk)) LOGGER.warn("Received reply {} was not of expected method Basic.CancelOk", replyCommand.getMethod()); _consumers.remove(consumerTag); //may already have been removed dispatcher.handleCancelOk(originalConsumer, consumerTag); return originalConsumer; } }; rpc(m, k); try { if(_rpcTimeout == NO_RPC_TIMEOUT) { k.getReply(); // discard result } else { try { k.getReply(_rpcTimeout); } catch (TimeoutException e) { throw wrapTimeoutException(m, e); } } } catch(ShutdownSignalException ex) { throw wrap(ex); } metricsCollector.basicCancel(this, consumerTag); } /** Public API - {@inheritDoc} */ @Override public Basic.RecoverOk basicRecover() throws IOException { return basicRecover(true); } /** Public API - {@inheritDoc} */ @Override public Basic.RecoverOk basicRecover(boolean requeue) throws IOException { return (Basic.RecoverOk) exnWrappingRpc(new Basic.Recover(requeue)).getMethod(); } /** Public API - {@inheritDoc} */ @Override public Tx.SelectOk txSelect() throws IOException { return (Tx.SelectOk) exnWrappingRpc(new Tx.Select()).getMethod(); } /** Public API - {@inheritDoc} */ @Override public Tx.CommitOk txCommit() throws IOException { return (Tx.CommitOk) exnWrappingRpc(new Tx.Commit()).getMethod(); } /** Public API - {@inheritDoc} */ @Override public Tx.RollbackOk txRollback() throws IOException { return (Tx.RollbackOk) exnWrappingRpc(new Tx.Rollback()).getMethod(); } /** Public API - {@inheritDoc} */ @Override public Confirm.SelectOk confirmSelect() throws IOException { if (confirmSelectActivated) { return new Confirm.SelectOk(); } if (nextPublishSeqNo == 0) nextPublishSeqNo = 1; Confirm.SelectOk result = (Confirm.SelectOk) exnWrappingRpc(new Confirm.Select(false)).getMethod(); confirmSelectActivated = true; return result; } /** Public API - {@inheritDoc} */ @Override public long getNextPublishSeqNo() { return nextPublishSeqNo; } @Override public void asyncRpc(Method method) throws IOException { transmit(method); } @Override public AMQCommand rpc(Method method) throws IOException { return exnWrappingRpc(method); } @Override public CompletableFuture asyncCompletableRpc(Method method) throws IOException { return exnWrappingAsyncRpc(method); } @Override public void enqueueRpc(RpcContinuation k) { _channelLock.lock(); try { super.enqueueRpc(k); dispatcher.setUnlimited(true); } finally { _channelLock.unlock(); } } @Override protected void markRpcFinished() { _channelLock.lock(); try { dispatcher.setUnlimited(false); } finally { _channelLock.unlock(); } } private void handleAckNack(long seqNo, boolean multiple, boolean nack) { if (multiple) { unconfirmedSet.headSet(seqNo + 1).clear(); } else { unconfirmedSet.remove(seqNo); } synchronized (unconfirmedSet) { onlyAcksReceived = onlyAcksReceived && !nack; if (unconfirmedSet.isEmpty()) unconfirmedSet.notifyAll(); } } private static void validateQueueNameLength(String queue) { if(queue.length() > 255) { throw new IllegalArgumentException("queue name must be no more than 255 characters long"); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy