jms-spec
  1. jms-spec
  2. JMS_SPEC-48

Specify that connection.stop() or close() may not be called from a MessageListener

    Details

    • Type: Improvement Improvement
    • Status: Resolved
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.0ED, 2.0
    • Labels:
      None

      Description

      Possible deadlock when calling connection.stop()

      If the javax.jms.MessageListener method onMessage calls Connection.stop() then, according to the JMS 1.1 specification, deadlock may result.

      Section 4.3.4 "Pausing Delivery of Incoming Messages" states:

      If MessageListeners are running when stop is invoked, stop must wait until all of them have returned before it may return. While these MessageListeners are completing, they must have the full services of the connection available to them.

      This means that if a MessageListener calls Connection.stop() then the call to stop() will block forever, waiting for the onMessage() method which is calling it to return.

      To avoid the possibility of applications causing deadlock, the JMS specification needs to be clarified to state that a MessageListener must not call connection.stop().

      The section above states that a MessageListener must have "the full services of the connection" available to it. The meaning of the term "full services" is not defined, but it needs to be made clear that this does not include the ability to call Connection.stop().

      Possible deadlock when calling connection.close()

      A similar issue exists for Connection.close(). If the javax.jms.MessageListener method onMessage calls Connection.close() then, according to the JMS 1.1 specification, deadlock may result.

      Section 4.3.5 "Closing a connection" states:

      If one or more of the connection's session's message listeners is processing a message at the point when connection close is invoked, all the facilities of the connection and its sessions must remain available to those listeners until they return control to the JMS provider.

      When connection close is invoked it should not return until message processing has been shut down in an orderly fashion. This means that all message listeners that may have been running have returned, and that all pending receives have returned.

      Again, this means that if a MessageListener calls Connection.close() then the call to close() will block forever, waiting for the onMessage() method which is calling it to return.

      To avoid the possibility of applications causing deadlock, the JMS specification needs similarly to be clarified to state that a MessageListener must not call connection.close().

      The section above states that a MessageListener must have "all the facilities of the connection" available to it. The meaning of the term "all the facilities" is again not defined, but it needs to be made clear that those does not include the ability to call Connection.close().

      Enforcement

      JMS provider should be required to throw a javax.jms.IllegalStateException if Connection.stop() or Connection.close() is called from the onMessage method of a javax.jms.MessageListener.

        Activity

        Hide
        Nigel Deakin added a comment -

        I've now drafted the spec changes as follows:

        Section 4.3.4 "Pausing Delivery of Incoming Messages"

        After:

        If MessageListeners are running when stop is invoked, stop must wait until all of them have returned before it may return. While these MessageListeners are completing, they must have the full services of the connection available to
        them.

        Append:

        However a MessageListeners must not call the connection's stop() method as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.stop() is called from the onMessage method of a javax.jms.MessageListener.

        Section 4.3.5 "Closing a Connection"

        After:

        If one or more of the connection’s session’s message listeners is processing a message at the point when connection close is invoked, all the facilities of the connection and its sessions must remain available to those listeners until they return control to the JMS provider.

        Append:

        However a message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.close() is called from the onMessage method of a javax.jms.MessageListener.

        Section 4.4.1 "Closing a Session"

        After:

        When session close is invoked, it should not return until its message processing has been shut down in an orderly fashion. This means that none of its message listeners are running, and that if there is a pending receive, it has
        returned with either null or a message.

        Append:

        A message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Session.stop() is called from the onMessage method of a javax.jms.MessageListener.

        Javadoc for Connection.stop()

        After:

        If message listeners are running when stop is invoked, the stop call must wait until all of them have returned before it may return. While these message listeners are completing, they must have the full services of the connection available to them.

        Insert:

        However a message listener must not call the connection's stop() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.stop() is called from the onMessage method of a javax.jms.MessageListener.

        Javadoc for Connection.close()

        After:

        When this method is invoked, it should not return until message processing has been shut down in an orderly fashion. This means that all message listeners that may have been running have returned, and that all pending receives have returned. A close terminates all pending message receives on the connection's sessions' consumers. The receives may return with a message or with null, depending on whether there was a message available at the time of the close. If one or more of the connection's sessions' message listeners is processing a message at the time when connection close is invoked, all the facilities of the connection and its sessions must remain available to those listeners until they return control to the JMS provider.

        Append:

        However a message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.close() is called from the onMessage method of a javax.jms.MessageListener.

        Javadoc for Session.close()

        After:

        This call will block until a receive call or message listener in progress has completed. A blocked message consumer receive call returns null when this session is closed.

        Append:

        A message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Session.close() is called from the onMessage method of a javax.jms.MessageListener.

        Show
        Nigel Deakin added a comment - I've now drafted the spec changes as follows: Section 4.3.4 "Pausing Delivery of Incoming Messages" After: If MessageListeners are running when stop is invoked, stop must wait until all of them have returned before it may return. While these MessageListeners are completing, they must have the full services of the connection available to them. Append: However a MessageListeners must not call the connection's stop() method as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.stop() is called from the onMessage method of a javax.jms.MessageListener. Section 4.3.5 "Closing a Connection" After: If one or more of the connection’s session’s message listeners is processing a message at the point when connection close is invoked, all the facilities of the connection and its sessions must remain available to those listeners until they return control to the JMS provider. Append: However a message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.close() is called from the onMessage method of a javax.jms.MessageListener. Section 4.4.1 "Closing a Session" After: When session close is invoked, it should not return until its message processing has been shut down in an orderly fashion. This means that none of its message listeners are running, and that if there is a pending receive, it has returned with either null or a message. Append: A message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Session.stop() is called from the onMessage method of a javax.jms.MessageListener. Javadoc for Connection.stop() After: If message listeners are running when stop is invoked, the stop call must wait until all of them have returned before it may return. While these message listeners are completing, they must have the full services of the connection available to them. Insert: However a message listener must not call the connection's stop() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.stop() is called from the onMessage method of a javax.jms.MessageListener. Javadoc for Connection.close() After: When this method is invoked, it should not return until message processing has been shut down in an orderly fashion. This means that all message listeners that may have been running have returned, and that all pending receives have returned. A close terminates all pending message receives on the connection's sessions' consumers. The receives may return with a message or with null, depending on whether there was a message available at the time of the close. If one or more of the connection's sessions' message listeners is processing a message at the time when connection close is invoked, all the facilities of the connection and its sessions must remain available to those listeners until they return control to the JMS provider. Append: However a message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Connection.close() is called from the onMessage method of a javax.jms.MessageListener. Javadoc for Session.close() After: This call will block until a receive call or message listener in progress has completed. A blocked message consumer receive call returns null when this session is closed. Append: A message listener must not call the connection's close() method, or the session's close() method, as this would lead to deadlock. The JMS provider must throw a javax.jms.IllegalStateException if Session.close() is called from the onMessage method of a javax.jms.MessageListener.
        Hide
        Nigel Deakin added a comment - - edited

        I've now updated the javadocs and the draft spec
        (these changes are additive, so those docs include other changes).

        The updated draft spec is here:
        http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf
        (all changes are highlighted clearly with changebars)

        Section 4.3.4 "Pausing Delivery of Incoming Messages"

        A message listener must not attempt to stop its own connection as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException.

        Section 4.3.5 "Closing a Connection"

        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 javax.jms.IllegalStateException.

        Section 4.4.1 "Closing a Session"

        A message listener must not attempt to close its own session as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException.

        Section 11.5.12 "Clarification of use of stop or close from a message listener (JMS_SPEC-48)"

        This is the change log for this issue.

        The updated Javadocs are here:
        http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar

        New javadoc comment on Connection.stop():

              * A message listener must not attempt to stop its own connection as this 
              * would lead to deadlock. The JMS provider must detect this and throw a 
              * javax.jms.IllegalStateException.
        

        New javadoc comment on Connection.close():

              * 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 
              * javax.jms.IllegalStateException.
        

        New javadoc comment on Session.close():

              * A message listener must not attempt to close its own session as this 
              * would lead to deadlock. The JMS provider must detect this and throw a 
              * javax.jms.IllegalStateException.
        

        New javadoc comment on MessagingContext.stop():

             * A message listener must not attempt to stop its own MessagingContext as this 
             * would lead to deadlock. The JMS provider must detect this and throw a 
             * javax.jms.IllegalStateRuntimeException.
        

        New javadoc comment on MessagingContext.close():

             * A message listener must not attempt to close its own MessagingContext as this 
             * would lead to deadlock. The JMS provider must detect this and throw a 
             * javax.jms.IllegalStateRuntimeException.
        
        Show
        Nigel Deakin added a comment - - edited I've now updated the javadocs and the draft spec (these changes are additive, so those docs include other changes). The updated draft spec is here: http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf (all changes are highlighted clearly with changebars) Section 4.3.4 "Pausing Delivery of Incoming Messages" A message listener must not attempt to stop its own connection as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException. Section 4.3.5 "Closing a Connection" 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 javax.jms.IllegalStateException. Section 4.4.1 "Closing a Session" A message listener must not attempt to close its own session as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException. Section 11.5.12 "Clarification of use of stop or close from a message listener ( JMS_SPEC-48 )" This is the change log for this issue. The updated Javadocs are here: http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar New javadoc comment on Connection.stop() : * A message listener must not attempt to stop its own connection as this * would lead to deadlock. The JMS provider must detect this and throw a * javax.jms.IllegalStateException. New javadoc comment on Connection.close() : * 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 * javax.jms.IllegalStateException. New javadoc comment on Session.close() : * A message listener must not attempt to close its own session as this * would lead to deadlock. The JMS provider must detect this and throw a * javax.jms.IllegalStateException. New javadoc comment on MessagingContext.stop() : * A message listener must not attempt to stop its own MessagingContext as this * would lead to deadlock. The JMS provider must detect this and throw a * javax.jms.IllegalStateRuntimeException. New javadoc comment on MessagingContext.close() : * A message listener must not attempt to close its own MessagingContext as this * would lead to deadlock. The JMS provider must detect this and throw a * javax.jms.IllegalStateRuntimeException.
        Hide
        Nigel Deakin added a comment -

        I'd like to reopen this issue. Since the changes described above was added to the spec it has been pointed out that a similar issue applies with the close() methods on MessageConsumer and JMSConsumer.

        I would therefore like to propose that we amend the definition of MessageConsumer.close() and JMSConsumer.close() to match the other methods listed here.

        Show
        Nigel Deakin added a comment - I'd like to reopen this issue. Since the changes described above was added to the spec it has been pointed out that a similar issue applies with the close() methods on MessageConsumer and JMSConsumer . I would therefore like to propose that we amend the definition of MessageConsumer.close() and JMSConsumer.close() to match the other methods listed here.
        Hide
        Nigel Deakin added a comment - - edited

        I've now drafted the necessary API changes:

        New javadoc comment on MessageConsumer.close():

        	 * This call blocks until a {@code receive} or message listener in progress
        	 * has completed. A blocked message consumer {@code receive} call returns
        	 * null when this message consumer is closed.
        	 * <p>
        	 * A {@code MessageListener} must not attempt to close its own
        	 * {@code MessageConsumer} as this would lead to deadlock. The JMS provider
        	 * must detect this and throw a {@code IllegalStateException}.
        	 * 
        	 * @exception IllegalStateException
        	 *                this method has been called by a {@code MessageListener}
        	 *                on its own {@code MessageConsumer}
        
        

        Note that the words "this call blocks until a... message listener in progress has completed" are not new and were in JMS 1.1.

        New javadoc comment on JMSConsumer.close()

        	 * This call blocks until a {@code receive} or message listener in progress has completed.
        	 * A blocked {@code receive} call returns null when
        	 * this {@code JMSConsumer} is closed.
        	 * <p>
        	 * A {@code MessageListener} must not attempt to close its own
        	 * {@code JMSConsumer} as this would lead to deadlock. The JMS provider
        	 * must detect this and throw a {@code IllegalStateRuntimeException}.
        	 * 
         	 * @exception IllegalStateRuntimeException
        	 *                this method has been called by a {@code MessageListener}
        	 *                on its own {@code JMSConsumer}
        

        In addition I've added a new section 4.5.3 "Closing a MessageConsumer"

        When the MessageConsumer's close method is invoked it should not return until its message processing has been shut down in an orderly fashion. This means that none of its message listeners are running, and that if there is a pending receive, it has returned with either null or a message.

        A message listener must not attempt to close its own MessageConsumer as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException.

        Show
        Nigel Deakin added a comment - - edited I've now drafted the necessary API changes: New javadoc comment on MessageConsumer.close() : * This call blocks until a {@code receive} or message listener in progress * has completed. A blocked message consumer {@code receive} call returns * null when this message consumer is closed. * <p> * A {@code MessageListener} must not attempt to close its own * {@code MessageConsumer} as this would lead to deadlock. The JMS provider * must detect this and throw a {@code IllegalStateException}. * * @exception IllegalStateException * this method has been called by a {@code MessageListener} * on its own {@code MessageConsumer} Note that the words "this call blocks until a... message listener in progress has completed" are not new and were in JMS 1.1. New javadoc comment on JMSConsumer.close() * This call blocks until a {@code receive} or message listener in progress has completed. * A blocked {@code receive} call returns null when * this {@code JMSConsumer} is closed. * <p> * A {@code MessageListener} must not attempt to close its own * {@code JMSConsumer} as this would lead to deadlock. The JMS provider * must detect this and throw a {@code IllegalStateRuntimeException}. * * @exception IllegalStateRuntimeException * this method has been called by a {@code MessageListener} * on its own {@code JMSConsumer} In addition I've added a new section 4.5.3 "Closing a MessageConsumer" When the MessageConsumer 's close method is invoked it should not return until its message processing has been shut down in an orderly fashion. This means that none of its message listeners are running, and that if there is a pending receive, it has returned with either null or a message. A message listener must not attempt to close its own MessageConsumer as this would lead to deadlock. The JMS provider must detect this and throw a javax.jms.IllegalStateException .
        Hide
        Nigel Deakin added a comment -

        The JMS EG has agreed to revise the behaviour of consumer close to:

        • Allow consumer.close to be called from within a message listener
        • Allowing a consumer to be closed from any thread, even if it isn't the thread of control

        The main changes are listed below.

        New Javadoc for MessageConsumer.close

        /**
         * Closes the message consumer.
         * <p>
         * Since a provider may allocate some resources on behalf of a
         * {@code MessageConsumer} outside the Java virtual machine, clients should
         * close them when they are not needed. Relying on garbage collection to
         * eventually reclaim these resources may not be timely enough.
         * <P>
         * This call will block until a {@code receive} call in progress on this
         * consumer has completed. A blocked {@code receive} call returns null when
         * this message consumer is closed.
         * <p>
         * If this method is called whilst a message listener is in progress in
         * another thread then it will block until the message listener has
         * completed.
         * <p>
         * This method may be called from a message listener's {@code onMessage}
         * method on its own consumer. After this method returns the
         * {@code onMessage} method will be allowed to complete normally. 
         * <p>
         * This method is the only {@code MessageConsumer} method that can be called
         * concurrently.
         * 
         * @exception JMSException
         *                if the JMS provider fails to close the consumer due to
         *                some internal error.
         */
        

        Spec changes: new section 8.8. Closing a consumer

        8.8. Closing a consumer

        The close methods on MessageConsumer, JMSConsumer, QueueReceiver and TopicSubscriber allow a consumer to be closed separately from the session or connection used to create it.

        Closing a consumer terminates the delivery of messages to the consumer.

        close is the only method on a consumer that may be invoked from a thread of control separate from the one which is currently controlling the session.

        If close is called in one thread whilst another thread is calling receive on the same consumer then the call to close must block until the receive call has completed. A blocked receive call returns null when the consumer is closed.

        If close is called in one thread whilst a message listener for this consumer is in progress in another thread then the call to close must block until the message listener has completed.

        If close is called from a message listener's onMessage method on its own consumer then after this method returns the onMessage method must be allowed to complete normally.

        Closing a consumer has no effect on the acknowledgement of messages delivered to the application, or on any transaction in progress. This is because message acknowledgement and transactions are functions of the session, not the consumer.

        • If the session mode is AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE then any messages delivered to the application will be automatically acknowledged as normal.
        • If the session mode is CLIENT_ACKNOWLEDGE then any messages delivered to the application may be acknowledged by calling acknowledge in the normal way. It makes no difference whether this occurs before or after the consumer is closed.
        • If the session is transacted then the application may commit or rollback the transaction as normal. It makes no difference whether this occurs before or after the consumer is closed.
        Show
        Nigel Deakin added a comment - The JMS EG has agreed to revise the behaviour of consumer close to: Allow consumer.close to be called from within a message listener Allowing a consumer to be closed from any thread, even if it isn't the thread of control The main changes are listed below. New Javadoc for MessageConsumer.close /** * Closes the message consumer. * <p> * Since a provider may allocate some resources on behalf of a * {@code MessageConsumer} outside the Java virtual machine, clients should * close them when they are not needed. Relying on garbage collection to * eventually reclaim these resources may not be timely enough. * <P> * This call will block until a {@code receive} call in progress on this * consumer has completed. A blocked {@code receive} call returns null when * this message consumer is closed. * <p> * If this method is called whilst a message listener is in progress in * another thread then it will block until the message listener has * completed. * <p> * This method may be called from a message listener's {@code onMessage} * method on its own consumer. After this method returns the * {@code onMessage} method will be allowed to complete normally. * <p> * This method is the only {@code MessageConsumer} method that can be called * concurrently. * * @exception JMSException * if the JMS provider fails to close the consumer due to * some internal error. */ Spec changes: new section 8.8. Closing a consumer 8.8. Closing a consumer The close methods on MessageConsumer, JMSConsumer, QueueReceiver and TopicSubscriber allow a consumer to be closed separately from the session or connection used to create it. Closing a consumer terminates the delivery of messages to the consumer. close is the only method on a consumer that may be invoked from a thread of control separate from the one which is currently controlling the session. If close is called in one thread whilst another thread is calling receive on the same consumer then the call to close must block until the receive call has completed. A blocked receive call returns null when the consumer is closed. If close is called in one thread whilst a message listener for this consumer is in progress in another thread then the call to close must block until the message listener has completed. If close is called from a message listener's onMessage method on its own consumer then after this method returns the onMessage method must be allowed to complete normally. Closing a consumer has no effect on the acknowledgement of messages delivered to the application, or on any transaction in progress. This is because message acknowledgement and transactions are functions of the session, not the consumer. If the session mode is AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE then any messages delivered to the application will be automatically acknowledged as normal. If the session mode is CLIENT_ACKNOWLEDGE then any messages delivered to the application may be acknowledged by calling acknowledge in the normal way. It makes no difference whether this occurs before or after the consumer is closed. If the session is transacted then the application may commit or rollback the transaction as normal. It makes no difference whether this occurs before or after the consumer is closed.
        Hide
        Nigel Deakin added a comment -

        This issue is resolved in the JMS 2.0 final release. Marking issue as resolved with a "fix version" of 2.0

        Show
        Nigel Deakin added a comment - This issue is resolved in the JMS 2.0 final release. Marking issue as resolved with a "fix version" of 2.0

          People

          • Assignee:
            Nigel Deakin
            Reporter:
            Nigel Deakin
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: