jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: [jms-spec users] Re: JMS Support for DI

From: Rüdiger zu Dohna <ruediger.dohna_at_1und1.de>
Date: Fri, 11 Nov 2011 15:49:17 +0100

On 11.11.2011, at 15:27, Nigel Deakin wrote:

Clebert,

On 10/11/2011 15:31, Clebert Suconic wrote:

What I just thought also was adding the idea of a context.

in 90% of the cases I have seen, users are always creating a consumer within a single Session. (because you have also
the requirement of a session per thread, per the spec).

I'm not sure I follow. A Consumer is always tied to a single Session. Do you mean that users are always creating one Consumer per Session?


It's easier to say this in Javanes than English. What I see users doing most of time is this:


Connection connReceiver = cf.createConnection(....);
Session sessionReceiver = connReceiver.createSession(....);
Consumer receiver = sessionReceiver = sessionReceiver.createConsumer(...);


i.e. a consumer will have a single connection and single session.


Actually, what I see a lot also is this, through JMSTemplate (which I hate it, since it teaches users to use message systems synchronously):


Connection connReceiver = cf.createConnection(....);
Session sessionReceiver = connReceiver.createSession(....);
Consumer receiver = sessionReceiver = sessionReceiver.createConsumer(...);
Message message = receiver.receive();
connReceiver.close();

** There is some caching done eventually on JMSTEmplate... but I don't want to discuss the template itself.



What I'm saying is.. that in 90% of the cases, users will have a 1 to 1 relationship between:

- consumer <-> session <-> connection
- producer <-> session <-> connection.


By suggesting a "context" object you seem to be re-inventing the Session, so I don't see the benefit.


The context / session would be encapsulated. This method would only be used on the cases where you need more than one object at the same context (or session if you prefer to call it that way).

On the supposedly new API, doing this:

NewConsumer cons = connectionFactory.createConsumer(... queue1);
NewProducer prod = connectionFactory.createProducer(....queue2);



Will have you creating two contexts.


If you want a commit call on cons to also commit prod, you would need both as part of the same context.


You could either do something like:


prod = cons.getContext().createProducer();




Or, you could have it encapsulated...

cons.createSameContextProducer(...);


I guess I'm proposing merging connection, session and producer altogether. And connection, session and consumer.

And the connections.. etc.. would be encapsulated.


I'm just trying to solve the scenario with 2 objects as part of the same local transaction, without requiring XA.

(again.. just a brainstorm).

OK, so just to compare your suggestion with mine (and thinking of Java EE only):

For sending messages:
We're both proposing merging connection, session and producer.
I've suggested we call the consolidated object a "newConnection" or perhaps a "context" (which is a meaningless word we haven't used in JMS yet), you suggest calling it a "producer".

For synchronously receiving messages:
I'm proposing merging connection and session to give the same "newConnection" or "context" object as before. However I think we still need a separate "producer" object, one for each destination, for the reasons I explained in my other mail: to allow the application to signify the intention to receive message prior to actually calling receive().

If you want to send a message and synchronously consume a message in the same transaction, in my proposal you would perform:

NewConnection newConnection = connectionFactory.createConnection(false,Session.AUTO_ACKNOWLEDGE);
NewConsumer newConsumer = newConnection.startReceiving(queue1,messageListener,messageSelector,noLocal);
Message someMessage1 = newConsumer.receive(timeout);
TextMessage textMessage = newConnection .createTextMessage(payload);
NewConnection.send(queue2,textMessage,DeliveryMode.PERSISTENT,0,0);

In your proposal you would perform:

NewConsumer cons = connectionFactory.createConsumer(queue1,false,Session.AUTO_ACKNOWLEDGE);
Context context = cons.getContext();
NewProducer prod = cons connectionFactory.createProducer(queue2);
TextMessage textMessage = // where will createMessage go?
prod.send(textMessage);

I think my proposal is simpler in concept: your suggestion would need createProducer and createConsumer in two places, on the connectionFactory and on the Context, which I don't think is an improvement.

(The above examples assume the transaction is committed by the transaction manager, and since they use the same underlying session there's no need for two-phase commit)

Nigel



How about:

NewConnection connection = connectionFactory.open(false); // this is for sending only!
NewConsumer consumer = connection.createConsumer(Session.AUTO_ACKNOWLEDGE, // this is for receiving only!
            queue1, messageSelector, ...);
Message incoming = consumer.receive(timeout);
TextMessage outgoing = connection.createTextMessage(payload);
connection.send(outgoing, queue2, ...);


And this is not limited to the Java EE case, is it? If there is a JTA around, you'd call "open" without the "transacted" boolean and "createConsumer" without the acknowledgement mode. Without it, you'd have to manage transactions by hand, as in the code above, which is easy, since it's all one connection, right?