[jsr343-experts] Re: Late issue: Calling MessageConsumer#close from onMessage
- From: Nigel Deakin <nigel.deakin@...>
- To: jsr343-experts@...
- Subject: [jsr343-experts] Re: Late issue: Calling MessageConsumer#close from onMessage
- Date: Fri, 01 Feb 2013 16:07:49 +0000
- Organization: Oracle Corporation
Thanks for the comments on this issue.
The only EG member who replied was Rüdiger, who voted for A+B
I vote for A+B, with a slight preference for deferring B until 2.1.
A community member "earmitage" emailed me directly to support A. He/she wrote "I think closing a message consumer can be
classified as application behavior with many business scenarios describable with words like receive until condition x is
met so allowing application code (onMessage) to close the consumer seems prudent enough"
I think we need more views, especially from vendors.
I've pasted my earlier message below. (If it isn't clear please say so)
-------- Original Message --------
Subject: [jsr343-experts] Late issue: Calling MessageConsumer#close from
Date: Fri, 18 Jan 2013 18:52:11 +0000
From: Nigel Deakin <nigel.deakin@...>
Organization: Oracle Corporation
I think an issue has arisen with JMS_SPEC-48, which clarified the issue of
what should happen if MessageConsumer#close,
Session#close and Connecton#close was called from within a message listener's
Here's a reminder of this issue (which was originally raised by Graham Wallis
In JMS 1.1:
* the API docs for Connection#close stated that it "should not return
until...all message listeners that may have been
running have returned"
* the API docs for Session#close stated that "This call will block until a...
message listener in progress has completed"
* the API docs for MessageConsumer#close stated that "This call blocks until
a... message listener in progress has
In all cases, this means that if one of these close methods is called within
a message listener's onMessage method then
it should never return, causing deadlock.
In JMS 2.0 we agreed that this didn't make sense, so we defined that:
* A message listener must not attempt to close its own connection as this
would lead to deadlock. The JMS provider must
detect this and throw a IllegalStateException.
* A MessageListener must not attempt to close its own Session as this would
lead to deadlock. The JMS provider must
detect this and throw a IllegalStateException.
* A MessageListener must not attempt to close its own MessageConsumer as this
would lead to deadlock. The JMS provider
must detect this and throw a IllegalStateException.
I think we may have "painted ourself into a corner" here, at least in the
case of MessageConsumer#close
Consider an application that creates a MessageConsumer and calls
setMessageListener to configure a message listener.
Once this method has been called (and the conneciton started), then it is
prohibited to call MessageConsumer#close from
any thread other than the one which delivers messages to the message listener.
(JMS 1.1 section 4.4.6 states: "...once the first message listener for a
session has been registered, the session is now
controlled by the thread of control that delivers messages to it. At this
point a client thread of control
cannot be used to further configure the session". This section explicitly
states that calling Session#close or
Connection#close is exempt from this restriction, but does not mention
However in JMS 2.0 we have said that it is prohibited to call
MessageConsumer#close from the message listener as well.
This means that there is no way of ever calling MessageConsumer#close when a
message listener is being used. This is
clearly a nonsensical state of affairs which we need to resolve.
I think there are two fairly obvious solutions:
Solution A. Change the specification for MessageConsumer#close to allow it to
be called from a message listener's
onMessage on its own consumer.
Solution B. Add MessageConsumer#close to the list of methods exempted from the
"single thread of control" restriction on
a session, just like Session#close and Connection#close.
Solution C. Both (A) and (B) together.
Discussion: Solution A
Allowing MessageConsumer#close to be called from a message listener on its
own consumer would make it possible to create
simple applications which need to consume a specific number of messages.
After a sufficient number of messages have been
received, onMessage calls MessageConsumer#close and no further messages are
Discussion: Solution B
Allowing MessageConsumer#close to be called from a thread other than the one
which is calling onMessage would be
consistent with Session#close and Connection#close, which are already allowed
to be called from a thread other than the
one which is calling onMessage.
However allowing this on its own does not allow the fine-grained control over
the number of messages delivered that is
offered by (A) .
Affect of consumer close on message acknowledgement
If we chose (A) then we'd need to decide what happens to the message which
was being delivered at the time.
I suggest we define that consumer.close() has no effect on message
acknowledgement. After all, acknowledgement is really
a function of the session rather than the consumer, and the session is still
We could clarify the behaviour for each acknowledgement mode as follows:
* Auto-acknowledge: calling consumer.close() has no effect on message
acknowledgement. The message will be automatically
acknowledged when onMessage returns.
* Client-acknowledge: calling consumer.close() has no effect on message
acknowledgement. If the message listener calls Message#acknowledge (or
the new JMSContext#acknowledge) then the message will be acknowledged.
If the message listener does not call acknowledge then the message will
be redelivered after the session is closed or recovered. It makes no
difference whether the message is acknowledged before or after the
consumer is closed.
* Local transaction: calling consumer.close() has no effect on
transaction commit or rollback. If the message listener calls
Session#commit (or the new JMSContext#commit) then the transaction will
be committed. If the message listener calls rollback then the transaction
will be rolled back and the message will be redelivered. It makes no
difference whether the transaction is committed or rolled back before or
after the consumer is closed.
So, which do you prefer? A, B or A+B?