Connecting IBM MQ with WSO2 MI — The Hard Way and the AI Way
Connecting WSO2 Micro Integrator 4.x to IBM MQ: A Step-by-Step Guide (With the Gotchas Nobody Tells You About)

TL;DR: WSO2 MI 4.2.0 can consume messages from IBM MQ queues via a JMS Inbound Endpoint — but it takes more than dropping JARs in a folder. You need an OSGi bundle, a properly generated JNDI
.bindingsfile, and a few non-obvious config flags. This guide walks through all of it.
🚀 Hey!! Claude Code user? Jump to the Bonus section and let it handle the setup.
What We’re Building
IBM MQ is still a first-class citizen in enterprise messaging — banking, telecom, logistics. If you're building integrations with WSO2 Micro Integrator in those spaces, this is a real deal.
WSO2 MI connects to IBM MQ through the standard JMS transport — MI polls a queue, routes messages through a Synapse sequence. Simple idea, but the setup has enough sharp edges that I spent more time fighting classloader conflicts than writing integration logic.
This guide covers the complete setup using WSO2 MI 4.2.0, IBM MQ 9.4.x (via Docker). By the end, you’ll have MI polling an IBM MQ queue and logging every message it receives — and you’ll understand exactly why each step is done the way it is.
Prerequisites
- WSO2 MI 4.x.x — this guide uses 4.2.0 (download the pack from wso2.com)
- Java 11 or 17 — MI 4.2.0 is tested with JDK 11 and 17 (Corretto, OpenJDK, Oracle, AdoptOpenJDK). I used Temurin JDK 17.
- Docker — IBM MQ runs as a container. I used Rancher Desktop, but Docker Desktop works too.
- Apache Maven 3.x — to build the OSGi bundles.
Apple Silicon users: The IBM MQ Docker image (
icr.io/ibm-messaging/mq:latest) isamd64only. Docker will use Rosetta 2 emulation automatically if you add--platform linux/amd64to yourdocker runcommand. Without this flag, the container will fail with "no matching manifest for linux/arm64".
Step 1 — Start IBM MQ in Docker
docker run -d \
--name ibmmq \
--platform linux/amd64 \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--env MQ_APP_PASSWORD=passw0rd \
--env MQ_ADMIN_PASSWORD=passw0rd \
-p 1414:1414 \
-p 9443:9443 \
icr.io/ibm-messaging/mq:latest
The --platform linux/amd64 flag is harmless on Intel and required on Apple Silicon — include it always for portability.
Wait ~30 seconds for the queue manager to fully initialize, then verify:
docker logs ibmmq 2>&1 | grep "Started queue manager"
The MQ Web Console is available at https://localhost:9443/ibmmq/console (login: admin / passw0rd). Good to sanity-check before going further.
Step 2 — Copy the IBM MQ Client JARs
WSO2 MI needs four JARs from the IBM MQ image to talk to the broker. Run this to copy them directly from the running container into a local wmq-client/lib/ directory:
mkdir -p wmq-client/lib && \
for jar in com.ibm.mq.allclient.jar jms.jar fscontext.jar providerutil.jar; do
docker cp "ibmmq:/opt/mqm/java/lib/$jar" "wmq-client/lib/$jar"
done
Keep these here for now — don’t copy them into MI yet.
Step 3 — Build an OSGi Bundle (The Step That Matters Most)
This is where most people get stuck, and it’s worth understanding why this step exists.
WSO2 MI runs on an OSGi runtime (Eclipse Equinox). It ships with its own embedded javax.jms implementation. If you drop the raw IBM MQ JARs into MI's lib/ folder, you get a classloader conflict — MI's javax.jms and IBM MQ's javax.jms fight each other, and you end up with a ClassCastExceptionlike:
*"com.ibm.mq.jms.MQConnectionFactory cannot be cast to javax.jms.QueueConnectionFactory"*
The fix: wrap all four IBM MQ JARs into a single OSGi bundle and place it in dropins/ instead of lib/. The bundle exports all packages cleanly under OSGi's classloading model, and the conflict disappears.
Create wmq-client/pom.xml:
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>wmq-client</groupId>
<artifactId>wmq-client</artifactId>
<version>9.4.5.0</version>
<packaging>bundle</packaging>
<dependencies>
<dependency>
<groupId>com.ibm</groupId>
<artifactId>fscontext</artifactId>
<version>9.4.5.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/fscontext.jar</systemPath>
</dependency>
<dependency>
<groupId>com.ibm</groupId>
<artifactId>providerutil</artifactId>
<version>9.4.5.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/providerutil.jar</systemPath>
</dependency>
<dependency>
<groupId>com.ibm</groupId>
<artifactId>allclient</artifactId>
<version>9.4.5.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/com.ibm.mq.allclient.jar</systemPath>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>jms</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/jms.jar</systemPath>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<!-- Use 5.x+ — IBM MQ 9.x JARs are compiled with Java 21,
which is incompatible with the older 2.3.4 plugin. -->
<version>5.1.9</version>
<extensions>true</extensions>
<configuration>
<instructions>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Bundle-Name>${project.artifactId}</Bundle-Name>
<Export-Package>*;-split-package:=merge-first</Export-Package>
<Private-Package/>
<Import-Package/>
<Embed-Dependency>*;scope=system;inline=true</Embed-Dependency>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
</project>
Note on the plugin version: IBM MQ 9.x JARs are compiled targeting Java 21. The older
maven-bundle-plugin 2.3.4can't parse them. Use5.1.9or later.
Now build:
cd wmq-client
mvn clean install
This produces wmq-client/target/wmq-client-9.4.5.0.jar — a single OSGi bundle containing all four IBM MQ JARs inlined.
Step 4 — Install Into MI
MI_HOME=/path/to/wso2mi-4.2.0
# OSGi bundle goes in dropins/ — NOT lib/
cp wmq-client/target/wmq-client-9.4.5.0.jar $MI_HOME/dropins/
IBM MQ’s JMS classes also require jta.jar — download it directly into MI's lib/:
curl -o $MI_HOME/lib/jta.jar \
https://repo1.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar
Step 5 — Generate the JNDI .bindings File
MI uses JNDI to look up the IBM MQ connection factory at runtime. You need a .bindings file generated by IBM MQ's JMSAdmin tool. This runs inside the container.
Create the JMSAdmin config:
# Execute inside the docker container
docker exec ibmmq bash -c "mkdir -p /tmp/jndi"
docker exec ibmmq bash -c "cat > /tmp/JMSAdmin.config << 'EOF'
INITIAL_CONTEXT_FACTORY=com.sun.jndi.fscontext.RefFSContextFactory
PROVIDER_URL=file:///tmp/jndi
EOF"
Define the connection factory and queue:
docker exec ibmmq bash -c "cat > /tmp/jmsadmin_cmds.txt << 'EOF'
DEFINE QCF(ConnectionFactory) QMGR(QM1) CHANNEL(DEV.APP.SVRCONN) HOSTNAME(localhost) PORT(1414) TRANSPORT(CLIENT)
DEFINE Q(DEV.QUEUE.1) QUEUE(DEV.QUEUE.1) QMGR(QM1)
END
EOF"
This runs IBM MQ’s JMSAdmin tool inside the container — it reads the config and command files you just created and generates the .bindings file in /tmp/jndi/:
docker exec ibmmq bash -c \
"cd /tmp && /opt/mqm/java/bin/JMSAdmin -cfg /tmp/JMSAdmin.config < /tmp/jmsadmin_cmds.txt"
Use
QCF, notCF. WSO2 MI's JMS transport internally casts the looked-up factory object tojavax.jms.QueueConnectionFactory. If you useDEFINE CF(...)(the generic type), IBM MQ creates anMQConnectionFactoryinstead — and MI throws aClassCastExceptionat startup even when the OSGi bundle is correctly installed. This tripped me up for an embarrassingly long time.
Now, Copy the .bindings file to MI:
mkdir -p $MI_HOME/repository/conf/jndi
docker cp ibmmq:/tmp/jndi/.bindings $MI_HOME/repository/conf/jndi/.bindings
You just got through the hard part. From here, it's all WSO2 MI configuration.
Step 6 — Configure deployment.toml
Add this to <MI_HOME>/conf/deployment.toml:
[[transport.jms.listener]]
name = "MyQueueConnectionFactory"
parameter.initial_naming_factory = "com.sun.jndi.fscontext.RefFSContextFactory"
parameter.provider_url = "file:///path/to/wso2mi-4.2.0/repository/conf/jndi"
parameter.connection_factory_name = "ConnectionFactory"
parameter.connection_factory_type = "queue"
parameter.username = "app"
parameter.password = "passw0rd"
[[transport.jms.listener]]
name = "default"
parameter.initial_naming_factory = "com.sun.jndi.fscontext.RefFSContextFactory"
parameter.provider_url = "file:///path/to/wso2mi-4.2.0/repository/conf/jndi"
parameter.connection_factory_name = "ConnectionFactory"
parameter.connection_factory_type = "queue"
parameter.username = "app"
parameter.password = "passw0rd"
Step 7 — Create the JMS Inbound Endpoint
Create <MI_HOME>/repository/deployment/server/synapse-configs/default/inbound-endpoints/IBMMQ_Inbound.xml:
<?xml version="1.0" encoding="UTF-8"?>
<inboundEndpoint name="IBMMQ_Inbound" sequence="IBMMQ_Log_Seq" onError="fault"
protocol="jms" suspend="false"
xmlns="http://ws.apache.org/ns/synapse">
<parameters>
<parameter name="interval">5000</parameter>
<parameter name="sequential">true</parameter>
<parameter name="coordination">true</parameter>
<!-- JNDI -->
<parameter name="java.naming.factory.initial">com.sun.jndi.fscontext.RefFSContextFactory</parameter>
<parameter name="java.naming.provider.url">file:///path/to/wso2mi-4.2.0/repository/conf/jndi</parameter>
<!-- JMS -->
<parameter name="transport.jms.ConnectionFactoryJNDIName">ConnectionFactory</parameter>
<parameter name="transport.jms.ConnectionFactoryType">queue</parameter>
<parameter name="transport.jms.Destination">DEV.QUEUE.1</parameter>
<parameter name="transport.jms.DestinationType">queue</parameter>
<parameter name="transport.jms.UserName">app</parameter>
<parameter name="transport.jms.Password">passw0rd</parameter>
<parameter name="transport.jms.SessionAcknowledgement">CLIENT_ACKNOWLEDGE</parameter>
<parameter name="transport.jms.CacheLevel">3</parameter>
<parameter name="transport.jms.SessionTransacted">false</parameter>
<parameter name="transport.jms.ResetConnectionOnFailure">true</parameter>
<parameter name="transport.jms.RetriesBeforeSuspension">5</parameter>
<parameter name="transport.jms.PollingSuspensionPeriod">3000</parameter>
</parameters>
</inboundEndpoint>
***transport.jms.CacheLevelmust be an integer.* The value3means "cache up to consumer level." Some docs and examples use the string"consumer"— that causes aNumberFormatExceptionat startup. Use3.
Step 8 — Create the Processing Sequence
Create <MI_HOME>/repository/deployment/server/synapse-configs/default/sequences/IBMMQ_Log_Seq.xml:
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="IBMMQ_Log_Seq">
<log level="full">
<property name="STATUS" value="MESSAGE RECEIVED FROM IBM MQ"/>
</log>
<drop/>
</sequence>
Replace <drop/> with your actual processing logic — call a backend, transform the payload, publish to another queue, whatever your integration needs.
Step 9 — Start MI and Test
Make sure you’re using JDK 11–17 before starting:
export JAVA_HOME="$HOME/.sdkman/candidates/java/17.0.17-tem"
export PATH="$JAVA_HOME/bin:$PATH"
sh /path/to/wso2mi-4.2.0/bin/micro-integrator.sh
Watch the logs. A successful startup looks like:
INFO {InboundEndpoint} - Initializing Inbound Endpoint: IBMMQ_Inbound
INFO {JMSProcessor} - Initializing inbound JMS listener for inbound endpoint IBMMQ_Inbound
INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][IBMMQ_Inbound-JMS--SYNAPSE_INBOUND_ENDPOINT0]
Now put a test message in the queue:
docker exec ibmmq bash -c "printf '<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">
<soapenv:Body><test>Hello from IBM MQ!</test></soapenv:Body>
</soapenv:Envelope>' | /opt/mqm/samp/bin/amqsput DEV.QUEUE.1 QM1"
And in MI’s logs, you should see:
INFO {LogMediator} - {inboundendpoint:IBMMQ_Inbound} STATUS = MESSAGE RECEIVED FROM IBM MQ,
Envelope: ....<test>Hello from IBM MQ!</test>....
🚀 Bonus — Set it Up in Minutes with Claude Code
If you have Claude Code installed, you don’t have to follow this guide manually. I’ve published a SETUP_GUIDE.md in the repo below that's written as agentic instructions for Claude Code.
When you point Claude Code at it, it will:
- Ask for your MI home path, Java home, working directory, and queue name before touching anything
- Check that Docker, Java 11–17, and Maven are present
- Run each step — Docker, OSGi bundle build, JNDI bindings, MI config — pausing for your confirmation before every command or file write
- Verify each step succeeded before moving to the next
- Send a test message and confirm the end-to-end connection is working
To use it:
# Clone the repo
git clone https://github.com/ramiiyan/agent-guider.git
cd agent-guider/wso2-mi-ibmmq
# Start Claude Code and hand it the guide
claude
> /read SETUP_GUIDE.md — then follow it to set up IBM MQ with WSO2 MI
Claude Code reads the instructions, asks your questions, and drives the whole setup from there. No copy-pasting commands — just answer its questions and confirm each step.
→ **SETUP_GUIDE.md on GitHub**
That’s it! 🎉 You’ve got WSO2 MI and IBM MQ up and running — messages flowing just the way they should. It takes a bit of setup the first time, but once it’s done, it just works. If you want to make it easier, with Claude Code, you can spin this whole thing up in minutes. 🚀
Happy integrating! 🥳
Tested with WSO2 MI 4.2.0, IBM MQ 9.4.5.0, JDK 17 (Temurin), and Rancher Desktop on macOS (Apple Silicon).
References
[1] https://wso2.com/micro-integrator
[2] https://mi.docs.wso2.com/en/4.2.0/install-and-setup/setup/brokers/configure-with-ibm-webspheremq
메타데이터
- post_id
- c543391dabfd
- slug
- connecting-ibm-mq-with-wso2-mi-the-hard-way-and-the-ai-way-c543391dabfd
- url
- https://medium.com/@ramiiyan.sriraguhan/connecting-ibm-mq-with-wso2-mi-the-hard-way-and-the-ai-way-c543391dabfd
- canonical_url
- https://medium.com/@ramiiyan.sriraguhan/connecting-ibm-mq-with-wso2-mi-the-hard-way-and-the-ai-way-c543391dabfd
- author_url
- https://medium.com/@ramiiyan.sriraguhan
- status
- ok
- fetched_at
- 2026-06-09 15:37:30