Table of Contents
- Overview
- How MQ connectivity is wired
- Reading a message: MQOPEN → MQGET → MQCLOSE
- Writing a message: MQPUT and MQPUT1
- Request–reply messaging
- Transactions & syncpoint
- Triggered (background) transactions
- Summary
- 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. ACOMPCODEofMQCC_FAILEDwith a reason such asMQRC_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:
- A trigger monitor (CKTI) starts automatically at deployment (listed in the application's program-list table, e.g.
plt.pi.1=ckti). - CKTI watches the initiation queue (
APP.INITQ). - When MQ places a trigger message on the initiation queue, CKTI reads it and starts the named transaction (CICS
START). - The triggered program runs,
MQGETs from the data queue, processes, and optionallyMQPUTs 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/REASONafter each call.
Related articles
- How Transactions Are Managed (ETP + PL/I Runtime) — syncpoint scope and the transaction lifecycle.
- JVM Options & Runtime Configuration — diagnostics and other startup settings.
0 Comments