On 09/01/2012 17:35, Nigel Deakin wrote:
> I refer to this issue:
> http://java.net/jira/browse/JMS_SPEC-65
This was discussed and agreed some time ago before the early draft, but I'd like to re-open discussion of a possible
clarification.
You will remember that this issue was 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."
(
http://jms-spec.java.net/2.0-SNAPSHOT/apidocs/javax/jms/Session.html#createDurableSubscriber%28javax.jms.Topic,%20java.lang.String,%20java.lang.String,%20boolean%29)
However it has been pointed out to me that further clarification may be needed. Please read the following discussion,
and review the proposed amendments at the end.
You may find it easier to review the formatted version that I have added to the JIRA issue at
http://java.net/jira/browse/JMS_SPEC-65#action_339660
When you've had a look, please let me know what you think.
Nigel
---------------------------------------------------------------------------------------------------------------------
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.
Any comments? Do you agree?