Details
Description
This bug happens when QuickFixJ is working in acceptor mode.
If the acceptor wants to initiate the logout procedure (send logout message to an initiator), it calls the "logout()" method on the Session.
This sets the boolean "enabled" to false.
public void logout() {
setEnabled(false);
}
Then, the next() method is called by the timer SessionTimerTask. On the first call, it send the logout message to the client because the not enabled, the client is logged on and the logout hasn't been sent yet.
if (!isEnabled()) {
if (isLoggedOn()) {
if (!state.isLogoutSent())
} else { return; }
}
On the next few calls on Session.next(), the timer might eventually disconnect the initiator if not logout response has been received.
if (state.isLogoutTimedOut()) {
getLog().onEvent("Timed out waiting for logout response");
disconnect();
}
The initiator is also disconnected if it send the logout message back.
Until here all is well. Now the problem begins.
Imagine that the same initiator want to reconnect. It sends the Login message to our acceptor. We receive it in the Session.next(Message message) method.
This method will call nextLogon(message) which will generate the logon message.
Then at then end of the next(Message message) method, we call next:
if (isLoggedOn()) {
next();
}
and we are faced once again with the following code which will generate a logout message immediately.
if (!isEnabled()) {
if (isLoggedOn()) {
if (!state.isLogoutSent()) { getLog().onEvent("Initiated logout request"); generateLogout(state.getLogoutReason()); }
} else
{ return; }}
This happens because the boolean returned by isEnabled() has not been reset to true. So the session is not enabled, it is logged on and not logout has been set yet.
Remember that the enabled boolean was set to false by the acceptor to logout the initiator a moment ago.
Conclusion: the initiator canot logon anymore! It will receive a successful logon response and immediately after a logout message!
The solution is to setEnabled(true) at the login call in case quickfix is in acceptor mode.
In the next(Message message) method change the current code:
if (msgType.equals(MsgType.LOGON)) {
nextLogon(message);
} else if (msgType.equals(MsgType.HEARTBEAT)) {
nextHeartBeat(message);
}
......
for this code:
if (msgType.equals(MsgType.LOGON)) {
if (!state.isInitiator())
nextLogon(message);
} else if (msgType.equals(MsgType.HEARTBEAT)) {
nextHeartBeat(message);
}
......