JMS queues are how Java and Jakarta applications do point-to-point work on IBM MQ without calling MQPUT and MQGET directly. You create a Session, obtain a Queue destination (often by name), use a QueueSender to put work, and a QueueReceiver or MessageListener to consume it. Under the hood the IBM MQ JMS provider opens the same QLOCAL your COBOL batch job uses, enforces the same authority records, honors persistence and syncpoint rules, and increments depth the operations console already monitors. Beginners often treat the JMS name as a logical label separate from MQSC reality—it is not. The string you pass to createQueue is the queue manager object name unless your team configured a custom destination resolver. This tutorial explains JMS queue semantics on IBM MQ, how they differ from Topic destinations, producer and consumer patterns, acknowledgement and transaction interaction, persistence and priority, request-reply over queues, administration with MQSC, and troubleshooting when messages never arrive or consumers fight each other unexpectedly.
In the JMS specification, javax.jakarta.jms.Queue is an interface representing a point-to-point destination. IBM MQ implements that interface by binding to a queue object defined on the server. When your code calls session.createQueue("ORDERS.NEW"), the provider typically issues an MQOPEN against ORDERS.NEW as a queue. If the object does not exist and your queue manager policy forbids dynamic creation, you receive a JMSException whose linked MQ reason explains the failure—often 2085 object not found. Alias queues (QALIAS) let you expose a stable JMS name while routing to different physical queues per environment. Remote queues are usually not opened directly from JMS clients; integration architects place a local queue in front of remote routing so JMS programs keep simple names.
| JMS API | Role | IBM MQ equivalent |
|---|---|---|
| Queue | Destination identity | QLOCAL / QALIAS name |
| QueueSender | Producer | MQPUT to queue |
| QueueReceiver | Pull consumer | MQGET (destructive) |
| MessageListener | Push consumer | Async MQGET dispatch |
| Message | Payload + headers | Buffer + MQMD |
Point-to-point means each message has one intended consumer among those listening on the queue. If three Spring @JmsListener instances consume PAY.IN with concurrency 3, IBM MQ still delivers a given message to only one of them. This differs from JMS Topic publish/subscribe where every active subscriber receives a copy. Architects choose queues when work must be load-balanced—payment files, job tickets, inventory updates—rather than broadcast. Ordering is FIFO for a single active consumer sequence; multiple consumers interleave completion times so strict global order requires a single consumer or application-level sequencing keys.
12345678910111213QueueConnectionFactory qcf = /* MQConnectionFactory with host, port, channel, QM */; QueueConnection conn = qcf.createQueueConnection(); conn.start(); QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue ordersQueue = session.createQueue("ORDERS.NEW"); QueueSender sender = session.createSender(ordersQueue); TextMessage msg = session.createTextMessage("{\"orderId\": 1001}"); msg.setJMSDeliveryMode(DeliveryMode.PERSISTENT); sender.send(msg); QueueReceiver receiver = session.createQueueReceiver(ordersQueue); TextMessage received = (TextMessage) receiver.receive(5000);
createQueueSession's first parameter false means non-transacted session for this example; the second parameter AUTO_ACKNOWLEDGE tells the provider to acknowledge consumption when the message is delivered to the application. A transacted session (true as first parameter) groups sends and receives into a unit committed with session.commit() or rolled back with session.rollback(). Connection.start() begins message delivery to listeners; without start, a synchronous receive may block indefinitely. Always close senders, receivers, sessions, and connections in finally blocks or try-with-resources patterns your framework provides.
JMS DeliveryMode.PERSISTENT maps to MQ persistent messages; NON_PERSISTENT maps to non-persistent. Persistence interacts with queue DEFPSIST when the application leaves defaults. Persistent messages survive queue manager restart if they reached the queue on disk under syncpoint rules. Non-persistent messages trade durability for speed—appropriate for refresh notifications you can afford to lose. Beginners should align JMS delivery mode with operations backup and recovery expectations; marking marketing email persistent on a high-volume queue can fill disks during incidents.
| JMS mode | MQMD persistence | Typical effect |
|---|---|---|
| PERSISTENT | MQPER_PERSISTENT | Logged to queue logs; survives restart if committed |
| NON_PERSISTENT | MQPER_NOT_PERSISTENT | Memory path; lost if failure before consumption |
| Default from queue | MQPER_AS_Q_DEF | DEFPSIST on QLOCAL decides |
Poison messages that always throw exceptions increment BackoutCount; when BOTHRESH on the queue is exceeded, IBM MQ routes to BOQNAME. JMS does not hide backout behavior—configure BOTHRESH and monitor backout depth alongside main queue depth.
Request-reply uses a temporary or model-defined dynamic queue for replies. The request message sets JMSReplyTo to that destination; the server puts the response there. IBM MQ implements temporary queues via model queues (QMODEL). Correlation uses JMSCorrelationID and JMSMessageID mapping to MQ CorrelId and MsgId. Mixed estates with COBOL requesters and Java responders need a documented byte layout for correlation fields—hex padding mistakes strand replies on the queue forever.
Spring Boot configures a DefaultJmsListenerContainer with concurrency to spin multiple consumers on one queue name—each consumer thread owns session discipline. @JmsListener("PAY.IN") is the common pattern. Set acknowledge mode explicitly when defaults are wrong for your recovery story. Enable IBM MQ client reconnect on the ConnectionFactory bean so listener containers survive queue manager restarts without manual intervention.
1234567DEFINE QLOCAL('ORDERS.NEW') REPLACE + DESCR('JMS point-to-point work queue') + MAXDEPTH(500000) DEFPSIST(YES) BOTHRESH(3) + BOQNAME('ORDERS.BOQ') DISPLAY QLOCAL('ORDERS.NEW') CURDEPTH IPPROCS OPROCS DISPLAY AUTHREC PROFILE('ORDERS.NEW') OBJTYPE(QUEUE)
Grant +put and +get to the MCAUSER or application principal your JMS client uses on SVRCONN. IPPROCS and OPROCS in DISPLAY QLOCAL show open handles—useful when JMS connection leaks leave consumers attached. MAXDEPTH protects against runaway producers; tune with peak message size and disk capacity.
Use Queue when one consumer should process each unit of work. Use Topic when many subscribers must each receive the same event. Putting order status on a queue when five microservices each need a copy forces sequential handoffs or duplicate manual gets—wrong tool. Putting payroll files on a topic floods every subscriber with data only one service should touch—also wrong. The next tutorial in this track covers JMS Topics in depth.
A JMS queue is a single ticket window at the deli. Each customer number is served by one clerk. Topics are the loudspeaker that calls everyone in the mall—different pattern entirely.
A JMS queue is a line where each note goes to the next person waiting—not to every person in the room. If three friends are waiting, only one friend gets each note.
Design queue names for order capture and fulfillment when both use JMS. Explain why you would not use one shared topic.
Your consumer uses AUTO_ACK but crashes after receive and before database commit. List two configuration changes that reduce message loss risk.
Run DISPLAY QLOCAL for a JMS workload queue during a test. Record CURDEPTH, IPPROCS, and what they imply.
1. A JMS Queue on IBM MQ maps to:
2. Two consumers on the same JMS queue:
3. PERSISTENT delivery mode means:
4. QueueSender sends to: