JMS message types and conversion

The choice of message type affects your approach to message conversion. The interaction of message conversion and message type is described for the JMS message types, JMSObjectMessage, JMSTextMessage, JMSMapMessage, JMSStreamMessage, and JMSBytesMessage.

JMSObjectMessage

JMSObjectMessage contains one object, and any objects that it references, serialized into a byte stream by the JVM. Text is serialized into UTF-8, and limited to strings or character arrays of no more than 65534 bytes. An advantage of JMSObjectMessage is that applications are not involved in any data conversion issues as long as they use only the methods and attributes of the object. JMSObjectMessage provides data conversion for complex objects without the application programmer considering how to encode an object in a message. The disadvantage of using JMSObjectMessage is it can be exchanged only with other JMS applications. By choosing one of the other JMS message types, it is possible to exchange JMS messages with non-JMS applications.

Sending and receiving a JMSObjectMessage shows a String object being exchanged in a message.

A JMS client application can receive a JMSObjectMessage only in a message that has a JMS-style body. The destination must specify a JMS style body.

JMSTextMessage

JMSTextMessage contains a single text string. When a text message is sent, the text Format is set to "MQSTR   ", WMQConstants.MQFMT_STRING. The CodedCharacterSetId of the text is set to the coded character set identifier defined for its destination. The text is encoded into the CodedCharacterSetId by WebSphere® MQ. The CodedCharacterSetId and Format fields are either set in the message descriptor, MQMD, or into the JMS fields in an MQRFH2. If the message is defined as having an WMQ_MESSAGE_BODY_MQ message body style, or the body style is unspecified, but the target destination is WMQ_TARGET_DEST_MQ, then the message descriptor fields are set. Otherwise the message has a JMS RFH2 and the fields are set in the fixed part of the MQRFH2.

An application can override the coded character set identifier defined for a destination. It must set the message property JMS_IBM_CHARACTER_SET to a coded character set identifier; see the example in Sending and receiving a JMSTextmessage.

When the JMS client calls the consumer.receive method queue manager conversion is optional. Queue manager conversion is enabled by setting the destination property WMQ_RECEIVE_CONVERSION to WMQ_RECEIVE_CONVERSION_QMGR. The queue manager converts the text message from the JMS_IBM_CHARACTER_SET specified for the message before transferring the message to the JMS client. The character set of the converted message is 1208, UTF-8, unless the destination has a different WMQ_RECEIVE_CCSID. The CodedCharacterSetId in the message that refers to the JMSTextMessage is updated to the target character set ID. The text is decoded from the target character set into Unicode by the getText method; see the example in Sending and receiving a JMSTextmessage.

A JMSTextMessage can be sent in an MQ-style message body, without a JMS MQRFH2 header. The value of the destination attributes, WMQ_MESSAGE_BODY and WMQ_TARGET_DEST determine the message body style, unless overridden by the application. The application can override the values set on the destination by calling destination.setMessageBodyStyle(WMQConstants.WMQ_MESSAGE_BODY_MQ) or destination.setTargetClient(WMQConstants.WMQ_TARGET_DEST_MQ).

If you send a JMSTextMessage with an MQ style body by sending it to a destination with WMQ_MESSAGE_BODY set to WMQ_MESSAGE_BODY_MQ, you cannot receive it as a JMSTextMessage from the same destination. All messages received from a destination with WMQ_MESSAGE_BODY set to WMQ_MESSAGE_BODY_MQ are received as a JMSBytesMessage. If you try to receive the message as a JMSTextMessage it causes an exception, ClassCastException: com.ibm.jms.JMSBytesMessage cannot be cast to javax.jms.TextMessage.

Note: Text in a JMSBytesMessage is not converted by the JMS client. The client can only receive the text in the message as a byte array. If queue manager conversion is enabled, the text is converted by the queue manager, but the JMS client must still receive it as a byte array in a JMSBytesMessage.

