[JMS_SPEC-65] Clarify use of NoLocal arg when using createDurableSubscriber Created: 22/Dec/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-168 Implement clarified behaviour of noLo... Closed
Tags: ed20-added

 Description   

This issue relates to the following method on javax.jms.Session:

TopicSubscriber createDurableSubscriber(Topic topic,
                                        java.lang.String name,
                                        java.lang.String messageSelector,
                                        boolean noLocal)
                                        throws JMSException

What does noLocal mean in this context? The javadoc states that this method "Creates a durable subscriber to the specified topic, using a message selector and specifying whether messages published by its own connection should be delivered to it". However it is ambiguous whether "it" refers to the durable subscription in the JMS provider or the TopicSubscriber object.

Does noLocal mean
(1) that messages published by this connection should not be added to the durable subscription, or that
(2) messages published by this connection should not be delivered to this particular TopicSubscriber? These do not mean the same thing, since in the latter case the messages might be saved in the subscription and delivered at some later stage.

This should be clarified.



 Comments   
Comment by Nigel Deakin [ 22/Dec/11 ]

The suggested interpretation is that it means (1): that messages published by its own connection should not be added to the durable subscription.

Comment by Hiram Chirino [ 22/Dec/11 ]

Agree it should be (1). Otherwise you will get an inconsistent behavior once a consumer reconnects, and lets face it folks hate inconsistencies.

Comment by Nigel Deakin [ 09/Jan/12 ]

I've now updated the draft spec and javadocs with details of this new feature (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

Section 6.11 "TopicSubscriber" has been reworded to state that "When a non-durable subscription is created on a topic, the noLocal argument may be used to specify that the subscriber must not receive messages published to the topic by its own connection."

Section 6.11.1 "Durable TopicSubscriber" has been reworded to state that "When a durable subscription is created on a topic, the noLocal argument may be used to specify that messages published to the topic by its own connection must not be added to the durable subscription."

These changes are also listed in the change log, section 11.5.13.

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

I've clarified the javadocs for the existing methods on Session that create both durable and non-durable subscribers:

Change to javadoc comment for Session.createConsumer(destination, messageSelector, NoLocal), which returns a MessageConsumer. This is used for non-durable topic subscriptions and for queues.

Old comment:

     /** Creates <CODE>MessageConsumer</CODE> for the specified destination, using a
      * message selector. This method can specify whether messages published by
      * its own connection should be delivered to it, if the destination is a
      * topic.

      . . .

      * <P>In some cases, a connection may both publish and subscribe to a
      * topic. The consumer <CODE>NoLocal</CODE> attribute allows a consumer
      * to inhibit the delivery of messages published by its own connection.
      * The default value for this attribute is False. The <CODE>noLocal</CODE>
      * value must be supported by destinations that are topics.

      . . .

      * @param NoLocal  - if true, and the destination is a topic,
      *                   inhibits the delivery of messages published
      *                   by its own connection.  The behavior for
      *                   <CODE>NoLocal</CODE> is
      *                   not specified if the destination is a queue.

New comment:

     /** Creates <CODE>MessageConsumer</CODE> for the specified destination, using a
      * message selector. This method can specify whether messages published by
      * its own connection should be delivered to it, if the destination is a
      * topic.

      . . .

      * <P>The <code>NoLocal</code> argument is for use when the
      * destination is a topic and the session's connection
      * is also being used to publish messages to that topic.
      * If <code>NoLocal</code> is set to true then the
      * <code>MessageConsumer</code> will not receive messages published
      * to the topic by its own connection. The default value of this
      * argument is false. If the destination is a queue
      * then the effect of setting <code>NoLocal</code>
      * to true is not specified.

      . . .

      * @param NoLocal  - if true, and the destination is a topic,
      *                   then the <code>MessageConsumer</code> will
      *                   not receive messages published to the topic
      *                   by its own connection.

Change to javadoc comment for Session.createDurableSubscriber(topic, name, messageSelector, noLocal), which returns a TopicSubscriber. This is used for durable topic subscriptions.

Old comment:

   /** Creates a durable subscriber to the specified topic,
        using a message selector and specifying whether messages
        published by its own connection should be delivered to it.

       . . .

     * @param noLocal if set, inhibits the delivery of messages published
     * by its own connection

New comment:

    /** Creates a durable subscription with the specified name on the
     * specified topic, and creates a <code>TopicSubscriber</code>
     * on that durable subscription, specifying a message
     * selector and whether messages published by its
     * own connection should be added to the durable subscription.

     . . .

     * <P>The <code>NoLocal</code> argument is for use when the session's
     * connection is also being used to publish messages to the topic.
     * If <code>NoLocal</code> is set to true then messages published
     * to the topic by its own connection will not be added to the
     * durable subscription. The default value of this
     * argument is false.

     . . .

     * @param noLocal if true, messages published by its own connection
     * will not be added to the durable subscription.

I've also updated the javadoc comment for Session.createDurableConsumer(topic, name, messageSelector, noLocal), which returns a MessageConsumer. This is proposed to satisfy JMS_SPEC-51 and is further modified here:

     /** Creates a durable subscription with the specified name on the
      * specified topic, and creates a <code>MessageConsumer</code>
      * on that durable subscription, specifying a message
      * selector and whether messages published by its
      * own connection should be added to the durable subscription.

      . . .

     * <P>The <code>NoLocal</code> argument is for use when the session's
     * connection is also being used to publish messages to the topic.
     * If <code>NoLocal</code> is set to true then messages published
     * to the topic by its own connection will not be added to the
     * durable subscription. The default value of this
     * argument is false.
      . . .

      * @param noLocal if true, messages published by its own connection
      * will not be added to the durable subscription.

In addition, I've updated all the relevant methods on the JMSContext interface proposed in http://java.net/jira/browse/JMS_SPEC64:

Updated javadoc comment for JMSContext.createConsumer(destination, messageSelector, NoLocal), which returns a JMSConsumer. This is proposed to satisfy JMS_SPEC-64 and is further modified here.

/**
 * Creates a <CODE>JMSConsumer</CODE> for the specified destination, using
 * a message selector. This method can specify whether messages published by
 * its own connection should be delivered to it, if the destination is a
 * topic.

 . . .

 * The <code>noLocal</code> argument is for use when the
 * destination is a topic and the JMSContext's connection
 * is also being used to publish messages to that topic.
 * If <code>noLocal</code> is set to true then the
 * <code>JMSConsumer</code> will not receive messages published
 * to the topic by its own connection. The default value of this
 * argument is false. If the destination is a queue
 * then the effect of setting <code>noLocal</code>
 * to true is not specified.
  
 . . .

 * @param noLocal  if true, and the destination is a topic,
 *                 then the <code>JMSConsumer</code> will
 *                 not receive messages published to the topic
 *                 by its own connection
 * 

Updated javadoc comment for JMSContext.createDurableConsumer(topic, name, messageSelector, noLocal), which returns a JMSConsumer.

/** Creates a durable subscription with the specified name on the
 * specified topic, and creates a <code>JMSConsumer</code> 
 * on that durable subscription, specifying a message selector and 
 * whether messages published by its own connection should be added to 
 * the durable subscription.  

   . . .

 * The <code>noLocal</code> argument is for use when the JMSContext's 
 * connection is also being used to publish messages to the topic. 
 * If <code>noLocal</code> is set to true then messages published
 * to the topic by its own connection will not be added to the
 * durable subscription. The default value of this 
 * argument is false. 

   . . .

 * @param noLocal if true, messages published by its own connection
 * will not be added to the durable subscription.

Updated 15 May 2012 to reflect the rename of MessagingContext to JMSContext and other changes to that interface.

Comment by Nigel Deakin [ 15/May/12 ]

This issue was discussed and agreed some time ago before the early draft, but I'd like to re-open it for discussion of a possible clarification.

You will remember that this issue is about the purpose of the noLocal parameter when creating a durable subscription.

The JMS 1.1 javadocs simply states that this flag "inhibits the delivery of messages published by its own connection".
(http://docs.oracle.com/javaee/6/api/javax/jms/Session.html#createDurableSubscriber%28javax.jms.Topic,%20java.lang.String,%20java.lang.String,%20boolean%29)

For JMS 2.0 we have already agreed to clarify this to state that if this flag is set "messages published by its own connection will not be added to the durable subscription."
(See comments above or at http://java.net/jira/browse/JMS_SPEC-65#action_328549)

However it has been pointed out to me that further clarification may be needed.

Let's review this in stages.

Case 1

Consider the simplest case. A client creates a connection and calls createDurableSubscriber with noLocal=true. This returns a TopicSubscriber. It then uses the same connection to create a MessageProducer and uses it to send a message to the topic. The client then uses the TopicSubscriber to try to receive the message.

String clientID = "foo";
String subscriptionName = "bar";
boolean noLocal=true;
String messageSelector=null;
ConnectionFactory connectionFactory = ... // lookup connection factory
Topic topic = ...                         // lookup topic
Connection connection = connectionFactory.createConnection();
connection.setClientID(clientID);
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber = session.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
MessageProducer messageProducer = session.createProducer(topic);
messageProducer.send(session.createTextMessage("Hello"));
			
connection.start();
Message message = subscriber.receive(1000));
// is a message received or not?
connection.close();

What happens? JMS 1.1 states that the noLocal flag "inhibits the delivery of messages published by its own connection". We're using the same connection to send and receive messages, so clearly no message is received.

Case 2

After the previous case is run, the client now creates a second connection with the same clientID as before and calls createDurableSubscriber with identical arguments as before. This returns a second TopicSubscriber. The client then uses the second TopicSubscriber to try to receive the message.

Here's the complete case:

String clientID = "foo";
String subscriptionName = "bar";
boolean noLocal=true;
String messageSelector=null;
ConnectionFactory connectionFactory = ... // lookup connection factory
Topic topic = ...                         // lookup topic

// Step 1
Connection connection = connectionFactory.createConnection();
connection.setClientID(clientID);
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber = session.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
MessageProducer messageProducer = session.createProducer(topic);
messageProducer.send(session.createTextMessage("Hello"));
			
connection.start();
Message message = subscriber.receive(1000));
// message is null
connection.close();


// Step 2
Connection connection2 = connectionFactory.createConnection();
connection2.setClientID(clientID);
Session session2 = connection2.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber2 = session2.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
connection2.start();
Message message2 = subscriber2.receive(1000));
// is a message received or not?
connection2.close();

What happens? JMS 1.1 simply states that the noLocal flag "inhibits the delivery of messages published by its own connection". But in this case the message was published by a different connection. Should it be delivered?

However we've already taken the decision in JMS 2.0 to clarify the effect of the noLocal flag to state that "messages published by its own connection will not be added to the durable subscription." In this case, the message was published using the same connection as was used to create the durable subscription. So in accordance with this new wording, the message is not added to the durable subscription and so will never be delivered, even to a subsequent consumer that uses a different connection.

Case 3

So far so good. But now let's consider a third case: what if this second connection is also used to send a second message to the topic? Is it added to the durable subscription or not?

Here's the complete case:

String clientID = "foo";
String subscriptionName = "bar";
boolean noLocal=true;
String messageSelector=null;
ConnectionFactory connectionFactory = ... // lookup connection factory
Topic topic = ...                        // lookup topic

// Step 1
Connection connection = connectionFactory.createConnection();
connection.setClientID(clientID);
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber = session.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
MessageProducer messageProducer = session.createProducer(topic);
messageProducer.send(session.createTextMessage("Hello"));
			
connection.start();
Message message = subscriber.receive(1000));
// message is null
connection.close();


// Step 2
Connection connection2 = connectionFactory.createConnection();
connection2.setClientID(clientID);
Session session2 = connection2.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber2 = session2.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
connection2.start();
Message message2 = subscriber2.receive(1000));
// message is null

MessageProducer messageProducer2 = session2.createProducer(session2.createTopic(topicName));
messageProducer2.send(session2.createTextMessage("Hello"));

connection2.close();

// Step 3
Connection connection3 = connectionFactory.createConnection();
connection3.setClientID(clientID);
Session session3 = connection3.createSession(false,Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber3 = session3.createDurableSubscriber(topic, subscriptionName, messageSelector, noLocal);
			
connection3.start();
Message message3 = subscriber3.receive(1000));
// is a message received or not?
connection3.close();

The message sent using the first connection was not added to the durable subscription. Similarly, I think that the message sent using the second connection should not be added to the durable subscription. Anything else would be inconsistent and not very useful.

Just to reiterate this: the effect of setting noLocal shouldn't be restricted to messages sent using the very first connection, the one used to create the durable subscription. It should apply throughout the life of the durable subscription.

However this leads to one further question. In case 3, step 2 above, does it matter whether the producer was created, and the message sent, before or after the durable subscription was activated? At what point does connection2 become "tainted" and unable to send messages to the durable subscription?

I think the simplest answer to that is to say that it doesn't matter at what point connection2 activates the durable subscription. It has the same clientID as was associated with the durable subscription, and so any messages it sends to the topic will never be added to the durable subscription. This is the only definition of noLocal which gives consistent behaviour throughout the life of the subscription.

However for JMS 2.0 we have made clientID optional when creating a durable subscription. This means we need to define what noLocal means when clientID is not set. The simplest approach would be to define that setting noLocal on a durable subscription has no effect unless clientID is set.

In summary

In summary, it is proposed to further clarify the meaning of the noLocal parameter in the method Session.createDurableConsumer as follows:

/** Creates a durable subscription with the specified name on the
 * specified topic, and creates a <code>MessageConsumer</code>
 * on that durable subscription, specifying a message
 * selector and the noLocal argument.

   . . .

 * <P>If <code>noLocal</code> is set to true, 
 * and the client identifier is set, then any messages published 
 * using this connection or any other with the same client identifier 
 * will not be added to the durable subscription. 
 * If the client identifier is unset then 
 * setting <code>noLocal</code> to true has no effect.
 * The default value of <code>noLocal</code> is false.


   . . .

 * @param noLocal if true, and the client identifier is set, 
 * then any messages published using this connection 
 * or any other with the same client identifier 
 * will not be added to the durable subscription. 

Similar changes will be made to Session.createDurableSubscriber and JMSContext.createDurableConsumer, and to the JMS specification itself.

Comment by Nigel Deakin [ 30/May/12 ]

I've now updated the draft spec and javadocs in accordance with the changes proposed in the previous comment.

The updated draft spec is here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf

Section 6.11.1 "Durable TopicSubscriber" has been reworded to define the effect of setting noLocal as follows:

When a durable subscription is created on a topic, the noLocal argument may be used to specify that messages published to the topic by its own connection or any other with the same client identifier will not be added to the durable subscription. If the client identifier is unset then setting noLocal to true has no effect.

In addition, the effect of supplying a different value of noLocal when creating a consumer on an existing durable subscription has been clarified as follows:

If there are no active consumers on the durable subscription (and no consumed messages from that subscription are still part of a pending transaction or are not yet acknowledged in the session), and this method is used to create a new consumer on that durable subscription, specifying the same name and client identifier (if set) but a different topic or message selector, or, if the client identifier is set, a different noLocal argument, then the durable subscription will be deleted and a new one created.

However if there is an active consumer on the durable subscription (or a consumed message from that subscription is still part of a pending transaction or is not yet acknowledged in the session), and an attempt is made to create an additional consumer, specifying the same name and client identifier (if set) but a different topic or message selector, or, if the client identifier is set, a different noLocal argument, then a JMSException or JMSRuntimeException will be thrown.

Note that changing noLocal only has an effect if the client identifier is set.

These changes are also listed in the change log, section B.5.16.

The updated Javadocs can be downloaded here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
or browsed online here:
http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/index.html

I've clarified the javadocs for all the existing methods on Session and JMSContext that create durable subscribers. The methods affected are the Session methods createDurableSubscriber and createDurableConsumer and the JMSContext methods createDurableConsumer. These all now have javadoc comments similar to the following example:

* Creates a durable subscription with the specified name on the specified
* topic (if one does not already exist), specifying a message selector and
* the <code>noLocal</code> parameter, and creates a
* <code>TopicSubscriber</code> on that durable subscription.


* If there are no active consumers on the durable subscription (and no
* consumed messages from that subscription are still part of a pending
* transaction or are not yet acknowledged in the session), and this method
* is used to create a new consumer on that durable subscription, specifying
* the same name and client identifier (if set) but a different topic or
* message selector, or, if the client identifier is set, a different
* noLocal argument, then the durable subscription will be deleted and a new
* one created. 
*
* However if there is an active consumer on the durable
* subscription (or a consumed message from that subscription is still part
* of a pending transaction or is not yet acknowledged in the session), and
* an attempt is made to create an additional consumer, specifying the same
* name and client identifier (if set) but a different topic or message
* selector, or, if the client identifier is set, a different noLocal
* argument, then a <code>JMSException</code> will be thrown.
*
* If <code>noLocal</code> is set to true, and the client identifier is set,
* then any messages published to the topic using this session's connection,
* or any other connection or <code>JMSContext</code> with the same client
* identifier, will not be added to the durable subscription. If the client
* identifier is unset then setting <code>noLocal</code> to true has no
* effect. The default value of <code>noLocal</code> is false.

. . .

* @param noLocal
*            if true, and the client identifier is set, then any messages
*            published to the topic using this session's connection, or any
*            other connection or <code>JMSContext</code> with the same
*            client identifier, will not be added to the durable
*            subscription.
Comment by Nigel Deakin [ 13/Sep/12 ]

I've added a further change:

If the client identifier is unset then setting noLocal to true will cause an IllegalStateException or IllegalStateRuntimeException (depending on the method signature) to be thrown.

Comment by clebertsuconic [ 13/Sep/12 ]

I agree.. clientID is non set, calling createDurableSubscription with noLocal=true should throw an exception, otherwise there's no way to replay durable subscriptions.

However.... if you set clientID==null on a non-durable subscription, it would be possible to filter out with some sort of UUID at the implementation. However the usage semantics would get complicated.. so I would vote to just keep it simple.. i.e.:

clientID==null && noLocal==true => throw SomeException

Comment by chris.barrow [ 13/Sep/12 ]

Are you suggesting the createConsumer method should follow the same logic? I would have to urge against that because clientID has nothing whatsoever to do with non-durables, so it would be confusing to suddenly relate the two. Moreover it would be an incompatible change from 1.1 (which currently allows createConsumer with noLocal=true whether or not there is a clientID set on the connection).

Comment by Nigel Deakin [ 13/Sep/12 ]

This change (and this whole issue) concerns durable subscriptions only, and was intended to clarify what the existing JMS 1.1 behaviour should be.

The definition of noLocal for non-durable, non-shared subscriptions is unchanged from JMS 1.1. In that case client identifier may be either set or unset when noLocal is true. If a message is sent by the same Connection object which created the subscription then the message will not be added to the subscription. No need to rely on UUID, since when the connection that created the subscription is closed, the subscription will be deleted.

The introduction of shared durable and non-durable subscriptions in JMS 2.0 does introduce the issue of what noLocal means in that case. I will raise this as part of issue JMS_SPEC-40 which covers shared durable and non-durable subscriptions.

Comment by clebertsuconic [ 13/Sep/12 ]

My bad .. this is is certainly only about durable subscription

I'm happy with what you propose then.. but as I told you in a private email I prefer some exception as opposed to just ignore it.

Comment by chris.barrow [ 13/Sep/12 ]

Sorry to eat my words here, but in light of the discussion about non-durables I would now recommend changing the behavior for createDurableSubscription with noLocal=true to make it independent of clientID and consistent with non-durable shared consumers. See my recent comment in JMS_SPEC_40.

Comment by clebertsuconic [ 13/Sep/12 ]

@chriss: you need some sort of client-id to replay the queues and do the correct association.

Most implementations I know will use an internal filter on the queue with message.originatingID != currentID.

how would you replay the queue on these circunstances? what about previous sent messages?

Comment by Nigel Deakin [ 20/Mar/13 ]

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





[JMS_SPEC-53] Make Connection and other interfaces implement AutoCloseable Created: 29/Sep/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-169 Make Connection and other interfaces ... Closed
Tags: ed20-added

 Description   

This is a proposal to change all the JMS interfaces that currently implement a close() method to implement the java.lang.AutoCloseable interface?

This would affect Connection, Session, MessageConsumer, MessageProducer, QueueBrowser.

This is a new feature of Java SE 7 which makes it easier to write code which closes a resource after use.

There's a nice explanation here:
http://www.javacodegeeks.com/2011/07/java-7-try-with-resources-explained.html]

Briefly, it allows you to write code such as:

try { 
   Connection conn = connectionFactory.createConnection();
   Session sess = conn.createSession(false,Session.AUTO_ACKNOWLEDGE;
   MessageProducer producer = seession.createProducer(dest);
}{
   Message mess = sess.createTextMessage("hello");
   producer.send(mess);
} catch(JMSException e){
  // exception handling
}

When this code is executed, the close() methods on the connection, session and producer are always called after use.

  • There's no need to call close() on any of the objects that are created.
  • There's no need to provide a finally block containing the calls to close().
  • Objects are closed in the reverse order in which they were created.
  • There's no need to guard against calling close() on a null value.
  • There's no need to use a nested try/catch block within the finally block to catch exceptions thrown by close()
  • If the try() block throws an exception, and a subsequent call to close() throws a second exception as a consequence, then it is the first exception, not the second exception, that is passed to the catch block. So you only see the exception that really matters. Any suppressed exceptions can still be accessed from the thrown exception if needed.

This change would be of benefit to both Java SE and Java EE applications. The only drawback I can think of is that it would introduce a dependency on Java SE 7. This isn't an issue for Java EE 7 applications since these are already dependent on Java SE 7. But it would force Java SE applications to use Java SE 7. However by the time JMS 2.0 is released, in a year from now, Java 7 will be pretty widespread so this might not be a problem.



 Comments   
Comment by Nigel Deakin [ 16/Dec/11 ]

I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes).

The updated Javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
(One-line changes to Connection, Session, MessageProducer, MessageConsumer and QueueBrowser)

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, but the only place I've changed is 4.3.5 "Closing a Connection", 4.4.1 "Closing a Session", the example in section 9.1.3. Creating a Connection", and, in the changelog, section 11.5.3 "Automatically closing a connection or session")

I've now made Connection, Session, MessageProducer, MessageConsumer and QueueBrowser all extend AutoCloseable. (Note that MessagingContext, part of the proposed simplified API, already extends AutoCloseable)

(This updates the previous version of this comment which only referred to changes to Connection).

Comment by Nigel Deakin [ 05/Feb/12 ]

I've also changed javax.jms.SyncMessageConsumer to extend AutoCloseable, for consistency with MessageConsumer. API and javadocs updated.





[JMS_SPEC-52] Clarify that a message may be sent using a different session from that used to create the message Created: 21/Sep/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-170 Implement clarified behaviour of meth... Open
Tags: ed20-added

 Description   

The JMS 1.1 API defines how a JMS message object is created using one of the following methods on a Session:

Message 	createMessage() 
BytesMessage 	createBytesMessage() 
MapMessage 	createMapMessage() 
ObjectMessage 	createObjectMessage() 
ObjectMessage 	createObjectMessage(java.io.Serializable object) 
StreamMessage 	createStreamMessage() 
TextMessage 	createTextMessage() 
TextMessage 	createTextMessage(java.lang.String text) 

The following question has been raised:

Can a message be sent using a MessageProducer that was created from a different Session than was used to create the message?

This is not stated explicitly in the specification, and discussions within the JSR 343 Expert group show that different individuals have come to different conclusions on this issue.

My own interpretation is that a MessageProducer must be able to send a javax.JMS.Message irrespective of how it was created. It might have been created using Session.createMessage(), or it may have been received by a MessageConsumer.

Section 4.4.5 "Optimized message implementations" of the JMS 1.1 specification states:

A session provides message create methods that use provider-optimized implementations. This allows a provider to minimize its overhead for handling messages.
Sessions must be capable of sending all JMS messages regardless of how they may be implemented.

Furthermore, Section 3.12 of the JMS 1.1 specification states explicitly that a MessageProducer must be able to send a message that was created using a different JMS provider, and which would therefore have used a different session:

A provider must be prepared to accept, from a client, a message whose implementation is not one of its own. A message with a 'foreign' implementation may not be handled as efficiently as a provider's own implementation; however, it must be handled.

Despite this, there is a widespread view within the JMS community that for messages created within the same JVM by the same JMS provider, the session used to create the message must be the same as that used to send it.

It is therefore proposed that the JMS specification be clarified that there is no such restriction, and that a MessageProducer can be used to send any message object

  • irrespective of which session or connection was used to create it,
  • irrespective of whether the message was created within this JVM or received from a JMS destination, and
  • irrespective of whether the MessageProducer and message are implemented by the same or different JMS providers.


 Comments   
Comment by Nigel Deakin [ 22/Sep/11 ]

Edited description to clarify that a MessageProducer can be used to send any message object
irrespective of what connection (as well as session) was used to create it.

Comment by Nigel Deakin [ 19/Dec/11 ]

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

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

The six message-creation methods on Session have been modified to include the following additional javadoc comments:

* The message object returned may be sent using any session or messaging context. 
* It is not restricted to being sent using the session used to create it.
* <p>
* The message object returned may be optimised for use with the JMS provider
* used to create it. However it can be sent using any JMS provider, not just the 
* JMS provider used to create it.

The second sentence above has been included because it would be confusing and inconsistent to mention that a message may be sent using a different session and not also mention the even more significant fact that a message may be sent using a different JMS provider.

The six message-creation methods on the proposed new MessagingContext interface have been modified to include the following additional javadoc comments:

* The message object returned may be sent using any session or messaging context. 
* It is not restricted to being sent using the messaging context used to create it.
* <p>
* The message object returned may be optimised for use with the JMS provider
* used to create it. However it can be sent using any JMS provider, not just the 
* JMS provider used to create it.

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. The changes for this issue are as follows:

Section 4.4.5. "Optimized Message Implementations" has been updated to state:

A session provides the following methods to create messages: createMessage, createBytesMessage, createMapMessage, createObjectMessage, createStreamMessage and createTextMessage.

These methods allow the JMS provider to create message implementations which are optimized for that particular provider and allow the provider to minimize its overhead for handling messages.

A session provides message create methods that use provider-optimized implementations. This allows a provider to minimize its overhead for handling messagesHowever .the fact that these methods are provided on a session does not mean that messages must be sent using a message producer created from the same session. Messages may be sent using any session, not just the session used to create the message.

Furthermore, Sessions must be capable of sending all JMS messages regardless of how they may be implemented. See section 3.12 "Provider Implementations of JMS Message Interfaces".

Section 11.5.5 "Clarification: message may be sent using any session" is the change log for this clarification.





[JMS_SPEC-51] New methods to replace Session.createDurableSubscriber() and return a MessageConsumer Created: 16/Sep/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-171 New methods to replace Session.create... Closed
Tags: ed20-added

 Description   

In JMS 1.1 the following two methods on Session return a TopicSubscriber.

TopicSubscriber createDurableSubscriber(Topic topic)

TopicSubscriber createDurableSubscriber(Topic topic, java.lang.String name, java.lang.String messageSelector, boolean noLocal)

These are the only "domain-independent" methods in the whole JMS 1.1 API which depend on a "domain-specific" interface. This dependency prevents the "domain-specific" interfaces being removed from JMS as proposed in JMS_SPEC-47.

It is therefore proposed to modify these methods to return a MessageConsumer. Since this is a supertype of TopicSubscriber it should not break existing applications.



 Comments   
Comment by Nigel Deakin [ 20/Sep/11 ]

It has been pointed out that the above proposal would require existing applications would need to cast the MessageConsumer that is returned down to a TopicSubscriber.

This suggests that we need to provide new versions of this method which return a MessageConsumer. This would allow us to mark the old methods as deprecated and propose them for removal.

We can't simply add new methods with the same name which return a MessageConsumer since the compiler would consider these duplicate methods.

Two options have been proposed:

Option 1

We could define new methods named createConsumer:

MessageConsumer createConsumer(Topic topic, java.lang.String name)

MessageConsumer createConsumer(Topic topic, java.lang.String name, java.lang.String messageSelector, boolean noLocal)

However there's potential confusion between the first of those methods and the existing method to create an ordinary consumer with a message selector:

MessageConsumer createConsumer(Destination destination, java.lang.String messageSelector)

The compiler would allow it, but if the user tried to create a durable subscription using a variable which was really a Topic but which was declared as Destination then the wrong method would be called.

It could also be argued that removing the word "durable" from the method name would be confusing for users.

We could either live with these drawbacks, or we could use:

Option 2

We could define new methods named createDurableConsumer:

MessageConsumer createDurableConsumer(Topic topic, java.lang.String name)

MessageConsumer createDurableConsumer(Topic topic, java.lang.String name, java.lang.String messageSelector, boolean noLocal)

Comment by Nigel Deakin [ 03/Jan/12 ]

Following a discussion within the JSR 343 expert group I've selected option 2 above.

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

The updated Javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
(Two createDurableConsumer methods have been added to Session. See below for javadocs).

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, but the only place I've changed is 6.11.1 "Durable TopicSubscriber" and 11.5.9 "createDurableConsumer" which is the change log).

Here are the two new methods:

    /** Creates a durable subscription with the specified name on the
      * specified topic, and creates a <code>MessageConsumer</code> 
      * on that durable subscription.
      * <P>
      * If the durable subscription already exists then this method
      * creates a <code>MessageConsumer</code> on the existing durable
      * subscription.
      * <p>
      * A durable subscription is used by a client which needs to receive
      * all the messages published on a topic, including the ones published 
      * when there is no <code>MessageConsumer</code> or <code>TopicSubscriber</code> associated with it. 
      * The JMS provider retains a record of this durable subscription 
      * and ensures that all messages from the topic's publishers are retained 
      * until they are delivered to, and acknowledged by,
      * a <code>MessageConsumer</code> or <code>TopicSubscriber</code> on this durable subscription
      * or until they have expired.
      * <p>
      * A durable subscription will continue to accumulate messages 
      * until it is deleted using the <code>unsubscribe</code> method. 
      * <p>
      * A durable subscription which has a <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * associated with it is described as being active. 
      * A durable subscription which has no <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * associated with it is described as being inactive. 
      * <p>
      * Only one session at a time can have a
      * <code>MessageConsumer</code> or <code>TopicSubscriber</code> for a particular durable subscription.
      * <p>
      * A durable subscription is identified by a name specified by the client
      * and by the client identifier if set. If the client identifier was set
      * when the durable subscription was first created then a client which 
      * subsequently wishes to create a <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * on that durable subscription must use the same client identifier.
      * <p>
      * A client can change an existing durable subscription by calling
      * <code>createDurableConsumer</code> 
      * with the same name and client identifier (if used),
      * and a new topic and/or message selector. 
      * Changing a durable subscriber is equivalent to 
      * unsubscribing (deleting) the old one and creating a new one.
      *
      * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
      * @param name the name used to identify this subscription
      *  
      * @exception JMSException if the session fails to create the durable subscription 
      *            and <code>MessageConsumer</code> due to some internal error.
      * @exception InvalidDestinationException if an invalid topic is specified.
      *
      * @since 2.0
      */ 
     MessageConsumer createDurableConsumer(Topic topic, String name) throws JMSException;
     /** Creates a durable subscription with the specified name on the
      * specified topic, and creates a <code>MessageConsumer</code> 
      * on that durable subscription, specifying a message 
      * selector and whether messages published by its
      * own connection should be delivered to it.
      * <P>
      * If the durable subscription already exists then this method
      * creates a <code>MessageConsumer</code> on the existing durable
      * subscription.
      * <p>
      * A durable subscription is used by a client which needs to receive
      * all the messages published on a topic, including the ones published 
      * when there is no <code>MessageConsumer</code> or <code>TopicSubscriber</code> associated with it. 
      * The JMS provider retains a record of this durable subscription 
      * and ensures that all messages from the topic's publishers are retained 
      * until they are delivered to, and acknowledged by,
      * a <code>MessageConsumer</code> or <code>TopicSubscriber</code> on this durable subscription
      * or until they have expired.
      * <p>
      * A durable subscription will continue to accumulate messages 
      * until it is deleted using the <code>unsubscribe</code> method. 
      * <p>
      * A durable subscription which has a <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * associated with it is described as being active. 
      * A durable subscription which has no <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * associated with it is described as being inactive. 
      * <p>
      * Only one session at a time can have a
      * <code>MessageConsumer</code> or <code>TopicSubscriber</code> for a particular durable subscription.
      * <p>
      * A durable subscription is identified by a name specified by the client
      * and by the client identifier if set. If the client identifier was set
      * when the durable subscription was first created then a client which 
      * subsequently wishes to create a <code>MessageConsumer</code> or <code>TopicSubscriber</code>
      * on that durable subscription must use the same client identifier.
      * <p>
      * A client can change an existing durable subscription by calling
      * <code>createDurableConsumer</code> 
      * with the same name and client identifier (if used),
      * and a new topic and/or message selector. 
      * Changing a durable subscriber is equivalent to 
      * unsubscribing (deleting) the old one and creating a new one.
      *
      * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
      * @param name the name used to identify this subscription
      * @param messageSelector only messages with properties matching the
      * message selector expression are delivered.  A value of null or
      * an empty string indicates that there is no message selector 
      * for the message consumer.
      * @param noLocal if set, inhibits the delivery of messages published
      * by its own connection
      *  
      * @exception JMSException if the session fails to create the durable subscription 
      *                         and <code>MessageConsumer</code> due to some internal error.
      * @exception InvalidDestinationException if an invalid topic is specified.
      * @exception InvalidSelectorException if the message selector is invalid.
      *
      * @since 2.0
      */ 
      MessageConsumer createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException;   
Comment by Nigel Deakin [ 03/Jan/12 ]

I've changed the summary line for this issue from
Change Session.createDurableSubscriber() to return a MessageConsumer
to
New methods to replace Session.createDurableSubscriber() and return a MessageConsumer.

This reflects the decision not to change the existing methods (to preserve backwards compatibility) but to add new methods.





[JMS_SPEC-50] Clarify that JMS providers must implement both P2P and Pub-Sub Created: 19/Aug/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: ed20-added

 Description   

In the JMS Specification 1.1, Chapter 1.3 "What Is Required by JMS" says:

Providers of JMS point-to-point functionality are not required to provide publish/subscribe functionality and vice versa.

However Java EE 6 in Section EE.2.7.8 "Java™ Message Service (JMS)" says

The Java Message Service is a standard API for messaging that supports reliable point-to-point messaging as well as the publish-subscribe model. This specification requires a JMS provider that implements both point-to-point messaging as well as publish-subscribe messaging.

It is proposed to change the JMS specification to bring it in to line with Java EE, and make it mandatory for a standalone JMS provider to implement both point-to-point messaging (Queues) and publish-subscribe messaging (Topics).



 Comments   
Comment by Nigel Deakin [ 16/Sep/11 ]

This has been agreed in principle by the JSR 343 Expert group and will be in the JMS 2.0 Early Draft.

Comment by Nigel Deakin [ 03/Jan/12 ]

I've now updated the draft spec to reflect this decision (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. The only sections affected are section 1.3 "What Is Required by JMS" and section 11.5.10 which is the change log for this issue.)





[JMS_SPEC-49] Improve specification of ExceptionListener Created: 16/Aug/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: ed20-added

 Description   

The use of a ExceptionListener is described in the JMS 1.1 specification in section 4.3.8 "ExceptionListener":

If a JMS provider detects a problem with a connection, it will inform the connection's ExceptionListener, if one has been registered. To retrieve an ExceptionListener, the JMS provider calls the connection's getExceptionListerer()
method. This method returns the ExceptionListener for the connection. If no ExceptionListener is registered, the value null is returned. The connection can then use the listener by calling the listener 's onException() method, passing it a JMSException describing the problem.

This allows a client to be asynchronously notified of a problem. Some connections only consume messages, so they would have no other way to learn their connection has failed.

A Connection serializes execution of its ExceptionListener.

A JMS provider should attempt to resolve connection problems itself prior to notifying the client of them.

The exceptions delivered to ExceptionListener are those that have no other place to be reported. If an exception is thrown on a JMS call it, by definition, must not be delivered to an ExceptionListener (in other words, ExceptionListener is not for the purpose of monitoring all exceptions thrown by a connection).

Despite this, there have been a number of questions about the use of an ExceptionListener. These questions are listed below, together with proposed answers. It is proposed that the answers to these questions be incorporated into the specification.

Question 1: When Connection.stop() or Connection.close() is called, must these methods wait until any active calls to ExceptionListeners for that connection have returned, in the same way that they must wait until any active call to MessageListeners have returned?

Proposed answer: No.

Question 2: Is there any restriction on the use of Connection.stop() or Connection.close() from an ExceptionListener?

Proposed answer: No.

Question 3: What does "a Connection serializes execution of its ExceptionListener" mean?

Proposed answer: This means that if a connection encounters multiple problems and therefore needs to call its ExceptionListener multiple times, then it will only invoke onMessage() from one thread at a time. Note however that if the same ExceptionListener is registered with different connections then it is undefined whether these connections could call onMessage() from different threads simultaneously.

Question 4: What is the effect of ExceptionListener.onException() being slow or blocking?

Proposed answer: This is undefined. There is no requirement that any other operation need block until ExceptionListener.onException() has return, apart from other calls to ExceptionListener.onException() for the same connection.



 Comments   
Comment by Nigel Deakin [ 16/Sep/11 ]

I've now written the following draft text:

Changes to section 4.3.8 "ExceptionListener":

After:

A Connection serializes execution of its ExceptionListener.

Insert:

This means that if a connection encounters multiple problems and therefore needs to call its ExceptionListener multiple times, then it will only invoke onMessage() from one thread at a time. Note however that if the same ExceptionListener is registered with multiple connections then it is undefined whether these connections could call onMessage() from different threads simultaneously.

At the end, insert:

There is no restriction on the use of the JMS API by the listener's onException method. However since that method will only be called when there is a serious problem with the connection, any attempt to use that connection may fail and cause exceptions.

New text in the javadoc for ExceptionListener:

There is no restriction on the use of the JMS API by the listener's onException method. However since that method will only be called when there is a serious problem with the connection, any attempt to use that connection may fail and cause exceptions.

A Connection serializes execution of its ExceptionListener. This means that if a connection encounters multiple problems and therefore needs to call its ExceptionListener multiple times, then it will only invoke onMessage from one thread at a time. Note however that if the same ExceptionListener is registered with multiple connections then it is undefined whether these connections could call onMessage from different threads simultaneously.

New text in the javadoc for Connection.stop():

For the avoidance of doubt, if an exception listener for this connection is running when stop is invoked, there is no requirement for the exception listener to return before the stop method returns.

New text in the javadoc for Connection.close():

For the avoidance of doubt, if an exception listener for this connection is running when stop is invoked, there is no requirement for the exception listener to return before the stop method returns.

Comment by Nigel Deakin [ 03/Jan/12 ]

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

The updated Javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
(See updated javadocs for Connection.stop, Connection.close, Session.close, MessagingContext.stop, MessagingContext.close)

  • The javadoc comments for the stop and close methods on the Connection interface have been amended to clarify that, if an exception listener for the connection is running when stop or close are invoked, there is no requirement for the stop or close call to wait until the exception listener has returned before it may return.
  • Similarly, the javadoc comment for the close method on the Session interface has been amended to clarify that, if an exception listener for the session's connection is running when close is invoked, there is no requirement for the close call to wait until the exception listener has returned before it may return.
  • The javadoc comments for the stop and close methods on the MessagingContext interface have been amended to clarify that, if an exception listener for the MessagingContext's connection is running when stop or close are invoked, there is no requirement for the stop or close call to wait until the exception listener has returned before it may return.

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.

The changes for this issue are to section 4.3.8 "Exception Listener":

  • The existing text which states that a connection "serializes execution of its ExceptionListener" has been extended to explain what this means.
  • A note has been added to state that there are no restrictions on the use of the JMS API by the listener's onException method.

In addition a new section 11.5.11 has been added which is the change log for this issue.





[JMS_SPEC-48] Specify that connection.stop() or close() may not be called from a MessageListener Created: 16/Aug/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: None
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: ed20-added

 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.



 Comments   
Comment by Nigel Deakin [ 16/Sep/11 ]

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.

Comment by Nigel Deakin [ 04/Jan/12 ]

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.
Comment by Nigel Deakin [ 05/Dec/12 ]

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.

Comment by Nigel Deakin [ 05/Dec/12 ]

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.

Comment by Nigel Deakin [ 12/Feb/13 ]

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.
Comment by Nigel Deakin [ 20/Mar/13 ]

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





[JMS_SPEC-45] Clarify and improve Connection.createSession Created: 09/Aug/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: None
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-172 Implement two new Connection.createS... Closed
Tags: ed20-added

 Description   

In the JMS 1.1 specification, the following method on a javax.jms.Connection is used to create a javax.jms.Session:

Session createSession(boolean transacted, int acknowledgeMode) throws JMSException

where transacted may be set to true or false and
acknowledgeMode may be set to Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, and Session.DUPS_OK_ACKNOWLEDGE

(There are similar methods on javax.jms.QueueConnection and javax.jms.TopicConnection: this whole issue applies to all three.)

This is a rather confusing method for several reasons:

  • It uses two arguments to define a single aspect of the session
  • In a Java EE transaction, both arguments are ignored anyway
  • In a Java EE unspecified transaction context, the meaning of the arguments is undefined and unclear

It uses two arguments to define the same thing

This method uses two arguments to define what is in practice a single aspect of the session with four possibilities: if transacted is set to false then the session is non-transacted and the acknowledgeMode argument defines which of three kinds of acknowledgement are used when receiving messages. If transacted is set to true then the acknowledgeMode argument is ignored.

This is inconsistent with the method Session.getAcknowledgeMode() which returns one of four values: Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, or Session.DUPS_OK_ACKNOWLEDGE if the session is not transacted and Session.SESSION_TRANSACTED if the session is transacted.

This also leads to code which is potentially misleading, since if transacted is false the user still has to set acknowledgeMode to some value even if it is ignored, which leads to code such as

Session session = connection.createSession(true,Session.AUTO_ACKNOWLEDGE);

Some developers like to use

Session session = connection.createSession(true,Session.SESSION_TRANSACTED);

though this is still misleading since since if transacted was set to true then the second argument is ignored anyway, and if transacted is false then setting acknowledgeMode to Session.SESSION_TRANSACTED would be an error.

In a Java EE transaction, both arguments are ignored anyway

In a Java EE transaction none of the four options listed above are permitted. Instead, both arguments to connection.createSession are ignored and a global transaction is used.

The EJB 3.1 Specification, section 13.3.5 "use of JMS APIs in Transactions" states that, in a container-managed or bean-managed transaction,

Because the container manages the transactional enlistment of JMS sessions on behalf of a bean, the parameters of the createSession(boolean transacted, int acknowledgeMode) method createQueueSession... are ignored.

This also applies to web applications. The Java EE platform spec, Section EE.6.7 "Java Message Service (JMS) 1.1 Requirements" specifies that

The behavior of a JMS provider should be the same in both the EJB container and the web container." It continues "The EJB specification describes restrictions on the use of JMS in an EJB container, as well as the interaction of JMS with transactions in an EJB container. Applications running in the web container should follow the same restrictions.

Instead, the receiving and sending of messages must be part of the container-managed or bean-managed transaction, and the transaction will be committed or rolled back in the way that container-managed or bean-managed transactions are committed or rolled back.

  • Container-managed transactions are either committed when the appropriate business method completes or are rolled back using EJBContext.setRollbackOnly.
  • Bean-managed transactions are either committed using UserTransaction.commit or rolled back using UserTransaction.rollback.

How explicit is the EJB specification about all this? In addition to specifying that in a transactional context the arguments to Connection.createSession are ignored, it also states the following:

  • Section 13.3.5 states that "within a transaction" the bean should not use the acknowledge method. This therefore covers both container-managed and bean-managed transactions.
  • Section 13.3.3 states that in the case of bean-managed transactions, the bean must not invoke the commit or rollback methods on the javax.jms.Session interface.
  • Section 13.3.4 states that in the case of container-managed transactions, the bean must not "use any resource-managed specific transaction management methods that would interfere with the container's demarcation of transaction boundaries" and again must not invoke the commit or rollback methods on the javax.jms.Session interface.
  • Section 13.1.1 states that in the case of bean-managed transactions, "all resource manager accesses between the UserTransaction.begin and UserTransaction.commit calls are part of a transaction", thereby apparently ruling out the use of non-transacted sessions using auto-acknowledgement and dups-ok-acknowledgement as well as those using client-acknowledgement.
  • Section 13.1.1 also states in a container-managed transaction the transaction demarcation depends on the transaction attributes of the bean method. It doesn't explicitly state that all resource manager accesses should be part of this transaction, but this is implied.

In a Java EE unspecified transaction context, the meaning of the arguments undefined and unclear

The previous section only relates to the use of the JMS API "in transactions". It does not cover how the JMS API should behave when there is no current container-managed or bean-managed transaction. That is, when there is an unspecified transaction context.

The EJB 3.1 Specification, section 13.6.5 "Handling of Methods that run with an unspecified transaction context" defines an "unspecified transaction context" as covering "the cases in which the EJB architecture does not fully define the transaction semantics of an enterprise bean method execution".

Section 13.6.5 goes on to give a list of examples of when an "unspecified transaction context" may arise. All the cases given are for container-managed transactions, leaving an ambiguity about what an "unspecified transaction context" means when using bean-managed transactions. An obvious interpretation is that that if a bean is configured to use bean-managed transactions, then business methods or onMessage() code executed before a call to userTransaction.begin(), or after a call to UserTransaction.commit or UserTransaction.rollback, is executed in an unspecified transaction context, as is any code executed in the four bean lifecycle callback methods listed in 13.6.5 (PostConstruct,PreDestroy, PostActivate, or PrePassivate). However this is not explicitly stated in the EJB spec.

So, what does the EJB spec say should happen in an "unspecified transaction context"?

Section 13.6.5 is written with all resource managers (not just JMS) in mind, and states that

"The EJB specification does not prescribe how the container should manage the execution of a method with an unspecified transaction context—the transaction semantics are left to the container implementation.

It goes on to give some options, which include treating "each call... to a resource manager as a single transaction", merging multiple calls into a single transaction, or accessing the resource manager "without a transaction context".

Now in the case of JMS the application has a way to give the container a hint as to what behaviour they desire: the arguments to {[createSession}}. So it would seem reasonable to follow these.

However the EJB 3.1 specification, section 13.3.5 does explicitly state that "The Bean Provider should not use the JMS acknowledge method either within a transaction or within an unspecified transaction context. Message acknowledgment in an unspecified transaction context is handled by the container.

It is curious that although Session.acknowledge is prohibited in a unspecified transaction context, Session.commit is not, even though both perform message acknowledgement. If the former is invalid, then the latter must be as well.

This means that within an unspecified transaction context:

  • a non-transacted session using client acknowledgement is explicitly prohibited
  • a (local) transacted session is implicitly prohibited
  • a non-transacted session is permitted, with both client-acknowledgement and dups-ok-acknowledgement (which is an optimised version of auto-acknowledgement) allowed.

Summary

