Package com.opengamma.transport.socket

Source Code of com.opengamma.transport.socket.ServerSocketFudgeConnectionReceiver$StrictBufferedOutputStream

/**
* 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.FilterOutputStream;
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.wire.FudgeMsgReader;
import org.fudgemsg.wire.FudgeRuntimeIOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.opengamma.transport.FudgeConnection;
import com.opengamma.transport.FudgeConnectionReceiver;
import com.opengamma.transport.FudgeConnectionStateListener;
import com.opengamma.transport.FudgeMessageReceiver;
import com.opengamma.transport.FudgeMessageSender;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.TerminatableJob;
import com.opengamma.util.TerminatableJobContainer;

/**
* Listens on a ServerSocket and passes FudgeConnections to an underlying FudgeConnectionReceiver
*/
public class ServerSocketFudgeConnectionReceiver extends AbstractServerSocketProcess {

  private static final Logger s_logger = LoggerFactory.getLogger(ServerSocketFudgeConnectionReceiver.class);

  private final FudgeConnectionReceiver _underlying;
  private final FudgeContext _fudgeContext;

  private final TerminatableJobContainer _connectionJobs = new TerminatableJobContainer();

  private boolean _lazyFudgeMsgReads;

  public ServerSocketFudgeConnectionReceiver(final FudgeContext fudgeContext, final FudgeConnectionReceiver underlying) {
    _fudgeContext = fudgeContext;
    _underlying = underlying;
  }

  public ServerSocketFudgeConnectionReceiver(final FudgeContext fudgeContext, final FudgeConnectionReceiver underlying,
      final ExecutorService executorService) {
    super(executorService);
    _fudgeContext = fudgeContext;
    _underlying = underlying;
  }

  public FudgeContext getFudgeContext() {
    return _fudgeContext;
  }

  public FudgeConnectionReceiver getUnderlying() {
    return _underlying;
  }

  public void setLazyFudgeMsgReads(final boolean lazyFudgeMsgReads) {
    _lazyFudgeMsgReads = lazyFudgeMsgReads;
  }

  public boolean isLazyFudgeMsgReads() {
    return _lazyFudgeMsgReads;
  }