It is generally better to use the WMQ_TARGET_DEST property to control whether a JMSTextMessage is sent with an MQ or JMS body style. You can then receive the message from a destination that has either WMQ_TARGET_DEST set to WMQ_TARGET_DEST_MQ or WMQ_TARGET_DEST_JMS. WMQ_TARGET_DEST has no effect on the receiver.

JMSMapMessage and JMSStreamMessage

These two JMS message types are similar. You can read and write primitive types to the messages using methods based on the DataInputStream and DataOutputStream interfaces; see Table of message types and conversion types. The details are described in JMS client message conversion and encoding. Each primitive is tagged; see The JMS message body.

Numeric data is read and written to the message encoded as XML text. No reference is made to the destination property, JMS_IBM_ENCODING. Text data is treated the same way as text in a JMSTextMessage. If you were to look at the message contents created by the example in Figure 5, all the message data would be in EBCDIC as it was sent with a character set value of 37.

You can send multiple items in a JMSMapMessage or JMSStreamMessage.

You can retrieve the individual items of data by name from a JMSMapMessage, or by position from a JMSStreamMessage. Each item is decoded when a get or read method is called using the CodedCharacterSetId value stored in the message. If the method used to retrieve the item returns a different type to the type that was sent, the type is converted. If the type cannot be converted, an exception is thrown. See Class JMSStreamMessage for details. The example in Sending data in a JMSStreamMessage and JMSMapMessage illustrates type conversion, and getting the JMSMapMessage contents out of sequence.

The MQRFH2.format field for the JMSMapMessage and JMSStreamMessage is set to MQSTR   . If the destination property WMQ_RECEIVE_CONVERSION is set to WMQ_RECEIVE_CONVERSION_QMGR, the message data is converted by the queue manager before being sent to the JMS client. The MQRFH2.CodedCharacterSetId of the message is the WMQ_RECEIVE_CCSID of the destination. The MQRFH2.Encoding is Native. If WMQ_RECEIVE_CONVERSION is WMQ_RECEIVE_CONVERSION_CLIENT_MSG the CodedCharacterSetId and Encoding of the MQRFH2 is the value set by the sender.

A JMS client application can receive a JMSMapMessage or JMSStreamMessage only in a message that has a JMS-style body, and from a destination that does not specify an MQ style body.

JMSBytesMessage

A JMSBytesMessage can contain multiple primitive types. You can read and write primitive types to the messages using methods based on the DataInputStream and DataOutputStream interfaces; see Table of message types and conversion types. The details are described in JMS message types and conversion.

The encoding of numeric data in the message is controlled by the value of JMS_IBM_ENCODING that is set before writing numeric data to the JMSBytesMessage. An application can override the default Native encoding defined for JMSBytesMessage by setting the message property JMS_IBM_ENCODING.

Text data can be read and written in UTF-8 using the readUTF and writeUTF, or in Unicode using the readChar and writeChar methods. There are no methods that use CodedCharacterSetId. Alternatively, the JMS client can encode and decode text into bytes using the Charset class. It transfers the bytes between the JVM and message without the WebSphere MQ classes for JMS performing any conversion; see Sending and receiving text in a JMSBytesMessage.

A JMSBytesMessage sent to an MQ application is typically sent in an MQ-style message body, without a JMS MQRFH2 header. If it is sent to a JMS application, the message body style is typically JMS. The value of the destination attributes, WMQ_MESSAGE_BODY and WMQ_TARGET_DEST determine the message body style, unless overridden by the application. The application can override the values set on the destination by calling destination.setMessageBodyStyle(WMQConstants.WMQ_MESSAGE_BODY_MQ) or destination.setTargetClient(WMQConstants.WMQ_TARGET_DEST_MQ).

If you send a JMSBytesMessage with an MQ style body, you can receive the message from a destination that defines either an MQ or a JMS message body style. If you send a JMSBytesMessage with a JMS style body, then you must receive the message from a destination that defines a JMS message body style. If you do not, the MQRFH2 is treated as part of the user message data, which might not be what you are expecting.

Whether a message has an MQ or a JMS body style, the way it is received is not affected by setting WMQ_TARGET_DEST.

The message might be transformed later, by the queue manager, if a Format is supplied for the message data, and queue manager data conversion is enabled. Do not use the format field for anything other than specifying the format of the message data, or leave it blank, MQConstants.MQFMT_NONE

You can send multiple items in a JMSBytesMessage. Each numeric item is converted when the message is sent using the encoding defined for the message.

You can retrieve the individual items of data from JMSBytesMessage. Call read methods in the same order as the write methods were called to create the message. Each numeric item is converted when the message is called using the Encoding value stored in the message.

Unlike JMSMapMessage and JMSStreamMessage, JMSBytesMessage contains only data written by the application. No additional data is stored in the message data, such as the XML tags used to define the items in a JMSMapMessage and JMSStreamMessage. For this reason, use JMSBytesMessage to transfer messages formatted for other applications.

Converting between JMSBytesMessage and DataInputStream and DataOutputStream is useful in some applications. Code based on the example, Reading and writing messages using DataInputStream and DataOutputStream, is necessary to use the com.ibm.mq.header package with JMS.

Examples

Sending and receiving a JMSObjectMessage

Figure 1. Sending and receiving a JMSObjectMessage
ObjectMessage omo = session.createObjectMessage();
omo.setObject(new String("A string"));            
producer.send(omo);
...
ObjectMessage omi = (ObjectMessage)consumer.receive();
System.out.println((String)omi.getObject());
...
A string

Sending and receiving a JMSTextmessage

A text message cannot contain text in different character sets. The example shows text in different character sets, sent in two different messages.

Figure 2. Send text message in the character set defined by the destination
TextMessage tmo = session.createTextMessage();
tmo.setText("Sent in the character set defined for the destination");
producer.send(tmo);
Figure 3. Send text message in ccsid 37
TextMessage tmo = session.createTextMessage();
tmo.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, 37);
tmo.setText("Sent in EBCDIC character set 37");
producer.send(tmo);
Figure 4. Receive text message
TextMessage tmi = (TextMessage)consumer.receive();
System.out.println(tmi.getText());
...
Sent in the character set defined for the destination

Sending data in a JMSStreamMessage and JMSMapMessage

Figure 5. Send data in JMSStreamMessage and JMSMapMessage
StreamMessage smo = session.createStreamMessage();
smo.writeString("256");
smo.writeInt(512);
smo.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, 37);
producer.send(smo);
...
MapMessage mmo = session.createMapMessage();
mmo.setString("First", "256");
mmo.setInt("Second", 512);
mmo.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, 37);
producer.send(mmo);
...
StreamMessage smi = (StreamMessage)consumer.receive();
System.out.println("Stream: First as float "   + smi.readFloat() + 
                          " Second as String " + smi.readString());
...
Stream: First as float: 256.0, Second as String: 512
...
MapMessage mmi = (MapMessage)consumer.receive();
System.out.println("Map: Second as String " + mmi.getString("Second") + 
                       " First as double "  + mmi.getDouble("First"));
...
Map: Second as String: 512, First as double: 256.0

Sending and receiving text in a JMSBytesMessage

The code in Figure 6 sends a string in a BytesMessage. For simplicity, the example sends a single string, for which a JMSTextMessage is more appropriate. To receive a text string in bytes message containing a mixture of types, you must know the length of the string in bytes, called TEXT_LENGTH in Figure 7. Even for a string with a fixed number of characters, the length of the byte representation might be longer.

Figure 6. Sending a String in a JMSBytesMessage
BytesMessage bytes = session.createBytesMessage();
String codePage = CCSID.getCodepage(((MQDestination) destination)
                  .getIntProperty(WMQConstants.WMQ_CCSID));
