Skip to main content

QMUX Tutorial

Where to work

The code for this tutorial lives under tutorials/qmux. This tutorial requires both the qserver and the channel it connects through. Start them first:

# Terminal 1 — the server
./gradlew :tutorials:qserver:installApp
./tutorials/qserver/build/install/qserver/bin/q2

# Terminal 2 — the QMUX tutorial (includes its own ChannelAdaptor)
./gradlew :tutorials:qmux:installApp
./tutorials/qmux/build/install/qmux/bin/q2

What is QMUX?

ChannelAdaptor manages a TCP connection and bridges it to Space queues. QMUX sits one layer above that and adds request/response correlation: you hand it an ISOMsg and a timeout, and it returns the matched response — or null if the remote host didn't reply in time.

The contract between QMUX and ChannelAdaptor is the same Space queues and ready semaphore introduced in the ChannelAdaptor tutorial.

QMUX XML descriptor

Add src/dist/deploy/20_qmux.xml:

<qmux name="upstream" logger="Q2">
<in>upstream-receive</in>
<out>upstream-send</out>
<ready>upstream.ready</ready>
<unhandled>unhandled</unhandled>
<key>11</key>
</qmux>

Key elements

ElementDescription
<in>Space queue where incoming messages arrive. This is the ChannelAdaptor's <out> queue.
<out>Space queue where outgoing messages are placed. This is the ChannelAdaptor's <in> queue.
<ready>One or more ChannelAdaptor ready indicators. isConnected() returns true only when at least one is present in Space.
<key>Field numbers used to correlate requests with responses. Defaults to 41, 11 (terminal ID + STAN). Here we use only field 11 since the tutorial's 2800 NMM doesn't carry field 41.
<unhandled>Space queue for messages that don't match any pending request (unsolicited inbounds, late responses). Optional but recommended.
Queue wiring

QMUX's <in> and <out> are named from QMUX's perspective (what comes in to QMUX, what goes out from QMUX). They are the mirror image of the ChannelAdaptor's <out> and <in> respectively. A common source of misconfiguration is wiring them in the same direction instead of crossing them.

ChannelAdaptor descriptor

The QMUX module includes its own ChannelAdaptor at src/dist/deploy/10_channel.xml — identical to the one from the ChannelAdaptor tutorial:

<channel-adaptor name="upstream" logger="Q2">
<channel class="org.jpos.iso.channel.XMLChannel" logger="Q2">
<property name="packager" value="org.jpos.iso.packager.XMLPackager"/>
<property name="host" value="${upstream.host}"/>
<property name="port" value="${upstream.port}"/>
</channel>
<in>upstream-send</in>
<out>upstream-receive</out>
<reconnect-delay>10000</reconnect-delay>
<keep-alive>no</keep-alive>
</channel-adaptor>

Build and Run

./gradlew :tutorials:qmux:installApp
./tutorials/qmux/build/install/qmux/bin/q2

Once the ChannelAdaptor connects to the QServer, upstream.ready appears in Space and QMUX's isConnected() returns true. The SendRequest bean then fires its first 2800 and you'll see something like:

<log realm="send-request" at="2024-09-26T15:30:05.001">
<info>sending 2800, STAN=000001</info>
</log>
<log realm="send-request" at="2024-09-26T15:30:05.044">
<info>response 2810, STAN=000001 rc=0000</info>
</log>

The next two pages cover how the correlation works and what happens to messages that don't match.