jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: (JMS_SPEC-43) New API to send a message with async acknowledgement from server

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Thu, 12 Jul 2012 19:06:29 +0100

On 27/06/2012 11:04, Nigel Deakin wrote:
> I sent an email on 25th May which described various clarifications in the definition of this new feature and which
> proposed a number of restrictions. I have received no comments from the Expert Group. However I have received a lot of
> feedback from within Oracle (where's we're thinking of how to implement it in the RI and other products) and am now
> proposing the following modified text. Please give your comments by the end of *Friday 6th July.*
>
I haven't had much feedback on this, with the only doubts coming from Chris Barrow who wondered whether these proposals
were "babysitting" the developer too much, which was followed by a comment from Rüdiger who thought they weren't. Given
the fact that there is clear support for this feature as a whole I think we should proceed with these detailed changes.
If you object say now. I'll draft the actual spec changes shortly.

A change that seems to have got lost in the various emails on this subject is a change that I think is needed to the
CompletionListener API. This is currently:

public interface CompletionListener {

     /**
      * Notifies the application that the message has been successfully sent
      *
      * @param message the message that was sent.
      */
     void onCompletion(Message message);

     /**
      * Notifies user that the specified exception was thrown while attempting to send the message
      *
      * @param exception the exception
      */
     void onException(Exception exception);
}

For consistency I think we should pass the Message into the onException method as well. This is needed to allow the
exception handling method to know which message caused the exception. So the method signature would change to

void onException(Message message, Exception exception);

I think this corrects a mistake and is not controversial, but if you have any comments please let me know.

Nigel

