解析base64编码的MQ消息

时间:2018-11-25 22:02:44

标签: ibm-mq ibm-integration-bus

带有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消息,以便可以访问标头和正文?

2 个答案:

答案 0 :(得分:0)

  

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

为什么?为什么要合并MQMD和MQRFH2结构,然后仅使用Base64对其进行编码,以便仅使用Java程序将其提取出来?

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

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

import tesserocr
from PIL import Image
import pytesseract
import matplotlib as plt
import cv2
import imutils
import numpy as np

image = cv2.imread(r'c:\ahmed\test3.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

gray = cv2.bilateralFilter(gray,30,40,40)
#gray = cv2.GaussianBlur(gray,(1,1), 0)
gray =cv2.fastNlMeansDenoising(gray ,None, 4, 7, 21)

image = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
            cv2.THRESH_BINARY,11,2)
k = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1))

blur = cv2.medianBlur(image,3)
erode = cv2.erode(blur, k)
dilat = cv2.dilate(erode,k)
cv2.imshow("gray", dilat)

#cv2.imshow("dilation", img_dilation)
#thresh = cv2.Canny(thresh, 70, 200)

#crop_img = gray[215:215+315, 783:783+684]
#cv2.imshow("cropped", crop_img)

#resize = imutils.resize(blur, width = 460)
#cv2.imshow("resize", resize)

text = pytesseract.image_to_string(dilat, lang='ara')
print(text)
with open(r"c:\ahmed\file.txt", "w", encoding="utf-8") as myfile:
    myfile.write(text)
cv2.waitKey(0)

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

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

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

答案 1 :(得分:0)

MQHeaderList类不是魔杖,但是,我希望IBM实际上将其用途扩展为包括每个MQ标头。您的问题是您假设MQHeaderList类可以做的事情超出其范围。 MQHeaderList类只能处理11个内部MQ头/结构(aka类)。看到这里: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类。

此外,您应该在这里阅读我对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>