bytes.writeBytes("In the destination code page".getBytes(codePage));
producer.send(bytes);
Figure 7. Receiving a String from a JMSBytesMessage
BytesMessage message = (BytesMessage)consumer.receive();
int TEXT_LENGTH = new Long(message.getBodyLength())).intValue();
byte[] textBytes = new byte[TEXT_LENGTH];
message.readBytes(textBytes, TEXT_LENGTH);
String codePage = message.getStringProperty(WMQConstants.JMS_IBM_CHARACTER_SET);
String textString = new String(textBytes, codePage);

Reading and writing messages using DataInputStream and DataOutputStream

The code in Figure 8 creates a JMSBytesMessage using a DataOutputStream.

Figure 8. Send a JMSBytesMessage using a DataOutputStream
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
BytesMessage messageOut = prod.session.createBytesMessage();
// messageOut.setIntProperty(WMQConstants.JMS_IBM_ENCODING, 
//                          ((MQDestination) (prod.destination)).getIntProperty
//                          (WMQConstants.WMQ_ENCODING));
int ccsidOut = (((MQDestination)prod.destination).getIntProperty(WMQConstants.WMQ_CCSID));
String codePageOut = CCSID.getCodepage(ccsidOut);
dout.writeInt(ccsidOut);
dout.write(codePageOut.getBytes(codePageOut));
messageOut.writeBytes(bout.toByteArray());
producer.send(messageOut);

The statement that sets the JMS_IBM_ENCODING property is commented out. The statement is valid, if writing directly to a JMSBytesMessage, but has no effect when writing to DataOutputStream. Numbers that are written to the DataOutputStream are encoded in Native encoding. Setting JMS_IBM_ENCODING has no effect.

The code in Figure 9 receives a JMSBytesMessage using a DataInputStream.

Figure 9. Receive a JMSBytesMessage using a DataInputStream
static final int ccsidIn_SIZE = (Integer.SIZE)/8;
...
connection.start();
BytesMessage messageIn = (BytesMessage) consumer.receive();
int messageLength = new Long(messageIn.getBodyLength()).intValue();
byte [] bin = new byte[messageLength];
messageIn.readBytes(bin, messageLength);
DataInputStream din = new DataInputStream(new ByteArrayInputStream(bin));
int ccsidIn = din.readInt();
byte [] codePageByte = new byte[messageLength - ccsidIn_SIZE];
din.read(codePageByte, 0, codePageByte.length);
System.out.println("CCSID " + ccsidIn + " code page " + new String(codePageByte, 
                    messageIn.getStringProperty(WMQConstants.JMS_IBM_CHARACTER_SET)));

The code page is printed out using the code page property of the input message data, JMS_IBM_CHARACTER_SET. On input JMS_IBM_CHARACTER_SET is a Java code page and not a numeric coded character set identifier.

Table of message types and conversion types

Table 1. Message types and conversion types
  Conversion type
Message type Text Numeric Other None
JMSObjectMessage
     
getObject
setObject
JMSTextMessage
getText
setText
     
JMSBytesMessage
readUTF
writeUTF
readDouble
readFloat
readInt
readLong
readShort
readUnsignedShort
writeDouble
writeFloat
writeInt
writeLong
writeShort
readBoolean
readObject
writeBoolean
writeObject
readByte
readUnsignedByte
readBytes
readChar
writeByte
writeBytes
writeChar
JMSStreamMessage
readString
writeString
readDouble
readFloat
readInt
readLong
readShort
writeDouble
writeFloat
writeInt
writeLong
writeShort
readBoolean
readObject
writeBoolean
writeObject
readByte
readBytes
readChar
writeByte
writeBytes
writeChar
JMSMapMessage
getString
setString
getDouble
getFloat
getInt
getLong
getShort
setDouble
setFloat
setInt
setLong
setShort
getBoolean
getObject
setBoolean
setObject
getByte
getBytes
readChar
setByte
setBytes
setChar