1

我正在研究可以被描述为自定义 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 配置跟踪,但我不知道如何为 WAS 执行此操作(我在星期二第一次使用 WAS)。标准 MQ 跟踪不提供更多信息。

我的 .NET 代码(使用 MQ 客户端 7.5 - amqmdnet.dll 7.5.0.0 的 WMQ API)非常复杂,但通常会:

接收者

  • 在访问时MQMessage通过标准Get调用接收消息(无特殊获取选项)MQQueueMQOO_INPUT_AS_Q_DEF
  • 从收到的所有消息头中读取MQMessage
  • 用于GetPropertyNames("%")获取所有消息属性的名称
  • 用于GetObjectProperty获取每个属性的值
  • 如果消息格式是MQSTR读取正文,ReadString否则由ReadBytes

发件人

  • 创建MQMessage
  • 设置接收到的所有有意义的消息头MQMessage——例如MessageIdReplyToQueue名称和其他一些不被复制——而是使用新的或正确的值。
  • MQMessage通过使用设置接收到的所有属性SetObjectProperty- 此步骤包含一些魔法,因为 .NET API 不一致并且从返回的值类型GetObjectProperty并不总是被接受SetObjectProperty- 通常我接收String但我必须通过intlong(例如:JmsDeliveryMode、JmsPriority 或 JmsTimestamp)。此步骤还覆盖 JmsDestination 和 JmsReplyTo 的目的地
  • 通过WriteString或编写消息正文WriteBytes
  • PutMQQueue要访问的消息MQOO_OUTPUT(没有特殊的看跌期权)

我不手动创建 RFH2 结构 - 我让 MQ 基础设施来处理它。所以我的问题是:如何从 .NET 创建有效的 JMS 消息,该消息将被消息驱动 bean 接受?

注意:我不想使用 IBM.XMS - 这个决定是很久以前做出的,因为 IBM 知识库中的一些文章描述了 XMS 和 WMQ 的优缺点。我需要同时支持 JMS 和非 JMS 消息传递。

4

1 回答 1

1

在启用WebSphere 跟踪并使用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>

我反编译了其中存储的WMQ资源适配器包%WAS_INSTALL%\lib\WMQ\ra\wmq.jmsra.rar,查看了_parseMcdFolder方法。令人惊讶的是,异常报告的行和方法代码不对应(还有另一种方法)。该方法的代码也应该正确解析传递的参数。

过了一会儿,我检查了整个 WAS 安装目录,发现有两次解压的资源适配器 - 一次在根InstalledComponents目录中,第二次在InstalledComponents服务器配置文件的目录中。这些资源适配器有不同的版本:

  • 全球:7.0.1.4-k701-104-110104
  • 配置文件:7.0.0.0-k700-L080820

反编译版本 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;
}

此错误已在适配器的某些后续版本中修复,但未传达,因为我没有找到与此问题相关的任何文档。

使用较新的适配器版本后,问题得到解决(实际上我只是转移到另一个问题)。

于 2012-08-06T10:53:20.387 回答