So in Connection.createSession (and QueueConnection.createQueueSession and TopicConnection.createTopicSession we have a method which offers different options depending on the context in which it is used:

  • In a Java EE transaction there are no options: the session is part of a transaction managed by the container and the application has no choice on the matter.
  • In a Java EE unspecified transaction context there are two options:
    • non-transacted session with auto-acknowledgement
    • non-transacted session with dups-ok-acknowledgement
  • In a Java SE environment there are four options:
    • non-transacted session with auto-acknowledgement
    • non-transacted session with dups-ok-acknowledgement
    • non-transacted session with client-acknowledgement
    • transacted session

So, in the light of all this, what are the problems?

  • the special behaviour of this method in a Java EE transaction is not mentioned anywhere in the JMS specification or in the javadocs, which means that users are surprised when they discover that the arguments to createSession are ignored. Fortunately, however, the required behaviour is clearly defined in the EJB specification.
  • the special behaviour in a Java EE unspecified transaction context is also not mentioned anywhere in the JMS specification or in the javadocs. Unfortunately, the required behaviour is not explicitly described in the EJB specification but has to be pieced together from various different sections, as in the analysis above. This needs to be confirmed and stated explicitly.
  • the actual API for createSession, with its two arguments, not only does not reflect the four options available in the normal Java SE case, it does not reflect the zero options available in the Java EE transaction case or the two options available in the Java EE unspecified transaction context case. However when compared the the two preceding issues this is perhaps not such a major issue.

Proposals

It is proposed that

  • The JMS specification and javadocs be updated to describe how createSession behaves in a Java EE applicaiton, both in a transaction and in an unspecified transaction context. The former case will be a restatement of the existing EJB spec, the latter case will be intended to remove any ambiguities in the EJB spec along the lines of the analysis above.
  • A new method be provided in a javax.jms.Connection
Session createSession(int sessionMode) throws JMSException

In a normal Java SE environment sessionMode may be set to 
Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, Session.DUPS_OK_ACKNOWLEDGE 
or Session.TRANSACTED

In a Java EE transaction, sessionMode is ignored.

In a Java EE undefined transaction context, sessionMode may be set to 
Session.AUTO_ACKNOWLEDGE or Session.DUPS_OK_ACKNOWLEDGE only.
  • A further new method be provided in a javax.jms.Connection
Session createSession() throws JMSException

This method is  particularly intended for use in a Java EE environment,
though it may also be used in a normal Java SE environment.

In a normal Java SE environmentm this is equivalent to calling createSession(Session.AUTO_ACKNOWLEDGE)

In a Java EE transaction, the session will be part of a transaction managed by the container. 

In a Java EE undefined transaction context, the session will have a sessionMode of Session.AUTO_ACKNOWLEDGE.
  • The existing method createSession(boolean transacted,int acknowledgeMode will remain with a note added to encourage applications to use the other two methods. In accordance with Java EE policy it will remain in the API indefinitely, and will not be formally marked as @Deprecated.


 Comments   
Comment by Nigel Deakin [ 23/Sep/11 ]

Description updated to propose new method Session createSession() with no arguments for use in a Java EE environment.

Comment by Nigel Deakin [ 30/Sep/11 ]

Updated to propose that the existing method createSession(boolean transacted,int acknowledgeMode will be formally marked as @Deprecated

Comment by Nigel Deakin [ 18/Oct/11 ]

Do we need a new method getSessionMode()?

There is already a method getAcknowledgeMode() which Session.AUTO_ACKNOWLEDGE, Session.CLIENT_ACKNOWLEDGE, Session.DUPS_OK_ACKNOWLEDGE or Session.TRANSACTED as appropriate. It therefore already returns the session mode though the name is misleading.

We have lived with this mis-named method for a long time so I don't think it is necessary to create a new method getSessionMode() which is functionally identical but more appropriately named. We could never remove the old method, so this would simply clutter up the API whilst confusing the user.

We should therefore stick with getAcknowledgeMode and change the javadoc documentation to clarify that what it returns is the same as the session mode.

Comment by Nigel Deakin [ 13/Jan/12 ]

Before we finalise the above proposals I'd like to explore in more detail exactly how createSession (and other methods in the JMS API) are required to behave in a Java EE environment. Before we can clarify the use of this method we need to be able to answer the following questions.

(I've already suggested answers to some, but not all, of these questions in my proposals above. However I think it's very important to get this right and it's work revisiting them. In addition, there are several additional issues which need to be answered before users can unambiguously know how this method is required to behave.)

1. JMS resources which do not support JTA transactions

In the EJB 3.1 specification, section 13.3.5 "use of JMS APIs in Transactions" states that:

Because the container manages the transactional enlistment of JMS sessions on behalf of a bean, the parameters of the createSession(boolean transacted, int acknowledgeMode) method createQueueSession... are ignored."

The reference to "transaction enlistment" appears to be a reference to JTA transactions managed by the application server, which may be "bean-managed" (using the UserTransaction API to begin and commit the transaction) or, for EJBs, "container-managed".

Q1.1: Does the sentence quoted above mean that the JMS session must participate in the transaction, or is it permitted to create a session which does not participate in the transaction.

There have been suggestions that connections which are "non-managed" or "non-JCA" connections are not required to participate in the transaction. However there is no mention of such cases in the EJB spec.

Q1.2: Is this permitted?

Q1.3: Are such cases also exempt from the requirement that the arguments to createSession be ignored?

2. When there isn't a container-managed or bean-managed transaction.

I'd also like to explore the case where there isn't a container-managed or bean-managed transaction. The EJB 3.1 Specification, section 13.6.5 "Handling of Methods that run with an unspecified transaction context" contains some guidance about the transaction semantics in such a case.

Q2.1: What is an "unspecified transaction context"? Does this mean any case where there is not an active JTA transaction, such as where the transaction attribute is set to "Never"? Does it include the BMT case before userTransaction.begin() or after userTransaction.commit()? Does it include all of the EJB callback methods?

3. The ban on calling acknowledge()

Section 13.3.5 of the EJB spec, "Use of JMS APIs in Transactions", states that "The Bean Provider should not use the JMS acknowledge method either within a transaction or within an unspecified transaction context. Message acknowledgment in an unspecified transaction context is handled by the container."

Q3.1: What is the reason for this restriction? Is it necessary?

Q3.2: If acknowledge is called in a Java EE environment, what should happen? Should an exception be thrown?

Q3.3: Do these restrictions on explicit message acknowledgement also apply to local transactions, given that calling session.commit has the effect of acknowledging all messages received in the local transaction?

4. Bean-managed transactions

In a application that uses bean-managed transactions, what happens if the session is created before the transaction is started? Consider the following:

Session session=connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(destination);
producer.send(message1);
userTransaction.begin();
producer.send(message2);
. . .
userTransaction.commit();

Q4.1: Is this valid?

Q4.2: Is message1 sent immediately or is it committed as part of the user transaction?

Q4.3: Is message2 sent immediately or is it committed as part of the user transaction?

Comment by Nigel Deakin [ 24/Jan/12 ]

I've now updated the API, javadocs and the draft spec in accordance with the proposals made above.

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

  • New methods Connection.createSession(int sessionMode) and Connection.createSession() which create a Session.
  • New javadoc comment for the existing method Connection.createSession(boolean transacted, int acknowledgeMode) which creates a Session.
  • New javadoc comments for the various methods on ConnectionFactory and MessagingContext which create a MessagingContext

The updated draft spec is here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf

  • Updated section 1.4.7 "Java Platform, Enterprise Edition (Java EE)"
  • Deleted section 1.4.8 "Integration of JMS with the EJB Components"
  • New chapter 12 "Use of JMS API in Java EE applications"
  • New section 12.2 "Use of JMS API in Java EE applications"
  • New sections 11.5.14 & 11.5.15 which are the change log for these changes

(I'll come back later and formally answer the questions I made in the previous comment)

Comment by Nigel Deakin [ 24/Jan/12 ]

I have updated the description above to remove the statement that the existing method createSession(boolean transacted,int acknowledgeMode will be marked as @Deprecated and that the javadocs will warn that this method may be removed in the future.

The Java EE Backwards Compatibility Requirements do not allow us to use @Deprecated, not do they allow us to remove existing methods from the API.

Instead the javadoc comment for this method will simply have a note which encourages applications to use the other two createSession methods instead.

Comment by Nigel Deakin [ 20/Mar/13 ]

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





[JMS_SPEC-44] New API to specify delivery delay Created: 08/Aug/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0PD, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-166 Implement Delivery Delay Closed
blocks MQ-184 Topic subscription matching time/Queu... Closed
Tags: ed20-added

 Description   

This issue was raised by members of the JSR 343 Expert Group and is logged here to allow the issue to be discussed and tracked.

This is a proposal to allow a JMS client to specify a delivery delay when sending a message. If a delivery delay has been specified for a message then the message will not be delivered to any consumers until after the delivery delay has expired.

This feature has also been referred to as "delivery time", "timed messages" or "future messages".

The API for delivery delay is intended to be similar to that for the existing timeToLive property of a MessageProducer and the corresponding JMSExpiration header field of a Message.

There would be new methods on javax.jms.MessageProducer:

 
public void setDeliveryDelay(long deliveryDelay) throws javax.jms.JMSException;

This sets the minimum length of time in milliseconds from its dispatch time 
that a produced message should be retained by the messaging system before 
delivery to a consumer. 

If the delivery delay is greater then the time to live then the message 
will always be expired before delivery.
public long getDeliveryDelay()

Returns the minimum length of time in milliseconds from its dispatch time 
that a produced message should be retained by the messaging system before delivery to a consumer. 

There would be new methods on javax.jms.Message:

public long getJMSDeliveryTime() throws JMSException

Returns the message's delivery time. 

When a message is sent, the JMSDeliveryTime header field is left unassigned. 
After completion of the send or publish method, it holds the minimum delivery time time 
of the message. This is the sum of the delivery delay value specified by the client and 
the GMT at the time of the send or publish. 

When the delivery delay is specified as zero, JMSDeliveryTime is set to zero to indicate
that there is no delivery delay.

A provider must not deliver a message before the defined delivery time has been reached.
public void setJMSDeliveryTime(long deliveryTime) throws JMSException

Sets the message's delivery time.

JMS providers set this field when a message is sent. 
This method can be used to change the value for a message that has been received. 

Note that this public method is not intended for use by clients but is needed to allow providers to set this value on a message implemented by a different provider. There is a separate issue to clarify this behaviour: JMS_SPEC-34



 Comments   
Comment by Nigel Deakin [ 19/Dec/11 ]

Following the various comments, the proposed API is as follows:

New methods on MessageProducer

    /** Sets the default minimum length of time in milliseconds from its dispatch time
     * before a produced message becomes visible on the target destination and available
     * for delivery to consumers.  
     *
     * <P>deliveryDelay is set to zero by default.
     *
     * @param deliveryDelay the delivery delay in milliseconds.
     *
     * @exception JMSException if the JMS provider fails to set the delivery
     *                         delay due to some internal error.
     *
     * @see javax.jms.MessageProducer#getDeliveryDelay
     * @see javax.jms.Message#DEFAULT_DELIVERY_DELAY
     * 
     * @since 2.0
     */
  
   void setDeliveryDelay(long deliveryDelay) throws JMSException;      
   
   /** Gets the default minimum length of time in milliseconds from its dispatch time
    * before a produced message becomes visible on the target destination and available
    * for delivery to consumers.  
    *
    * @return the delivery delay in milliseconds.
    *
    * @exception JMSException if the JMS provider fails to get the delivery 
    *                         delay due to some internal error.
    *
    * @see javax.jms.MessageProducer#setDeliveryDelay
    * 
    * @since 2.0
    */ 

  long getDeliveryDelay() throws JMSException;   

New methods on MessagingContext:

/** Sets the default minimum length of time in milliseconds from its dispatch time
 * before a produced message becomes visible on the target destination and available
 * for delivery to consumers.  
 *
 * <P>deliveryDelay is set to zero by default.
 *
 * @param deliveryDelay the delivery delay in milliseconds.
 *
 * @exception JMSRuntimeException if the JMS provider fails to set the delivery
 *                         delay due to some internal error.
 *
 * @see javax.jms.MessagingContext#getDeliveryDelay
 * @see javax.jms.Message#DEFAULT_DELIVERY_DELAY
 */

void setDeliveryDelay(long deliveryDelay);      

/** Gets the default minimum length of time in milliseconds from its dispatch time
* before a produced message becomes visible on the target destination and available
* for delivery to consumers.  
*
* @return the delivery delay in milliseconds.
*
* @exception JMSRuntimeException if the JMS provider fails to get the delivery 
*                         delay due to some internal error.
*
* @see javax.jms.MessagingContext#setDeliveryDelay
*/ 

long getDeliveryDelay();   

New methods on Message:

    /** Gets the message's delivery time value.
     *  
     * <P>When a message is sent, the <CODE>JMSDeliveryTime</CODE> header field 
     * is left unassigned. After completion of the <CODE>send</CODE> or 
     * <CODE>publish</CODE> method, it holds the delivery time of the
     * message. This is the sum of the deliveryDelay value specified by the
     * client and the GMT at the time of the <CODE>send</CODE> or 
     * <CODE>publish</CODE>.
     *
     * <P>A message's delivery time is the earliest time when a provider may
     * make the message visible on the target destination and available for
     * delivery to consumers. 
     *
     * <P>Clients must not receive messages before the delivery time has been reached.
     * 
     * @return the message's delivery time, which is the sum of the deliveryDelay 
     * value specified by the client and the GMT at the time of the <CODE>send</CODE> or 
     * <CODE>publish</CODE>.
     *  
     * @exception JMSException if the JMS provider fails to get the message 
     *                         expiration due to some internal error.
     *
     * @see javax.jms.Message#setJMSDeliveryTime(long)
     * 
     * @since 2.0
     */ 
   long getJMSDeliveryTime() throws JMSException;


   /** Sets the message's delivery time value.
     *
     * <P>This method is for use by JMS providers only to set this field 
     * when a message is sent. This message cannot be used by clients 
     * to configure the delivery time of the message. This method is public
     * to allow one JMS provider to set this field when sending a message
     * whose implementation is not its own.
     *  
     * @param expiration the message's delivery time value
     *  
     * @exception JMSException if the JMS provider fails to set the delivery 
     *                         time due to some internal error.
     *
     * @see javax.jms.Message#getJMSDeliveryTime() 
     * 
     * @since 2.0
     */ 
   void setJMSDeliveryTime(long deliveryTime) throws JMSException;    

A new static constant on Message:

    /** The message producer's default delivery delay is zero.
     * @since 2.0
     */
    static final long DEFAULT_DELIVERY_DELAY = 0;  
Comment by Nigel Deakin [ 19/Dec/11 ]

I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes).

The updated Javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
(See two new methods on MessageProducer, two new methods on MessagingContext, and two new methods and a static constant on Message.

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. The changes for this issue are as follows:

A new section 3.4.13 "JMSDeliveryTime" which states:

When a message is sent, its delivery time is calculated as the sum of the delivery delay value specified on the send method and the current GMT value. On return from the send method, the message’s JMSDeliveryTime header field contains this value. When a message is received its JMSDeliveryTime header field contains this same value.

A message's delivery time is the earliest time when a provider may make the message visible on the target destination and available for delivery to consumers.

Clients must not receive messages before the delivery time has been reached.

A new section 4.13 "Delivery delay" which states:

A client can specify a delivery delay value in milliseconds for each message it sends. This value defines a message delivery time which is the sum of the message’s delivery delay and the GMT it is sent (for transacted sends, this is the time the client sends the message, not the time the transaction is committed).

A message's delivery time is the earliest time when a JMS provider may make the message visible on the target destination and available for delivery to consumers. The provider must not deliver messages before the delivery time has been reached.

For more information on message delivery delay, see Section 3.4.13 "JMSDeliveryTime".

Section 4.4.10.2 "Order of message sends" has been updated to state that messages with a later delivery time may be delivered after messages with an earlier delivery time.

Section 4.4.11 "Message Acknowledgement" has been updated to state that when a session's recover method is called the messages it now delivers may be different from those that were originally delivered due to the delivery of messages which could not previously be delivered as they had not reached their specified delivery time.

Section 4.6 "Message Producer" has been updated to mention that a client may now define a default delivery delay for messages sent by a producer.

Section 11.5.4 "Delivery delay" is the change log for this feature.

A deliberate decision was made to leave section 3.4.12 "Overriding message header fields" unchanged. This means that the spec will not permit an administrator to configure JMS to override the client specified values for JMSDeliveryTime.

Comment by Nigel Deakin [ 21/Sep/12 ]

Further updates have been made. For an up-to-date description of this feature, see section 4.12 "Delivery delay" of the JMS 2.0 public draft.

Just one issue remains: this is described in section A.3.1 in the JMS 2.0 early draft.





[JMS_SPEC-43] New API to send a message with async acknowledgement from server Created: 05/Aug/11  Updated: 26/Nov/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0PD, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-174 Implement new API to send a message w... Closed
Duplicate
is duplicated by JMS_SPEC-12 Message producer confirms aka async m... Resolved
Tags: ed20-added

 Description   

This is a proposal for a new API which will send a message and return immediately without blocking until an acknowledgement has been received from the server. Instead, when the acknowledgement is received, an asynchronous callback will be invoked.

Here is a suggested API:

There would be a new method on javax.jms.MessageProducer:

public void setAcknowledgeListener(AcknowledgeListener listener);

This would change the behaviour of any subsequent call to any of the four send methods so that they would return without blocking until an acknowledgement has been received from the server. When the acknowledgement is received, the callback method on the AcknowledgeListener is received.

This feature will only be available for non-transacted sessions. if setAcknowledgeListener was called on a transacted session then a javax.jms.IllegalStateException would be thrown.

 
package javax.jms;
import javax.jms.Message;

public interface AcknowledgeListener {
	public void onAcknowledge(Message message);
}



 Comments   
Comment by timlfox [ 05/Aug/11 ]

Another, IMO, nicer way of doing this is to pass in some kind of CompletionHandler as an extra method in the send() method on the MessageProducer. When the message is acked, this would be called.

This technique allows an anonymous class (or lambda in Java 8) to implement the CompletionHandler which can act as a closure around some state associated with the send, which makes programming easier for the developer.

e.g

for (int i = 0; i < 100; i++) {
  producer.send(message, new CompletionHandler() {
    public void onComplete() { database.delete(i); }
  });
}

or in Java 8:

for (int i = 0; i < 100; i++) {
  producer.send(message, #{ database.delete(i) });
}

With your proposal this would be more complex - the developer would have to maintain a map of message against, whatever data was necessary to complete the request.

This technique is also more in fitting with the new asynchronous channels used in java.nio.channels

Comment by Nigel Deakin [ 05/Aug/11 ]

Good idea. Thanks for this and all your other comments.

Comment by Nigel Deakin [ 20/Sep/11 ]

Here is an alternative proposal based on Tim's comments above:

This is a proposal for a new API which will send a message and return immediately without blocking until an acknowledgement has been received from the server. Instead, when the acknowledgement is received, an asynchronous callback will be invoked.

There would be two new send methods on javax.jms.MessageProducer:

send(Message message, 
   javax.jms.AcknowledgeListener acknowledgeListener ) 
send(Message message, int deliveryMode, int priority, long timeToLive, 
   javax.jms.AcknowledgeListener acknowledgeListener) 

This would have the same behaviour as the corresponding existing send methods except that that they would return without blocking until an acknowledgement has been received from the server. When the acknowledgement is received, the callback method on the supplied AcknowledgeListener is invoked.

These methods allow a different callback object to be specified for each message that is sent. This allows applications to can pass in an anonymous class with message-specific state, which would not be possible if the same callback object were used for multiple messages.

This feature will only be available for non-transacted sessions. if these methods are called on a transacted session then a javax.jms.IllegalStateException would be thrown.

 
package javax.jms;
import javax.jms.Message;

public interface AcknowledgeListener {
	public void onAcknowledge(Message message);
}

Comment by Hiram Chirino [ 13/Dec/11 ]

A send may fail so the interface AcknowledgeListener should extend ExceptionListener.

Comment by Hiram Chirino [ 13/Dec/11 ]

Async acknowledgements would be handy for all potentially blocking JMS operations. I would be nice to have the same kind of support added to the commit and message.acknowledge methods.

Comment by Hiram Chirino [ 13/Dec/11 ]

Since JMS already uses the 'Acknowledge' term for consumer acking messages, perhaps AcknowledgeListener is not the best interface name. I'd rather see it renamed CompletionHandler as Tim suggested or Callback etc.

Comment by julienrenaut [ 14/Dec/11 ]

Hello all.

Some scenarios might benefit from having two different kind of callbacks. One to confirm a message has correctly arrived on the destination queue and another to confirm a message has been correctly delivered to it's recipient (picked up from destination queue).

I propose two different callback interfaces then.

MessageArrivalCallback.java
package javax.jms;
import javax.jms.Message;

public interface MessageArrivalCallback {
    void onArrival(Message message);
}
MessageDeliveryCallback.java
package javax.jms;
import javax.jms.Message;

public interface MessageDeliveryCallback {
    void onDelivery(Message message);
}

And the corresponding overloads of the send method.

send(Message message, MessageArrivalCallback arrivalCallback);

send(Message message, MessageDeliveryCallback deliveryCallback);

send(Message message, MessageArrivalCallback arrivalCallback, MessageDeliveryCallback deliveryCallback);

I don't know if this should be considered a different feature request or if it adds up to this one.

Comment by Nigel Deakin [ 15/Dec/11 ]

Following the various comments, the proposed API is as follows:

I've changed the name of the callback interface to CompletionListener. This allows us to use with other operations than sending a message. It also allows me to word this whole feature without using the word "acknowledge".

I've worded this whole feature in terms of the send operation being performed in a separate thread, including waiting for any "confirmation" from "a JMS server", though I've also tried to state that it is up to the provider exactly how work is divided between the calling thread and the separate thread. I've also clarified that this method can be used even in cases where an ack isn't sent back from the server.

I agree this feature might be extended to Session.commit() or Message.acknowledge() but have deferred that until we have made more progress with the initial use case.

New methods on MessageProducer:

    void
    send(Destination destination, 
	 Message message, 
	 int deliveryMode, 
	 int priority,
	 long timeToLive) throws JMSException;
    
    /** Sends a message using the <CODE>MessageProducer</CODE>'s 
    * default delivery mode, priority, and time to live, 
    * returning immediately and notifying the specified completion listener 
    * when the operation has completed.
    * <p>
    * This method allows the JMS provider to perform the actual sending of the message,
    * and the wait for any confirmation from a JMS server, to take place in a separate thread
    * without blocking the calling thread. When the sending of the message is complete,
    * and any confirmation has been received from a JMS server, the JMS provider calls
    * the <code>onCompletion(Message)</code> method of the specified completion listener. 
    * If an exception occurs in the separate thread 
    * then the JMS provider calls the <code>onException(JMSException)</code> method of the specified completion listener.
    * <p>
    * JMS does not define what operations are performed in the calling thread and what operations, if any,
    * are performed in the separate thread. In particular the use of this method does not itself specify whether
    * the separate thread should obtain confirmation from a JMS server. 
    * <p>
    * The exceptions listed below may be thrown in either thread. 
    *
    * @param message the message to send 
    * @param completionListener a <code>CompletionListener</code> to be notified when the send has completed
    *  
    * @exception JMSException if the JMS provider fails to send the message 
    *                         due to some internal error.
    * @exception MessageFormatException if an invalid message is specified.
    * @exception InvalidDestinationException if a client uses
    *                         this method with a <CODE>MessageProducer</CODE> with
    *                         an invalid destination.
    * @exception java.lang.UnsupportedOperationException if a client uses this
    *                         method with a <CODE>MessageProducer</CODE> that did
    *                         not specify a destination at creation time.
    * 
    * @see javax.jms.Session#createProducer 
    * @see javax.jms.CompletionListener 
    *
    * @since 2.0 
    */
void send(Message message, CompletionListener completionListener) throws JMSException;
  
    /** Sends a message, specifying delivery mode, priority, and time to live, 
    * returning immediately and notifying the specified completion listener 
    * when the operation has completed.
    * <p>
    * This method allows the JMS provider to perform the actual sending of the message,
    * and the wait for any confirmation from a JMS server, to take place in a separate thread
    * without blocking the calling thread. When the sending of the message is complete,
    * and any confirmation has been received from a JMS server, the JMS provider calls
    * the <code>onCompletion(Message)</code> method of the specified completion listener. 
    * If an exception occurs in the separate thread 
    * then the JMS provider calls the <code>onException(JMSException)</code> method of the specified completion listener.
    * <p>
    * JMS does not define what operations are performed in the calling thread and what operations, if any,
    * are performed in the separate thread. In particular the use of this method does not itself specify whether
    * the separate thread should obtain confirmation from a JMS server. 
    * <p>
    * The exceptions listed below may be thrown in either thread. 
    *
    * @param message the message to send
    * @param deliveryMode the delivery mode to use
    * @param priority the priority for this message
    * @param timeToLive the message's lifetime (in milliseconds)
    * @param completionListener a <code>CompletionListener</code> to be notified when the send has completed
    *  
    * @exception JMSException if the JMS provider fails to send the message 
    *                         due to some internal error.
    * @exception MessageFormatException if an invalid message is specified.
    * @exception InvalidDestinationException if a client uses
    *                         this method with a <CODE>MessageProducer</CODE> with
    *                         an invalid destination.
    * @exception java.lang.UnsupportedOperationException if a client uses this
    *                         method with a <CODE>MessageProducer</CODE> that did
    *                         not specify a destination at creation time.
    *
    * @see javax.jms.Session#createProducer
    * @see javax.jms.CompletionListener 
    * @since 2.0 
    */

  void 
  send(Message message, 
	 int deliveryMode, 
	 int priority,
	 long timeToLive, CompletionListener completionListener) throws JMSException;
  

New callback interface. I decided not to make it extend ExceptionListener because this requires the exception to be a JMSException, whereas when used with the methods on MessagingCOntext (see below) the exception may be a JMSRuntimeException.

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);
}

New methods on MessagingContext:

    /** Sends a message to the specified destination, using
     * the <CODE>MessagingContext</CODE>'s default delivery mode, priority,
     * and time to live,
     * returning immediately and notifying the specified completion listener 
     * when the operation has completed.
     * <p>
     * This method allows the JMS provider to perform the actual sending of the message,
     * and the wait for any confirmation from a JMS server, to take place in a separate thread
     * without blocking the calling thread. When the sending of the message is complete,
     * and any confirmation has been received from a JMS server, the JMS provider calls
     * the <code>onCompletion(Message)</code> method of the specified completion listener. 
     * If an exception occurs in the separate thread 
     * then the JMS provider calls the <code>onException(JMSException)</code> method of the specified completion listener.
     * <p>
     * JMS does not define what operations are performed in the calling thread and what operations, if any,
     * are performed in the separate thread. In particular the use of this method does not itself specify whether
     * the separate thread should obtain confirmation from a JMS server. 
     * <p>
     * The exceptions listed below may be thrown in either thread. 
     *
     * @param destination the destination to send this message to
     * @param message the message to send
     * @param completionListener a <code>CompletionListener</code> to be notified when the send has completed
     *  
     * @exception JMSRuntimeException if the JMS provider fails to send the message 
     *                         due to some internal error.
     * @exception MessageFormatRuntimeException if an invalid message is specified.
     * @exception InvalidDestinationRuntimeException if a client uses
     *                         this method with an invalid destination.
     * 
     * @see javax.jms.MessagingContext#setDeliveryMode
     * @see javax.jms.MessagingContext#setPriority
     * @see javax.jms.MessagingContext#setTimeToLive
     * @see javax.jms.CompletionListener
     *
     */
     void send(Destination destination, Message message,CompletionListener completionListener);
    /** Sends a message to the specified destination, 
     * specifying delivery mode, priority and time to live,
     * returning immediately and notifying the specified completion listener 
     * when the operation has completed.
     * <p>
     * This method allows the JMS provider to perform the actual sending of the message,
     * and the wait for any confirmation from a JMS server, to take place in a separate thread
     * without blocking the calling thread. When the sending of the message is complete,
     * and any confirmation has been received from a JMS server, the JMS provider calls
     * the <code>onCompletion(Message)</code> method of the specified completion listener. 
     * If an exception occurs in the separate thread 
     * then the JMS provider calls the <code>onException(JMSException)</code> method of the specified completion listener.
     * <p>
     * JMS does not define what operations are performed in the calling thread and what operations, if any,
     * are performed in the separate thread. In particular the use of this method does not itself specify whether
     * the separate thread should obtain confirmation from a JMS server. 
     * <p>
     * The exceptions listed below may be thrown in either thread. 
     * 
     * @param destination the destination to send this message to
     * @param message the message to send
     * @param deliveryMode the delivery mode to use
     * @param priority the priority for this message
     * @param timeToLive the message's lifetime (in milliseconds)
     * @param completionListener a <code>CompletionListener</code> to be notified when the send has completed
     *  
     * @exception JMSRuntimeException if the JMS provider fails to send the message 
     *                         due to some internal error.
     * @exception MessageFormatRuntimeException if an invalid message is specified.
     * @exception InvalidDestinationRuntimeException if a client uses
     *                         this method with an invalid destination.
     *
     * @see javax.jms.CompletionListener
     */
     void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive,CompletionListener completionListener);	
Comment by Nigel Deakin [ 15/Dec/11 ]

I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes).

The updated Javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
(See two new methods on MessageProducer, two new methods on MessagingContext, and a new interface CompletionListener)

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, but the only place I've changed is Section 4.6 "MessageProducer" and section 11.5.2. "Sending messages asynchronously", which is just a change log)

Comment by Nigel Deakin [ 18/Jul/12 ]

Further changes have been made as follows.

The updated spec is here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf

The updated javadocs are here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/target/jms-2.0-javadoc.jar
and can be browsed online at
http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/index.html

API changes

On the CompletionListener callback interface, the onException method has an additional parameter: the message object:

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 specified message
   * 
   * @param message the message that was sent.
   * @param exception the exception
   * 
   */
   void onException(Message message, Exception exception);
}

Spec changes

Section 4.6 "MessageProducer" has been expanded and divided into two: subsection 4.6.1 "Synchronous send" and subsection 4.6.2 "Asynchronous send". Here is the text in 4.6.2 "Asynchronous send":

4.6.2 Asynchronous send

Clients may alternatively send a message using the following methods which permit the JMS provider to perform part of the work involved in sending the message in a separate thread. JMS refers to this as an "asynchronous send".

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

When the message has been successfully sent the JMS provider invokes the callback method onCompletion 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. If for some reason the acknowledgement is not received the JMS provider would notify the application by invoking the CompletionListener's onException method.

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 requirements given in the following sections:

4.6.2.1. 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 CompletionListener. The CompletionListener must not be invoked earlier than this.

4.6.2.2. Message order

If the same MessageProducer or JMSContext is used to send multiple messages then JMS message ordering requirements (see section 4.4.10 "Message order") must be satisfied. This applies even if a combination of synchronous and asynchronous sends has been performed. The application is not required to wait for an asynchronous send to complete before sending the next message.

4.6.2.3. Close, commit or rollback

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 callbacks have returned before performing the commit or rollback.

If the close method 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.

4.6.2.4. Restrictions on usage in Java EE

An asynchronous send is not permitted in a Java EE EJB or web container. If the application component violates this restriction the send method may throw a JMSException or JMSRuntimeException (depending on the method signature).

4.6.2.5. Message headers

JMS defines a number of message header fields and message properties which must be set by the "JMS provider on send". See section 3.4.11 "How message header values are set" and section 3.5.9 "JMS defined properties". If the send is asynchronous these fields and properties may be accessed on the sending client only after the CompletionListener has been invoked. If the CompletionListener's onException method is called then the state of these message header fields and properties is undefined. See also section 4.6.2.8 "Restrictions on the use of the Message object" below.

4.6.2.6. 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 CompletionListener does not cause the session to be dedicated to the thread of control which calls the CompletionListener. The application thread may therefore continue to use the session after performing an asynchronous 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.

4.6.2.7. 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 asynchronous send method.

A JMS provider must not invoke the CompletionListener from the thread that is calling the asynchronous send method.

An application which does 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.

4.6.2.8. 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.

A JMSException may be thrown if the application attempts to access or modify the Message object after the send method has returned and before the CompletionListener has been invoked.

Javadoc changes

The javadoc comments for all send methods on MessageProducer and JMSContext which perform an asynchromous send have been updated to include the above text.

Comment by Nigel Deakin [ 21/Sep/12 ]

A number of clarifications have been added. For an up-to-date description of this feature, see section 4.6.2 "Asynchronous send" in the JMS 2.0 public draft.





[JMS_SPEC-42] Make support for JMSXDeliveryCount mandatory Created: 05/Aug/11  Updated: 02/Nov/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
blocks MQ-173 Implement JMSXDeliveryCount Closed
blocks MQ-236 Broker support JMS2.0 mandatory JMSXD... Closed
Tags: ed20-added

 Description   

This issue was raised by a member of the JSR 343 Expert Group and is logged here to allow the issue to be discussed and tracked.

It is proposed that support for the JMS defined message property JMSXDeliveryCount be made mandatory.

The JMS 1.1 specification currently defines an optional JMS defined message property JMSXDeliveryCount. When used, this is set by the JMS provider when a message is received, and is set to the number of times this message has been delivered (including the first time). The first time is 1, the second time 2, etc.

Support for this property would allow arbitrary containers and applications to improve the way they handle "poisonous" messages - messages which cannot be consumed for some reason and need to be redelivered. For example, it would allow applications to detect when a message has been redelivered more than a specified number of times and perform some special handling such as redirecting it to some other destination.

This property wouldn't need to be perfectly accurate every time. For example, it wouldn't be necessary to persist this value in the server. A "best efforts" implementation would probably be adequately.



 Comments   
Comment by Nigel Deakin [ 20/Dec/11 ]

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

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

The only change is to Message. This wording is very generic and doesn't mention individual properties. All I've done is to mention that some properties may be mandatory, some may be optional. I've also added a clarification to state that the effect of setting a message selector on a property (such as JMSXDeliveryCount) which is set by the provider on receive is undefined.

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. The changes for this issue are as follows:

Section 3.5.9 "JMS Defined Properties" has been updated to state that the property JMSXDeliveryCount is now mandatory with a reference to 3.5.11 "JMSXDeliveryCount" for a definition.

Some of the wording in this section has been rearranged to reflect the fact that some properties are optional but that one (JMSXDeliveryCount) is now mandatory. A clarification has been added to state that the effect of setting a message selector on a property (such as JMSXDeliveryCount) which is set by the provider on receive is undefined.

Section 3.4.7 "JMSRedelivered" has been updated to say "The JMS-defined message property JMSXDeliveryCount will be set to the number of times a particular message has been delivered. See section 3.5.11 "JMSXDeliveryCount".

A new section 3.5.11 "JMSXDeliveryCount" has been added. This states:

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.

If the JMSRedelivered value is set then the {{JMSXDeliveryCount}}property must always be 2 or more.

The purpose of the JMSXDeliveryCount property is to allow consuming applications to identify whether a particular message is being repeatedly redelivered and take appropriate action.

The value of the JMSXDeliveryCount property is not guaranteed to be exactly correct. The JMS provider is not expected to persist this value to ensure that its value is not lost in the event of a failure.

4.4.11 Message Acknowledgement: The sentence "a session must set the JMSRedelivered flag of messages it redelivers due to a recovery" has been amended to state that it should also increment the JMSXDeliveryCount property as well.

4.4.12 "Duplicate Delivery of Messages": The sentence "The JMSRedelivered message header field will be set for a message redelivered under these circumstances" has been amended to the JMSXDeliveryCount property should be incremented as well.

4.5.2 "Asynchronous delivery": This states that if a message listener throws an exception in AUTO_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE mode the message will be immediately redelivered and the JMSRedelivered message header field will be set. This has been amended to state that the JMSXDeliveryCount message property will be incremented as well.

4.10. "Reliability": This states "Unacknowledged messages redelivered due to system failure must have the JMSRedelivered message header field set by the JMS provider.". This has been amended to state "Unacknowledged messages redelivered due to system failure must have the JMSRedelivered message header field set, and the JMSXDeliveryCount incremented, by the JMS provider, as described in sections 3.4.7 "JMSRedelivered" and 3.5.11 "JMSXDeliveryCount"

11.5.6 "JMSXDeliveryCount" is the change log for this change.





[JMS_SPEC-39] Make clientId optional when creating a durable subscription Created: 05/Aug/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: ed20-added

 Description   

(There was a change of mind on this issue. Initially this feature was added for both JMS 1.1 unshared subscriptions as well as JMS 2.0 shared subscriptions but we changed our mind and in the end it was added for JMS 2.0 shared subscriptions only. This note was added retrospectively to make this issue easier to understand when reading it.)

This is a proposal to make clientId optional when creating a durable subscription

In JMS 1.1, a durable subscription is defined by the combination of clientId and subscriptionName. Section 6.11.1 of the Specification states:

Sessions with durable subscribers must always provide the same client
identifier. In addition, each client must specify a name that uniquely identifies
(within client identifier) each durable subscription it creates.

Whilst it is clear that a durable subscription needs to have a name to allow it to be identified, it is less clear why it needs to be identified by clientId. This is the only place in the JMS API where a connection must have clientId set.

This requirement to have clientId set causes complication for applications, especially in a Java EE application, where clientId must be set on the connection factory. This means that the connection factory can only be used to create a single instance, and if a connection pool is used it must be constrained to allow only a single instance.

It is therefore proposed that the use of clientId be made optional when creating a durable subscription. In this case, the subscription will be defined by the name of the subscription alone.

This change is not intended to change the existing restrictions stated in Section 6.11.1:

Only one session at a time can have a TopicSubscriber for a particular durable subscription.



 Comments   
Comment by timlfox [ 05/Aug/11 ]

I don't understand how client id can be optional. The unique id of a durable subscription is effectively defined by a tuple (client_id, subscription_name).

This means there can be many different durable subscriptions with the same value of subscription_name but different values of client_id.

Client id normally maps to some kind of notion of user, so this allows each user to maintain their own namespace within which they don't have to worry about naming of durable subscriptions.

Removing client id would mean there is effectively one "global" namespace of durable subscriptions. This is not desirable IMHO.

Comment by Nigel Deakin [ 05/Aug/11 ]

If clientID was set then it would still be used (to maintain backwards compatibility). However this change would allow a durable subscription to be created for which its identifying "tuple" was (null,subscription_name).

Comment by Nigel Deakin [ 21/Dec/11 ]

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

The only changes are to comments, not to the API. The updated comments as follows:

On the Session interface the javadocs for the two createDurableSubscriber methods and the unsubscribe method have been rewritten as follows:

    /** Creates a durable subscription with the specified name on the
     * specified topic, and creates a <code>TopicSubscriber</code>
     * on that durable subscription.
     * <P>
     * If the durable subscription already exists then this method
     * creates a <code>TopicSubscriber</code> on the existing durable
     * subscription.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no <code>TopicSubscriber</code> associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a <code>TopicSubscriber</code> on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A durable subscription which has a <code>TopicSubscriber</code>
     * associated with it is described as being active.
     * A durable subscription which has no <code>TopicSubscriber</code>
     * associated with it is described as being inactive.
     * <p>
     * Only one session at a time can have a
     * <CODE>TopicSubscriber</CODE> for a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a <code>TopicSunscriber</code>
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * <code>createDurableSubscriber</code>
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     *
     * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
     * @param name the name used to identify this subscription
     *
     * @exception JMSException if the session fails to create a subscriber
     * due to some internal error.
     * @exception InvalidDestinationException if an invalid topic is specified.
     *
     * @since 2.0
     */
    TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException;
    /** Creates a durable subscription with the specified name on the
     * specified topic, and creates a <code>TopicSubscriber</code>
     * on that durable subscription, specifying a message
     * selector and whether messages published by its
     * own connection should be delivered to it.
     * <P>
     * If the durable subscription already exists then this method
     * creates a <code>TopicSubscriber</code> on the existing durable
     * subscription.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no <code>TopicSubscriber</code> associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a <code>TopicSubscriber</code> on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A durable subscription which has a <code>TopicSubscriber</code>
     * associated with it is described as being active.
     * A durable subscription which has no <code>TopicSubscriber</code>
     * associated with it is described as being inactive.
     * <p>
     * Only one session at a time can have a
     * <CODE>TopicSubscriber</CODE> for a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a <code>TopicSunscriber</code>
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * <code>createDurableSubscriber</code>
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     *
     * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
     * @param name the name used to identify this subscription
     * @param messageSelector only messages with properties matching the
     * message selector expression are delivered. A value of null or
     * an empty string indicates that there is no message selector
     * for the message consumer.
     * @param noLocal if set, inhibits the delivery of messages published
     * by its own connection
     *
     * @exception JMSException if the session fails to create a subscriber
     * due to some internal error.
     * @exception InvalidDestinationException if an invalid topic is specified.
     * @exception InvalidSelectorException if the message selector is invalid.
     *
     * @since 2.0
     */
     TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException;
    /** Unsubscribes a durable subscription that has been created by a client.
      *
      * <P>This method deletes the state being maintained on behalf of the
      * subscriber by its provider.
      * <p>
      * A durable subscription is identified by a name specified by the client
      * and by the client identifier if set. If the client identifier was set
      * when the durable subscription was created then a client which
      * subsequently wishes to use this method to
      * delete a durable subscription must use the same client identifier.
      *
      * <P>It is erroneous for a client to delete a durable subscription
      * while there is an active <CODE>MessageConsumer</CODE>
      * or <CODE>TopicSubscriber</CODE> for the
      * subscription, or while a consumed message is part of a pending
      * transaction or has not been acknowledged in the session.
      *
      * @param name the name used to identify this subscription
      *
      * @exception JMSException if the session fails to unsubscribe to the
      * durable subscription due to some internal error.
      * @exception InvalidDestinationException if an invalid subscription name
      * is specified.
      *
      * @since 1.1
      */
    void unsubscribe(String name) throws JMSException;
Comment by Nigel Deakin [ 21/Dec/11 ]

On the proposed MessagingContext interface the javadocs for the two createSyncDurableSubscriber methods, the two setMessageListener methods used to create durable subscriptions, the two setBatchMessageListener methods used to create durable subscriptions and the unsubscribe method have been rewritten as follows:

    /** Creates a durable subscription with the specified name on the
     * specified topic, and creates a <code>SyncMessageConsumer</code>
     * on that durable subscription.
     * <P>
     * If the durable subscription already exists then this method
     * creates a <code>SyncMessageConsumer</code> on the existing durable
     * subscription.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a <code>SyncMessageConsumer</code>
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * <code>createSyncDurableSubscriber</code>
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     *
     * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
     * @param name the name used to identify this subscription
     *
     * @exception JMSRuntimeException if the session fails to create a subscriber
     * due to some internal error.
     * @exception InvalidDestinationRuntimeException if an invalid topic is specified.
     *
     */
    SyncMessageConsumer createSyncDurableSubscriber(Topic topic, String name);
    /** Creates a durable subscription with the specified name on the
     * specified topic, and creates a <code>SyncMessageConsumer</code>
     * on that durable subscription, specifying a message selector and
     * whether messages published by its own connection should be delivered to it.
     * <P>
     * If the durable subscription already exists then this method
     * creates a <code>SyncMessageConsumer</code> on the existing durable
     * subscription.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a <code>SyncMessageConsumer</code>
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * <code>createSyncDurableSubscriber</code>
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     *
     * @param topic the non-temporary <CODE>Topic</CODE> to subscribe to
     * @param name the name used to identify this subscription
     * @param messageSelector only messages with properties matching the
     * message selector expression are delivered. A value of null or
     * an empty string indicates that there is no message selector
     * for the message consumer.
     * @param noLocal if set, inhibits the delivery of messages published
     * by its own connection
     *
     * @exception JMSRuntimeException if the session fails to create a subscriber
     * due to some internal error.
     * @exception InvalidDestinationRuntimeException if an invalid topic is specified.
     * @exception InvalidSelectorRuntimeException if the message selector is invalid.
     *
     */
    SyncMessageConsumer createSyncDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal);
    /**
     * Creates a durable subscription with the specified name on the specified topic,
     * and creates a consumer on that durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>.
     * <p>
     * If the durable subscription already exists then this method creates a consumer on the existing durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session or messaging context at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a consumer
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * this or any of the other methods listed above
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     * <p>
     * If the specified listener is null then this method does nothing.
     *
     * @param topic - The topic from which messages are to be consumed
     * @param subscriptionName - The name used to identify the durable subscription
     * @param listener - The listener to which the messages are to be delivered
     * @throws JMSRuntimeException - If the operation fails due to some internal error.
     * @throws InvalidDestinationRuntimeException - If an invalid topic is specified.
     */
void setMessageListener (Topic topic, String subscriptionName, MessageListener listener);
    /**
     * Creates a durable subscription with the specified name on the specified topic,
     * and creates a consumer on that durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>,
     * specifying a message selector and whether messages published by its
     * own connection should be delivered to it.
     * <p>
     * If the durable subscription already exists then this method creates a consumer on the existing durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session or messaging context at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a consumer
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * this or any of the other methods listed above
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     * <p>
     * If the specified listener is null then this method does nothing.
     *
     * @param topic - The topic from which messages are to be consumed
     * @param subscriptionName - The name used to identify the durable subscription
     * @param messageSelector - Only messages with properties matching the message selector
     * expression are delivered. A value of null or an empty string
     * indicates that there is no message selector for the message
     * consumer.
     * @param NoLocal - If true, inhibit* subsequently wishes to create a consumer
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * this or any of the other methods listed above
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     * <p>
     * If the specified listener is null then this method does nothing.
     *
     * @param topic - The topic from which messages are to be consumed
     * @param subscriptionName - The name used to identify the durable subscription
     * @param messageSelector - Only messages with properties matching the message selector
     * expression are delivered. A value of null or an empty string
     * indicates that there is no message selector for the message
     * consumer.
     * @param NoLocal - If true, inhibits the delivery of messages published by its own connection.
     * @param listener - The listener to which the messages are to be delivered
     * @throws JMSRuntimeException - If the operation fails due to some internal error.
     * @throws InvalidDestinationRuntimeException - If an invalid topic is specified.
     * @throws InvalidSelectorRuntimeException - If the message selector is invalid.
     */
    void setMessageListener(Topic topic, String subscriptionName, String messageSelector, boolean NoLocal, MessageListener listener);
   /**
     * Creates a durable subscription with the specified name on the specified topic,
     * and creates a consumer on that durable subscription
     * that will deliver messages in batches to the specified <code>BatchMessageListener</code>
     * with the specified maximum batch size and timeout.
     * <p>
     * If the durable subscription already exists then this method creates a consumer on the existing durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session or messaging context at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a consumer
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * this or any of the other methods listed above
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     * <p>
     * Messages will be delivered to the specified <code>BatchMessageListener</code>
     * in batches whose size is no greater than the specified maximum batch size.
     * The JMS provider may defer message
     * delivery until the specified batch timeout has expired in order to
     * assemble a batch of messages that is as large as possible but no greater
     * than the batch size.
     * <p>
     * If the specified listener is null then this method does nothing.
     *
     * @param topic - The topic from which messages are to be consumed
     * @param subscriptionName - The name used to identify the durable subscription
     * @param listener - The listener to which the messages are to be delivered
     * @param maxBatchSize - The maximum batch size that should be used. Must be greater than zero.
     * @param batchTimeout - The batch timeout in milliseconds. A value of zero means no
     * timeout is required The JMS provider may override the
     * specified timeout with a shorter value.
     * @throws JMSRuntimeException - If the operation fails due to some internal error.
     * @throws InvalidDestinationRuntimeException - If an invalid topic is specified.
     */
    void setBatchMessageListener(Topic topic, String subscriptionName, BatchMessageListener listener, int maxBatchSize, long batchTimeout);
    /**
     * Creates a durable subscription with the specified name on the specified topic,
     * using the specified message message selector,
     * and creates a consumer on that durable subscription
     * that will deliver messages in batches to the specified <code>BatchMessageListener</code>
     * with the specified maximum batch size and timeout.
     * This method can specify whether messages published by its own connection
     * should be delivered to the specified listener.
     * <p>
     * If the durable subscription already exists then this method creates a consumer on the existing durable subscription
     * that will deliver messages to the specified <code>MessageListener</code>.
     * <p>
     * A durable subscription is used by a client which needs to receive
     * all the messages published on a topic, including the ones published
     * when there is no active consumer associated with it.
     * The JMS provider retains a record of this durable subscription
     * and ensures that all messages from the topic's publishers are retained
     * until they are delivered to, and acknowledged by,
     * a consumer on this durable subscription
     * or until they have expired.
     * <p>
     * A durable subscription will continue to accumulate messages
     * until it is deleted using the <code>unsubscribe</code> method.
     * <p>
     * A consumer may be created on a durable subscription using the
     * <code>createSyncDurableSubscriber</code> methods on <code>MessagingContext</code>,
     * the appropriate <code>setMessageListener</code> methods on <code>MessagingContext</code>
     * or the <code>createDurableSubscriber</code> methods on <code>Session</code> or <code>TopicSession</code>
     * A durable subscription which has a consumer
     * associated with it is described as being active.
     * A durable subscription which has no consumer
     * associated with it is described as being inactive.
     * <p>
     * Only one session or messaging context at a time can have a
     * active consumer on a particular durable subscription.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which
     * subsequently wishes to create a consumer
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * this or any of the other methods listed above
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector.
     * Changing a durable subscriber is equivalent to
     * unsubscribing (deleting) the old one and creating a new one.
     * <p>
     * Messages will be delivered to the specified <code>BatchMessageListener</code>
     * in batches whose size is no greater than the specified maximum batch size.
     * The JMS provider may defer message
     * delivery until the specified batch timeout has expired in order to
     * assemble a batch of messages that is as large as possible but no greater
     * than the batch size.
     * <p>
     * If the specified listener is null then this method does nothing.
     *
     * @param topic - The topic from which messages are to be consumed
     * @param subscriptionName - The name used to identify the durable subscription
     * @param messageSelector - Only messages with properties matching the message selector
     * expression are delivered. A value of null or an empty string
     * indicates that there is no message selector for the message
     * consumer.
     * @param NoLocal - If true, inhibits the delivery of messages published by its own connection.
     * @param listener - The listener to which the messages are to be delivered
     * @param maxBatchSize - The maximum batch size that should be used. Must be greater than zero.
     * @param batchTimeout - The batch timeout in milliseconds. A value of zero means no
     * timeout is required The JMS provider may override the
     * specified timeout with a shorter value.
     * @throws JMSRuntimeException - If the operation fails due to some internal error.
     * @throws InvalidDestinationRuntimeException - If an invalid topic is specified.
     * @throws InvalidSelectorRuntimeException - If the message selector is invalid.
     */
    void setBatchMessageListener(Topic topic, String subscriptionName, String messageSelector, boolean NoLocal,
BatchMessageListener listener, int maxBatchSize, long batchTimeout);
    /** Unsubscribes a durable subscription that has been created by a client.
     *
     * <P>This method deletes the state being maintained on behalf of the
     * subscriber by its provider.
     * <p>
     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was created then a client which
     * subsequently wishes to use this method to
     * delete a durable subscription must use the same client identifier.
     *
     * <P>It is erroneous for a client to delete a durable subscription
     * while there is an active consumer on that subscription,
     * or while a consumed message is part of a pending
     * transaction or has not been acknowledged in the session.
     * <P>
     * If the active consumer is represented by a <CODE>SyncMessageConsumer</CODE> then calling
     * <CODE>close</CODE> on either that object or the <CODE>MessagingContext</CODE> used to create it
     * will render the consumer inactive and allow the subscription to be deleted.
     * <P>
     * If the active consumer was created by calling <code>setMessageListener</code> on the <CODE>MessagingContext</CODE>
     * then calling <CODE>close</CODE> on the <CODE>MessagingContext</CODE>
     * will render the consumer inactive and allow the subscription to be deleted.
     * <p>
     * If the active consumer is represented by a <code>MessageConsumer</code> or <code>TopicSubscriber</code> then calling
     * <code>close</code> on that object or on the <code>Session</code> or <code>Connection</code> used to create it
     * will render the consumer inactive and allow the subscription to be deleted.
     *
     * @param name the name used to identify this subscription
     *
     * @exception JMSRuntimeException if the session fails to unsubscribe to the
     * durable subscription due to some internal error.
     * @exception InvalidDestinationRuntimeException if an invalid subscription name
     * is specified.
     */
    void unsubscribe(String name);
Comment by Nigel Deakin [ 21/Dec/11 ]

The updated draft spec is here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf
All changes are hightlighted. The changes made for this issue are as follows:

In JMS 2.0 it is no longer mandatory for the client identifier to be set when creating or activating a durable subscription. The following changes have been updated to reflect this change:

Section 4.3.2 "Client Identifier" has been changed to state that its use in identifying a durable subscription is optional.

Section 6.3 "Durable Subscription" has been amended by adding a cross-reference to Section 6.11.1 "Durable TopicSubscriber" at the end.

Section 6.11.1 "Durable TopicSubscriber" has been completely rewritten. This section was essentially a copy of the javadoc comment on the createDurableSubscriber methods and since those javadoc comments have been rewritten to make them clearer this section has been rewritten as well.

The only substantive change is to state that the use of client identifier in identifying a durable subscription is optional. Cross-references to Section 6.3 "Durable Subscription" and Section 4.3.2 "Client Identifier" have been added at the end.

Section 11.5.7. Durable subscriptions has been added, which is a change log.

Comment by Nigel Deakin [ 18/Jan/12 ]

Additional changes have been made to the javadoc comments for the following methods to clarify the case where the durable subscription already exists:

  • Session.createDurableSubscriber (2 methods)
  • Session.createDurableConsumer (2 methods, added for JMS_SPEC-51)
  • MessagingContext.createSyncDurableConsumer (2 methods)
  • MessagingContext.setMessageListener (only the 2 methods for durable subscriptions)
  • MessagingContext.setBatchMessageListener (only the 2 methods for durable subscriptions

Here's an example for Session.createDurableSubscriber:

This is the old comment:

     * If the durable subscription already exists then this method
     * creates a <code>TopicSubscriber</code> on the existing durable
     * subscription.

This is the new comment:

     * If a durable subscription already exists with the same name 
     * and client identifier (if set) and the same topic and message selector 
     * then this method creates a <code>TopicSubscriber</code> on the existing durable
     * subscription.

Note that the javadoc comment for these methods already states the following:

     * A durable subscription is identified by a name specified by the client
     * and by the client identifier if set. If the client identifier was set
     * when the durable subscription was first created then a client which 
     * subsequently wishes to create a <code>TopicSunscriber</code>
     * on that durable subscription must use the same client identifier.
     * <p>
     * A client can change an existing durable subscription by calling
     * <code>createDurableSubscriber</code> 
     * with the same name and client identifier (if used),
     * and a new topic and/or message selector. 
     * Changing a durable subscriber is equivalent to 
     * unsubscribing (deleting) the old one and creating a new one.

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

Comment by Nigel Deakin [ 26/Nov/12 ]

It has now been decided that changing the Session method createDurableSubscriber to make clientId optional for durable subscriptions breaks the http://java.net/projects/javaee-spec/pages/CompatibilityRequirements Backwards Compatibility Requirements for Java EE Specifications.

It will therefore remain mandatory to set clientId when creating a non-shared durable subscription using the following existing or new methods:

Methods on Session:

  • TopicSubscriber createDurableSubscriber(Topic topic, String name) (JMS 1.1)
  • TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) (JMS 1.1)
  • MessageConsumer createDurableConsumer(Topic topic, String name) (JMS 2.0)
  • MessageConsumer createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal) (JMS 2.0)

