users@jms-spec.java.net

[jms-spec users] Re: Sending a previously-received Message and supporting JMSXUserID

From: Robbie Gemmell <robbie.gemmell_at_gmail.com>
Date: Thu, 12 Dec 2013 20:59:57 +0000

Hi Nigel,


On 12 December 2013 19:20, Nigel Deakin <nigel.deakin_at_oracle.com> wrote:

> Robbie,
>
>
> On 12/12/2013 17:01, robbie.gemmell_at_gmail.com wrote:
>
>> Hi all,
>>
>> I wondered if anyone could shed some light on a question I have around
>> handling of JMSXUserID (and JMSXAppID as well, I guess).
>>
>> The JMS-defined JMSXUserID property is optional, but if supported is of
>> the 'set by Provider on Send' variety. The spec says that properties
>> are read-only when a Message is received. What then is the appropriate
>> action to take when supporting JMSXUserID and sending a
>> previously-received Message?
>>
>
> Hmm. Interesting question. Here are my initial thoughts on this:
>
> I would expect the JMS provider's send() implementation to set the
> JMSXUserID property on the message passed to send(), prior to actually
> sending the message. If the message passed to send() is its own
> implementation then it can use whatever method it likes to do this. This is
> just one part of the provider calling another part of the same provider and
> so there is no requirement to do this using the JMS API. So even if the
> message properties are read-only the JMS provider can easily by-pass this.
>

I did think that at one point (In my case would be doing the same thing as
I mention in option 2 regarding "just set the property directly on the
underlying message", but without the copy first) and it does seem to be the
only clean way to handle our own messages without property-clearing, I
would just have preferred to treat all messages equally while handling the
spec-required steps in relation to setting headers etc (but without doing
what you suggested below to our own messages just for the sake of making it
the same for all).


>
> Different issues arise if the message passed to send() is a "foreign"
> implementation which was received using a different JMS provider. In this
> case the JMS provider's send() implementation will need to call the JMS
> standard method setStringProperty("JMSXUserID",userID).
>
> As you say, if the message properties are read-only then this will throw a
> MessageNotWriteableException. (I think requiring this even for JMSX
> properties that are set by the provider is a flaw in the spec, but never
> mind...)
>
>
I agree it seems like a flaw, as if it should really have been defined as a
header or been given explicit exemption from the read-only restriction.


> So the JMS provider's send() implementation will need to work around this
> by taking a copy of any existing properties, calling clearProperties(),
> restoring the saved properties and then calling
> setStringProperty("JMSXUserID",userID).
>
> An alternative would be to be optimistic and simply call
> setStringProperty. If a MessageNotWriteableException was thrown would it be
> necessary to take a copy of any existing properties, call
> clearProperties(), restore the saved properties and then call
> setStringProperty() again.
>
> I agree this doesn't sound very elegant, but none of this inelegance is
> exposed to the application. It's all internal to the JMS provider.
>
>
I thought that might be the suggestion though I somewhat hoped it wouldn't
be :)

I didn't really like it and so didn't suggest it due to the side effect of
changing the message properties to become writable without any way to set
them read-only again (it would be really nice to be able to control
write-access without clearing the existing properties..), thus going
against what the spec says will be true of the recieved message. However,
it was also the only thing I could think of that would ensure ability to
actually set the property on an original foreign message.


>
>
>> I have searched through the spec and looked at a few implementations to
>> see what they do, but haven't as yet really been able to settle on an
>> answer to my question. Is there a clarifying area of the spec that I
>> have missed? Any other thoughts?
>>
>> I can see there are various somewhat ugly options, e.g:
>>
>> 1. Since the spec says properties are read-only in that situation,
>> accept this also includes properties the spec defines a provider itself
>> sets on send and let the send methods throw
>> MessageNotWriteableException. Require that users clear and re-set all
>> the properties of recieved Message objects before trying to send them
>> if JMSXUserID support is enabled.
>>
>
> I don't see why "users" need to worry about this case at all. The JMS
> provider itself can (and should) do all the work.
>
>
The main reason I left the suggestion as above instead of 'we do this bit
for the application during send' was the side effect mentioned above.
Admittedly the spec says applications shouldn't be trying to set properties
at that point anyway, and so it shouldnt really cause a problem, but alas
not everyone reads specs/API's as closely as they could reasonably be
expected to. From a 'does what that user expected' perspective when sending
a received message and finding the correct headers and properties set on
it, that does however seem the best option in hindsight.


>
> 2. Copy the Message within the send method to get something writable
>> and set the property on that (or possibly just set the property
>> directly on the underlying message being sent, e.g using a specific
>> field of an underlying protocols native message format), allowing the
>> value to be sent but not actually setting it on the Message object
>> originally provided.
>>
>
> That wouldn't work because the spec says that "JMSX properties ‘set by
> provider on send’ are available to both the producer and the consumers of
> the message."
>
> This means that the JMS provider needs to set the JMSXUserID property on
> the exact same message object that was passed to the send() method. After
> the send() method returns the application needs to be able to call
> getStringProperty() to read the value.
>
>
Yep, I understood that, which is why this one is definitely super-ugly and
a spec breaker. I probably wouldn't even have thought of it if I hadn't
noticed that the RI seems to do it.

Ultimately I consider everything mentioned so far to break the intention of
the spec in some way or other (except magically setting of the property on
our own messages without actually using the JMS methods, which is really
just an implementation choice), its just a case of how much.. option 2) and
3) are clearly the worst offenders by far though.


>
> 3. Try to set the property, catch the MessageNotWriteableException,
>> continue to send without the value (or potentially with the wrong value
>> if it already existed).
>>
>
> If a MessageNotWriteableException is thrown the JMS provider's send method
> can handle this correctly, as described earlier.
>
>
>
>> The JMS 2.0 reference implementation appears to do a combination of 1)
>> and 2), though I admit I didn't actually test it in practice. If
>> support for JMSXUserID is enabled (looks to be disabled by default) it
>> seems like it would throw a MessageNotWriteableException from the send
>> method, so long as the message was one of its own. If the Message was
>> from a foreign provider, it seems like it would perform a conversion
>> and then succeed in setting the property on the converted message and
>> not even attempt setting it on the original.
>>
>
> I don't think the RI sets the JMSXUserID property anywhere, but I haven't
> looked very hard.
>
> Nigel
>

It is rather buried away, occurring in a different class
(com.sun.messaging.jmq.jmsclient.ProtocolHandler) well after most of the
header manipulation and message conversion occurs in the producer, which is
why it looks to react differently to which providers message is supplied:
        //set JMSXUserID if requested
        if (setJMSXUserID) {
            message.setStringProperty(ConnectionMetaDataImpl.JMSXUserID,
                                      jmsxUserID);
        }


Many thanks for your thoughts,

Robbie