package org.f1x.v1;
import org.f1x.api.FixParserException;
import org.f1x.api.session.FailedLockException;
import org.f1x.api.session.SessionManager;
import org.f1x.io.parsers.SimpleMessageScanner;
import org.gflogger.GFLog;
import org.gflogger.GFLogFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
public abstract class SessionAcceptorWrapper implements Runnable {
protected static final GFLog LOGGER = GFLogFactory.getLog(SessionAcceptorWrapper.class);
protected final SessionManager manager;
protected final SessionIDByteReferences sessionID;
protected final byte[] logonBuffer;
protected final int logonTimeout;
protected Socket socket;
protected SessionAcceptorWrapper(int logonBufferSize, int logonTimeout, SessionManager manager) {
this.manager = manager;
this.logonBuffer = new byte[logonBufferSize];
this.sessionID = new SessionIDByteReferences();
this.logonTimeout = logonTimeout;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
int logonLength = waitLogon();
runAcceptor(logonLength);
} catch (SocketTimeoutException e) {
LOGGER.warn().append("Error occurred during starting acceptor. Logon timeout expired").commit();
close(socket);
} catch (ConnectionProblemException e) {
LOGGER.warn().append("Error occurred during starting acceptor: ").append(e.getMessage()).commit();
close(socket);
} catch (FixParserException e) {
LOGGER.warn().append("Error occurred during starting acceptor. Invalid logon message: ").append(e.getMessage()).commit();
close(socket);
} catch (FailedLockException e) {
LOGGER.info()
.append("Error occurred during starting acceptor. Failed to lock session id: ").append(e.getMessage())
.append(" (Sender Comp ID: ").append(sessionID.getSenderCompId())
.append(", Sender Sub ID: ").append(sessionID.getSenderSubId())
.append(", Target Comp ID: ").append(sessionID.getTargetCompId())
.append(", Target Sub ID: ").append(sessionID.getTargetSubId())
.append(")").commit();
close(socket);
} catch (IOException e) {
LOGGER.warn().append("Error occurred during starting acceptor: ").append(e).commit();
close(socket);
} finally {
sessionID.clear();
socket = null;
onStop();
}
}
protected abstract void onStop();
protected void runAcceptor(int logonLength) throws IOException, FailedLockException {
FixSessionAcceptor acceptor = manager.lockSession(sessionID);
try {
acceptor.connect(socket);
acceptor.run(logonBuffer, logonLength);
} finally {
manager.unlockSession(sessionID);
}
}
protected int waitLogon() throws IOException, ConnectionProblemException, FixParserException {
int oldSoTimeout = socket.getSoTimeout();
socket.setSoTimeout(logonTimeout);
int logonLength = extractSessionID();
socket.setSoTimeout(oldSoTimeout);
return logonLength;
}
protected int extractSessionID() throws IOException, ConnectionProblemException {
InputStream in = socket.getInputStream();
int logonLength = 0;
int requiredLogonLength = SimpleMessageScanner.MIN_MESSAGE_LENGTH;
boolean continueExtraction = true;
while (continueExtraction) {
int bytesRead = in.read(logonBuffer, logonLength, logonBuffer.length - logonLength);
if (bytesRead <= 0)
throw ConnectionProblemException.NO_SOCKET_DATA;
logonLength += bytesRead;
if (logonLength >= requiredLogonLength) {
int parsingResult = SessionIDParser.parseOpposite(logonBuffer, 0, logonLength, sessionID);
if (parsingResult > 0) {
continueExtraction = false;
} else {
requiredLogonLength = logonLength - parsingResult;
if (requiredLogonLength > logonBuffer.length)
throw LogonMessageIsTooLongException.INSTANCE;
}
}
}
return logonLength;
}
protected static void close(Socket socket) {
try {
socket.close();
} catch (IOException e) {
LOGGER.info().append("Error closing socket: ").append(e).commit();
}
}
protected static class LogonMessageIsTooLongException extends FixParserException {
protected static final LogonMessageIsTooLongException INSTANCE = new LogonMessageIsTooLongException("Logon message length is more than logon buffer length");
protected LogonMessageIsTooLongException(String message) {
super(message);
}
@Override
public Throwable fillInStackTrace() {
return this;
}
}
}