MuleSoft JMS and ActiveMQ
JMS (Java Message Service) is a standard messaging API that lets applications exchange messages through a message broker. ActiveMQ is the most popular open-source JMS broker. MuleSoft's JMS Connector communicates with ActiveMQ and other JMS-compatible brokers like IBM MQ, RabbitMQ (with JMS plugin), and Amazon SQS. Messaging makes integrations resilient, decoupled, and scalable.
Why Message Brokers
A message broker is a middleman for data. Instead of Application A calling Application B directly, A drops a message into the broker, and B picks it up when ready. If B is down, messages queue up safely. When B restarts, it processes the backlog. No data is lost.
Direct Call vs Message Broker
Direct Call (fragile):
Order System ──── POST /process ────► Fulfillment System
(if down, order is lost!)
Message Broker (resilient):
Order System ──── publish to queue ──► [BROKER: orders-queue]
│
(holds messages safely, even if consumer down)
│
▼
Fulfillment System
(picks up when ready)
Key Messaging Concepts
- Queue: A point-to-point channel. Each message is consumed by exactly one receiver. Like a task list — one worker picks up each task.
- Topic: A publish-subscribe channel. One message is delivered to ALL current subscribers. Like a broadcast — every listener gets a copy.
- Message: The data packet sent through the broker. Has a body (payload) and optional headers (metadata).
- Producer: The application that sends messages to the broker.
- Consumer: The application that receives messages from the broker.
- Acknowledgment: The consumer's confirmation that it received and processed the message successfully.
Queue vs Topic
QUEUE (point-to-point):
Producer → [orders-queue] → Consumer A
(Consumer B gets nothing — each message consumed once)
Useful for: task distribution, load balancing, guaranteed once-processing
TOPIC (publish-subscribe):
Publisher → [order-events-topic] → Consumer A (gets copy)
→ Consumer B (gets copy)
→ Consumer C (gets copy)
(All subscribers get the same message)
Useful for: event broadcasting, cache invalidation, audit logging
Installing ActiveMQ Locally
For development, download Apache ActiveMQ from the ActiveMQ website. Extract it and run activemq start (Linux/Mac) or activemq.bat start (Windows). The broker starts on port 61616 for JMS connections and port 8161 for the web admin console (http://localhost:8161). Default credentials are admin / admin.
JMS Connector Configuration in MuleSoft
ActiveMQ Connection Setup
JMS Connector Config Name: JMS_ActiveMQ_Config
Connection Factory: Active MQ Connection Factory
Broker URL: tcp://localhost:61616
Username: admin
Password: ${jms.password}
Connection Pool:
Max Connections: 5
Min Eviction Time: 60000 ms
Publishing a Message to a Queue
Order Processing with JMS Publish
Producer Flow: receiveOrderFlow
[HTTP Listener: POST /orders]
│
▼
[Validate: check required fields]
│
▼
[Transform: prepare order message]
%dw 2.0
output application/json
---
{
"orderId": uuid(),
"customer": payload.customer,
"items": payload.items,
"total": payload.total,
"timestamp": now() as String
}
│
▼
[JMS: Publish]
Destination: orders-processing-queue
Destination Type: QUEUE
Message Body: #[payload]
Headers:
JMSType: "OrderCreated"
JMSCorrelationID: #[vars.correlationId]
│
▼
[HTTP Response: 202 Accepted]
{ "message": "Order accepted for processing", "correlationId": vars.correlationId }
Consuming Messages from a Queue
Order Consumer Flow
Consumer Flow: processOrderFlow (separate Mule app or module)
[JMS: On New Message (trigger)]
Destination: orders-processing-queue
Destination Type: QUEUE
Number of Consumers: 4 (4 parallel threads reading from queue)
Acknowledgment Mode: AUTO (auto-acknowledge on successful processing)
│
▼ (fires when a new message arrives)
[Transform: parse JSON message]
│
▼
[Database: INSERT order into orders table]
│
▼
[Salesforce: Create Order record]
│
▼
[Email Connector: send confirmation to customer]
│
▼
[Logger: "Order processed: #[payload.orderId]"]
(JMS auto-acknowledges the message — it is removed from the queue)
Dead Letter Queue
A Dead Letter Queue (DLQ) holds messages that failed processing after the maximum number of retries. Instead of losing the failed message, the broker moves it to the DLQ. An operations team can inspect DLQ messages, fix the problem, and replay them.
DLQ Setup and Monitoring
Normal Processing:
orders-processing-queue → Consumer processes message ✓
Message Processing Fails:
Attempt 1: fails → wait 5 seconds → retry
Attempt 2: fails → wait 10 seconds → retry
Attempt 3: fails → message moved to orders-dlq
Operations team:
→ Checks orders-dlq in ActiveMQ console
→ Sees the failed message and the error
→ Fixes the bug or the data
→ Replays the message from the DLQ back to orders-processing-queue
MuleSoft monitors the DLQ:
[JMS: On New Message]
Destination: orders-dlq
│
▼
[Email: notify operations team]
"Failed message in DLQ: #[payload.orderId]"
Message Selectors
Message selectors filter which messages a consumer receives from a queue or topic. Use selectors when multiple consumer types share one queue and each should only process certain messages.
Messages in "tasks-queue":
Message 1: JMSType = "EmailTask", body = {to: "alice@x.com"}
Message 2: JMSType = "SMSTask", body = {phone: "+1555..."}
Message 3: JMSType = "EmailTask", body = {to: "bob@x.com"}
Email Consumer:
[JMS: On New Message]
Destination: tasks-queue
Selector: JMSType = 'EmailTask'
Receives: Messages 1 and 3 only
SMS Consumer:
[JMS: On New Message]
Destination: tasks-queue
Selector: JMSType = 'SMSTask'
Receives: Message 2 only
Request-Reply with Temporary Queues
When you need a synchronous-like response from an asynchronous message flow, use the JMS Publish and Consume operation. It sends a message, creates a temporary reply queue, waits for a response on that queue, and returns the response to the calling flow.
[JMS: Publish and Consume]
Destination: credit-check-service-queue
Send Content-Type As: application/json
Reply Timeout: 10000 ms (wait up to 10 seconds for reply)
Sends: { "customerId": "CUST-001", "requestedAmount": 5000 }
Receives: { "approved": true, "creditLimit": 10000 }
│
▼
(continues flow with credit check response as payload)
