jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: (JMS_SPEC-36) Allow messages to be delivered asynchronously in batches

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Tue, 13 Mar 2012 19:02:24 +0000

I'd like to resurrect our discussion on batch delivery.
This is http://java.net/jira/browse/JMS_SPEC-36

As I mentioned earlier, and explicitly wrote in section A.2. "Unresolved issues in the JMS 2.0 Early Draft", I'm
concerned that the API for batch delivery is rather more complicated than it needs to be. Here is what I wrote:

"The proposed API involves a new BatchMessageListener interface. This has necessitated the addition of two new methods
on MessageConsumer and five additional methods on MessagingContext.

"In addition, since a MessageConsumer can only have a single listener it has been necessary to explain how a call to
setBatchBatchListener would override a previous call to setMessageListener. This is complicated and potentially confusing.

"One alternative way to allow messages to be delivered in batches might be to invent a new message type, a BatchMessage,
which would contain an ordered collection of normal Message objects. This would allow batches of messages to be
delivered to a normal MessageListener and so remove the need for a separate BatchMessageListener interface. It would
also allow batch delivery to synchronous consumers."

Another advantage of replacing BatchMessageListener with BatchMessage is that it would avoid the need to extend resource
adapters and MDB code to support the new listener interface.

I'd now like to take this new idea further. The main change is that we would drop the new BatchMessageListener interface
with its method onMessages(Message[] messages). Instead the existing MessageListener interface would be used with its
existing method onMessage(Message message). When the JMS server wants to deliver a batch of messages to the consumer it
would create a new type of message, a javax.jms.BatchMessage, load it up with a batch-worth of messages, and then
deliver it to the MessageListener in the usual way.

I've sketched out below what the new API might look like. Before I work this through in detail, I'd like to invite
feedback on various points of principle:

==================================================================================

QUESTIONS

1. Which is better, using a BatchMessageListener or allowing the server to create BatchMessage objects?

2. I'm currently suggesting that BatchMessage objects would never be created by the application, They would simply be a
convenient container object that the JMS provider could use when delivering batches of messages. However we could if we
wanted allow the producer client to create them and use them to send batches of messages to the server, which would then
break them down into their constituent messages before adding to the destination. Would this have any benefit?

3. Should we take advantage of BatchMessage objects to allow the server to deliver messages in batches to a synchronous
consumer?

==================================================================================

SUMMARY OF PROPOSAL

THE NEW BatchMessage CLASS

/** An <CODE>BatchMessage</CODE> object is a special type of message which can be
  * created by the JMS provider and used to deliver an ordered batch of ordinary
  * messages to a consumer. It inherits from the <CODE>Message</CODE> interface
  * and adds methods to allow the individual messages to be extracted.
  *
  * Note that this message type is created by the JMS provider, not by
  * the application, and therefore only provides public methods to
  * extract messages, not to set them.
  *
  * @see javax.jms.ObjectMessage
  * @see javax.jms.BytesMessage
  * @see javax.jms.MapMessage
  * @see javax.jms.Message
  * @see javax.jms.StreamMessage
  * @see javax.jms.TextMessage
  */

public interface BatchMessage extends Message {
        
        /**
         * returns the number of messages in this BatchMessage
         *
         * @return the number of messages in this BatchMessage
         *
         * @throws JMSException if the JMS provider fails to get the size due to some internal error
         */
        int size() throws JMSException;
        
        /**
         * returns the Message at the specified position in the batch
         * @param index index of the Messagr to return
         *
         * @return the Message at the specified position in the batch
         *
         * @throws JMSException if the JMS provider fails to get the Message due to some internal error
         */
        Message get(int index) throws JMSException;
}

NEW METHODS ON Session FOR CREATING A MESSAGE CONSUMER

In addition, two new methods on Session would be needed to allow the batch size and timeout when creating a MessageConsumer:

MessageConsumer createConsumer(Destination destination, String messageSelector, boolean NoLocal, int maxBatchSize, long
batchTimeout)

MessageConsumer createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal, int
maxBatchSize, long batchTimeout)

NEW METHODS ON MessagingContext FOR SETTING A MESSAGE LISTENER

We would need similar new methods on MessagingContext. You will remember that this currently offers methods to set the
message listener directly, five for MessageListener and five for BatchMessageListener, making ten in total.

We would drop the latter five, and add two new methods methods (one for queues and non-durable subscriptions and one for
durable subscriptions) to allow batch size and timeout to be supplied, making seven in total:

setMessageListener(Destination destination, String messageSelector, boolean noLocal, int maxBatchSize, long
batchTimeout, MessageListener listener)

setMessageListener(Topic topic, String subscriptionName, String messageSelector, boolean noLocal, int maxBatchSize, long
batchTimeout, MessageListener listener)

NEW METHODS ON MessagingContext FOR CREATING A SYNC MESSAGE CONSUMER

If we wanted to extend this feature to synchronous consumers we would also add two new methods for creating a
SyncMessageConsumer (one for queues and non-durable subscriptions and one for durable subscriptions):

SyncMessageConsumer createSyncConsumer(Destination destination, String messageSelector, boolean noLocal, int
maxBatchSize, long batchTimeout)

SyncMessageConsumer createSyncDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal, int
maxBatchSize, long batchTimeout)

==================================================================================

I hope this is clear! If not, please let me know.

Nigel