jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: JMS 2.0 PR: Feedback from Goldman Sachs

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Wed, 06 Feb 2013 13:23:11 +0000

Here's my reply to these interesting and useful comments from Mike.

(Anyone is welcome to join in and add their own comments. Mike's team raise a few points on which other views are invited)

On 04/02/2013 11:52, Nigel Deakin wrote:
> I received some feedback on the public draft from Mike Marzo. JCP member representing Goldman Sachs. I've forwarded
> it here (below) with permission and will reply shortly.
>
> Nigel
> *
> Commentson the Public Draft*
>
> *_Clarifications_
> *
> *Load balancing for shared non-durable and durable subscriptions*
>
> When a shared subscription has multiple consumers, are there any fairness/load-balancing guarantees around how
> messages will be distributed to each of these consumers?
No. JMS doesn't define how messages from a queue are distributed to multiple consumers, and similarly it doesn't define
how messages from a shared subscription are distributed to multiple consumers.

Section 4.4.9. "Multiple sessions" explicitly says this for queues, and I think this should be extended to refer to
shared subscriptions as well. (Note however that if something is not defined in the spec it is "undefined", irrespective
of whether the spec actually says so!)

>
> *Semantics of Session.unsubscribe when using shared subscriptions
> *
> The specification is not clear on the semantics of Session.unsubscribe when using shared subscriptions.
>
> Consider the case where a shared subscription has consumers in two different clients. When the first client calls
> Session.unsubscribe, does it result in the subscription being removed for both the clients? Or does
> Session.unsubscribe take effect only if there are no active message consumers for the subscription?

The javadoc for Session.unsubscribe() says "It is erroneous for a client to delete a durable subscription while there is
an active |MessageConsumer| or |TopicSubscriber| for the subscription, or while a consumed message is part of a pending
transaction or has not been acknowledged in the session." The same sentence is also in the spec (6.11.4).

I think this explicitly answers your question: you can only delete a shared subscription when there are no active consumers.

(The javadoc for JMSContext.unsubscribe() mentions "active consumers". I'll change that to "active (not closed)
consumers" and amend Session.unsubscribe() to match).

>
> *Sending messages asynchronously - commit completion
> *
> The specification is explicit about the completion of CompletionListener callbacks for locally transacted sessions,
> but does not say anything about XA sessions.
>
> /If the session is transacted (uses a local transaction) then when the commit or rollback method is called the JMS
> provider must block until any incomplete send operations have been completed and all CompletionListener callbacks have
> returned before performing the commit or rollback./
>
> Even with XA sessions, commit(on the JTA transaction manager, transaction object) will block till all
> CompletionListener callbacks have been completed?
When I designed this feature I thought it would be difficult to implement in a JTA transaction. The problem isn't so
much the need to wait for CompletionListener callbacks to complete but because the semantics of the commit require it to
block until all incomplete sends have been completed, and the commit could be called from any thread. That's why the
spec forbids its use in a Java EE web or EJB container.

I actually started a FAQ of questions like this:
http://java.net/projects/jms-spec/pages/JMS20ReasonsFAQ#Why_is_asynchronous_send_not_permitted_in_a_Java_EE_web_or_EJB_container?

>
> *Sending messages asynchronously - callback completion order
> *
> Should the specification clarify that there are no guarantees about the order in which the CompletionListener
> callbacks will be called?
>
> Specifically, if message1 is sent before message2, the callback for message2 can be invoked before the callback for
> message1?

This is answered in 4.6.2.8. "Use of the CompletionListener by the JMS provider" which states

/A session will only invoke one CompletionListener callback method at a time. For a given MessageProducer or JMSContext,
callbacks (both onCompletion and onException) will be performed in the same order as the corresponding calls to the
asynchronous send method./

>
> _*Suggestions/Enhancements*_
>
> *Context creation using the new API - "Unspecified session mode"*
>
> These two methods on JMSContext allow for a context to be created in a "unspecified" sessionMode.
>
> JMSContext createContext()
> /Creates a JMSContext with the default user identity and an unspecified sessionMode./
>
> JMSContext createContext(String userName,String password)
> /Creates a JMSContext with the specified user identity and an unspecified sessionMode./
>
> What is the semantics of this mode? Can this be tightened up to be one of the valid session modes?

JMSContext has no such methods. The only method on JMSContext which creates a JMSContext takes the session mode as a
parameter.

I presume you're referring to the methods on ConnectionFactory. The javadocs describe the semantics:
http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/javax/jms/ConnectionFactory.html#createContext%28%29

    The behaviour of the session that is created depends on whether this method is called in a Java SE environment, in
    the Java EE application client container, or in the Java EE web or EJB container. If this method is called in the
    Java EE web or EJB container then the behaviour of the session also depends on whether or not there is an active JTA
    transaction in progress.

    In a *Java SE environment* or in *the Java EE application client container*:

      * The session will be non-transacted and received messages will be acknowledged automatically using an
        acknowledgement mode of |JMSContext.AUTO_ACKNOWLEDGE| For a definition of the meaning of this acknowledgement
        mode see the link below.

    In a *Java EE web or EJB container, when there is an active JTA transaction in progress*:

      * The session will participate in the JTA transaction and will be committed or rolled back when that transaction
        is committed or rolled back, not by calling the |JMSContext|'s |commit| or |rollback| methods.

    In the *Java EE web or EJB container, when there is no active JTA transaction in progress*:

    The session will be non-transacted and received messages will be acknowledged automatically using an acknowledgement
    mode of |JMSContext.AUTO_ACKNOWLEDGE| For a definition of the meaning of this acknowledgement mode see the link below

If you think this is unclear please do follow up with further questions.

>
> *Context creation in the new API - confusing method calls*
>
> The new API offers two different methods to create a context. The subtle difference in the behavior of these two
> methods might not be obvious to the user. Can the API be changed to make the distinction between these two methods
> more explicit?
>
> Consider creating two sessions from the same connection. In the new API, the first session (context) is created by
>
> Context firstContext = connectionFactory.createContext("user", "passord", JMSContext.CLIENT_ACKNOWLE
>
> To create the second session (context) using the same connection, one has to use a different method call; the context
> has to be created using the previously created Context and not the ConnectionFactory
>
> Context secondContext = firstContext.createContext(JMSContext.CLIENT_ACKNOWLEDGE)
>
> Using the first method call results in the creation of a new connection and a new session. The old API, by virtue of
> having separate Connection and Session classes, made this distinction obvious to the user of the API.

11.1. "Goals of the simplified API" lists the goals of the simplified API. One of these was "To reduce the number of
objects needed to send and receive messages, and in particular to combine the JMS Connection, Session and
MessageProducer objects into a single object."

This was considered particularly appropriate for Java EE web or EJB applications, given that in such applications you're
only allowed to create one session per connection anyway.

However for Java SE applications (and the Java EE application client container) we need to allow multiple JMSContext
objects to share the same connection. I initially suggested having a method on ConnectionFactory which took an existing
JMSContext as a parameter, but someone suggested moving the method to the JMSContext itself, and I agreed that this was
simpler.

>
> *Setting a CompletionListener on a JMSMessageProducer - Asymmetric API*
>
> The method to set a CompletionListener on a JMSMessageProducer is different than that for a MessageProducer. Can both
> the methods be made symmetric?
>
> When using a MessageProducer, the asynchronous send callback has to be set on per send basis
>
> send(Destination destination, Message message, CompletionListener completionListener)
>
> But when using a JMSMessageProducer, the callback can be set once on the producer
>
> setAsync(CompletionListener completionListener)
>
> Given that old and new APIs are intended to be "parallel"/"equivalent" APIs, it might help to make the method calls as
> symmetric as possible.

The two APIs are intended to offer the same messaging functionality, but they are not intended to offer the same API
style. The whole point of the simplified API was to offer a better style.

The JMSProducer API is designed to avoid an explosion in send methods by allowing each option to be specified using a
separate method. Essentially the JMSProducer is a temporary, throwaway object which simply holds parameters for the
subsequent send (builder pattern).

context.createProducer().
    setProperty("foo", "bar").
    setTimeToLive(10000).
    setDeliveryMode(NON_PERSISTENT).
    setDisableMessageTimestamp(true).
    send(inboundQueue, body);

MessageProducer has a different style, with all the send options being specified as parameters on the send method:

     send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive)

I think the JMSProducer style is much more flexible.

Allowing completion listener to be specified as a parameter on the MessageProducer send method was an attempt to confirm
with the existing API style of MessageProducer. This does have a drawback of requiring many send methods.

We could have certainly done that differently, with a method MessageProducer.setAsync, though that would be less
consistent with the other methods on Session. Would you prefer that? Would anyone else like to comment?

>
> In this case, both JMSMessageProducer and MessageProducer can allow setting a CompletionListener during creation and
> during send.


>
> *Delivery Delay - Relationship to JMSExpiration*
>
> The specification does not describe the behavior when the message delivery delay and the expiration are contradictory.

Hmm. I would have thought that the required behaviour was fairly obvious: if the time to live is less than the delivery
time then the message would expire before it was delivered. Does anyone else think this is unclear?

Note that even if the time to live was greater than the delivery time, if there are no consumers on the destination (or
they are very slow) then the message would expire before it was delivered. Setting delivery delay does not override
message expiration.

>
> Specifically, the send method should throw an exception, if the message's delivery delay is greater than the message's
> expiration (time to live). (i.e JMSDeliveryTime cannot be greater than JMSExpiration).

I think an exception is most appropriate when the JMS provider simply doesn't know what to do with the information
provided. For example a negative time to live is meaningless, and so a provider would be at liberty to reject this as
invalid. However I'm not sure that this is the case when the time to live is less than the delivery delay, since by
expiring the message at the time specified the JMS provider would be doing exactly what it was asked to do.

>
> *Delivery Delay - Unresolved issue*
>
> From the specification
> /
> There is an unresolved issue regarding the delivery of messages to topics which have a delivery delay specified. This
> is whether the decision to add a message to a subscription (whether durable or non-durable) should be made (a) when
> the message reaches its delivery time or (b) when the message is sent./
>
> We agree that this is an edge case. But we would prefer option (a) as it closely mirrors the publisher's intent. i.e
> If a publisher intends for a message to be available for consumption 30 minutes from now, consumers who are currently
> not connected but who connect to the broker 30 minutes from now should be able to receive the message.

Thank you for your comment.

The spec goes on to say

    If (a) were adopted it would require the JMS provider to keep all messages in the server until their delivery time
    is reached, even if there were no durable or non-durable subscriptions in existence. This might even be needed for
    non-persistent messages. Adopting (b) does not require this and by not requiring a message to be held separately
    from a subscription is closer to the way in which JMS providers work now. Forcing a JMS provider to implement (a)
    might potentially add a significant burden simply to cater for an unimportant edge case.

    In this specification option (b) has been chosen, and section 4.12 "Delivery delay" states that if a message is
    published to a topic, it will only be added to a durable or non-durable subscription on that topic if the
    subscription exists at the time the message is sent and continues to exist at the time the specified message
    delivery time is reached.

Since then I raised this with the expert group and proposed that the reasons for (b) still stand and we should confirm
that option
http://java.net/projects/jms-spec/lists/jsr343-experts/archive/2013-01/message/30
There weren't strong views, and although one or two people suggested deliberately leaving this undefined, no-one was
explicitly opposed to (b), so that's where thing stand currently.

More views on this would be welcome.

>
> *Disallow white space in subscription names
> *
> JMS 1.1 did not define what characters were valid in a durable subscription name but JMS 2.0 defines a minimum set of
> characters that must be valid in a durable or non-durable subscription name and supported across all providers.
>
> Whitespace is not in the list of valid identifiers but being a white list, it might still be allowed by JMS providers.
> Single/multiple leading/trailing white spaces in durable names cause operational problems which are difficult to
> diagnose and troubleshoot. e.g Accidental addition of a trailing white space results in a completely new durable being
> created.
>
> Can white space be disallowed in subscription names?

Java EE compatibility rules mean we can't introduce restrictions that would break existing applications, so all we can
do is to define what characters a JMS provider must support, not those that they can't.

http://java.net/projects/javaee-spec/pages/CompatibilityRequirements

>
> *JMSXDeliveryCount - Clarify prefetch behavior
> *
> Some JMS providers, increment the delivery count on message prefetch. e.g A message which has been prefetched (by the
> client library) 5 times will have a JMSXDeliveryCount of 5 even though the message was actually delivered to the
> client (from a receive call) only once. This results in incorrect poison message handling.
>
3.5.11. "JMSXDeliveryCount" says "When a client receives a message the mandatory JMS-defined message property
JMSXDeliveryCount will be set to the number of times the message has been delivered. The first time a message is
received it will be set to 1, so a value of 2 or more means the message has been redelivered."

This is pretty clear that "The first time a message is received it will be set to 1".

Whilst JMSXDeliveryCount, like JMSRedelivered, may sometimes give false positives (e.g. after a consumer failure, due to
a provider not knowing whether a message was delivered or not) both values are related to deliveries to the client
application, not the movement of messages from one part of the JMS provider to another.

Which JMS provider(s) have the behaviour you mention? Most of the major providers are represented on this expert group...

> Can the specification be explicit the JMSXDeliveryCount should be incremented only if the message has been returned
> from a receive call?
>

Hmm. The specification uses the word "delivered" in hundreds of places, including in the spec for JMSRedelivered. In all
cases the word "delivered" means delivered to the client application, not delivered from one part of the JMS provider to
another.

I didn't think that needs to be spelled out...but if existing vendors have a different interpretation then perhaps we
should.


> *Security- Kerberos authentication
> *
> Can JMS standardize on an authentication scheme like Kerberos? (SASL with Kerberos will be more flexible).
>
> We realize that this might not be possible for JMS 2.0, but will be good feature to have in future versions.

Can you please log this as an issue at http://java.net/jira/browse/JMS_SPEC
What would this actually mean in terms of the JMS API? Please be as specific as possible.

>
> _*Typos
> *_
> *Non-durable subscriptions*
>
> Refer: JMS_SPEC-40, Section 6.11, Pg 69. The last paragraph (bullet point) should read
>
> /A shared durable subscription is identified by name and an/
>
> instead of
>
> /A shared non-durable subscription is identified by name and an/
>
> *Delivery Delay*
>
> Refer: Section B.5.8, Pg 163. The second paragraph of section B.5.8 should read
>
> /A new section 4.12 "Delivery delay" and a corresponding new section/
>
> instead of
>
> /A new section 4.13 "Delivery delay" and a corresponding new section/
>
>
Thanks for these!

Nigel
>
>
>
>
>