Unhandled Messages
Not every message that arrives on the <in> queue will match a pending request. There are two common situations:
- Late responses — a response arrives after its request has already timed out. The
.reqmarker is gone, so QMUX can't correlate it. - Unsolicited inbounds — the remote host initiates a message (e.g. a network management request from the acquirer, a reversal advice, a cutover notification). These are valid messages that simply have no pending request on this side.
Both land in processUnhandled().
The <unhandled> queue
<qmux name="upstream" logger="Q2">
...
<unhandled>unhandled</unhandled>
</qmux>
When configured, any unmatched message is placed on this Space queue with a 120-second TTL. Something else in the system — a transaction participant, a monitoring QBean, another request listener — can consume from unhandled and handle it appropriately.
If <unhandled> is not configured, unmatched messages are silently discarded (after passing through any configured request-listeners). In production, always configure it and monitor the queue depth.
request-listener on QMUX
Just like QServer, QMUX supports one or more ISORequestListener implementations. They run before the message is placed on the <unhandled> queue:
<qmux name="upstream" logger="Q2">
<in>upstream-receive</in>
<out>upstream-send</out>
<ready>upstream.ready</ready>
<unhandled>unhandled</unhandled>
<key>11</key>
<request-listener class="org.jpos.tutorial.HandleUnsolicited" logger="Q2" />
</qmux>
The listener chain follows the same contract as QServer: each listener is called in order; the first to return true stops the chain and the message is not forwarded to <unhandled>.
public class HandleUnsolicited implements ISORequestListener {
@Override
public boolean process(ISOSource source, ISOMsg m) {
try {
if ("2800".equals(m.getMTI())) {
// Remote host sent us a 2800 — respond
ISOMsg r = (ISOMsg) m.clone();
r.setResponseMTI();
r.set(39, "0000");
source.send(r);
return true;
}
} catch (Exception e) { /* log */ }
return false;
}
}
When a QMUX request-listener receives an unhandled message, source is the QMUX instance (which implements ISOSource). Calling source.send(r) puts the response onto the <out> queue, which the ChannelAdaptor picks up and sends over the wire. This is the same send() path used by any other component that wants to push a message through the channel without waiting for a response.
Counters and diagnostics
QMUX exposes counters via JMX (through QMUXMBean) and via dump():
tx=12, rx=12, tx_expired=0, tx_pending=0, rx_expired=0,
rx_pending=0, rx_unhandled=0, rx_forwarded=0,
connected=true, last=1727364605123, idle=3421ms
| Counter | Meaning |
|---|---|
tx | Total messages sent |
rx | Total responses matched |
tx_expired | Sent messages that timed out (no response received) |
tx_pending | Messages on the outbound queue not yet picked up by ChannelAdaptor |
rx_expired | Responses received after their request already timed out |
rx_pending | Requests currently waiting for a response |
rx_unhandled | Unmatched messages posted to the <unhandled> queue |
rx_forwarded | Unmatched messages handled by a request-listener |
tx_expired growing steadily is the key signal that the remote host is slow or unreachable. rx_unhandled growing is a sign that either the MTI mapping is misconfigured (responses aren't being correlated) or the remote host is sending unsolicited messages that nothing is consuming.