Methods on JMSContext:

  • JMSConsumer createDurableConsumer(Topic topic, String name)
  • JMSConsumer createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal)

These methods:

  • Will only allow a single consumer on a durable subscription
  • clientId MUST be set

However setting clientId will be optional when creating a shared durable subscriptions using any of the following new methods:

Methods on Session:

  • MessageConsumer createSharedDurableConsumer(Topic topic, String name)
  • MessageConsumer createSharedDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal)

Methods on JMSContext:

  • JMSConsumer createSharedDurableConsumer(Topic topic, String name)
  • JMSConsumer createSharedDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal)

These methods:

  • Will allow multiple consumers on a durable subscription
  • clientId may or may not be set
Comment by Nigel Deakin [ 20/Mar/13 ]

Marking this issue as "resolved", with a resolution of "Won't fix"





[JMS_SPEC-34] Calling setJMSDeliveryMode or setJMSPriority on a javax.jms.Message before it is sent don't have any effect Created: 25/Jul/11  Updated: 21/Sep/12  Resolved: 21/Sep/12

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Major
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
duplicates JMS_SPEC-60 Obscurity setting special JMSHeaders Closed
Tags: ed20-added

 Description   

This issue relates to the following methods on javax.jms.Message:

setJMSPriority
setJMSDeliveryMode

