/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.transport.socket; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.ExecutorService; import org.fudgemsg.FudgeContext; import org.fudgemsg.FudgeMsg; import org.fudgemsg.FudgeMsgEnvelope; import org.fudgemsg.mapping.FudgeDeserializer; import org.fudgemsg.wire.FudgeMsgReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.transport.FudgeRequestReceiver; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.TerminatableJob; import com.opengamma.util.TerminatableJobContainer; /** * Receives Fudge encoded requests from raw sockets and dispatches them * to a {@link FudgeRequestReceiver} and returns results over the same socket. * */ public class ServerSocketFudgeRequestDispatcher extends AbstractServerSocketProcess { private static final Logger s_logger = LoggerFactory.getLogger(ServerSocketFudgeRequestDispatcher.class); private final FudgeRequestReceiver _underlying; private final FudgeContext _fudgeContext; private final TerminatableJobContainer _messageReceiveJobs = new TerminatableJobContainer(); public ServerSocketFudgeRequestDispatcher(final FudgeRequestReceiver underlying, final FudgeContext fudgeContext) { ArgumentChecker.notNull(underlying, "underlying"); ArgumentChecker.notNull(fudgeContext, "fudgeContext"); _underlying = underlying; _fudgeContext = fudgeContext; } public ServerSocketFudgeRequestDispatcher(final FudgeRequestReceiver underlying, final FudgeContext fudgeContext, final ExecutorService executorService) { super(executorService); ArgumentChecker.notNull(underlying, "underlying"); ArgumentChecker.notNull(fudgeContext, "fudgeContext"); _underlying = underlying; _fudgeContext = fudgeContext; } /** * @return the underlying */ public FudgeRequestReceiver getUnderlying() { return _underlying; } /** * @return the fudgeContext */ public FudgeContext getFudgeContext() { return _fudgeContext; } @Override protected synchronized void socketOpened(Socket socket) { ArgumentChecker.notNull(socket, "socket"); s_logger.info("Opened socket to remote side {}", socket.getRemoteSocketAddress()); InputStream is; OutputStream os; try { is = socket.getInputStream(); os = socket.getOutputStream(); } catch (IOException e) { s_logger.warn("Unable to open InputStream and OutputStream for socket {}", new Object[] {socket}, e); return; } RequestDispatchJob job = new RequestDispatchJob(socket, new BufferedInputStream(is), new BufferedOutputStream(os)); _messageReceiveJobs.addJobAndStartThread(job, "Request Dispatch " + socket.getRemoteSocketAddress()); } @Override protected void cleanupPreAccept() { _messageReceiveJobs.cleanupTerminatedInstances(); } @Override public void stop() { super.stop(); _messageReceiveJobs.terminateAll(); } private class RequestDispatchJob extends TerminatableJob { private final Socket _socket; private final FudgeMsgReader _reader; private final MessageBatchingWriter _writer; // NOTE kirk 2010-05-12 -- Have to pass in the InputStream and OutputStream explicitly so that // we can force the IOException catch up above. public RequestDispatchJob(Socket socket, InputStream inputStream, OutputStream outputStream) { ArgumentChecker.notNull(socket, "Socket"); ArgumentChecker.notNull(inputStream, "inputStream"); ArgumentChecker.notNull(outputStream, "outputStream"); _socket = socket; _reader = getFudgeContext().createMessageReader(inputStream); _writer = new MessageBatchingWriter(getFudgeContext(), outputStream); } @Override protected void runOneCycle() { if (_socket.isClosed()) { terminate(); return; } final FudgeMsgEnvelope envelope; try { envelope = _reader.nextMessageEnvelope(); } catch (Exception e) { s_logger.warn("Unable to read message from underlying stream - terminating connection", e); terminate(); return; } if (envelope == null) { s_logger.info("Nothing available on the stream. Returning and terminating."); terminate(); return; } final ExecutorService executorService = getExecutorService(); if (executorService != null) { executorService.execute(new Runnable() { @Override public void run() { dispatch(envelope); } }); } else { dispatch(envelope); } } private void dispatch(final FudgeMsgEnvelope envelope) { FudgeMsg response = null; try { s_logger.debug("Received message with {} fields. Dispatching to underlying.", envelope.getMessage().getNumFields()); response = getUnderlying().requestReceived(new FudgeDeserializer(_fudgeContext), envelope); } catch (Exception e) { s_logger.warn("Unable to dispatch message to underlying receiver", e); return; } if (response != null) { try { s_logger.debug("Sending response with {} fields.", response.getNumFields()); _writer.write(response); } catch (Exception e) { s_logger.warn("Unable to dispatch response to client - terminating connection", e); terminate(); } } } @Override public void terminate() { if (!_socket.isClosed()) { try { s_logger.debug("Closing socket"); _socket.close(); } catch (IOException ex) { s_logger.warn("Couldn't close socket to release blocked I/O", ex.getMessage()); } } super.terminate(); } } }