io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.connection.RabbitUtils Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.connection;
import java.io.IOException;
import java.util.Collection;
import io.bitsensor.plugins.shaded.org.apache.commons.logging.Log;
import io.bitsensor.plugins.shaded.org.apache.commons.logging.LogFactory;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.AmqpIOException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.AmqpRejectAndDontRequeueException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.MessageRejectedWhileStoppingException;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.support.RabbitExceptionTranslator;
import io.bitsensor.plugins.shaded.org.springframework.util.Assert;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.AMQP;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.AlreadyClosedException;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.Channel;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.Method;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.ShutdownSignalException;
import io.bitsensor.plugins.shaded.com.rabbitmq.client.impl.recovery.AutorecoveringChannel;
/**
* @author Mark Fisher
* @author Mark Pollack
* @author Gary Russell
*/
public abstract class RabbitUtils {
public static final int DEFAULT_PORT = AMQP.PROTOCOL.PORT;
private static final Log logger = LogFactory.getLog(RabbitUtils.class);
private static final ThreadLocal physicalCloseRequired = new ThreadLocal();
/**
* Close the given RabbitMQ Connection and ignore any thrown exception. This is useful for typical
* finally
blocks in manual RabbitMQ code.
* @param connection the RabbitMQ Connection to close (may be null
)
*/
public static void closeConnection(Connection connection) {
if (connection != null) {
try {
connection.close();
}
catch (AlreadyClosedException ace) {
// empty
}
catch (Exception ex) {
logger.debug("Ignoring Connection exception - assuming already closed: " + ex.getMessage(), ex);
}
}
}
/**
* Close the given RabbitMQ Channel and ignore any thrown exception. This is useful for typical finally
* blocks in manual RabbitMQ code.
* @param channel the RabbitMQ Channel to close (may be null
)
*/
public static void closeChannel(Channel channel) {
if (channel != null) {
try {
channel.close();
}
catch (AlreadyClosedException ace) {
// empty
}
catch (IOException ex) {
logger.debug("Could not close RabbitMQ Channel", ex);
}
catch (ShutdownSignalException sig) {
if (!isNormalShutdown(sig)) {
logger.debug("Unexpected exception on closing RabbitMQ Channel", sig);
}
}
catch (Exception ex) {
logger.debug("Unexpected exception on closing RabbitMQ Channel", ex);
}
}
}
/**
* Commit the Channel if not within a JTA transaction.
* @param channel the RabbitMQ Channel to commit
*/
public static void commitIfNecessary(Channel channel) {
Assert.notNull(channel, "Channel must not be null");
try {
channel.txCommit();
}
catch (IOException ex) {
throw new AmqpIOException(ex);
}
}
public static void rollbackIfNecessary(Channel channel) {
Assert.notNull(channel, "Channel must not be null");
try {
channel.txRollback();
}
catch (IOException ex) {
throw new AmqpIOException(ex);
}
}
public static void closeMessageConsumer(Channel channel, Collection consumerTags, boolean transactional) {
if (!channel.isOpen() && !(channel instanceof ChannelProxy
&& ((ChannelProxy) channel).getTargetChannel() instanceof AutorecoveringChannel)
&& !(channel instanceof AutorecoveringChannel)) {
return;
}
try {
for (String consumerTag : consumerTags) {
try {
channel.basicCancel(consumerTag);
}
catch (IOException e) {
if (logger.isDebugEnabled()) {
logger.debug("Error performing 'basicCancel'", e);
}
}
catch (AlreadyClosedException e) {
if (logger.isTraceEnabled()) {
logger.trace(channel + " is already closed");
}
}
}
if (transactional) {
/*
* Re-queue in-flight messages if any (after the consumer is cancelled to prevent the broker from simply
* sending them back to us). Does not require a tx.commit.
*/
channel.basicRecover(true);
}
/*
* If not transactional then we are auto-acking (at least as of 1.0.0.M2) so there is nothing to recover.
* Messages are going to be lost in general.
*/
}
catch (Exception ex) {
throw RabbitExceptionTranslator.convertRabbitAccessException(ex);
}
}
/**
* Declare to that broker that a channel is going to be used transactionally, and convert exceptions that arise.
*
* @param channel the channel to use
*/
public static void declareTransactional(Channel channel) {
try {
channel.txSelect();
}
catch (IOException e) {
throw RabbitExceptionTranslator.convertRabbitAccessException(e);
}
}
/**
* Sets a ThreadLocal indicating the channel MUST be physically closed.
* @param b true if the channel must be closed.
*/
public static void setPhysicalCloseRequired(boolean b) {
physicalCloseRequired.set(b);
}
/**
* Gets and removes a ThreadLocal indicating the channel MUST be physically closed.
* @return true if the channel must be physically closed
*/
public static boolean isPhysicalCloseRequired() {
Boolean mustClose = physicalCloseRequired.get();
if (mustClose == null) {
mustClose = Boolean.FALSE;
}
else {
physicalCloseRequired.remove();
}
return mustClose;
}
/**
* Return true if the {@link ShutdownSignalException} reason is AMQP.Connection.Close and
* the reply code was AMQP.REPLY_SUCCESS (200) and the text equals "OK".
* @param sig the exception.
* @return true for a normal connection close.
*/
public static boolean isNormalShutdown(ShutdownSignalException sig) {
Method shutdownReason = sig.getReason();
return shutdownReason instanceof AMQP.Connection.Close
&& AMQP.REPLY_SUCCESS == ((AMQP.Connection.Close) shutdownReason).getReplyCode()
&& "OK".equals(((AMQP.Connection.Close) shutdownReason).getReplyText());
}
/**
* Return true if the {@link ShutdownSignalException} reason is AMQP.Channel.Close and
* the reply code was AMQP.REPLY_SUCCESS (200) and the text equals "OK".
* @param sig the exception.
* @return true for a normal channel close.
*/
public static boolean isNormalChannelClose(ShutdownSignalException sig) {
Method shutdownReason = sig.getReason();
return isNormalShutdown(sig) ||
(shutdownReason instanceof AMQP.Channel.Close
&& AMQP.REPLY_SUCCESS == ((AMQP.Channel.Close) shutdownReason).getReplyCode()
&& "OK".equals(((AMQP.Channel.Close) shutdownReason).getReplyText()));
}
/**
* Return true if the {@link ShutdownSignalException} reason is AMQP.Channel.Close
* and the operation that failed was exchangeDeclare or queueDeclare.
* @param sig the exception.
* @return true if the failure meets the conditions.
*/
public static boolean isPassiveDeclarationChannelClose(ShutdownSignalException sig) {
Method shutdownReason = sig.getReason();
return shutdownReason instanceof AMQP.Channel.Close
&& AMQP.NOT_FOUND == ((AMQP.Channel.Close) shutdownReason).getReplyCode()
&& ((((AMQP.Channel.Close) shutdownReason).getClassId() == 40 // exchange
|| ((AMQP.Channel.Close) shutdownReason).getClassId() == 50) // queue
&& ((AMQP.Channel.Close) shutdownReason).getMethodId() == 10); // declare
}
/**
* Return true if the {@link ShutdownSignalException} reason is AMQP.Channel.Close
* and the operation that failed was basicConsumer and the failure text contains
* "exclusive".
* @param sig the exception.
* @return true if the declaration failed because of an exclusive queue.
*/
public static boolean isExclusiveUseChannelClose(ShutdownSignalException sig) {
Method shutdownReason = sig.getReason();
return shutdownReason instanceof AMQP.Channel.Close
&& AMQP.ACCESS_REFUSED == ((AMQP.Channel.Close) shutdownReason).getReplyCode()
&& ((AMQP.Channel.Close) shutdownReason).getClassId() == 60 // basic
&& ((AMQP.Channel.Close) shutdownReason).getMethodId() == 20 // consume
&& ((AMQP.Channel.Close) shutdownReason).getReplyText().contains("exclusive");
}
/**
* Return true if there is a {@link ShutdownSignalException} in the cause tree and its
* reason is "PRECONDITION_FAILED" and the operation being performed was queueDeclare.
* This can happen if a queue has mismatched properties (auto-delete etc) or arguments
* (x-message-ttl etc).
* @param e the exception.
* @return true if the exception was due to queue declaration precondition failed.
* @since 1.6
*/
public static boolean isMismatchedQueueArgs(Exception e) {
Throwable cause = e;
ShutdownSignalException sig = null;
while (cause != null && sig == null) {
if (cause instanceof ShutdownSignalException) {
sig = (ShutdownSignalException) cause;
}
cause = cause.getCause();
}
if (sig == null) {
return false;
}
else {
Method shutdownReason = sig.getReason();
return shutdownReason instanceof AMQP.Channel.Close
&& AMQP.PRECONDITION_FAILED == ((AMQP.Channel.Close) shutdownReason).getReplyCode()
&& ((AMQP.Channel.Close) shutdownReason).getClassId() == 50 // queue
&& ((AMQP.Channel.Close) shutdownReason).getMethodId() == 10; // declare
}
}
/**
* Return true if there is a {@link ShutdownSignalException} in the cause tree and its
* reason is "COMMAND_INVALID" and the operation being performed was exchangeDeclare.
* For example attempting to declare an exchange that is not supported by the broker or
* its plugins.
* @param e the exception.
* @return true if the exception was due to exchange declaration failed.
* @since 1.6
*/
public static boolean isExchangeDeclarationFailure(Exception e) {
Throwable cause = e;
ShutdownSignalException sig = null;
while (cause != null && sig == null) {
if (cause instanceof ShutdownSignalException) {
sig = (ShutdownSignalException) cause;
}
cause = cause.getCause();
}
if (sig == null) {
return false;
}
else {
Method shutdownReason = sig.getReason();
return shutdownReason instanceof AMQP.Connection.Close
&& AMQP.COMMAND_INVALID == ((AMQP.Connection.Close) shutdownReason).getReplyCode()
&& ((AMQP.Connection.Close) shutdownReason).getClassId() == 40 // exchange
&& ((AMQP.Connection.Close) shutdownReason).getMethodId() == 10; // declare
}
}
/**
* Determine whether a message should be requeued; returns true if the throwable is a
* {@link MessageRejectedWhileStoppingException} or defaultRequeueRejected is true and
* there is not an {@link AmqpRejectAndDontRequeueException} in the cause chain.
* @param defaultRequeueRejected the default requeue rejected.
* @param throwable the throwable.
* @param logger the logger to use for debug.
* @return true to requeue.
* @since 1.7.1
*/
public static boolean shouldRequeue(boolean defaultRequeueRejected, Throwable throwable, Log logger) {
boolean shouldRequeue = defaultRequeueRejected ||
throwable instanceof MessageRejectedWhileStoppingException;
Throwable t = throwable;
while (shouldRequeue && t != null) {
if (t instanceof AmqpRejectAndDontRequeueException) {
shouldRequeue = false;
}
t = t.getCause();
}
if (logger.isDebugEnabled()) {
logger.debug("Rejecting messages (requeue=" + shouldRequeue + ")");
}
return shouldRequeue;
}
}