Java tutorial
/* * Copyright 2002-2014 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 org.springframework.amqp.rabbit.connection; import java.io.IOException; import java.lang.reflect.Method; import java.util.Collection; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.amqp.AmqpIOException; import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; import com.rabbitmq.client.AMQP; import com.rabbitmq.client.Channel; import com.rabbitmq.client.ShutdownSignalException; /** * @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<Boolean> physicalCloseRequired = new ThreadLocal<Boolean>(); private static final Method shutDownSignalReasonMethod; static { Method method = null; try { method = ReflectionUtils.findMethod(ShutdownSignalException.class, "getReason"); } finally { shutDownSignalReasonMethod = method; } } /** * Close the given RabbitMQ Connection and ignore any thrown exception. This is useful for typical * <code>finally</code> blocks in manual RabbitMQ code. * @param connection the RabbitMQ Connection to close (may be <code>null</code>) */ public static void closeConnection(Connection connection) { if (connection != null) { try { connection.close(); } 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 <code>finally</code> * blocks in manual RabbitMQ code. * @param channel the RabbitMQ Channel to close (may be <code>null</code>) */ public static void closeChannel(Channel channel) { if (channel != null && channel.isOpen()) { try { channel.close(); } 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 (Throwable 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<String> consumerTags, boolean transactional) { if (!channel.isOpen()) { return; } try { synchronized (consumerTags) { for (String consumerTag : consumerTags) { channel.basicCancel(consumerTag); } } 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; } public static boolean isNormalShutdown(ShutdownSignalException sig) { Object shutdownReason = determineShutdownReason(sig); return shutdownReason instanceof AMQP.Connection.Close && AMQP.REPLY_SUCCESS == ((AMQP.Connection.Close) shutdownReason).getReplyCode() && "OK".equals(((AMQP.Connection.Close) shutdownReason).getReplyText()); } public static boolean isNormalChannelClose(ShutdownSignalException sig) { Object shutdownReason = determineShutdownReason(sig); return shutdownReason instanceof AMQP.Channel.Close && AMQP.REPLY_SUCCESS == ((AMQP.Channel.Close) shutdownReason).getReplyCode() && "OK".equals(((AMQP.Channel.Close) shutdownReason).getReplyText()); } protected static Object determineShutdownReason(ShutdownSignalException sig) { if (shutDownSignalReasonMethod == null) { return false; } try { return ReflectionUtils.invokeMethod(shutDownSignalReasonMethod, sig); } catch (Exception e) { return false; } } }