> The key changes compared with my previous email are:
>
> * I've dropped my suggestion to pass in a messageID rather than a Message to the CompletionListener. I'll stick to
> the original proposal to pass in the Message itself.
>
> * I've dropped my proposal that the send() call be required to set the message header fields that are defined in the
> spec as being set by the "JMS provider send method" before returning. These are the JMSDestination,
> JMSDeliveryMode, JMSExpiration, JMSDeliveryTime, JMSPriority, JMSMessageID and JMSTimestamp header fields. This is
> because this would cause difficulties for providers which generate some of these values on the server. For reasons
> of consistency the same will apply for the optional message properties JMSXUserID, JMSXAppID, JMSXProducerTXID and
> JMSXConsumerTXID. These headers must be set by the time the success callback is performed.
>
> * A new comment has been added to clarify that the application is not required to wait for an asynchronous send to
> complete before sending a subsequent message.
>
> * Since the JMS provider will be setting these message header fields in a separate thread, after the asynchronous
> send method has returned the application must not attempt to read the message until the send is complete.
> Applications which do so must receive an exception.
>
> * In theory this means that it would be safe for the application to send a Message object a second time so long as
> the previous send had completed. However if this were permitted there is a danger of a developer writing an
> application which works when acks are fast but which throws exceptions when acks are slow. I have therefore
> proposed that we simply state that an application uses an async send to send a message it must not attempt to
> change the state of the message or send the message a second time, either using an asynchronous or normal send
> operation. If the application attempts to do this the JMS provider will throw an exception.
>
> * I have changed the proposed behaviour of close(). My previous proposal was that if close() was called then there
> is no requirement for the JMS provider to block until any incomplete send operations have completed. This would
> have interfered with connection pooling. It would also have made to raised other issues which would have needed
> clarification. To keep things simple I am now proposing that close() should block until any incomplete send
> operations have completed. This makes it consistent with commit() and rollback() for transacted sessions.
>
> * I have clarified that completion listener callbacks for a given session should be serialised and take place in the
> same order as the corresponding call to the async send method.
>
>
> *Proposed new description of this feature*
>
> The following description of this feature will be added to the spec and javadocs.
>
> This method is provided to allow the JMS provider to perform part of the work involved in sending a message in a
> separate thread, allowing the application to do something else whilst this is happening. JMS refers to this as an
> "asynchronous send". When the message has been successfully sent the JMS provider invokes a callback method on an
> application-specified CompletionListener object. Only when that callback has been invoked can the application be
> sure that the message has been successfully sent with the same degree of confidence as if a normal synchronous
> send had been performed. An application which requires this degree of confidence must therefore wait for the
> callback to be invoked before continuing.
>
> The following information is intended to give an indication of how an asynchronous send would typically be
> implemented.
>
> In some JMS providers, a normal synchronous send involves sending the message to a remote JMS server and then
> waiting for an acknowledgement to be received before returning. It is expected that such a provider would
> implement an asynchronous send by sending the message to the remote JMS server and then returning without waiting
> for an acknowledgement. When the acknowledgement is received, the JMS provider would notify the application by
> invoking the onCompletion method on the application-specified CompletionListener object.
>
> In those cases where the JMS specification permits a lower level of reliability, a normal synchronous send might
> not wait for an acknowledgement. In that case it is expected that an asynchronous send would be similar to a
> synchronous send: the JMS provider would send the message to the remote JMS server and then return without waiting
> for an acknowledgement. However the JMS provider would still notify the application that the send had completed by
> invoking the onCompletion method on the application-specified CompletionListener object.
>
> It is up to the JMS provider to decide exactly what is performed in the calling thread and what, if anything, is
> performed asynchronously, so long as it satisfies the following requirements.
>
> Quality of service
>
> * After the send operation is complete, which means that the message has been successfully sent with the same
> degree of confidence as if a normal synchronous send had been performed, the JMS provider must invoke the
> completion listener. The completion listener must not be invoked earlier than this.
>
> Message order
>
> * If the same MessageProducer or JMSContext is used to send multiple messages then JMS message ordering
> requirements (see section 4.4.10.1 "Order of message receipt) must be satisfied. This applies even if a
> combination of synchronous and asynchronous sends have been performed. The application is not required to wait
> for an asynchronous send to complete before sending the next message.
>
> Close, commit or rollback
>
> * If the session is transacted (uses a local transaction) then when commit() or rollback() is called the JMS
> provider must block until any incomplete send operations have been completed and all callbacks have returned
> before performing the commit or rollback.
>
> * If close() is called (on the MessageProducer, Session, Connection or JMSContext object) then the JMS provider
> must block until any incomplete send operations have been completed and all callbacks have returned before
> closing the object and returning.
>
> Restrictions on usage in Java EE
>
> * An asynchronous send is not permitted in a Java EE EJB or web container
>
> Message headers
>
> * The JMS specification defines a number of message header fields and JMS-defined message properties of the
> specified Message which must be set by the "JMS provider send method" (see section 3.4.11 "How message header
> values are set" and 3.5.9 "JMS defined properties"). This does not apply if the send is asynchronous. These
> fields and properties must however be set before the CompletionListener's onCompletion method is invoked. If
> the CompletionListener's onException method is called then the state of these message header fields and
> properties is undefined
>
> Restrictions on threading
>
> * Applications that perform an asynchronous send must confirm to the threading restrictions defined in section
> 4.4.6. "Conventions for using a session". This means that the session may be used by only one thread at a time.
>
> * Setting a completion listener does not cause the session to be dedicated to the thread of control which calls
> the completion listener. The application thread may therefore continue to use the session after performing an
> async send. However the CompletionListener's callback methods must not use the session if an application
> thread might be using the session at the same time.
>
> Use of the CompletionListener by the JMS provider
>
> * A session will only invoke one CompletionListener callback method at a time. For a given MessageProducer or
> JMSContext, callbacks will be performed in the same order as the corresponding calls to the async send method.
>
> * A JMS provider must not invoke the CompletionListener from the thread that is calling the async send method.
>
> * An application which do not need to receive notifications when the send has completed or has failed may supply
> a null CompletionListener. This does not remove the requirement for the close(), commit() or rollback()
> methods to block until any incomplete send operations have been completed.
>
> Restrictions on the use of the Message object
>
> * Applications which perform an asynchronous send must take account of the restriction that a Message object is
> designed to be accessed by one logical thread of control at a time and does not support concurrent use. See
> section 2.8 "Multi-threading".
>
> * After the send method has returned, the application must not attempt to read the headers, properties or
> payload of the Message object until the CompletionListener's onCompletion or onException method has been
> called. This is because the JMS provider may be modifying the Message object in another thread during this
> time. The JMS provider must throw a JMSException or JMSRuntimeException (depending on the method signature) if
> the application attempts to read the Message in such a case.
>
> * After the send method has returned the application must not attempt to change the state of the message or send
> the message a second time, either using an asynchronous or normal send operation. This applies even after the
> CompletionListener's onCompletion or onException method has been called. Even though it would be theoretically
> possible to allow the Message object to be re-used after the CompletionListener's onCompletion or onException
> method has been called, such use is not allowed to avoid an application being created which succeeds when
> callbacks were fast and which fails when callbacks are slow
>
> Nigel