LATEST VERSION: 3.6.5 - CHANGELOG
Pivotal RabbitMQ v3.x

API Implementation Details

This section provides additional implementation details for specific JMS API classes in JMS Client for RabbitMQ.

Deviations from the specification are implemented to support common acknowledgement behaviours.

Previous topic: Pivotal RabbitMQ Implementation of JMS API

Next topic: Pivotal System Administration Modules and Plugins

QueueBrowser support

Overview of queue browsers

The JMS API includes objects and methods to browse an existing queue destination, reading its messages without removing them from the queue. Topic destinations cannot be browsed in this manner.

A QueueBrowser can be created from a (queue) Destination, with or without a selector expression. The browser has a getEnumeration()method, which returns a Java Enumeration of Messages copied from the queue.

If no selector is supplied, then all messages in the queue appear in the Enumeration. If a selector is supplied, then only those messages that satisfy the selector appear.

Implementation

The destination queue is read when the getEnumeration() method is called. A snapshot is taken of the messages in the queue; and the selector expression, if one is supplied, is used at this time to discard messages that do not match.

The message copies may now be read using the Enumeration interface (nextElement() and hasMoreElements()).

The selector expression and the destination queue of the QueueBrowser may not be adjusted after the QueueBrowser is created.

An Enumeration cannot be “reset”, but the getEnumeration() method may be re-issued, taking a new snapshot from the queue each time.

The contents of an Enumeration survive session and/or connection close, but a QueueBrowser may not be used after the session that created it has closed. QueueBrowser.close() has no effect.

Implementation Details

Which messages are included

Messages that arrive, expire, are re-queued, or are removed after the getEnumeration() call have no effect on the contents of theEnumeration it produced. If the messages in the queue change while the Enumeration is being built, they may or may not be included. In particular, if messages from the queue are simultaneously read by another client (or session), they may or may not appear in theEnumeration.

Message copies do not “expire” from an Enumeration.

Order of messages

If other client sessions read from a queue that is being browsed, then it is possible that some messages may subsequently be received out of order.

Message order will not be disturbed if no other client sessions read the queue at the same time.

Memory usage

When a message is read from the Enumeration (with nextElement()), then no reference to it is retained in the Java Client. This means the storage it occupies in the client is eligible for release (by garbage collection) if no other references are retained. Retaining an Enumeration will retain the storage for all message copies that remain in it.

If the queue has many messages – or the messages it contains are very large – then a getEnumeration() method call may consume a large amount of memory in a very short time. This remains true even if only a few messages are selected. There is currently limited protection against OutOfMemoryError conditions that may arise because of this. See the next section.

Setting a maximum number of messages to browse

Each connection is created with a limit on the number of messages that are examined by a QueueBrowser. The limit is set on the RMQConnectionFactory by RMQConnectionFactory.setQueueBrowserReadMax(int) and is passed to each Connection subsequently created byConnectionFactory.createConnection().

The limit is an integer that, if positive, stops the queue browser from reading more than this number of messages when building an enumeration. If it is zero or negative, it is interpreted as imposing no limit on the browser, and all of the messages on the queue are scanned.

The default limit for a factory is determined by the rabbit.jms.queueBrowserReadMax system property, if set, and the value is specified as 0 if this property is not set or is not an integer.

If a RMQConnectionFactory value is obtained from a JNDI provider, then the limit set when the factory object was created is preserved.

RJMS Release support

Support for QueueBrowsers is introduced in JMS Client for RabbitMQ 1.2.0. Prior to that release, calling Session.createBrowser(Queue queue[, String selector]) resulted in an UnsupportedOperationException.

Group and individual acknowledgement

Prior to JMS Client for RabbitMQ 1.2.0, in client acknowledgement mode (Session.CLIENT_ACKNOWLEDGE), acknowledging any message from an open session would acknowledge every unacknowledged message of that session, whether they were received before or after the message being acknowledged.

Currently, the behaviour of Session.CLIENT_ACKNOWLEDGE mode is modified so that, when calling msg.acknowledge(), only the message msg and all previously received unacknowledged messages on that session are acknowledged. Messages received after msg was received are not affected. This is a form of group acknowledgement, which differs slightly from the JMS 1.1 specification but is likely to be more useful, and is compatible with the vast majority of uses of the existing acknowledge function.

For even finer control, a new acknowledgement mode may be set when creating a session, called RMQSession.CLIENT_INDIVIDUAL_ACKNOWLEDGE.

A session created with this acknowledgement mode will mean that messages received on that session will be acknowledged individually. That is, the call msg.acknowledge() will acknowledge only the message msg and not affect any other messages of that session.

The acknowledgement mode RMQSession.CLIENT_INDIVIDUAL_ACKNOWLEDGE is equivalent to Session.CLIENT_ACKNOWLEDGE in all other respects. In particular the getAcknowledgeMode() method returns Session.CLIENT_ACKNOWLEDGE even if RMQSession.CLIENT_INDIVIDUAL_ACKNOWLEDGE has been set.

Arbitrary Message support

Any instance of a class that implements the javax.jms.Message interface can be sent by an RJMS message producer.

All properties of the message required by send() are correctly interpreted except that the JMSReplyTo header and objects (as property values or the body of an ObjectMessage) that cannot be deserialized are ignored.

The implementation extracts the properties and body from the Message instance using interface methods and recreates it as a message of the right (RMQMessage) type (BytesMessage, MapMessage, ObjectMessage, TextMessage, or StreamMessage) before sending it. This means that there is some performance loss due to the copying; but in the normal case, when the message is an instance ofcom.rabbitmq.jms.client.RMQMessage, no copying is done.