Message-Driven Beans are the Jakarta EE pattern for event-driven Java: you write a class with an onMessage method, deploy it to Liberty or WebSphere, and the application server pulls messages from IBM MQ on your behalf. No while-loop calling receive in application code—the container manages Connection pooling, session lifecycle, transaction boundaries, and concurrency. MDBs remain common in enterprises that standardized on Java EE before Spring @JmsListener became popular; many mainframe-adjacent hubs still run WAS or Liberty clusters consuming from queues that COBOL producers fill overnight. Beginners should understand how activation specifications wire destinations to beans, how container-managed transactions differ from bean-managed or JMS transacted sessions, and how failure handling maps to IBM MQ backout and poison message queues. This tutorial covers MDB anatomy, activation configuration, queue versus topic MDBs, concurrency tuning, security principals, integration with XA datasources, poison message behavior, and migration notes toward Spring listeners.
Producers—whether JMS clients, REST adapters, or mainframe bridges—put messages on IBM MQ queues or topics. The MDB container subscribes or receives from that destination using a ConnectionFactory configured in the server. When a message arrives, the server allocates a thread from the MDB pool and calls onMessage(Message). Your code parses the payload, calls business services, and returns; the container commits or rolls back the transaction based on outcome and configuration. This separation lets operations scale consumers by changing maxConcurrency without redeploying business logic—within channel and database limits.
| Component | Role | IBM MQ touchpoint |
|---|---|---|
| ConnectionFactory (JNDI) | Connect to QM | SVRCONN channel |
| Activation specification | Destination + ack mode | Queue or topic name |
| MDB class | onMessage logic | Indirect via JMS API |
| Transaction manager | CMT boundaries | Syncpoint on get/put |
| maxConcurrency | Thread pool size | Competing MQGET consumers |
12345678910111213141516171819@MessageDriven(activationConfig = { @ActivationConfigProperty( propertyName = "destinationType", propertyValue = "jakarta.jms.Queue"), @ActivationConfigProperty( propertyName = "destination", propertyValue = "PAY.IN") }) @TransactionAttribute(TransactionAttributeType.REQUIRED) public class PaymentMdb implements MessageListener { @Override public void onMessage(Message message) { try { String body = ((TextMessage) message).getText(); // business processing — failure throws to trigger rollback } catch (JMSException e) { throw new RuntimeException(e); } } }
destinationType Queue targets point-to-point. For topics, use jakarta.jms.Topic and configure durable subscription properties on the activation spec per provider documentation. REQUIRED means the container begins a transaction before onMessage and commits if no unchecked exception propagates—unchecked exceptions roll back and may redeliver the message depending on acknowledgement and backout configuration.
12345678
jmsDestinationRef and jmsConnectionFactoryRef must resolve to administratively defined JMS resources pointing at IBM MQ objects that exist on the queue manager. maxConcurrency 10 means up to ten parallel onMessage invocations—ten competing consumers on PAY.IN if all are active. Tune with database connection pool size and MQ channel MAXINST limits.
Queue MDBs process work items once per message across the pool—ideal for payment files and job steps. Topic MDBs receive broadcast copies; each MDB deployment instance or durable subscription receives publications matching its subscription. Using a topic MDB when you meant queue semantics duplicates processing across every deployed EAR unless you use shared durable subscription features carefully. Architecture reviews should state destination type explicitly in deployment descriptors.
When onMessage always fails—bad XML schema, missing foreign key—the container rolls back repeatedly. IBM MQ increments BackoutCount; exceeding BOTHRESH routes to BOQNAME. MDBs do not magically skip poison messages; configure BOTHRESH, monitor backout queues, and fix data or code. Idempotent processing remains essential because redelivery happens below threshold too. Some teams use NOT_SUPPORTED for logging side effects that must persist even when business logic rolls back—document those exceptions clearly.
The server connects to MQ using credentials bound to the ConnectionFactory or mapping modules. MCAUSER on the channel may differ from the end-user principal that put the message. DISPLAY CHSTATUS shows the effective user. Align AUTHREC with the identity the MDB uses, not only the interactive user who triggered upstream work.
Spring listeners offer similar asynchronous consumption with lighter deployment models for microservices. MDBs shine when you already run Liberty/WAS with standardized JMS resources, need portable Jakarta EE packaging, or integrate with other EJB components in the same transaction. Greenfield cloud-native services often choose Spring; existing enterprise EARs keep MDBs. Both ultimately call IBM MQ through the same JMS provider.
An MDB is a team of receptionists hired by the building manager (application server). Mail arrives at IBM MQ; the manager assigns the next free receptionist to open each letter. You write what the receptionist does with the letter—not how the mail truck arrives.
An MDB is a helper robot the grown-up computer starts for you. When a message shows up in the mailbox, the robot wakes up and runs your instructions automatically.
Given PAY.IN depth growing, list three MDB tuning knobs and one non-MDB cause.
When should onMessage throw versus catch and log? Relate to CMT rollback.
Sketch activation spec resources for a topic durable subscriber MDB.
1. MDB onMessage is invoked:
2. Activation spec links:
3. CMT with REQUIRED means:
4. Higher maxConcurrency on a queue MDB: