Package com.opengamma.transport.socket

Source Code of com.opengamma.transport.socket.SocketFudgeConnection

/**
* 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.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.OpenGammaRuntimeException;
import com.opengamma.transport.FudgeConnection;
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;

/**
* A Socket implementation of FudgeConnection
*/
public class SocketFudgeConnection extends AbstractSocketProcess implements FudgeConnection {

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

  private final FudgeContext _fudgeContext;
  private final ExecutorService _executorService;
  private final MessageBatchingWriter _writer = new MessageBatchingWriter() {

    /**
     * Prevents re-entrant calls to startIfNecessary if a message is sent as part of a connection
     * reset callback.
     */
    private boolean _isSending;

    @Override
    protected void beforeWrite() {
      if (!_isSending) {
        _isSending = true;
        try {
          startIfNecessary();
        } catch (OpenGammaRuntimeException e) {
          if (e.getCause() instanceof IOException) {
            notifyConnectionFailed((IOException) e.getCause());
            // Should we still carry on and throw the exception if the user's been given it as a callback? Maybe allow the connectionFailed callback specify which to rethrow?
          }
          throw e;
        } finally {
          _isSending = false;
        }
      }
    }

  };

  private FudgeMessageReceiver _receiver;
  private TerminatableJob _receiverJob;
  private volatile FudgeConnectionStateListener _stateListener;

  private final FudgeMessageSender _sender = new FudgeMessageSender() {

    @Override
    public FudgeContext getFudgeContext() {
      return _fudgeContext;
    }

    @Override
    public void send(FudgeMsg message) {
      try {
        _writer.write(message);
      } catch (FudgeRuntimeIOException e) {
        if (exceptionForcedByClose(e.getCause())) {
          s_logger.info("Connection terminated - message not sent");
        } else {
          s_logger.warn("I/O exception during send - {} - stopping socket to flush error", e.getCause().getMessage());
          stop();
          notifyConnectionFailed(e);
        }
        throw e;
      }
    }

  };

  /**
   * Creates a connection where received messages are processed inline with socket read operations.
   *
   * @param fudgeContext the Fudge context, not null
   */
  public SocketFudgeConnection(final FudgeContext fudgeContext) {
    ArgumentChecker.notNull(fudgeContext, "fudgeContext");
    _fudgeContext = fudgeContext;
    _executorService = null;
  }

  /**
   * Creates a connection where received messages run out of thread to the socket reader using the given
   * {@link ExecutorService}.
   *
   * @param fudgeContext the Fudge context, not null
   * @param executorService an executor service to run received messages via, not null
   */
  public SocketFudgeConnection(final FudgeContext fudgeContext, final ExecutorService executorService) {
    ArgumentChecker.notNull(fudgeContext, "fudgeContext");
    ArgumentChecker.notNull(executorService, "executorService");
    _fudgeContext = fudgeContext;
    _executorService = executorService;
  }

  /**
   * Sets a delay before flushing data messages to allow adjacent messages to be coalesced. Only useful if the
   * message sender is being used concurrently.
   *
   * @param microseconds the time to wait before flushing, or {@code 0} to flush immediately after a message (or coalesced group)
   */
  public void setFlushDelay(final int microseconds) {
    _writer.setFlushDelay(microseconds);
  }

  /**
   * Note that the message sender may be called concurrently. All messages will be sent from a single thread
   * with others returning immediately. Thus successful completion of a {@link FudgeMessageSender#send} does
   * not guarantee message arrival or that it has even been (or will be) passed to the transport.
   *
   * @return the Fudge message sender component of the connection
   */
  @Override
  public FudgeMessageSender getFudgeMessageSender() {
    return _sender;
  }

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

  @Override
  protected void socketOpened(Socket socket, BufferedOutputStream os, BufferedInputStream is) {
    final FudgeMsgReader reader = _fudgeContext.createMessageReader(is);
    _writer.setFudgeMsgWriter(_fudgeContext, os);
    _receiverJob = new TerminatableJob() {

      @Override
      protected void runOneCycle() {
        final FudgeMsgEnvelope envelope;
        try {
          envelope = reader.nextMessageEnvelope();
        } catch (FudgeRuntimeIOException e) {
          if (exceptionForcedByClose(e.getCause())) {
            s_logger.info("Connection terminated");
          } else {
            s_logger.warn("I/O exception during recv - {} - stopping socket to flush error", e.getCause());
            stop();
            notifyConnectionFailed(e);
          }
          return;
        }
        if (envelope == null) {
          s_logger.info("Nothing available on stream. Terminating connection");
          stop();
          return;
        }
        final FudgeMessageReceiver receiver = _receiver;
        if (receiver != null) {
          if (_executorService != null) {
            _executorService.execute(new Runnable() {
              @Override
              public void run() {
                dispatch(receiver, envelope);
              }
            });
          } else {
            dispatch(receiver, envelope);
          }
        }
      }

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

    };
    final Thread thread = new Thread(_receiverJob, "Incoming " + socket.getRemoteSocketAddress());
    thread.setDaemon(true);
    thread.start();
    // We don't keep hold of the thread as we're never going to join it; terminating the socket will let cause it to stop, finish and be GCd
    final FudgeConnectionStateListener stateListener = _stateListener;
    if (stateListener != null) {
      stateListener.connectionReset(this);
    }
  }

  @Override
  protected void socketClosed() {
    _writer.setFudgeMsgWriter(null);
    _receiverJob.terminate();
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append("FudgeConnection to ");
    sb.append(getInetAddresses());
    sb.append(':');
    sb.append(getPortNumber());
    if (!isRunning()) {
      sb.append(" (not connected)");
    }
    return sb.toString();
  }

  @Override
  public void setConnectionStateListener(FudgeConnectionStateListener listener) {
    _stateListener = listener;
  }
 
  protected void notifyConnectionFailed(Exception e) {
    final FudgeConnectionStateListener stateListener = _stateListener;
    if (stateListener != null) {
      try {
        stateListener.connectionFailed(SocketFudgeConnection.this, e);
      } catch (Exception e2) {
        s_logger.warn("Error notifying state listener of connection failure", e2);
      }
    }
  }

}
TOP

Related Classes of com.opengamma.transport.socket.SocketFudgeConnection

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.