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
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.
QueueBrowser can be created from a (queue)
Destination, with or without a selector expression. The browser has a
getEnumeration()method, which returns a Java
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.
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 (
The selector expression and the destination queue of the
QueueBrowser may not be adjusted after the
QueueBrowser is created.
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.
Messages that arrive, expire, are re-queued, or are removed after the
getEnumeration() call have no effect on the contents of the
Enumeration 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 the
Message copies do not “expire” from an
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.
When a message is read from the
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.
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.setQueueBrowserReadMax(int) and is passed to each
Connection subsequently created by
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.
RMQConnectionFactory value is obtained from a JNDI provider, then the limit set when the factory object was created is preserved.
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
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
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.
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 (
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 of
com.rabbitmq.jms.client.RMQMessage, no copying is done.