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.

There is a newer version: 5.22.0
Show newest version
//   The contents of this file are subject to the Mozilla Public License
//   Version 1.1 (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.mozilla.org/MPL/
//
//   Software distributed under the License is distributed on an "AS IS"
//   basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//   License for the specific language governing rights and limitations
//   under the License.
//
//   The Original Code is RabbitMQ.
//
//   The Initial Developers of the Original Code are LShift Ltd,
//   Cohesive Financial Technologies LLC, and Rabbit Technologies Ltd.
//
//   Portions created before 22-Nov-2008 00:00:00 GMT by LShift Ltd,
//   Cohesive Financial Technologies LLC, or Rabbit Technologies Ltd
//   are Copyright (C) 2007-2008 LShift Ltd, Cohesive Financial
//   Technologies LLC, and Rabbit Technologies Ltd.
//
//   Portions created by LShift Ltd are Copyright (C) 2007-2010 LShift
//   Ltd. Portions created by Cohesive Financial Technologies LLC are
//   Copyright (C) 2007-2010 Cohesive Financial Technologies
//   LLC. Portions created by Rabbit Technologies Ltd are Copyright
//   (C) 2007-2010 Rabbit Technologies Ltd.
//
//   All Rights Reserved.
//
//   Contributor(s): ______________________________________.
//

