Follow

How to Use IBM MQ Messaging in a Migrated PL/I Application

Table of Contents

  1. Overview
  2. How MQ connectivity is wired
  3. Reading a message: MQOPEN → MQGET → MQCLOSE
  4. Writing a message: MQPUT and MQPUT1
  5. Request–reply messaging
  6. Transactions & syncpoint
  7. Triggered (background) transactions
  8. Summary
  9. Related articles

Audience: developers and operations engineers running PL/I message-queue applications migrated to Java with Heirloom.


Overview

Your PL/I programs call IBM MQ exactly as they did on the mainframe — MQOPEN, MQGET, MQPUT, MQCLOSE, syncpoint, and (optionally) MQ-triggered transactions. Heirloom preserves that programming model: the migrated code keeps its MQI calls and option structures (MQMD, MQGMO, MQOD, MQPMO), and the runtime connects them to a real IBM MQ queue manager.

This article shows what you configure and how the common patterns work. It does not require changing your PL/I source.

Note: All queue, queue-manager, channel, host, transaction, and program names below are examples. Replace them with your own values.


How MQ connectivity is wired

A migrated application connects to MQ in client mode (a TCP connection to a remote queue manager). You provide the connection coordinates through configuration; the runtime and the underlying execution platform open and pool the connection for you.

Setting Example Meaning
Queue manager QMGR1 The target MQ queue manager
Host / port mq.example.internal / 1414 Listener address of the queue manager
Channel APP.SVRCONN Server-connection channel for client apps
User / password <mq-user> / <mq-password> MQ credentials
Initiation queue APP.INITQ Queue the trigger monitor watches (triggered apps only)

