如何在.NET中通过MQ发送JMS消息?

时间:2012-08-02 16:56:37

标签: .net jms websphere ibm-mq

我正在研究一些可以描述为自定义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,则ReadStringReadBytes
  • 读取正文

发件人

  • 创建MQMessage
  • 设置收到的MQMessage所有有意义的邮件标题 - 例如MessageIdReplyToQueue名称,其他一些不会被复制 - 使用新的或正确的值
  • 使用MQMessage设置收到的SetObjectProperty的所有属性 - 此步骤包含一些魔法,因为.NET API不一致,GetObjectProperty返回的值的类型并不总是被SetObjectProperty接受{1}} - 通常我收到String但我必须通过intlong(例如:JmsDeliveryMode,JmsPriority或JmsTimestamp)。此步骤还会覆盖JmsDestination和JmsReplyTo
  • 的目标
  • 通过WriteStringWriteBytes
  • 撰写邮件正文
  • Put向[{1}}访问MQQueue的消息(无特殊看跌期权)

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

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

1 个答案:

答案 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.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;
}

此错误已在某个以下版本的适配器中修复,但未通知,因为我没有找到与此问题相关的任何文档。

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