JMS 客户机消息转换和编码

列出了用于执行 JMS 客户机消息转换和编码的方法以及每种转换类型的代码示例。

当在 JMS 消息中读取或写入 Java 原语或对象时,会进行转换和编码。 该转换称为 JMS 客户机数据转换,以将其与队列管理器数据转换和应用程序数据转换区分开来。 当从 JMS 消息读取数据或将数据写入消息时,将严格执行转换。 文本将在内部 16 位 Unicode 表示法 1 之间转换为用于消息中文本的字符集。 数字数据将转换为 Java 基本数字类型,并转换为针对消息定义的编码。 是否执行转换以及执行的转换类型取决于 JMS 消息类型以及读或写操作。

表 1 按执行的转换类型对不同 JMS 消息类型的读写方法进行分类。 下表后的文本描述了转换类型。
表 1. 消息类型和转换类型
  转换类型
消息类型 文本 数字 其他 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
文本

目标的缺省 CodedCharacterSetId 是 1208,UTF-8。 缺省情况下,将从 Unicode 转换文本,并作为 UTF-8 文本字符串进行发送。 接收时,文本将从客户机接收的消息中的编码字符集转换为 Unicode。

setTextwriteString 方法会将文本从 Unicode 转换为针对目标定义的字符集。 应用程序可通过设置消息属性 JMS_IBM_CHARACTER_SET,来覆盖目标字符集。 JMS_IBM_CHARACTER_SET,发送消息时必须是数字编码字符集标识 2

发送和接收 JMSTextmessage 中的代码片段会发送两条消息。 一条在为目标定义的字符集中发送,另一条在应用程序定义的字符集 37 中发送。

getTextreadString 方法可将消息中的文本从消息中定义的字符集转换为 Unicode。 该方法使用消息属性 JMS_IBM_CHARACTER_SET 中定义的代码页。 除非消息为 MQ 型消息且不包含 MQRFH2,否则将从 MQRFH2.CodedCharacterSetId 映射代码页。 如果消息为 MQ 型消息且无 MQRFH2,那么将从 MQMD.CodedCharacterSetId 映射代码页。

图 5 中的代码片段接收发送到目标的消息。 消息中的文本将从代码页 IBM®037 转换回 Unicode。
注: 检查文本是否转换为编码字符集 37 的简单方法是使用 IBM MQ Explorer。 浏览队列并显示消息属性,然后进行检索。

图 4 中的代码片段与 图 1中不正确的代码片段进行对比。 在不正确的片段中,文本字符串将转换两次,一次由应用程序转换,再次由 IBM MQ转换。

图 1。 不正确的代码页转换
TextMessage tmo = session.createTextMessage();
tmo.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, 37);
tmo.setText(new String("Sent in EBCDIC character set 37".getBytes(CCSID.getCodepage(37))));
producer.send(tmo);

writeUTF 方法可将文本从 Unicode 转换为 1208,UTF-8。 文本字符串以双字节长度开头。 文本字符串的最大长度是 65534 字节。 readUTF 方法可读取由 writeUTF 方法写入的消息中的项。 它会精确读取由 writeUTF 方法写入的字节数。

数字

目标的缺省数字编码为 NativeJavaNative 编码常量具有值 273 x'00000111',这对于所有平台都是相同的。 在接收时,消息中的数字将正确转换为数字 Java 原语。 转换过程使用消息中定义的编码以及由读取方法返回的类型。

发送方法可将由 setwrite 添加到消息中的数字转换为针对目标定义的数字编码。 设置消息属性的应用程序可以为消息覆盖目标编码 JMS_IBM_ENCODING;例如:
message.setIntProperty(WMQConstants.JMS_IBM_ENCODING, WMQConstants.WMQ_ENCODING_INTEGER_REVERSED);

getread 数字方法可从消息中定义的数字编码转换消息中的数字。 它们将数字转换为 readget 方法指定的类型; 请参阅 ENCODING 属性。 这些方法使用 JMS_IBM_ENCODING 中定义的编码。 除非消息为 MQ 型消息且不包含 MQRFH2,否则将从 MQRFH2.Encoding 映射编码。 如果消息为 MQ 型消息且无 MQRFH2,那么这些方法将使用 MQMD.Encoding 中定义的编码。

图 6 中的示例显示了以目标格式编码数字并在 JMSStreamMessage中发送该数字的应用程序。 将 图 6 中的示例与 图 7中的示例进行比较。 区别在于必须在 JMSBytesMessage中设置 JMS_IBM_ENCODING
注: 检查数字是否正确编码的简单方法是使用 IBM MQ Explorer。 浏览队列并显示消息属性,然后进行使用。
其他

boolean 方法将 truefalse 编码为 JMSByteMessageJMSStreamMessageJMSMapMessage中的 x'01'x'00'

UTF 方法可将 Unicode 编码和解码为 UTF-8 文本字符串。 字符串限于少于 65536 个字符,并以双字节长度字段开头。

Object 方法可将原语类型包装为对象。 系统会对数字和文本类型进行编码或转换,如同使用数字和文本方法读写原语类型一样。

None

readBytereadBytesreadUnsignedBytewriteBytewriteBytes 方法可在不进行转换的情况下,在应用程序与消息间获取或放置单个字节或字节数组。 readCharwriteChar 方法可在不进行转换的情况下,在应用程序与消息间获取和放置双字节 Unicode 字符。

通过使用 readByteswriteBytes 方法,应用程序可以执行自己的代码点转换,如 在 JMSBytesMessage

IBM MQ 不会在客户机中执行任何代码页转换,因为消息是 JMSBytesMessage,并且由于使用了 readByteswriteBytes 方法。 但是,如果字节表示文本,请确保应用程序使用的代码页与目标的编码字符集匹配。 消息可能重新由队列管理器转换出口进行转换。 另一种可能性是,接收 JMS 客户机程序可能遵循以下约定: 使用消息中的 JMS_IBM_CHARACTER_SET 属性将表示消息中文本的任何字节数组转换为字符串或字符。

在该示例中,客户机使用目标编码字符集进行转换:

bytes.writeBytes("In the destination code page".getBytes(
CCSID.getCodepage(((MQDestination) destination)
.getIntProperty(WMQConstants.WMQ_CCSID))));
或者,客户机可能已选择代码页,然后在消息的 JMS_IBM_CHARACTER_SET 属性中设置相应的编码字符集。 IBM MQ classes for Java 使用 JMS_IBM_CHARACTER_SETMQRFH2或消息描述符 MQMD中的 JMS 属性中设置 CodedCharacterSetId 字段:
String codePage = CCSID.getCodepage(37);
message.setIntProperty(WMQConstants.JMS_IBM_CHARACTER_SET, codePage);
3

如果将字节数组写入 JMSStringMessageJMSMapMessage,那么 IBM MQ classes for JMS 不会执行数据转换,因为字节被输入为十六进制数据,而不是作为 JMSStringMessageJMSMapMessage中的文本。

如果字节表示应用程序中的字符,那么您必须考虑要对消息读写哪些代码点。 图 2 中的代码遵循使用目标编码字符集的约定。 如果使用缺省字符集为 JVM 创建字符串,那么字节内容将取决于平台。 Windows 上的 JVM 通常具有缺省 Charset windows-1252UNIXUTF-8WindowsUNIX 之间的交换要求您选择用于以字节形式交换文本的显式代码页。

图 2。 在 JMSStreamMessage 中使用目标字符集写入表示字符串的字节
StreamMessage smo = producer.session.createStreamMessage();
smo.writeBytes("123".getBytes(CCSID.getCodepage(((MQDestination) destination)
.getIntProperty(WMQConstants.WMQ_CCSID))));

示例

发送和接收 JMSTextmessage

文本消息无法包含不同的字符集中的文本。 该示例显示了位于不同的字符集中且在两条不同的消息中发送的文本。

图 3。 在目标定义的字符集中发送文本消息
TextMessage tmo = session.createTextMessage();
tmo.setText("Sent in the character set defined for the destination");
producer.send(tmo);
图 4: 在 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);
图 5。 接收文本消息
TextMessage tmi = (TextMessage)consumer.receive();
System.out.println(tmi.getText());
...
Sent in the character set defined for the destination

编码示例

这些事例显示在为目标定义的编码中发送的数字。 请注意,必须将 JMSBytesMessageJMS_IBM_ENCODING 属性设置为对目标指定的值。

图 6。 在 JMSStreamMessage 中使用目标编码发送数字
StreamMessage smo = session.createStreamMessage();
smo.writeInt(256);
producer.send(smo);
...
StreamMessage smi = (StreamMessage)consumer.receive();
System.out.println(smi.readInt());
...
256
图 7。 在 JMSBytesMessage 中使用目标编码发送数字
BytesMessage bmo = session.createBytesMessage();
bmo.writeInt(256);
int encoding = ((MQDestination) (destination)).getIntProperty
    (WMQConstants.WMQ_ENCODING)
bmo.setIntProperty(WMQConstants.JMS_IBM_ENCODING, encoding);
producer.send(bmo);
...
BytesMessage bmi = (BytesMessage)consumer.receive();
System.out.println(bmi.readInt());
...
256

JMSBytesMessage 中发送和接收文本

图 8 中的代码在 BytesMessage中发送字符串。 为简便起见,示例发送单个字符串,JMSTextMessage 对于其更为适用。 要接收包含混合类型的文本字符串 (以字节为单位) 消息,必须知道该字符串的长度 (以字节为单位) ,在 图 9中称为 TEXT_LENGTH 。 即使对于字符数固定的字符串,字节表示长度也可能会更长。

图 8。 在 JMSBytesMessage 中发送 String
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);
图 9。 从 JMSBytesMessage 接收 String
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);
1 某些 Unicode 表示需要超过 16 位。 请参阅 Java SE 参考。
2 接收消息时, JMS_IBM_CHARACTER_SETJava Charset 代码页名称。
3 SetStringProperty(WMQConstants.JMS_IBM_CHARACTER_SET, codePage) 当前仅接受数字字符集标识。