  @Override
  protected 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;
    }
    final ConnectionJob job = new ConnectionJob(socket, is, os);
    _connectionJobs.addJobAndStartThread(job, "Connection dispatch " + socket.getRemoteSocketAddress());
  }

  @Override
  protected void cleanupPreAccept() {
    _connectionJobs.cleanupTerminatedInstances();
  }

  @Override
  public void stop() {
    super.stop();
    _connectionJobs.terminateAll();
  }

  /**
   * An output stream with buffered behavior that only writes to the underlying stream when
   * the buffer is full or when a flush takes place. This is different to the behavior of
   * {@link BufferedOutputStream} which may make partial writes if given data larger than
   * its internal buffer. This class is best used over network transports that have an optimal
   * message size.
   * <p>
   * Note that it is not thread-safe.
   */
  private static class StrictBufferedOutputStream extends FilterOutputStream {

    // TODO: move this out into Util and use for other network buffered outputs
    // TODO: set the packet size from the MTU details of the network used

    private final byte[] _buffer;
    private int _bytes;

    public StrictBufferedOutputStream(final OutputStream out, final int bytes) {
      super(out);
      ArgumentChecker.isTrue(bytes > 0, "bytes");
      _buffer = new byte[bytes];
    }

    public StrictBufferedOutputStream(final OutputStream out) {
      this(out, 1500);
    }

    @Override
    public void write(final int b) throws IOException {
      _buffer[_bytes++] = (byte) b;
      if (_bytes == _buffer.length) {
        out.write(_buffer);
        _bytes = 0;
      }
    }

    @Override
    public void write(final byte[] b, int ofs, int len) throws IOException {
      if (_bytes > 0) {
        final int room = _buffer.length - _bytes;
        if (room < len) {
          // Start of packet fits in buffer
          System.arraycopy(b, ofs, _buffer, _bytes, room);
          out.write(_buffer);
          _bytes = 0;
          ofs += room;
          len -= room;
        } else {
          System.arraycopy(b, ofs, _buffer, _bytes, len);
          if (room == len) {
            // Packet just fits in buffer
            System.arraycopy(b, ofs, _buffer, _bytes, len);
            out.write(_buffer);
            _bytes = 0;
          } else {
            // Packet fits entirely in buffer
            _bytes += len;
          }
          return;
        }
      }
      while (len >= _buffer.length) {
        // Part of the packet can be sent directly
        out.write(b, ofs, _buffer.length);
        ofs += _buffer.length;
        len -= _buffer.length;
      }
      if (len > 0) {
        // Part of the packet is left
        System.arraycopy(b, ofs, _buffer, _bytes, len);
        _bytes += len;
      }
    }

    @Override
    public void flush() throws IOException {
      if (_bytes > 0) {
        out.write(_buffer, 0, _bytes);
        _bytes = 0;
        out.flush();
      }
    }

  }

  private class ConnectionJob extends TerminatableJob {

    private final Socket _socket;
    private final FudgeMsgReader _reader;
    private final FudgeMessageSender _sender;
    private final FudgeConnection _connection;
    private FudgeMessageReceiver _receiver;
    private volatile FudgeConnectionStateListener _listener;

    ConnectionJob(final Socket socket, final InputStream is, final OutputStream os) {
      _socket = socket;
      _reader = getFudgeContext().createMessageReader(new BufferedInputStream(is));
      _reader.setLazyReads(isLazyFudgeMsgReads());
      _sender = new FudgeMessageSender() {

        private final MessageBatchingWriter _writer = new MessageBatchingWriter(getFudgeContext(),
            new StrictBufferedOutputStream(os));

        @Override
        public FudgeContext getFudgeContext() {
          return ServerSocketFudgeConnectionReceiver.this.getFudgeContext();
        }

        @Override
        public void send(FudgeMsg message) {
          try {
            _writer.write(message);
          } catch (FudgeRuntimeIOException e) {
            terminateWithError("Unable to write message to underlying stream - terminating connection", e.getCause());
            throw e;
          }
        }

        @Override
        public String toString() {
          return _socket.getRemoteSocketAddress().toString();
        }

      };
      _connection = new FudgeConnection() {

        @Override
        public FudgeMessageSender getFudgeMessageSender() {
          return _sender;
        }

        @Override
        public void setFudgeMessageReceiver(FudgeMessageReceiver receiver) {
          _receiver = receiver;
        }

        @Override
        public String toString() {
          final StringBuilder sb = new StringBuilder();
          sb.append("FudgeConnection from ");
          sb.append(_socket.getRemoteSocketAddress().toString());
          return sb.toString();
        }

        @Override
        public void setConnectionStateListener(final FudgeConnectionStateListener listener) {
          _listener = listener;
        }

      };
    }

    @Override
    protected void runOneCycle() {
      if (_socket.isClosed()) {
        terminate();
        return;
      }
      final FudgeMsgEnvelope envelope;
      try {
        envelope = _reader.nextMessageEnvelope();
      } catch (FudgeRuntimeIOException e) {
        terminateWithError("Unable to read message from underlying stream - terminating connection", e.getCause());
        return;
      }
      if (envelope == null) {
        terminateWithError("Nothing available on stream - terminating connection", null);
        return;
      }
      final FudgeMessageReceiver receiver = _receiver;
      if (receiver != null) {
        final ExecutorService executorService = getExecutorService();
        if (executorService != null) {
          executorService.execute(new Runnable() {
            @Override
            public void run() {
              dispatchReceiver(receiver, envelope);
            }
          });
        } else {
          dispatchReceiver(receiver, envelope);
        }
      } else {
        try {
          getUnderlying().connectionReceived(getFudgeContext(), envelope, _connection);
        } catch (Exception e) {
          s_logger.warn("Unable to dispatch connection to receiver", e);
        }
      }
    }

    private void dispatchReceiver(final FudgeMessageReceiver receiver, final FudgeMsgEnvelope envelope) {
      try {
        receiver.messageReceived(getFudgeContext(), envelope);
      } catch (Exception e) {
        s_logger.warn("Unable to dispatch message to receiver", e);
      }
    }

    private void terminateWithError(final String errorMessage, final Exception cause) {
      if (cause != null) {
        if (exceptionForcedByClose(cause)) {
          s_logger.info("Connection terminated");
        } else {
          s_logger.warn(errorMessage, cause);
          terminate();
        }
      } else {
        s_logger.info(errorMessage);
        terminate();
      }
      final FudgeConnectionStateListener listener = _listener;
      if (listener != null) {
        listener.connectionFailed(_connection, cause);
      }
    }

    @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();
    }

  }

}
TOP

Related Classes of com.opengamma.transport.socket.ServerSocketFudgeConnectionReceiver$StrictBufferedOutputStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.