package com.rabbitmq.client.impl;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Command;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.FlowListener;
import com.rabbitmq.client.GetResponse;
import com.rabbitmq.client.MessageProperties;
import com.rabbitmq.client.ReturnListener;
import com.rabbitmq.client.ShutdownSignalException;
import com.rabbitmq.client.UnexpectedMethodError;
import com.rabbitmq.client.impl.AMQImpl.Basic;
import com.rabbitmq.client.impl.AMQImpl.Channel;
import com.rabbitmq.client.impl.AMQImpl.Exchange;
import com.rabbitmq.client.impl.AMQImpl.Queue;
import com.rabbitmq.client.impl.AMQImpl.Tx;
import com.rabbitmq.utility.Utility;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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}();
 * ch1.{@link ChannelN#open open}();
 * 
*/ public class ChannelN extends AMQChannel implements com.rabbitmq.client.Channel { private static final String UNSPECIFIED_OUT_OF_BAND = ""; /** * When 0.9.1 is signed off, tickets can be removed from the codec * and this field can be deleted. */ @Deprecated private static final int TICKET = 0; /** * 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. */ public final Map _consumers = Collections.synchronizedMap(new HashMap()); /** Reference to the currently-active ReturnListener, or null if there is none. */ public volatile ReturnListener returnListener = null; /** Reference to the currently-active FlowListener, or null if there is none. */ public volatile FlowListener flowListener = null; /** Reference to the currently-active default consumer, or null if there is * none. */ public volatile Consumer defaultConsumer = null; /** * 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 */ public ChannelN(AMQConnection connection, int channelNumber) { super(connection, channelNumber); } /** * Package method: open the channel. * This is only called from AMQConnection. * @throws java.io.IOException if any problem is encountered */ public void open() throws IOException { // wait for the Channel.OpenOk response, then ignore it Channel.OpenOk openOk = (Channel.OpenOk) exnWrappingRpc(new Channel.Open(UNSPECIFIED_OUT_OF_BAND)).getMethod(); Utility.use(openOk); } /** Returns the current ReturnListener. */ public ReturnListener getReturnListener() { return returnListener; } /** * Sets the current ReturnListener. * A null argument is interpreted to mean "do not use a return listener". */ public void setReturnListener(ReturnListener listener) { returnListener = listener; } /** Returns the current FlowListener. */ public FlowListener getFlowListener() { return flowListener; } /** * Sets the current FlowListener. * A null argument is interpreted to mean "do not use a flow listener". */ public void setFlowListener(FlowListener listener) { flowListener = listener; } /** Returns the current default consumer. */ public Consumer getDefaultConsumer() { return defaultConsumer; } /** * Sets the current default consumer. * A null argument is interpreted to mean "do not use a default consumer". */ public void setDefaultConsumer(Consumer consumer) { defaultConsumer = consumer; } /** * Protected API - sends a ShutdownSignal to all active consumers. * @param signal an exception signalling channel shutdown */ public void broadcastShutdownSignal(ShutdownSignalException signal) { Map snapshotConsumers; synchronized (_consumers) { snapshotConsumers = new HashMap(_consumers); } for (Map.Entry entry: snapshotConsumers.entrySet()) { Consumer callback = entry.getValue(); try { callback.handleShutdownSignal(entry.getKey(), signal); } catch (Throwable ex) { _connection.getExceptionHandler().handleConsumerException(this, ex, callback, entry.getKey(), "handleShutdownSignal"); } } } /** * Protected API - overridden to broadcast the signal to all * consumers before calling the superclass's method. */ @Override public void processShutdownSignal(ShutdownSignalException signal, boolean ignoreClosed, boolean notifyRpc) { super.processShutdownSignal(signal, ignoreClosed, notifyRpc); broadcastShutdownSignal(signal); } public void releaseChannelNumber() { _connection.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(); if (isOpen()) { // We're in normal running mode. if (method instanceof Channel.Close) { releaseChannelNumber(); ShutdownSignalException signal = new ShutdownSignalException(false, false, command, this); synchronized (_channelMutex) { try { processShutdownSignal(signal, true, false); quiescingTransmit(new Channel.CloseOk()); } finally { notifyOutstandingRpc(signal); } } notifyListeners(); return true; } else if (method instanceof Basic.Deliver) { Basic.Deliver m = (Basic.Deliver) method; Consumer callback = _consumers.get(m.consumerTag); 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.deliveryTag, m.redelivered, m.exchange, m.routingKey); try { callback.handleDelivery(m.consumerTag, envelope, (BasicProperties) command.getContentHeader(), command.getContentBody()); } catch (Throwable ex) { _connection.getExceptionHandler().handleConsumerException(this, ex, callback, m.consumerTag, "handleDelivery"); } return true; } else if (method instanceof Basic.Return) { ReturnListener l = getReturnListener(); if (l != null) { Basic.Return basicReturn = (Basic.Return) method; try { l.handleBasicReturn(basicReturn.replyCode, basicReturn.replyText, basicReturn.exchange, basicReturn.routingKey, (BasicProperties) command.getContentHeader(), command.getContentBody()); } catch (Throwable ex) { _connection.getExceptionHandler().handleReturnListenerException(this, ex); } } return true; } else if (method instanceof Channel.Flow) { Channel.Flow channelFlow = (Channel.Flow) method; synchronized (_channelMutex) { _blockContent = !channelFlow.active; transmit(new Channel.FlowOk(channelFlow.active)); _channelMutex.notifyAll(); } FlowListener l = getFlowListener(); if (l != null) { try { l.handleFlow(channelFlow.active); } catch (Throwable ex) { _connection.getExceptionHandler().handleFlowListenerException(this, ex); } } return true; } else if (method instanceof Basic.RecoverOk) { for (Consumer callback: _consumers.values()) { callback.handleRecoverOk(); } // 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 { return false; } } else { // We're in quiescing mode. if (method instanceof Channel.Close) { // We're already shutting down, so just send back an ok. synchronized (_channelMutex) { quiescingTransmit(new Channel.CloseOk()); } return true; } else 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; } } } /** Public API - {@inheritDoc} */ public void close() throws IOException { close(AMQP.REPLY_SUCCESS, "OK"); } /** Public API - {@inheritDoc} */ public void close(int closeCode, String closeMessage) throws IOException { close(closeCode, closeMessage, true, null, false); } /** Public API - {@inheritDoc} */ public void abort() throws IOException { abort(AMQP.REPLY_SUCCESS, "OK"); } /** Public API - {@inheritDoc} */ public void abort(int closeCode, String closeMessage) throws IOException { close(closeCode, closeMessage, true, null, true); } /** * Protected API - Close channel with code and message, indicating * the source of the closure and a causing exception (null if * none). */ public void close(int closeCode, String closeMessage, boolean initiatedByApplication, Throwable cause, boolean abort) throws IOException { // 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 SimpleBlockingRpcContinuation(); boolean notify = false; try { // Synchronize the block below to avoid race conditions in case // connnection wants to send Connection-CloseOK synchronized (_channelMutex) { processShutdownSignal(signal, !initiatedByApplication, true); quiescingRpc(reason, k); } // Now that we're in quiescing state, channel.close was sent and // we wait for the reply. We ignore the result. (It's always // close-ok.) notify = true; k.getReply(-1); } catch (TimeoutException ise) { // Will never happen since we wait infinitely } 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. releaseChannelNumber(); notifyListeners(); } } } /** Public API - {@inheritDoc} */ public void basicQos(int prefetchSize, int prefetchCount, boolean global) throws IOException { exnWrappingRpc(new Basic.Qos(prefetchSize, prefetchCount, global)); } /** Public API - {@inheritDoc} */ public void basicQos(int prefetchCount) throws IOException { basicQos(0, prefetchCount, false); } /** Public API - {@inheritDoc} */ public void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException { basicPublish(exchange, routingKey, false, false, props, body); } /** Public API - {@inheritDoc} */ public void basicPublish(String exchange, String routingKey, boolean mandatory, boolean immediate, BasicProperties props, byte[] body) throws IOException { BasicProperties useProps = props; if (props == null) { useProps = MessageProperties.MINIMAL_BASIC; } transmit(new AMQCommand(new Basic.Publish(TICKET, exchange, routingKey, mandatory, immediate), useProps, body)); } /** Public API - {@inheritDoc} */ public Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable, boolean autoDelete, Map arguments) throws IOException { return (Exchange.DeclareOk) exnWrappingRpc(new Exchange.Declare(TICKET, exchange, type, false, durable, autoDelete, false, false, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public Exchange.DeclareOk exchangeDeclare(String exchange, String type, boolean durable) throws IOException { return exchangeDeclare(exchange, type, durable, false, null); } /** Public API - {@inheritDoc} */ public Exchange.DeclareOk exchangeDeclare(String exchange, String type) throws IOException { return exchangeDeclare(exchange, type, false, false, null); } /** Public API - {@inheritDoc} */ public Exchange.DeclareOk exchangeDeclarePassive(String exchange) throws IOException { return (Exchange.DeclareOk) exnWrappingRpc(new Exchange.Declare(TICKET, exchange, "", true, false, false, false, false, null)).getMethod(); } /** Public API - {@inheritDoc} */ public Exchange.DeleteOk exchangeDelete(String exchange, boolean ifUnused) throws IOException { return (Exchange.DeleteOk) exnWrappingRpc(new Exchange.Delete(TICKET, exchange, ifUnused, false)).getMethod(); } /** Public API - {@inheritDoc} */ public Exchange.DeleteOk exchangeDelete(String exchange) throws IOException { return exchangeDelete(exchange, false); } /** Public API - {@inheritDoc} */ public Exchange.BindOk exchangeBind(String destination, String source, String routingKey, Map arguments) throws IOException { return (Exchange.BindOk) exnWrappingRpc( new Exchange.Bind(TICKET, destination, source, routingKey, false, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public Exchange.BindOk exchangeBind(String destination, String source, String routingKey) throws IOException { return exchangeBind(destination, source, routingKey, null); } /** Public API - {@inheritDoc} */ public Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey, Map arguments) throws IOException { return (Exchange.UnbindOk) exnWrappingRpc( new Exchange.Unbind(TICKET, destination, source, routingKey, false, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public Exchange.UnbindOk exchangeUnbind(String destination, String source, String routingKey) throws IOException { return exchangeUnbind(destination, source, routingKey, null); } /** Public API - {@inheritDoc} */ public Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map arguments) throws IOException { return (Queue.DeclareOk) exnWrappingRpc(new Queue.Declare(TICKET, queue, false, durable, exclusive, autoDelete, false, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public com.rabbitmq.client.AMQP.Queue.DeclareOk queueDeclare() throws IOException { return queueDeclare("", false, true, true, null); } /** Public API - {@inheritDoc} */ public Queue.DeclareOk queueDeclarePassive(String queue) throws IOException { return (Queue.DeclareOk) exnWrappingRpc(new Queue.Declare(TICKET, queue, true, false, true, true, false, null)).getMethod(); } /** Public API - {@inheritDoc} */ public Queue.DeleteOk queueDelete(String queue, boolean ifUnused, boolean ifEmpty) throws IOException { return (Queue.DeleteOk) exnWrappingRpc(new Queue.Delete(TICKET, queue, ifUnused, ifEmpty, false)).getMethod(); } /** Public API - {@inheritDoc} */ public Queue.DeleteOk queueDelete(String queue) throws IOException { return queueDelete(queue, false, false); } /** Public API - {@inheritDoc} */ public Queue.BindOk queueBind(String queue, String exchange, String routingKey, Map arguments) throws IOException { return (Queue.BindOk) exnWrappingRpc(new Queue.Bind(TICKET, queue, exchange, routingKey, false, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public Queue.BindOk queueBind(String queue, String exchange, String routingKey) throws IOException { return queueBind(queue, exchange, routingKey, null); } /** Public API - {@inheritDoc} */ public Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey, Map arguments) throws IOException { return (Queue.UnbindOk) exnWrappingRpc(new Queue.Unbind(TICKET, queue, exchange, routingKey, arguments)).getMethod(); } /** Public API - {@inheritDoc} */ public Queue.PurgeOk queuePurge(String queue) throws IOException { return (Queue.PurgeOk) exnWrappingRpc(new Queue.Purge(TICKET, queue, false)).getMethod(); } /** Public API - {@inheritDoc} */ public Queue.UnbindOk queueUnbind(String queue, String exchange, String routingKey) throws IOException { return queueUnbind(queue, exchange, routingKey, null); } /** Public API - {@inheritDoc} */ public GetResponse basicGet(String queue, boolean autoAck) throws IOException { AMQCommand replyCommand = exnWrappingRpc(new Basic.Get(TICKET, queue, autoAck)); Method method = replyCommand.getMethod(); if (method instanceof Basic.GetOk) { Basic.GetOk getOk = (Basic.GetOk)method; Envelope envelope = new Envelope(getOk.deliveryTag, getOk.redelivered, getOk.exchange, getOk.routingKey); BasicProperties props = (BasicProperties)replyCommand.getContentHeader(); byte[] body = replyCommand.getContentBody(); int messageCount = getOk.messageCount; return new GetResponse(envelope, props, body, messageCount); } else if (method instanceof Basic.GetEmpty) { return null; } else { throw new UnexpectedMethodError(method); } } /** Public API - {@inheritDoc} */ public void basicAck(long deliveryTag, boolean multiple) throws IOException { transmit(new Basic.Ack(deliveryTag, multiple)); } /** Public API - {@inheritDoc} */ public void basicReject(long deliveryTag, boolean requeue) throws IOException { transmit(new Basic.Reject(deliveryTag, requeue)); } /** Public API - {@inheritDoc} */ public String basicConsume(String queue, Consumer callback) throws IOException { return basicConsume(queue, false, callback); } /** Public API - {@inheritDoc} */ public String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException { return basicConsume(queue, autoAck, "", callback); } /** Public API - {@inheritDoc} */ 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} */ public String basicConsume(String queue, boolean autoAck, String consumerTag, boolean noLocal, boolean exclusive, Map arguments, final Consumer callback) throws IOException { BlockingRpcContinuation k = new BlockingRpcContinuation() { public String transformReply(AMQCommand replyCommand) { String actualConsumerTag = ((Basic.ConsumeOk) replyCommand.getMethod()).consumerTag; _consumers.put(actualConsumerTag, callback); // We need to call back inside the connection thread // in order avoid races with 'deliver' commands try { callback.handleConsumeOk(actualConsumerTag); } catch (Throwable ex) { _connection.getExceptionHandler().handleConsumerException(ChannelN.this, ex, callback, actualConsumerTag, "handleConsumeOk"); } return actualConsumerTag; } }; rpc(new Basic.Consume(TICKET, queue, consumerTag, noLocal, autoAck, exclusive, false, arguments), k); try { return k.getReply(); } catch(ShutdownSignalException ex) { throw wrap(ex); } } /** Public API - {@inheritDoc} */ public void basicCancel(final String consumerTag) throws IOException { BlockingRpcContinuation k = new BlockingRpcContinuation() { public Consumer transformReply(AMQCommand replyCommand) { Basic.CancelOk dummy = (Basic.CancelOk) replyCommand.getMethod(); Utility.use(dummy); Consumer callback = _consumers.remove(consumerTag); // We need to call back inside the connection thread // in order avoid races with 'deliver' commands try { callback.handleCancelOk(consumerTag); } catch (Throwable ex) { _connection.getExceptionHandler().handleConsumerException(ChannelN.this, ex, callback, consumerTag, "handleCancelOk"); } return callback; } }; rpc(new Basic.Cancel(consumerTag, false), k); try { Consumer callback = k.getReply(); Utility.use(callback); } catch(ShutdownSignalException ex) { throw wrap(ex); } } /** Public API - {@inheritDoc} */ public Basic.RecoverOk basicRecover() throws IOException { return basicRecover(true); } /** Public API - {@inheritDoc} */ public Basic.RecoverOk basicRecover(boolean requeue) throws IOException { return (Basic.RecoverOk) exnWrappingRpc(new Basic.Recover(requeue)).getMethod(); } /** Public API - {@inheritDoc} */ public void basicRecoverAsync(boolean requeue) throws IOException { transmit(new Basic.RecoverAsync(requeue)); } /** Public API - {@inheritDoc} */ public Tx.SelectOk txSelect() throws IOException { return (Tx.SelectOk) exnWrappingRpc(new Tx.Select()).getMethod(); } /** Public API - {@inheritDoc} */ public Tx.CommitOk txCommit() throws IOException { return (Tx.CommitOk) exnWrappingRpc(new Tx.Commit()).getMethod(); } /** Public API - {@inheritDoc} */ public Tx.RollbackOk txRollback() throws IOException { return (Tx.RollbackOk) exnWrappingRpc(new Tx.Rollback()).getMethod(); } /** Public API - {@inheritDoc} */ public Channel.FlowOk flow(final boolean a) throws IOException { return (Channel.FlowOk) exnWrappingRpc(new Channel.Flow() {{active = a;}}).getMethod(); } /** Public API - {@inheritDoc} */ public Channel.FlowOk getFlow() { return new Channel.FlowOk(!_blockContent); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy