【问题标题】:Parsing a base64 encoded MQ message解析 base64 编码的 MQ 消息
【发布时间】:2019-04-27 14:05:49
【问题描述】:

具有 MQ 输入节点的 IIB 流的事务启动监视事件会生成 MQ 消息的 base64 编码字节数组。现在我想编写一个 Java 程序来重构这个字节数组,这样我就可以读取标题和正文。

base64 MQ 消息如下所示:

TUQgIAIAAAAAAAAACAAAAP////8AAAAAEQEAALgEAABNUUhSRjIgIAQAAAABAAAAQU1RIENFTUJSQSAgICAgIKVV+Fslx7YCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENFTUJSQSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhbmllbCAgICAgIBYBBRUAAADiboF1+wHSKOpNUf3pAwAAAAAAAAAAAAALICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICALAAAAMC45XGNvbW1vblxqZGtcYmluXGphdmF3LmV4ZTIwMTgxMTI1MTQzNjEyNDcgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA/////1JGSCAAAAACAAAAvAAAAREAAAS4TVFTVFIgICAAAAAAAAAEuAAAACA8bWNkPjxNc2Q+am1zX3RleHQ8L01zZD48L21jZD4gIAAAAEg8am1zPjxEc3Q+cXVldWU6Ly8vTU9OSTwvRHN0PjxUbXM+MTU0MzE1NjU3MjQ1NjwvVG1zPjxEbHY+MjwvRGx2Pjwvam1zPiAAAAAkPHVzcj48VGhlS2V5PlRoZVZhbHVlPC9UaGVLZXk+PC91c3I+PGZvbz5iYXI8L2Zvbz4=

我做了以下测试以在 Java 中解析它:

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.Test;
import com.ibm.mq.headers.CCSID;
import com.ibm.mq.headers.MQHeaderList;
import com.ibm.mq.headers.MQMD;
import com.ibm.mq.headers.MQRFH2;

public class MqMsgTest {

    @Test
    public void allGood() throws Exception {
        String msgBase64 = IOUtils.toString(getClass().getResourceAsStream("/mq-msg.base64"), "UTF-8");
        byte[] msgBytes = DatatypeConverter.parseBase64Binary(msgBase64);
        DataInputStream msgStream = new DataInputStream(new ByteArrayInputStream(msgBytes));
        MQMD mqmd = new MQMD(msgStream);
        Assert.assertEquals("MQHRF2  ", mqmd.getFormat());
        Assert.assertEquals("daniel      ", mqmd.getUserIdentifier());
        MQRFH2 mqrfh2 = new MQRFH2(msgStream);
        Assert.assertEquals("TheValue", mqrfh2.getStringFieldValue("usr", "TheKey"));
        String body = IOUtils.toString(msgStream, CCSID.getCodepage(mqrfh2.nextCharacterSet()));
        Assert.assertEquals("<foo>bar</foo>", body);
    }

    @Test
    public void doesNotWork() throws Exception {
        String msgBase64 = IOUtils.toString(getClass().getResourceAsStream("/mq-msg.base64"), "UTF-8");
        byte[] msgBytes = DatatypeConverter.parseBase64Binary(msgBase64);
        DataInputStream msgStream = new DataInputStream(new ByteArrayInputStream(msgBytes));
        MQHeaderList headers = new MQHeaderList(msgStream, true);
        Assert.assertEquals(2, headers.size());
    }
}

allGood() 测试很好地解析了标题和正文。但如果消息不包含 RFH2 标头,它将失败。 doesNotWork() 测试应该以通用方式解析标头,但它不起作用。

如何以灵活的方式解析 base64 编码的 MQ 消息,以便可以访问标头和正文?

【问题讨论】:

    标签: ibm-mq ibm-integration-bus


    【解决方案1】:

    带有 MQ 输入的 IIB 流的事务启动监控事件 节点生成 MQ 消息的 base64 编码字节数组。现在我 想写一个重构这个字节数组的Java程序 所以我可以阅读标题和正文。

    为什么?为什么你将 MQMD 和 MQRFH2 结构结合起来,然后用 Base64 对其进行编码,只是想用 Java 程序提取它?

    这听起来像是一个非常糟糕的设计。

    我将您的 Base64 消息放入了一个输出十六进制转储的程序中:

    000000: 4D442020 02000000 00000000 08000000  MD  ............
    000010: FFFFFFFF 00000000 11010000 B8040000  ????........?...
    000020: 4D514852 46322020 04000000 01000000  MQHRF2  ........
    000030: 414D5120 43454D42 52412020 20202020  AMQ CEMBRA      
    000040: A555F85B 25C7B602 00000000 00000000  ?U?[%??.........
    000050: 00000000 00000000 00000000 00000000  ................
    000060: 00000000 20202020 20202020 20202020  ....            
    000070: 20202020 20202020 20202020 20202020                  
    000080: 20202020 20202020 20202020 20202020                  
    000090: 20202020 43454D42 52412020 20202020      CEMBRA      
    0000a0: 20202020 20202020 20202020 20202020                  
    0000b0: 20202020 20202020 20202020 20202020                  
    0000c0: 20202020 64616E69 656C2020 20202020      daniel      
    0000d0: 16010515 000000E2 6E8175FB 01D228EA  .......?n?u?.?(?
    0000e0: 4D51FDE9 03000000 00000000 0000000B  MQ??............
    0000f0: 20202020 20202020 20202020 20202020                  
    000100: 20202020 20202020 20202020 20202020                  
    000110: 0B000000 302E395C 636F6D6D 6F6E5C6A  ....0.9\common\j
    000120: 646B5C62 696E5C6A 61766177 2E657865  dk\bin\javaw.exe
    000130: 32303138 31313235 31343336 31323437  2018112514361247
    000140: 20202020 00000000 00000000 00000000      ............
    000150: 00000000 00000000 00000000 01000000  ................
    000160: 00000000 00000000 FFFFFFFF 52464820  ........????RFH 
    000170: 00000002 000000BC 00000111 000004B8  .......?.......?
    000180: 4D515354 52202020 00000000 000004B8  MQSTR   .......?
    000190: 00000020 3C6D6364 3E3C4D73 643E6A6D  ... <mcd><Msd>jm
    0001a0: 735F7465 78743C2F 4D73643E 3C2F6D63  s_text</Msd></mc
    0001b0: 643E2020 00000048 3C6A6D73 3E3C4473  d>  ...H<jms><Ds
    0001c0: 743E7175 6575653A 2F2F2F4D 4F4E493C  t>queue:///MONI<
    0001d0: 2F447374 3E3C546D 733E3135 34333135  /Dst><Tms>154315
    0001e0: 36353732 3435363C 2F546D73 3E3C446C  6572456</Tms><Dl
    0001f0: 763E323C 2F446C76 3E3C2F6A 6D733E20  v>2</Dlv></jms> 
    000200: 00000024 3C757372 3E3C5468 654B6579  ...$<usr><TheKey
    000210: 3E546865 56616C75 653C2F54 68654B65  >TheValue</TheKe
    000220: 793E3C2F 7573723E 3C666F6F 3E626172  y></usr><foo>bar
    000230: 3C2F666F 6F3E                        </foo>          
    

    解码后的消息以 364 字节的 MQMD 开始,然后是 202 字节的 MQRFH2 结构。

    MQHeaderList 类对解析 MQMessage 非常挑剔。消息必须以已知 MQ 类之一开头,否则将引发异常。将 MQHeaderList 类用于问题消息可能只会导致更多问题。

    最好像我一样简单地将消息转储为十六进制,然后让支持人员或开发人员找出问题所在。

    【讨论】:

    • 将这样的监控事件写入数据库是IBM集成总线工程师的设计,他们称之为数据捕获存储。使用我的工具,我想从这家商店中读取信息,并以比十六进制转储更好的人类可读格式显示此经过审核的消息中可能出现的任何内容。对我来说,这很有意义。你知道为什么 MQHeaderList 会阻塞这条消息吗?
    • 查看我新发布的项目。
    【解决方案2】:

    MQHeaderList 类不是魔杖,不过,我希望 IBM 能够真正扩展它的用途以包含每个 MQ 标头。您的问题是您假设 MQHeaderList 类可以做的比它所能做的更多。 MQHeaderList 类只能处理 11 个内部 MQ 标头/结构(又名类)。见这里:https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/com.ibm.mq.dev.doc/q030880_.htm

    即MQRFH, MQRFH2, MQCIH, MQDLH, MQIIH, MQRMH, MQSAPH, MQWIH, MQXQH, MQDH & MQEPH

    如您所见,MQMD 不在列表中。因此,您解码的 Base64 消息将不适用于 MQHeaderList 类。

    另外,您应该在此处阅读我的 cmets,了解 MQRFH2 是嵌入式消息:Issue While Setting MQRFH2 header in IBM MQ

    您必须插入并插入 IIB 创建的数据流。这是完成这项工作的一些基本代码:

    import java.io.ByteArrayInputStream;
    import java.io.DataInputStream;
    import javax.xml.bind.DatatypeConverter;
    import com.ibm.mq.headers.MQMD;
    import com.ibm.mq.headers.MQRFH2;
    
    public class Test_IIB_Data
    {
       public static void main(String[] args)
       {
          String msgBase64 = "TUQgIAIAAAAAAAAACAAAAP////8AAAAAEQEAALgEAABNUUhSRjIgIAQAAAABAAAAQU1RIENFTUJSQSAgICAgIKVV+Fslx7YCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENFTUJSQSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhbmllbCAgICAgIBYBBRUAAADiboF1+wHSKOpNUf3pAwAAAAAAAAAAAAALICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICALAAAAMC45XGNvbW1vblxqZGtcYmluXGphdmF3LmV4ZTIwMTgxMTI1MTQzNjEyNDcgICAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAA/////1JGSCAAAAACAAAAvAAAAREAAAS4TVFTVFIgICAAAAAAAAAEuAAAACA8bWNkPjxNc2Q+am1zX3RleHQ8L01zZD48L21jZD4gIAAAAEg8am1zPjxEc3Q+cXVldWU6Ly8vTU9OSTwvRHN0PjxUbXM+MTU0MzE1NjU3MjQ1NjwvVG1zPjxEbHY+MjwvRGx2Pjwvam1zPiAAAAAkPHVzcj48VGhlS2V5PlRoZVZhbHVlPC9UaGVLZXk+PC91c3I+PGZvbz5iYXI8L2Zvbz4=";
    
          try
          {
             byte[] msgBytes = DatatypeConverter.parseBase64Binary(msgBase64);
             DataInputStream msgStream = new DataInputStream(new ByteArrayInputStream(msgBytes));
    
             MQMD md = new MQMD(msgStream);
    
             System.out.println("md.getFormat="+md.getFormat());
             System.out.println("md.getPutApplName="+md.getPutApplName());
             System.out.println("md.getUserIdentifier="+md.getUserIdentifier());
    
             MQRFH2 rfh2 = new MQRFH2(msgStream);
    
             System.out.println("rfh2.getFormat="+rfh2.getFormat());
             System.out.println("rfh2.usr.TheKey="+rfh2.getStringFieldValue("usr", "TheKey"));
    
             int bodyLen = msgBytes.length - rfh2.getStrucLength() - md.size();
             byte[] body = new byte[bodyLen];
             msgStream.read(body);
             System.out.println("body="+new String(body));
          }
          catch (Exception e)
          {
             e.printStackTrace();
          }
       }
    }
    

    当你运行它时,输出应该是:

    md.getFormat=MQHRF2  
    md.getPutApplName=0.9\common\jdk\bin\javaw.exe
    md.getUserIdentifier=daniel      
    rfh2.getFormat=MQSTR   
    rfh2.usr.TheKey=TheValue
    body=<foo>bar</foo>
    

    【讨论】:

    • 感谢您的解释。在我的问题中,您的 main() 与我的 allGood() 方法大致匹配。我一直在寻找一种通用的方式,但似乎不可能。我想知道 RFHUTIL 是如何做到的。也许我必须深入研究它的 C 源代码。
    • 嗯,IH03 Support Pac的source.zip中没有RFHUTIL的源代码。并且 RFHUTIL 的文档还指出仅支持 5 种标头类型。似乎没有可用于一般处理 MQ 消息的工具/API。
    • C 程序几乎可以处理 MQMD 之类的结构 - 只需直接从结构中写入字节(可能转换为字符)。同样在结构之上阅读。指针的乐趣。类似“write(fd,&mqmd,sizeof(mqmd))”...
    • @Daniel 我认为源代码不可用。由于您是用 Java 编写的,因此您有 2 个选择:(1) 像我提到的那样进行即插即用 - 注意:结构的前 4 个字节将始终是 StrucId 或
    • (2) 您可以创建自己的 MQMD 类(即 MyMQMD)并按照此处的信息进行操作:ibm.com/support/knowledgecenter/en/SSFKSJ_8.0.0/…,然后 MQHeaderList 应该在您注册 MyMQMD 后工作。请参阅 MQHeaderRegistry ibm.com/support/knowledgecenter/en/SSFKSJ_9.1.0/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2021-01-06
    • 2019-02-12
    • 2019-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多