/* * 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.tools; import com.lmax.disruptor.BlockingWaitStrategy; import com.lmax.disruptor.ExceptionHandler; import org.f1x.SessionIDBean; import org.f1x.api.FixInitiatorSettings; import org.f1x.api.FixVersion; import org.f1x.api.session.SessionID; import org.f1x.io.OutputChannel; import org.f1x.io.RingBufferStreamChannel; import org.f1x.io.disruptor.ByteRing; import org.f1x.io.disruptor.MessageProcessorPool; import org.f1x.io.disruptor.RingBufferBlockProcessor; import org.f1x.io.socket.RingBuffer2StreamProcessor; import org.f1x.v1.FixSessionInitiator; import java.io.IOException; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Extension of FixInitiator that uses ring buffer for asynchronous send. */ public class BufferedFixInitiator extends FixSessionInitiator { private final Executor executor; private final ByteRing ring; private MessageProcessorPool processorPool; private final ExceptionHandler exceptionHandler = new ExceptionHandler () { @Override public void handleEventException(Throwable e, long l, Object o) { disconnect("Error " + e.getMessage()); } @Override public void handleOnStartException(Throwable e) { disconnect("Error " + e.getMessage()); } @Override public void handleOnShutdownException(Throwable e) { disconnect("Error " + e.getMessage()); } }; public BufferedFixInitiator(String host, int port, FixVersion fixVersion, SessionID sessionID, int queueSize, FixInitiatorSettings settings) { super(host, port, fixVersion, sessionID, settings); ring = new ByteRing (queueSize, new BlockingWaitStrategy()); //TODO: Use BusySpinWaitStrategy? executor = Executors.newCachedThreadPool(); } @Override protected OutputChannel getOutputChannel(Socket socket) throws IOException { if (processorPool != null) throw new IllegalStateException("Previous processor pool still uses Ring Buffer"); //ring.reset(); //TODO: Not the best thing to create all that each time we reconnect? RingBufferBlockProcessor socketSender = new RingBuffer2StreamProcessor(socket.getOutputStream(), exceptionHandler); //TODO: RingBufferBlockProcessor logger = BufferLogger.createLogger(new File("d:\\fixlog.bin"), 8192, exceptionHandler); processorPool = new MessageProcessorPool (ring, ring.newBarrier(), exceptionHandler, socketSender); //,logger ring.addGatingSequences(processorPool.getWorkerSequences()); processorPool.start(executor); return new RingBufferStreamChannel(ring); } @Override public void disconnect(CharSequence cause) { super.disconnect(cause); if (processorPool != null) { processorPool.halt(); //TODO: special mode in which we do processor.drainAndHalt() ? processorPool = null; } //TODO: Handle data that is still in the buffer } public static void main (String [] args) throws InterruptedException, IOException { final String host = (args.length > 0) ? args[0] : "192.168.1.105"; final int port = (args.length > 1) ? Integer.parseInt(args[1]) : 2508; final int queueSize = 64*1024; new BufferedFixInitiator(host, port, FixVersion.FIX44, new SessionIDBean("CLIENT", "SERVER"), queueSize, new FixInitiatorSettings()).run(); } }