jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: (JMS_SPEC-64) Define simplified JMS API

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Thu, 15 Mar 2012 12:35:26 +0000

I'd like to continue our discussion on the simplified API, and in particular on the best API for consuming messages.
This is http://java.net/jira/browse/JMS_SPEC-64

This is another issue which I suggested was unresolved in the JMS 2.0 Early Draft. I wrote quite a long discussion about
it in section A.2 "Unresolved issues in the JMS 2.0 Early Draft" which I won't repeat here.

*Recap

*Let's start with a recap of what we currently have in the Early Draft:
*
***Applications that wish to consume messages *synchronously *would create a MessagingContext (representing the
connection and session) and a separate SyncMessageConsumer (representing a consumer) and use one of three method on
SyncMessageConsumer, receive(), receive(int timeout) or receiveNoWait()) to receive each message.

Applications that wish to consume messages *asynchronously *would create a MessagingContext and then call one of the
following methods which would both create a consumer and register a message listener. For queues and non-durable topic
subscriptions these are:

setMessageListener(Destination destination, MessageListener listener)
setMessageListener(Destination destination, String messageSelector, MessageListener listener)
setMessageListener(Destination destination, String messageSelector, boolean noLocal, MessageListener listener)

for durable topic subscriptions these are:

setMessageListener(Topic topic, String subscriptionName, MessageListener listener)
setMessageListener(Topic topic, String subscriptionName, String messageSelector, boolean noLocal, MessageListener listener)

(It was an arbitrary decision not to include
setMessageListener(Topic topic, String subscriptionName, boolean noLocal, MessageListener listener)
and perhaps this should be added as well)

Note that these methods would not be available to applications running in the Java EE web or EJB container.

*Goals*

When devising the above API I was attempting to satisfy two conflicting goals:

  * To remove the need for a separate consumer object (in addition to MessagingContext),
  * To provide an easy-to-understand API which provides the same features of the existing API. This is worth
    emphasising: the simplified API is intended to be simpler to use, but not at the cost of reduced functionality. I
    don't want people to think of it as being for simple applications only.

(There's a longer list of goals at This is http://java.net/jira/browse/JMS_SPEC-64)

*Synchronous message delivery - why we still need a separate consumer object *

For synchronous message delivery, the Early Draft proposes that applications create a MessagingContext (representing the
connection and session) and then create a separate SyncMessageConsumer (representing each consumer) which provides the
various receive() methods. This has the following attractions:

  * No explosion of multiple methods with the same name and different arguments
  * It allows a MessagingContext to have multiple synchronous consumers, perhaps on different destinations or with
    different message selectors, in the same way as a session can.
  * It allows a consumer to be created prior to the first call to receive(), so the first call to receive() doesn't need
    to carry the overhead of creating the consumer.
  * And it allows the application to close a consumer (and perhaps clear any cached messages) without the need to close
    the session and connection.

Essentially, this is very similar to the standard API, providing exactly the same functionality and flexibility, and
looking very familiar to users of the existing API, but building on the new MessagingContext API using a new interface
object which allows us to define new method signatures which don't throw JMSException. It only has one drawback in that
it uses a separate object rather than using methods directly on MessagingContext.

I've considered the possibility of moving all the receive() methods on to MessagingContext, whilst continuing to allow
multiple consumers to be created,. This requires each receive() method to have an additional argument for the
destination, and requires additional receive() methods to be defined which allow a message selector and/or durable
subscription name to be specified. The result is quite a complicated API, with lots of receive() methods with lots of
arguments, and which does not make obvious whether a call to receive() is creating a new consumer or reusing an existing
one.

I've also considered whether it might be possible to move all the receive() methods on to MessagingContext and offer a
less complicated API by only allowing one consumer to exist at a time. In this case it, instead of passing in
destination, message selector and durable subscription name as arguments to receive() methods they could be set using
new MessagingContext methods setConsumerDestination, setDurableSubscriptionName and setMessageSelector.We could also
provide a a method closeConsumer() to close the consumer prior to closing the MessagingContext. The result would
certainly be simpler but the restriction to a single consumer at a time would represent a reduction in features compared
with the existing API. In addition there would be no way to create a consumer prior to calling receive, so the first
call to receive would always carry the overhead of creating the consumer.

Neither of these alternatives sounds particularly attractive, so I think the current proposal to use a separate
SyncMessageConsumer object is still the best.

*Asynchronous message delivery - why I am having second thoughts *

For /asynchronous /delivery I thought it might be possible to avoid a separate consumer object, and so proposed the API
summarised above. This has the following attractions:

  * It avoids the need for a separate consumer object
  * It allows applications to create multiple consumers on the same MessagingContext
  * Relatively simple methods

However in actually drafting the javadocs, and thinking about this more, I have come to the conclusion that the API I
proposed has a number of problems:

  * It needs up to six setMessageListener methods instead of one (plus an additional six to support BatchMessageListener)
  * This is inconsistent with synchronous delivery which uses a separate consumer object
  * There is no way to change the listener used by an existing consumer. (It would be possible to define that if
    setMessageListener was called a second time with all the same arguments as before but a different listener then the
    existing listener would be replaced, but this is distinctly obscure behaviour which would be much better expressed
    by calling setMessageListener on an existing consumer object.)
  * There is no way to close a consumer prior to closing the MessagingContext, which might interfere with a message
    caching strategy used by the provider. (It would be possible to define that if setMessageListener was called a
    second time with all the same arguments as before but a null listener then the existing consumer would be closed,
    but again this is distinctly obscure behaviour which would be much better expressed by calling close() on an
    existing consumer object.)

I think, therefore, that we should abandon this attempt at avoiding a separate consumer object for async delivery and
reinstate a separate consumer object similar to that used in the standard API.

Having hesitated over this for some time, the "clincher"for me was remembering what we already have a separate consumer
object for sync delivery (so this is actually a simplification), and that this API will never be used in Java EE web or
EJB applications anyway.

I can't think of a good reason for having separate SyncMessageConsumer and AsyncMessageConsumer objects - this would be
inconsistent with the standard API and would require twice as many createConsumer methods. We could use a standard
MessageConsumer, but this would be inconsistent with one of the other goals of the simplified API, which is to offer
method signatures which did not throw JMSException.

I'd therefore like to propose a "simplified message consumer", used for both sync and async delivery, for use with
MessagingContext. However I'm not sure what name to give it. Does anybody have any suggestions? Here are some ideas:

MessageConsumer (but in a different package)
MessagingContextConsumer
ContextConsumer

Please let me know your comments (and your naming suggestions).

Note that I think we need to resolve this before we can finalise any new API for batch delivery.

Nigel