These are typically supplied as environment variables (or the deployment's MQ properties file) and read at application startup:

MQ_QUEUE_MANAGER=QMGR1
MQ_HOST=mq.example.internal
MQ_PORT=1414
MQ_CHANNEL=APP.SVRCONN
MQ_USER=<mq-user>
MQ_PASSWORD=<mq-password>

Option: a JNDI connection factory / managed connection pool

For application-server deployments, you can instead have the server own MQ connection pooling, credentials, and lifecycle, and point the runtime at a JNDI connection factory. Configure it in the MQ properties file:

com.heirloomcomputing.ecs.mqi.jms.ConnectionFactory=jms/JmsConnectionFactory

The runtime resolves that JNDI name (e.g. jms/JmsConnectionFactory) and uses the pooled connection. You define the connection factory / pool — queue manager, host/port, channel, credentials — in the server's admin console or domain configuration, exactly as for any JMS / IBM MQ resource adapter. Tested with Payara 6.

Use the direct coordinates above when the application manages its own client connection; use the JNDI connection factory when you want the application server to manage the MQ connection pool.


Reading a message: MQOPEN → MQGET → MQCLOSE

The classic consume pattern. Your PL/I opens the queue for input, gets a message into a buffer, then closes the queue:

/* Open the request queue for input */
OBJDESC.OBJECTNAME = 'APP.REQUEST.QUEUE';
CALL MQOPEN(HCONN, OBJDESC, MQOO_INPUT_SHARED, HOBJ, COMPCODE, REASON);

/* Get one message (wait up to the interval in GMO) */
GMO.OPTIONS = MQGMO_WAIT + MQGMO_SYNCPOINT;
GMO.WAITINTERVAL = 5000;            /* milliseconds */
CALL MQGET(HCONN, HOBJ, MSGDESC, GMO, BUFLEN, BUFFER, DATALEN,
           COMPCODE, REASON);

IF COMPCODE = MQCC_OK THEN
   /* process BUFFER (DATALEN bytes) */ ;

CALL MQCLOSE(HCONN, HOBJ, MQCO_NONE, COMPCODE, REASON);

Key option structures

Structure Controls
MQOD (Object Descriptor) Which queue/object to open (ObjectName, ObjectQMgrName)
MQMD (Message Descriptor) Message identity & properties (MsgId, CorrelId, Format, Priority, Persistence)
MQGMO (Get Message Options) Wait vs. no-wait, syncpoint, match options

Always check COMPCODE / REASON. A COMPCODE of MQCC_FAILED with a reason such as MQRC_NO_MSG_AVAILABLE (2033) is normal (the queue was empty); others (connection, authorization, syncpoint) need handling.


Writing a message: MQPUT and MQPUT1

To put a message on a queue you already opened, use MQPUT. To put a single message without keeping the queue open, use MQPUT1:

MSGDESC.FORMAT = MQFMT_STRING;
PMO.OPTIONS = MQPMO_SYNCPOINT;

/* On an already-open queue */
CALL MQPUT(HCONN, HOBJ, MSGDESC, PMO, BUFLEN, BUFFER, COMPCODE, REASON);

/* Open + put + close in one call */
OBJDESC.OBJECTNAME = 'APP.REPLY.QUEUE';
CALL MQPUT1(HCONN, OBJDESC, MSGDESC, PMO, BUFLEN, BUFFER, COMPCODE, REASON);

MQPMO (Put Message Options) controls syncpoint and other put behavior.


Request–reply messaging

For request–reply, the sender sets ReplyToQ (and optionally ReplyToQMgr) in the request's MQMD, and the responder echoes the request's MsgId into the reply's CorrelId so the original requester can match the answer:

/* Responder: correlate the reply to the request */
REPLY_MD.CORRELID = REQUEST_MD.MSGID;
REPLY_OD.OBJECTNAME = REQUEST_MD.REPLYTOQ;
CALL MQPUT1(HCONN, REPLY_OD, REPLY_MD, PMO, LEN, REPLY, COMPCODE, REASON);

Transactions & syncpoint

When MQGET/MQPUT use MQGMO_SYNCPOINT / MQPMO_SYNCPOINT, the message operations join the unit of work. They are committed or rolled back with the transaction (for example alongside your DB2 updates) at SYNCPOINT / ROLLBACK. This gives you the all-or-nothing guarantee: a failed transaction leaves the input message on the queue for retry and discards any reply.

Operational tip: If you see MQRC_SYNCPOINT_NOT_AVAILABLE (2072), the application server enlisted the queue in a global (XA) transaction that the queue/connection doesn't support. Configure the MQ connection pool for local transactions instead of XA. This is a deployment setting, not a code change.


Triggered (background) transactions

Many MQ workloads are triggered: a message arriving on a queue automatically starts a transaction to process it — no user at a terminal. Heirloom supports the standard trigger-monitor model:

  1. A trigger monitor (CKTI) starts automatically at deployment (listed in the application's program-list table, e.g. plt.pi.1=ckti).
  2. CKTI watches the initiation queue (APP.INITQ).
  3. When MQ places a trigger message on the initiation queue, CKTI reads it and starts the named transaction (CICS START).
  4. The triggered program runs, MQGETs from the data queue, processes, and optionally MQPUTs a reply — all under syncpoint.
message → APP.REQUEST.QUEUE ──(trigger)──▶ APP.INITQ
                                              │  CKTI reads trigger
                                              ▼
                                  START transaction TRN1
                                              │
                                              ▼
                          TRN1: MQGET → process → MQPUT reply → SYNCPOINT

You configure the initiation queue and ensure the trigger monitor is in the program-list table; the runtime handles the rest.


Summary

  • Provide MQ connection coordinates (queue manager, host/port, channel, credentials) via configuration — no code change. Or use a JNDI connection factory so the application server manages the MQ connection pool (tested with Payara 6).
  • Your PL/I keeps its MQI calls: MQOPEN/MQGET/MQPUT/MQPUT1/MQCLOSE, with MQMD/MQGMO/MQOD/MQPMO option structures.
  • Use syncpoint options so MQ work commits/rolls back with the transaction.
  • Triggered transactions are driven by a trigger monitor watching an initiation queue.
  • Always check COMPCODE / REASON after each call.

Was this article helpful?
0 out of 0 found this helpful
Have more questions? Submit a request

0 Comments

Article is closed for comments.
Powered by Zendesk