3

From a Java app I'm trying to browse messages from a mainframe IBM MQ queue (EBCDIC messages). I need to browse messages, not consume them. Here is the code:

JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();

// Set properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, host);
cf.setIntProperty(WMQConstants.WMQ_PORT, port);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channel);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, qmgr);
cf.setIntProperty(WMQConstants.WMQ_RECEIVE_CONVERSION, WMQConstants.WMQ_RECEIVE_CONVERSION_QMGR);

QueueBrowser browser = context.createBrowser(context.createQueue("queue:///" + queueName  + "?targetClient=1"));
Enumeration enumeration = browser.getEnumeration();

while (enumeration.hasMoreElements()) {
    TextMessage messageInTheQueue = (TextMessage) enumeration.nextElement();
    System.out.println(messageInTheQueue);
    nbRecords++;
}

The result of System.out.println() looks like:

  JMSMessage class: jms_text
  JMSType:          null
  JMSDeliveryMode:  2
  JMSMessageID:     ID:c1d4d840d4d8e3c1e2f24040404040405e2432bd21aa1b02
  JMSTimestamp:     1579537307450
  JMSRedelivered:   false
    JMSXAppID:  
    JMSXDeliveryCount: 1
    JMSXUserID:    
    JMS_IBM_Character_Set: IBM037
    JMS_IBM_Encoding: 273
    JMS_IBM_Format: MQSTR   
    JMS_IBM_MsgType: 8
    JMS_IBM_PutApplType: 8
    JMS_IBM_PutDate: 20200120
    JMS_IBM_PutTime: 16214745
ÍÍÑÀ ...

I would like to convert this EBCDIC message ÍÍÑÀ ... to something readable (ASCII).

I tried to cast enumeration.nextElement() to JMSByteMessage but get this exception:

class com.ibm.msg.client.jms.internal.JmsTextMessageImpl cannot be cast to class com.ibm.jms.JMSBytesMessage

How could I do that?

Solution: Use the MQ classes for Java instead of the MQ JMS classes for Java:

byte[] strData = new byte[theMessage.getMessageLength()];
theMessage.readFully(strData, 0, theMessage.getMessageLength());

Some example here: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q030840_.htm

Stephaneuh
  • 31
  • 5
  • Have you tried without `cf.setIntProperty(WMQConstants.WMQ_RECEIVE_CONVERSION, WMQConstants.WMQ_RECEIVE_CONVERSION_QMGR);`? Note that `targetClient` is related to the producer not the receiver, it probably had no impact in the context you set it. – JoshMc Jan 20 '20 at 19:41
  • Are you connecting directly to the mainframe? I believe that if you don't set a CCSID, the CCSID will default to that of the queue manager for purposes of the `WMQ_RECEIVE_CONVERSION_QMGR`. If you do not set `WMQ_RECEIVE_CONVERSION` the default should be for the IBM MQ classes for JMS to do the conversion, the CCSID target of this conversion is normally `1208` which is `UTF-8`. I think if you do not set `WMQ_RECEIVE_CONVERSION` you should get text you can read, alternatively set your CCSID to `1208` and see if the queue manager can convert it. – JoshMc Jan 20 '20 at 22:19
  • I tried with and without setting `WMQ_RECEIVE_CONVERSION`, but the result is the same, still no readable 'UTF-8'. I think that the `enumeration.nextElement();` only returns text `JMSTextMessage` . According to: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q032120_.htm I need a `JMSBytesMessage` so I can perform a conversion. – Stephaneuh Jan 21 '20 at 14:14
  • What version if IBM MQ jar files are you using? – JoshMc Jan 21 '20 at 15:31
  • I finally switched to the `com.ibm.mq` Jars instead of `com.ibm.msg.client.jms` and `com.ibm.jms`. In that case no more JMS layer. Here is the code I used: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_7.5.0/com.ibm.mq.dev.doc/q030840_.htm Basically with this olution I was able to get a byte array and do the conversion to String: ```byte[] strData = new byte[theMessage.getMessageLength()]; theMessage.readFully(strData, 0, theMessage.getMessageLength());``` I edited the original post with this solution. – Stephaneuh Jan 28 '20 at 14:46
  • MQ 7.5 is no longer supported. MQ v8.0 goes out of support in April. I would suggest you download and try MQ v9.1 and see if you still have the same issue. – JoshMc Jan 28 '20 at 15:13
  • @JoshMc according to the following documentation: https://www.ibm.com/docs/en/ibm-mq/9.2?topic=reference-properties-mq-classes-jms-objects the properties related to receive conversion are only valid for MQDestination. They are not valid for any connection factory classes. I'm actually trying to do the same thing as the OP is with the JMS classes & browsing z/OS EBCDIC messages and I'm curious to see if this can actually be done with the MQ classes for JMS. Edit: Aha! Success. I will post the solution here. – root Nov 03 '21 at 08:24

1 Answers1

1

Regarding the OP's original code, the following documentation states that receive conversion & CCSID are only valid for the MQDestination class: https://www.ibm.com/docs/en/ibm-mq/9.2?topic=reference-properties-mq-classes-jms-objects

Therefore this can be done with the IBM MQ Classes for JMS. You just have to do it in a more round-about fashion.

In order to make the QueueBrowser read EBCDIC formatted messages, you need to cast the JMS Queue to MQDestination, then set the receiveConversion & receiveCCSID, then when creating the browser, cast the MQDestination back to regular JMS Queue. Like so:

JmsFactoryFactory ff = JmsFactoryFactory.getInstance(WMQConstants.WMQ_PROVIDER);
JmsConnectionFactory cf = ff.createConnectionFactory();

// Set properties
cf.setStringProperty(WMQConstants.WMQ_HOST_NAME, host);
cf.setIntProperty(WMQConstants.WMQ_PORT, port);
cf.setStringProperty(WMQConstants.WMQ_CHANNEL, channel);
cf.setIntProperty(WMQConstants.WMQ_CONNECTION_MODE, WMQConstants.WMQ_CM_CLIENT);
cf.setStringProperty(WMQConstants.WMQ_QUEUE_MANAGER, qmgr);

JMSContext context = cf.createContext();
MQDestination targetQueue = (MQDestination) context.createQueue("queue:///" + queueName)
targetQueue.setReceiveCCSID(WMQConstants.CCSID_UTF8);
targetQueue.setReceiveConversion(WMQConstants.WMQ_RECEIVE_CONVERSION_QMGR);
targetQueue.setMessageBodyStyle(WMQConstants.WMQ_MESSAGE_BODY_MQ);
QueueBrowser browser = context.createBrowser((Queue) targetQueue);
Enumeration enumeration = browser.getEnumeration();

while (enumeration.hasMoreElements()) {
    TextMessage messageInTheQueue = (TextMessage) enumeration.nextElement();
    System.out.println(messageInTheQueue);
    nbRecords++;
}

You also don't need to use the JMS URI properties for setting the target client when doing it this way, as MQDestination has all the necessary getters/setters to modify loads of properties that would otherwise be done with a URI.

For more information on MQDestination, consult the following docs: https://www.ibm.com/docs/en/ibm-mq/9.2?topic=jms-mqdestination

root
  • 125
  • 1
  • 10