check-enabled and LogonManager Integration
The problem
QMUX.isConnected() returns true as soon as the TCP connection is up — before any logon has taken place. In a production environment, a connected-but-not-logged-on link must not receive financial transactions. Without a logon gate, the pool would route traffic to a fresh connection before LogonManager has had a chance to complete the sign-on sequence.
check-enabled bridges this gap: a MUX is only considered usable by the pool after LogonManager explicitly marks it as enabled.
The enabled semaphore
When check-enabled=true is set on MUXPool, isUsable() runs this check for each candidate MUX:
String enabledKey = qmux.getName() + ".enabled"; // e.g. "upstream-a.enabled"
String readyKey = qmux.getReadyIndicatorNames()[0]; // e.g. "upstream-a.ready"
return mux.isConnected()
&& sp.rdp(enabledKey) == sp.rdp(readyKey); // reference equality!
Two conditions must both be true:
- The QMUX's own
isConnected()— the ready semaphore is present in Space. - The
enabledkey holds the exact same object reference as thereadykey.
The == comparison is object reference equality, not .equals(). This is intentional and crucial to the design.
Why reference equality?
ChannelAdaptor publishes the ready semaphore as:
sp.out(ready, new Date()); // a new Date instance on every connect
TSpace stores object references. When LogonManager reads the ready semaphore and stores it under enabled:
Object sessionId = sp.rd(readyKey, 60000); // same Date reference from TSpace
// ...after successful logon...
sp.out(enabledKey, sessionId); // same reference stored in enabled
Both sp.rdp(enabledKey) and sp.rdp(readyKey) now return the same in-memory Date object → == is true → MUX is usable.
On reconnect, ChannelAdaptor creates a brand-new Date:
ready → Date@7b3a4e9f (new)
enabled → Date@4c2d8b12 (old, from previous session)
Date@7b3a4e9f == Date@4c2d8b12 is false → MUX is not usable → LogonManager must re-logon before traffic flows again.
This is the same reconnect detection mechanism used by LogonManager's own logonRequired() check — both rely on the identity of the Date object that ChannelAdaptor puts in Space.
LogonManager integration
With check-enabled=true, LogonManager's doLogon() adds one step after a successful logon:
private void doLogon(Object sessionId) throws ISOException {
ISOMsg resp = mux.request(buildMsg(logonTemplate), timeout);
if (resp != null && "0000".equals(resp.getString(39))) {
sp.inp(logonKey);
sp.out(logonKey, sessionId, logonInterval);
if (checkEnabled) {
sp.inp(enabledKey);
sp.out(enabledKey, sessionId); // ← same object, no TTL
}
getLog().info("Logon OK — " + muxName);
}
}
Note that enabledKey is stored without a TTL. It lives until explicitly removed. This is intentional: if the logon session is still valid (the ready semaphore hasn't changed) the link should remain enabled even after logon-interval elapses — the LogonManager will re-logon and refresh the entry before it matters.
doLogoff() removes enabledKey first, before sending the logoff message, so the pool stops routing to this link immediately:
private void doLogoff() throws ISOException {
sp.inp(logonKey);
if (checkEnabled)
sp.inp(enabledKey); // ← pool sees it as unavailable immediately
mux.request(buildMsg(logoffTemplate), timeout);
}
Full lifecycle with check-enabled
The window during which the pool skips a reconnecting link is exactly the time between ChannelAdaptor publishing the new ready semaphore and LogonManager completing re-logon. No additional coordination is required — the pattern emerges from the shared Space.