/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.f1x.v1;
import org.f1x.api.FixAcceptorSettings;
import org.f1x.api.FixVersion;
import org.f1x.api.session.SessionID;
import org.f1x.api.session.SessionStatus;
import org.f1x.v1.schedule.SessionTimes;
import org.gflogger.GFLog;
import org.gflogger.GFLogFactory;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* FIX Communicator that plays FIX Accepts role for inbound FIX connections
*/
public class FixSessionAcceptor extends FixSocketCommunicator {
protected static final GFLog LOGGER = GFLogFactory.getLog(FixSessionAcceptor.class);
private final SessionID sessionID;
private final AtomicBoolean running = new AtomicBoolean();
public FixSessionAcceptor(FixVersion fixVersion, SessionID sessionID, FixAcceptorSettings settings) {
super(fixVersion, settings);
this.sessionID = sessionID;
}
@Override
public SessionID getSessionID() {
return sessionID;
}
@Override
public final void run() {
run0(null, 0);
}
// Mockito does not mock final methods.
public void run(byte[] logonBuffer, int length) {
checkLogonBuffer(logonBuffer, length);
run0(logonBuffer, length);
}
private void run0(byte[] logonBuffer, int length) {
if (!running.compareAndSet(false, true))
throw new IllegalStateException("Already running");
try {
init();
try {
work(logonBuffer, length);
} finally {
destroy();
}
} finally {
//TODO: Find a better way to recycle state
closeInProgress = false;
running.set(false);
}
}
protected void work(byte[] logonBuffer, int length) {
assertSessionStatus(SessionStatus.SocketConnected);
boolean started = startSession();
if (!started) {
disconnect("Session is down");
return;
}
try {
processInboundMessages(logonBuffer, length);
} catch (Throwable e) {
LOGGER.error().append("Terminating FIX Acceptor due to error: ").append(e).commit();
} finally {
endSession();
}
assertSessionStatus(SessionStatus.Disconnected);
}
@Override
public FixAcceptorSettings getSettings() {
return (FixAcceptorSettings) super.getSettings();
}
protected boolean startSession() {
if (schedule != null) {
final long now = timeSource.currentTimeMillis();
SessionTimes sessionTimes = schedule.getCurrentSessionTimes(now);
final long sessionStart = sessionTimes.getStart();
if (now < sessionStart)
return false;
final long lastConnectionTime = sessionState.getLastConnectionTimestamp();
if (lastConnectionTime < sessionStart) {
sessionState.resetNextSeqNums();
messageStore.clean();
}
long sessionEnd = sessionTimes.getEnd();
scheduleSessionEnd(sessionEnd - now);
}
//TODO: Do this when we transition to ApplicationConnected
scheduleSessionMonitoring();
return true;
}
protected void endSession() {
unscheduleSessionEnd();
//TODO: Do this when we transition from ApplicationConnected
unscheduleSessionMonitoring();
}
private static void checkLogonBuffer(byte[] logonBuffer, int length) {
if (logonBuffer == null)
throw new NullPointerException("logonBuffer == null");
if (length < 0 || logonBuffer.length < length)
throw new IllegalArgumentException("length < 0 || logonBuffer.length < length");
}
}