我正在研究一些可以描述为自定义WMQ消息路由器/转发器的东西。目前,我在处理通过MQ发送的JMS消息时遇到了一些非常令人困惑的问题。我能够通过JMS(使用MQ作为传输)从Java应用程序接收消息发送,但是我无法向JMS端点上的Java应用程序发送消息。
我有一个测试servlet和消息驱动bean - 两者都托管在WebSphere Application Server 7.0中(WebSphere MQ 7.0用于消息传输)。 Servlet能够与bean通信,但如果我将转发器放在它们之间(通过重新配置servlet与转发器进行通信,转发器将重构消息并将它们转发给bean),bean无法处理请求。我在WAS日志中有这个错误:
[8/2/12 14:38:51:359 CEST] 00000031 SibMessage W [:] CWSJY0003W: JMSCC0110: An exception '
Message : java.lang.NullPointerException
Class : class java.lang.NullPointerException
Stack : com.ibm.msg.client.wmq.internal.messages.WMQMessageBase._parseMcdFolder(WMQMessageBase.java:445)
: com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.constructProviderMessageFromRFH2(WMQReceiveMarshal.java:341)
: com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.createProviderMessage(WMQReceiveMarshal.java:447)
: com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.exportProviderMessage(WMQReceiveMarshal.java:607)
: com.ibm.msg.client.wmq.internal.WMQConsumerShadow.getMsg(WMQConsumerShadow.java:1115)
: com.ibm.msg.client.wmq.internal.WMQSyncConsumerShadow.receive(WMQSyncConsumerShadow.java:334)
: com.ibm.msg.client.wmq.internal.WMQSession.loadMessageReference(WMQSession.java:1082)
: com.ibm.msg.client.jms.internal.JmsSessionImpl.consume(JmsSessionImpl.java:2847)
: com.ibm.msg.client.jms.internal.JmsSessionImpl.run(JmsSessionImpl.java:2549)
: com.ibm.mq.jms.MQSession.run(MQSession.java:860)
: com.ibm.mq.connector.inbound.WorkImpl.run(WorkImpl.java:172)
: com.ibm.ejs.j2c.work.WorkProxy.run(WorkProxy.java:399)
: com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1604)
[8/2/12 14:38:51:781 CEST] 00000031 SibMessage W [:] CWSJY0003W: MQJCA4004: Message delivery to an MDB 'XXX' failed with exception: 'null'
此错误后,消息端点会自动暂停。
我使用RFHUtils来获取JMS和我的应用程序发送的消息并进行比较 - UI中没有真正的区别(除了不同的交付模式,但这是另一个故事)但是当我将消息保存到文件并比较它们时我看到了这个差异(只是第一个RFH2文件夹):
直接发送给JMS:
<mcd><Msd>jms_text</Msd></mcd>
通过我的应用发送:
<mcd><Msd dt="string" >jms_text</Msd></mcd>
RFH2中的所有元素也包含类型。 jms
文件夹中的元素顺序不同,但根据例外情况,问题应该直接与mcd
文件夹一起使用。我不确定如何更好地诊断问题 - 我已经尝试为JMS配置tracing但我不知道如何为WAS做这个(我在星期二第一次使用WAS)。标准MQ跟踪不再提供任何信息。
我的.NET代码(使用MQ Client 7.5的WMQ API - amqmdnet.dll 7.5.0.0)非常复杂,但通常会这样:
接收机
MQMessage
访问的Get
上的标准MQQueue
来接收MQOO_INPUT_AS_Q_DEF
条消息(无特殊获取选项)MQMessage
GetPropertyNames("%")
获取所有邮件属性的名称GetObjectProperty
获取每个属性的值MQSTR
,则ReadString
按ReadBytes
发件人
MQMessage
MQMessage
所有有意义的邮件标题 - 例如MessageId
,ReplyToQueue
名称,其他一些不会被复制 - 使用新的或正确的值MQMessage
设置收到的SetObjectProperty
的所有属性 - 此步骤包含一些魔法,因为.NET API不一致,GetObjectProperty
返回的值的类型并不总是被SetObjectProperty
接受{1}} - 通常我收到String
但我必须通过int
或long
(例如:JmsDeliveryMode,JmsPriority或JmsTimestamp)。此步骤还会覆盖JmsDestination和JmsReplyTo WriteString
或WriteBytes
Put
向[{1}}访问MQQueue
的消息(无特殊看跌期权)我不手动创建RFH2结构 - 我让MQ基础设施来处理它。所以我的问题是:如何从.NET创建有效的JMS消息,消息驱动bean将接受它?
注意:我不想使用IBM.XMS - 这个决定是很久以前做出的,因为IBM知识库中的一些文章描述了XMS和WMQ的优缺点。我需要支持JMS和非JMS消息传递。
答案 0 :(得分:1)
启用WebSphere tracing并使用Java Decompiler检查一些WebSphere .jar包之后,我通过纯粹的机会找到了错误的原因。
跟踪显示了传递给失败的_parseMcdFolder
方法的值:
[8/3/12 12:16:00:199 CEST] 0000003a > UOW= source=com.ibm.msg.client.wmq.internal.messages.WMQMessageBase method=_parseMcdFolder(String,String,String) (com.ibm.msg.client.wmq.internal.messages.WMQMessageBase) [:] org=IBM prod=WebSphere component=Application Server thread=[WMQJCAResourceAdapter : 0]
Entry parm0=<mcd><Msd dt="string" >jms_text</Msd></mcd> parm1=jms_text parm2=<null>
[8/3/12 12:16:00:199 CEST] 0000003a 3 UOW= source=com.ibm.msg.client.jms.internal.JmsSessionImpl org=IBM prod=WebSphere component=Application Server thread=[WMQJCAResourceAdapter : 0]
(com.ibm.msg.client.jms.internal.JmsSessionImpl) [:/50d450d4] Caught exception: java.lang.NullPointerException in class: com.ibm.msg.client.jms.internal.JmsSessionImpl method: run() <exitIndex: 2>
[8/3/12 12:16:00:199 CEST] 0000003a 1 UOW= source=com.ibm.msg.client.jms.internal.JmsSessionImpl org=IBM prod=WebSphere component=Application Server thread=[WMQJCAResourceAdapter : 0]
(com.ibm.msg.client.jms.internal.JmsSessionImpl) [:/50d450d4] Tracing exception:
java.lang.NullPointerException
at com.ibm.msg.client.wmq.internal.messages.WMQMessageBase._parseMcdFolder(WMQMessageBase.java:445)
at com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.constructProviderMessageFromRFH2(WMQReceiveMarshal.java:341)
at com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.createProviderMessage(WMQReceiveMarshal.java:447)
at com.ibm.msg.client.wmq.internal.messages.WMQReceiveMarshal.exportProviderMessage(WMQReceiveMarshal.java:607)
at com.ibm.msg.client.wmq.internal.WMQConsumerShadow.getMsg(WMQConsumerShadow.java:1115)
at com.ibm.msg.client.wmq.internal.WMQSyncConsumerShadow.receive(WMQSyncConsumerShadow.java:334)
at com.ibm.msg.client.wmq.internal.WMQSession.loadMessageReference(WMQSession.java:1082)
at com.ibm.msg.client.jms.internal.JmsSessionImpl.consume(JmsSessionImpl.java:2847)
at com.ibm.msg.client.jms.internal.JmsSessionImpl.run(JmsSessionImpl.java:2549)
at com.ibm.mq.jms.MQSession.run(MQSession.java:860)
at com.ibm.mq.connector.inbound.WorkImpl.run(WorkImpl.java:172)
at com.ibm.ejs.j2c.work.WorkProxy.run(WorkProxy.java:399)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1604)
传递给_parseMcdFolder
的参数是:
parm0=<mcd><Msd dt="string" >jms_text</Msd></mcd> parm1=jms_text parm2=<null>
我反编译了%WAS_INSTALL%\lib\WMQ\ra\wmq.jmsra.rar
中存储的WMQ资源适配器包并检查了_parseMcdFolder
方法。令人惊讶的是,来自异常和方法代码的报告行不对应(还有另一种方法)。此方法的代码也应正确解析传递的参数。
过了一会儿,我检查了整个WAS安装目录,发现两次解压缩的资源适配器 - 一个在根InstalledComponents
目录中,第二个在服务器配置文件的InstalledComponents
目录中。 这些资源适配器有不同的版本:
在反编译版本7.0.0.0-k700-L080820的代码后,我发现确实存在一个错误 - 访问未初始化的消息:
static WMQMessage _parseMcdFolder(String s, String fbClass, String forcedMessageClass)
throws JMSException
{
WMQMessage newMessage = null;
// 350+ lines of code trying to parse s and initialize newMessage
// but no fallback so newMessage could be null after the processing
newMessage.isNullMessage = isNullMsgFlag; // Line 445 - BOOM!
return newMessage;
}
此错误已在某个以下版本的适配器中修复,但未通知,因为我没有找到与此问题相关的任何文档。
使用较新的适配器版本后问题得到解决(实际上我已经移动到另一个问题)。