jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: [jms-spec users] Re: (JMS_SPEC-101) New method Message.getPayload(Class<T> c)

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Wed, 24 Oct 2012 12:04:05 +0100

John,

On 24/10/2012 01:22, John D. Ament wrote:
> Nigel,
>
> That byte[].class thing seems new to me. Anyways yes it does apparently work so thank you for fixing my mis-step.
>
> So far it's looking good. One thing that does stick out to me is that JPA has a similar method on the EntityManager
> interface - unwrap(Class c).
> http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#unwrap(java.lang.Class)
> <http://docs.oracle.com/javaee/6/api/javax/persistence/EntityManager.html#unwrap%28java.lang.Class%29> - This class
> allows access to lower level objects. I'm wondering if a better for the method is "unwrapPayload," or something along
> those lines. Are we "unwrapping" the underlying payload in this case (for example, maybe the implementation needs to
> deserialize the object on this method's invocation?).

The word "payload" already implies the idea that it is a wrapped object, so I think unwrapPayload would be a tautology.

However, having just checked the spec, I see that the word "payload" is my own invention. Although the word "payload"
appears 45 times in the 2.0 spec, it doesn't appear at all in the 1.1 spec. The term used in 1.1 is "body" (e.g. in
methods such as clearBody()). This makes me wonder whether I should replace all references to "payload" with "body". So
this new method on Message would become "getBody" and the receivePayload() methods on JMSConsumer would become
receiveBody(). Hmm.

Does anyone have any comments on that? Is a mix of "payload" and "body" OK, or should I make everything "body" consistently?

>
> I know you mentioned the JMSConsumer class needing change as well, is there any benefit to adding a "setPayload" to
> the Message interface to be able to set the payload directly and receive a class cast exception if it wasn't the right
> type? I also experimented with this a while ago - if the Message interface was parameterized, TextMessage could
> extend Message<String> so that the expected type would be a String (compilation wise), ObjectMessage could extend
> Message<Serializable> and so forth. I only got mixed reactions to this idea, so not sure if it actually is a big
> benefit or just a little thing that may make a few things easier. StreamMessage was the one I ended up getting stuck
> on. Since we're essentially using a DataInputStream & DataOutputStream could we just use those as the types perhaps
> (and allow the implementation to "fix" how they're handled internally?)

I don't understand exactly what you are proposing. Can you give a code fragment or two to explain? Are you suggesting a
set of new methods on Session (and JMSContext) as follows:

Message createMessage(String string);
Message createMessage(Serializable object);
Message createMessage(byte[] bytes);
Message createMessage(Map map);

If we did this we would probably also want to add
BytesMessage createBytesMessage(byte[] bytes)
MapMessage createMapMessage(Map map)
to add to the existing methods
ObjectMessage createObjectMessage(Serializable object);
TextMessage createTextMessage(String string);

(these are all methods on Session and on JMSContext)

>
> One other question - just because I have it on the top of my head from prior work, if I were using WebLogic as my JMS
> implementation and the underlying message type was their XMLMessage type; can I pass in org.w3c.dom.Document.class to
> my Message object and have it return the appropriate result? Or is that too container specific of a question/up to
> the implementer?

Interesting point. None of the JMS-defined message types allow a Document as the payload (Document does not extend
Serializable); a message object which allows it is not a JMS object, even if it implements javax.jms.Message. If the
message object was a JMS object (i.e. one of the defined message types) then calling
getPayload(org.w3c.dom.Document.class) would cause a MessageFormatException - and this could be enforced in a CTS test.

However since a WebLogic XMLMessage isn't a JMS object, it's completely up to the vendor to decide how it behaves. There
would never be a CTS test for this. A vendor must not claim that this is JMS (and of course WebLogic doesn't).

Nigel

>
> Thanks,
>
> John
>
> On Tue, Oct 23, 2012 at 2:01 PM, Nigel Deakin <nigel.deakin_at_oracle.com <mailto:nigel.deakin_at_oracle.com>> wrote:
>
> On 23/10/2012 15:07, John D. Ament wrote:
>
> Ok, so looking throught it a bit more I think we have a few issues
>
> 1. You cannot pass byte[].class to the method (that's not a valid class). I think we need a work around here.
>
>
> You can, actually :-)
>
>
> 2. I think the case should simply pass when Class.isAssignableFrom(Otherclass.class) is passed.
>
>
> I like this general approach: the implementation extracts the payload, calls isAssignableFrom to see if this can
> be cast to the required return type, and then performs the cast.
>
> (If we adopt this approach we should change the JMSConsumer receivePayload methods to work in the same way)
>
>
> > So if I pass in
>
> CharSequence.class, I should be able to get back a CharSequence in the following cases:
>
>
> - The message is a TextMessage, the underlying result is a String back (returned as a CharSequence instead)
>
> >
>
> - The message is an ObjectMessage, the underlying type is an implementation of CharSequence (StringBuilder,
> StringBuffer, String).
>
>
> Yes.
>
>
> IMHO from the initial messages about this method it seemed like the goal was to make the client ignorant (lack
> of a
> better term) to the fact that it's a text message or object message or map message.
>
>
> > Also passing in Object.class should
>
> just return the payload, regardless of type, as long as it's not a StreamMessage.
>
> Thoughts?
>
>
> Yes, though in this case the returned type would be an Object, so the application would need to perform a cast to
> do anything useful with the payload.
>
> Here's an updated javadoc. More views please!
>
> /**
> * Returns the messages's payload, which must be assignable to the specified
> * type. If the message has no payload then null is returned. This method
> * may be used to obtain the payload of any type of message except for
>
> * <tt>StreamMessage</tt>.
> *
> * @param c
> * The class of the payload.
> * <br/>
>
> * If the message is a <code>TextMessage</code> then this should
> * be set to <code>String.class</code> or any other class to
> * which a String is assignable.
> * <br/>
>
> * If the message is a <code>ObjectMessage</code> then this
> * should be set to <code>java.io.Serializable.class</code> or
> * any other class to which the payload is assignable.
>
> * <br/>
> * If the message is a <code>MapMessage</code> then this should
> * be set to <code>java.util.Map.class</code>.
> * <br/>
>
> * If the message is a <code>BytesMessage</code> then this should
> * be set to <code>byte[].class</code>.
> * <br/>
> * If the message is a <code>StreamMessage</code>, or the payload
> * cannot be assigned to the specified type a
>
> * <code>MessageFormatException</code> will be thrown
> *
> * @return the messages's payload
> *
> * @exception JMSException
> * if the JMS provider fails to get the payload due to some
> * internal error.
> * @exception MessageFormatException
> * if the message is a <code>StreamMessage</code>, or the
> * payload cannot be assigned to the specified type, or the
> * message is an ObjectMessage and object deserialization
>
> * fails.
> * @Exception MessageNotReadableException - if the message is a BytesMessage
> * and the message is in write-only mode.
> */
> <T> T getPayload(Class<T> c) throws JMSException;
>
> Nigel
>
>