users@jms-spec.java.net

[jms-spec users] Re: Confusion about JMSProducer methods and CDI

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Mon, 09 Jun 2014 11:40:13 +0100

Elias,

On 05/06/2014 19:29, Elias Ross wrote:
> I'm really confused by this because this code doesn't do what you might expect:
>
> JMSProducer p = JMSContext.createProducer();
> p.setTimeToLive(1000);
> p.send(...)
> JMSProducer p2 = JMSContext.createProducer();
> p2.send(...) // time to live is set to 1000 here!
>
> The settings from JMSProducer p are carried over to p2.
>
> What's the point of having two producers here? The JavaDoc says "new
> producer" but in fact the settings are carried over from the old
> producer object.

That isn't right. Settings on one JMSProducer shouldn't affect the state of another.

>
> I was using HornetQ, but it looks like the reference implementation
> does do the same thing, though, but to me it seems really unintuitive.

If you think you have found a bug in HornetQ you should log it with the team that develops it.

I'm pretty sure the RI (Open MQ) does the right thing, but if you think you have found a bug in the RI please log it at
https://java.net/jira/browse/MQ

> The JavaDocs state that JMSContext represents a connection and
> session,

Yes

> and JMSContext represents a producer.

The javadocs for JMSProducer state that "A JMSProducer is a simple object used to send messages on behalf of a JMSContext"

https://jms-spec.java.net/2.0/apidocs/javax/jms/JMSProducer.html

>
> The RI has it as:
>
> public class JMSContextImpl implements JMSContext, Traceable {
> ....
> MessageProducer messageProducer;

Yes, this is how the RI implements the requirement (also stated in the javadocs for JMSProducer) that "Instances of
JMSProducer are intended to be lightweight objects which can be created freely and which do not consume significant
resources. This interface therefore does not provide a close method."

So in the RI the JMSProducer is actually just a collection of properties and delivery settings (including timeToLive),
so that instances can be created and discarded freely without the need for any cleanup.

As you have spotted, in the RI the JMSContextImpl object contains a cached MessageProducer object. When the
JMSProducer's send() method is called, all the properties set on that JMSProducer (including timeToLive) are copied to
this MessageProducer object, and then this MessageProducer object is used to send the message.

The implementation is here:
https://java.net/projects/mq/sources/mq5/content/mq/main/mq-client/src/main/java/com/sun/messaging/jmq/jmsclient/JMSProducerImpl.java

Other implementations may work differently (and may need to be more optimised for performance).

(Detailed discussion of Open MQ should probably take place over in the MQ user forum. Sign up details at
https://java.net/projects/mq/lists)

>
> The other question I had was the scoping for CDI. For example:
>
> @ApplicationScoped
> public class MyClient {
> @Inject JMSContext context;
> }
>
> public class MyServlet extends HttpServlet {
> @Inject MyClient client;
> }
>
> Then JMSContext ends up being potentially shared between multiple
> threads. I would expect the scoping of JMSContext to be request, not
> @Dependent here.
>

Why do you say that the "JMSContext ends up being potentially shared between multiple threads"?

The scope of the injected JMSContext is defined in the JMS 2.0 spec, section 12.4.4. "Scope of injected JMSContext
objects". Essentially, if there's a transaction in progress then the injected JMSContext will have transaction scope,
otherwise it will have request scope.

It looks like you're in a servlet here, and you haven't started a transaction, so the injected JMSContext must have
request scope.

Nigel