Many novice developers think these methods can be used to set the priority and delivery mode of a message before it is sent. In fact these methods have no effect: the only way to set priority and delivery mode is by using methods on javax.jms.MessageProducer.

This a common cause of confusion to developers. Can we either deprecate these methods or else change them so that actually do something?



 Comments   
Comment by Nigel Deakin [ 25/Jul/11 ]

The same issue applies to the following methods on javax.jms.Message as well:

setJMSDeliveryMode
setJMSExpiration
setJMSPriority
setJMSMessageID
setJMSTimestamp
setJMSRedelivered
setJMSDeliveryTime (proposed in JMS_SPEC-44)

If any of these methods are called on a message before it is sent they have no effect. The javadoc comment on each of these methods states "This method can be used to change the value for a message that has been received.", despite it being far from obvious why an application might want to do this.

Comment by Nigel Deakin [ 22/Nov/11 ]

These methods are needed because a JMS provider is required to be capable of sending messages whose implementation is not its own. This would typically occur if an application received a message from provider 1 and then sent the same message using provider 2.

When this happens, provider 2 is responsible for setting the JMSDestination, JMSDeliveryMode, JMSExpiration, JMSPriority, JMSMessageID and JMSTimestamp header field values on the message. The only way that provider 2 can set these values on a message implemented by provider 1 is using these public setter methods.

Here's an example: if the application sends a message with a priority of 3 then it does this either by setting the priority on the MessageProducer by calling its setPriority method prior to sending the message, or by setting the priority on the individual message send by calling the MessageProducer method send(destination, message, deliveryMode, priority, timeToLive). The provider is responsible for ensuring that, after the message has been sent, the message's JMSPriority header holds the appropriate priority so that the line of code following the call to send could obtain this value. (See section 3.4.10 of the JMS 1.1 spec).

// message1 was obtained from provider 1
// we're sending it using provider 2
messageConsumer.setPriority(3);
messageConsumer.send(message1);
int priority = message1.getJMSPriority(); // should return 3

In the above example, provider 2's send method needs to be able to set the JMSPriority header on message1, which was implemented by provider 1, so it needs a standard, public API to do this.

We therefore cannot remove these methods. It would, however, be possible to clarify the javadoc comments to make it clear that these methods cannot be used by the sending application to control the message priority, delivery mode. We will improve the wording in JMS 2.0.

I suspect that some people who fall into the trap of misunderstanding these methods simply guess the method name, see that it gets accepted by their IDE, and use it. There's not much we can do about this (though since most IDEs can display javadoc comments clarifying the javadoc may help).

Comment by koen.serneels [ 23/Nov/11 ]

My opinion is still that it should always be the goal of a good API to minimize the percentage of obscurity.
By offering methods in a public API that do nothing, you are creating confusion from the start.

Can't there be a Message SPI or some other way that the provider can set the priority before sending? Imho this is just bad design which leads to errornous code and time consuming debegugging to find out "why this setter method doesn't work" ?.

Comment by Nigel Deakin [ 23/Nov/11 ]

@koen.serneels: You're perfectly right, the existence of these methods is misleading and with hindsight the people who designed this API should have done something different. But removing them would be a non-compatible change so I think we're stuck with them.

Comment by Nigel Deakin [ 22/Dec/11 ]

I've now updated the javadocs and the draft spec with details of this new feature (these changes are additive, so those docs include other changes).

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

The javadoc comments for the following methods on Message have been updated:

setJMSDeliveryMode
setJMSExpiration
setJMSPriority
setJMSMessageID
setJMSTimestamp
setJMSRedelivered
setJMSDeliveryTime (proposed in JMS_SPEC-44)

Currently the javadoc comments for these methods simply states:

    /** Sets the message's [name of property] value
      *  
      * <P>JMS providers set this field when a message is sent. This method
      * can be used to change the value for a message that has been received.

These have been changed as follows:

   /** Sets the message's [name of property] value.
     *
     * <P>This method is for use by JMS providers only to set this field 
     * when a message is sent. This message cannot be used by clients 
     * to configure the [describe feature] of the message. This method is public
     * to allow one JMS provider to set this field when sending a message
     * whose implementation is not its own.

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. The changes for this issue are as follows:

Section 3.4.1 "How Message Header Values Are Set" has been updated.

Section 11.5.8. "Clarification: message headers that are intended to be set by the JMS provder" is the change log for this issue.

Comment by Nigel Deakin [ 23/Feb/12 ]

Clarified the explanation in the second comment .





[JMS_SPEC-33] Improving the JMS API with API simplifications, annotations and CDI Created: 21/Jul/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0PD, 2.0

Type: Improvement Priority: Critical
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Dependency
depends on JMS_SPEC-64 Define simplified JMS API Resolved
depends on JMS_SPEC-70 Define annotations for injecting Mess... Resolved
Tags: ed20-added

 Description   

We should provide a modern, easier-to-use API, probably using annotations and CDI. There seemed to be a variety of ideas of how we might do this, and we will need to explore these in detail. Whilst I think we mostly see this as a feature for Java EE containers, there seems to be interest in offering features to those using a Java SE environment as well.

(This issue is a placeholder for discussion on the JSR 343 EG. More information will be added later)



 Comments   
Comment by Nigel Deakin [ 20/Mar/13 ]

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





[JMS_SPEC-27] Clarify the relationship between the JMS and other Java EE specifications Created: 06/Jul/11  Updated: 20/Mar/13  Resolved: 20/Mar/13

Status: Resolved
Project: jms-spec
Component/s: None
Affects Version/s: 1.1
Fix Version/s: 2.0ED, 2.0

Type: Improvement Priority: Critical
Reporter: Nigel Deakin Assignee: Nigel Deakin
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Tags: ed20-added

 Description   

Summary

We need to clarify the relationship between the JMS and other Java EE specifications. This is mainly a documentation exercise, especially for those methods which are forbidden or modified in a EJB or Web container, but there are some ambiguities, especially when the transaction context is undefined.

More detail

If you want use the JMS API in a Java EE application such as a EJB or servlet you soon discover that much of the JMS spec (including the JMS javadocs) does not apply, at least in the EJB and web containers. For example, you aren't allowed to create message listeners, you need to defined MDBs instead. You aren't allowed to create local transactions, you need to use CMT or BMT transactions instead. You aren't allowed to set clientID on a connection. You aren't allowed to perform client acknowledgement. Essentially, in a Java EE container, the JMS API is simply different. However this is not explained in the JMS spec but is described in other specs, notably the EJB and Java EE platform specs.

Some of this information of necessity in other specs, but the absence of any mention of it in the JMS spec or javadocs is confusing to users.

I would like the JMS expert group to review what it says about JMS in other specs and consider whether this material should be moved to the JMS spec, duplicated in the JMS spec, or cross-referenced from the JMS spec. In addition the javadocs need to be clarified to mention that some API is not allowed, or has a different effect, in a Java EE web or EJB container.

I see this mainly as a documentation exercise. However this analysis will inevitably identify that some areas of behaviour are not well defined. For example, the EJB spec states explicitly that in an "undefined transactional context" it is not defined how the JMS provider should handle transactions. This means that vendors may behave differently which reduces the portability of application. If possible this should be resolved.



 Comments   
Comment by Nigel Deakin [ 26/Jan/12 ]

I've now updated the API, javadocs and the draft spec.
The updated draft spec is here:
http://java.net/projects/jms-spec/sources/repository/content/jms2.0/specification/word/JMS20.pdf

I've written a completely new section to add to the new chapter 12 "Use of JMS API in Java EE applications" which was added for JMS_SPEC-45. This is intended to simply restate what is already defined in the EJB 3.1 and Java EE 6 platform specs, though I have extended it to cover the simplified API. here it is:

12.2. Restrictions on the use of JMS API in the Java EE web or EJB container

JMS applications which run in the Java EE web or EJB container are subject to a number of restrictions in the way the JMS API may be used. These restrictions are necessary for the following reasons:

  • In a Java EE web or EJB container, a JMS provider operates as a transactional resource manager which must participate in JTA transactions as defined in the Java EE platform specification. This overrides the behaviour of JMS sessions as defined elsewhere in the JMS specification. For more details see section 12.3 "Behaviour of JMS sessions in the Java EE web or EJB container".
  • The Java EE web or EJB containers need to be able to manage the threads used to run applications.
  • The Java EE web and EJB containers perform connection management which may include the pooling of JMS connections.

The restrictions described in this section do not apply to the Java EE application client container.
Applications running in the Java EE web and EJB containers must not attempt to create more than one active (not closed) Session object per connection.

  • If an application attempts to use the Connection object’s createSession method when an active Session object exists for that connection then a JMSException should be thrown.
  • If an application attempts to use the MessagingContext object's createMessagingContext method then a JMS{{RuntimeException}} should be thrown, since the first messaging context already contains a connection and session and this method would create a second session on the same connection.

The following methods are intended for use by the application server and their use by applications running in the Java EE web or EJB container may interfere with the container's ability to properly manage the threads used in the runtime environment. They must therefore not be called by applications running in the Java EE web or EJB container:

  • javax.jms.Session method setMessageListener
  • javax.jms.Session method getMessageListener
  • javax.jms.Session method setBatchMessageListener
  • javax.jms.Session method getBatchMessageListener
  • javax.jms.Session method run
  • javax.jms.Connection method createConnectionConsumer
  • javax.jms.Connection method createDurableConnectionConsumer

The following methods may interfere with the container's ability to properly manage the threads used in the runtime environment and must not be used by applications running in the Java EE web or EJB container:

  • javax.jms.MessageConsumer method setMessageListener
  • javax.jms.MessageConsumer method getMessageListener
  • javax.jms.MessageConsumer method setBatchMessageListener
  • javax.jms.MessageConsumer method getBatchMessageListener
  • javax.jms.MessagingContext method setMessageListener
  • javax.jms.MessagingContext method getMessageListener
  • javax.jms.MessagingContext method setBatchMessageListener
  • javax.jms.MessagingContext method getBatchMessageListener

This restriction means that applications running in the Java EE web or EJB container which need to receive messages asynchronously may only do so using message-driven beans.

The following methods may interfere with the container's management of connections and must not be used by applications running in the Java EE web or EJB container:

  • javax.jms.Connection method setClientID
  • javax.jms.Connection method stop
  • javax.jms.Connection method setExceptionListener
  • javax.jms.MessagingContext method setClientID
  • javax.jms.MessagingContext method stop
  • javax.jms.MessagingContext method setExceptionListener

Applications which need to use a specific client identifier must set it on the connection factory, as described in section 4.3.2 "Client Identifier"

All the methods listed in this section may throw a javax.jms.JMSException (if allowed by the method) or a javax.jms.JMSRuntimeException (if not) when called by an application running in the Java EE web or EJB container. This is recommended but not required.

Comment by Nigel Deakin [ 26/Jan/12 ]

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

The javadocs for all the methods listed above which are not permitted in a Java EE web or Java EE container now include the following statement:

* This method must not be used in a Java EE web or EJB application. 
* Doing so may cause a <code>JMSException</code> to be thrown though this is not guaranteed.

In addition, the list of exceptions for these methods now states that they may throw a JMSException (or JMSRuntimeException if called in a Java EE web or EJB container, together with the caveat that "it is not guaranteed that an exception is thrown in this case"

The three Connection methods createSession now include the statement:

* Applications running in the Java EE web and EJB containers must not attempt 
* to create more than one active (not closed) <code>Session</code> object per connection. 
* If this method is called in a Java EE web or EJB container when an active
* <code>Session</code> object already exists for this connection 
* then a <code>JMSException</code> will be thrown.

The list of exceptions for these methods states that they will (must) throw a JMSException if "this method is being called in a Java EE web or EJB application and an active session already exists for this connection."

The MessageConsumer method createMessagingContext now includes the statement:

* This method is for use in a Java SE environment or in the Java EE application client container only. 
* It may not be used in a Java EE environment because this would violate
* the Java EE restriction that a connection may have only one session.

The list of exceptions for this method now states that it will (must) throw an exception if "this method is being called in a Java EE web or EJB application"

Comment by Nigel Deakin [ 20/Mar/13 ]

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





Generated at Sat May 30 01:06:14 UTC 2015 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.