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

com.espertech.esperio.amqp.QueueingConsumer Maven / Gradle / Ivy

There is a newer version: 9.0.0
Show newest version
/*
 ***************************************************************************************
 *  Copyright (C) 2006 EsperTech, Inc. All rights reserved.                            *
 *  http://www.espertech.com/esper                                                     *
 *  http://www.espertech.com                                                           *
 *  ---------------------------------------------------------------------------------- *
 *  The software in this package is published under the terms of the GPL license       *
 *  a copy of which has been included with this distribution in the license.txt file.  *
 ***************************************************************************************
 */
package com.espertech.esperio.amqp;

import com.rabbitmq.client.*;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.utility.Utility;

import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * Convenience class: an implementation of {@link Consumer} with
 * straightforward blocking semantics.
 * 

* The general pattern for using QueueingConsumer is as follows: * *

 * // Create connection and channel.
 * {@link ConnectionFactory} factory = new ConnectionFactory();
 * Connection conn = factory.newConnection();
 * {@link Channel} ch1 = conn.createChannel();
 *
 * // Declare a queue and bind it to an exchange.
 * String queueName = ch1.queueDeclare().{@link AMQP.Queue.DeclareOk#getQueue getQueue}();
 * ch1.{@link Channel#queueBind queueBind}(queueName, exchangeName, queueName);
 *
 * // Create the QueueingConsumer and have it consume from the queue
 * QueueingConsumer consumer = new {@link QueueingConsumer#QueueingConsumer(Channel) QueueingConsumer}(ch1);
 * ch1.{@link Channel#basicConsume basicConsume}(queueName, false, consumer);
 *
 * // Process deliveries
 * while (/* some condition * /) {
 *     {@link QueueingConsumer.Delivery} delivery = consumer.{@link QueueingConsumer#nextDelivery nextDelivery}();
 *     // process delivery
 *     ch1.{@link Channel#basicAck basicAck}(delivery.{@link QueueingConsumer.Delivery#getEnvelope getEnvelope}().{@link Envelope#getDeliveryTag getDeliveryTag}(), false);
 * }
 * 
* * *

For a more complete example, see LogTail in the test/src/com/rabbitmq/examples * directory of the source distribution.

* deprecated QueueingConsumer was introduced to allow * applications to overcome a limitation in the way Connection * managed threads and consumer dispatching. When QueueingConsumer * was introduced, callbacks to Consumers were made on the * Connection's thread. This had two main drawbacks. Firstly, the * Consumer could stall the processing of all * Channels on the Connection. Secondly, if a * Consumer made a recursive synchronous call into its * Channel the client would deadlock. * QueueingConsumer provided client code with an easy way to * obviate this problem by queueing incoming messages and processing them on * a separate, application-managed thread. * The threading behaviour of Connection and Channel * has been changed so that each Channel uses a distinct thread * for dispatching to Consumers. This prevents * Consumers on one Channel holding up * Consumers on another and it also prevents recursive calls from * deadlocking the client. * As such, it is now safe to implement Consumer directly or * to extend DefaultConsumer. */ public class QueueingConsumer extends DefaultConsumer { private final BlockingQueue queue; // When this is non-null the queue is in shutdown mode and nextDelivery should // throw a shutdown signal exception. private volatile ShutdownSignalException shutdown; private volatile ConsumerCancelledException cancelled; // Marker object used to signal the queue is in shutdown mode. // It is only there to wake up consumers. The canonical representation // of shutting down is the presence of _shutdown. // Invariant: This is never on _queue unless _shutdown != null. private static final Delivery POISON = new Delivery(null, null, null); public QueueingConsumer(Channel ch) { this(ch, new LinkedBlockingQueue()); } public QueueingConsumer(Channel ch, BlockingQueue q) { super(ch); this.queue = q; } @Override public void handleShutdownSignal(String consumerTag, ShutdownSignalException sig) { shutdown = sig; queue.add(POISON); } @Override public void handleCancel(String consumerTag) throws IOException { cancelled = new ConsumerCancelledException(); queue.add(POISON); } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { checkShutdown(); this.queue.add(new Delivery(envelope, properties, body)); } /** * Encapsulates an arbitrary message - simple "bean" holder structure. */ public static class Delivery { private final Envelope envelope; private final AMQP.BasicProperties properties; private final byte[] body; public Delivery(Envelope envelope, AMQP.BasicProperties properties, byte[] body) { this.envelope = envelope; this.properties = properties; this.body = body; } /** * Retrieve the message envelope. * * @return the message envelope */ public Envelope getEnvelope() { return envelope; } /** * Retrieve the message properties. * * @return the message properties */ public BasicProperties getProperties() { return properties; } /** * Retrieve the message body. * * @return the message body */ public byte[] getBody() { return body; } } /** * Check if we are in shutdown mode and if so throw an exception. */ private void checkShutdown() { if (shutdown != null) throw Utility.fixStackTrace(shutdown); } /** * If delivery is not POISON nor null, return it. *

* If delivery, _shutdown and _cancelled are all null, return null. *

* If delivery is POISON re-insert POISON into the queue and * throw an exception if POISONed for no reason. *

* Otherwise, if we are in shutdown mode or cancelled, * throw a corresponding exception. */ private Delivery handle(Delivery delivery) { if (delivery == POISON || delivery == null && (shutdown != null || cancelled != null)) { if (delivery == POISON) { queue.add(POISON); if (shutdown == null && cancelled == null) { throw new IllegalStateException( "POISON in queue, but null _shutdown and null _cancelled. " + "This should never happen, please report as a BUG"); } } if (null != shutdown) throw Utility.fixStackTrace(shutdown); if (null != cancelled) throw Utility.fixStackTrace(cancelled); } return delivery; } /** * Main application-side API: wait for the next message delivery and return it. * * @return the next message * @throws InterruptedException if an interrupt is received while waiting * @throws ShutdownSignalException if the connection is shut down while waiting * @throws ConsumerCancelledException if this consumer is cancelled while waiting */ public Delivery nextDelivery() throws InterruptedException, ShutdownSignalException, ConsumerCancelledException { return handle(queue.take()); } /** * Main application-side API: wait for the next message delivery and return it. * * @param timeout timeout in millisecond * @return the next message or null if timed out * @throws InterruptedException if an interrupt is received while waiting * @throws ShutdownSignalException if the connection is shut down while waiting * @throws ConsumerCancelledException if this consumer is cancelled while waiting */ public Delivery nextDelivery(long timeout) throws InterruptedException, ShutdownSignalException, ConsumerCancelledException { return handle(queue.poll(timeout, TimeUnit.MILLISECONDS)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy