Skip to main content

Unhandled Messages

Not every message that arrives on the <in> queue will match a pending request. There are two common situations:

  1. Late responses — a response arrives after its request has already timed out. The .req marker is gone, so QMUX can't correlate it.
  2. 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;
}
}
source is the QMUX itself

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
CounterMeaning
txTotal messages sent
rxTotal responses matched
tx_expiredSent messages that timed out (no response received)
tx_pendingMessages on the outbound queue not yet picked up by ChannelAdaptor
rx_expiredResponses received after their request already timed out
rx_pendingRequests currently waiting for a response
rx_unhandledUnmatched messages posted to the <unhandled> queue
rx_forwardedUnmatched 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.