Package com.opengamma.transport.amqp

Source Code of com.opengamma.transport.amqp.AmqpByteArrayRequestSender

/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.transport.amqp;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Address;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.Lifecycle;

import com.google.common.base.Charsets;
import com.opengamma.transport.ByteArrayMessageReceiver;
import com.opengamma.transport.ByteArrayRequestSender;
import com.opengamma.util.ArgumentChecker;
import com.rabbitmq.client.AMQP.Queue;
import com.rabbitmq.client.Channel;

/**
* RabbitMQ based sender for AMQP.
*/
public class AmqpByteArrayRequestSender extends AbstractAmqpByteArraySender implements ByteArrayRequestSender, MessageListener, Lifecycle {

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

  private final String _replyToQueue;
  private final AtomicLong _correlationIdGenerator = new AtomicLong();
  private final long _timeout;
  private final ScheduledExecutorService _executor;
  private final SimpleMessageListenerContainer _container;
  private final ConcurrentHashMap<String, ByteArrayMessageReceiver> _correlationId2MessageReceiver = new ConcurrentHashMap<String, ByteArrayMessageReceiver>();

  /**
   * Creates an instance.
   *
   * @param connectionFactory  the connection factory, not null
   * @param exchange  the exchange, not null
   * @param routingKey  the routing key, not null
   */
  public AmqpByteArrayRequestSender(ConnectionFactory connectionFactory, String exchange, String routingKey) {
    this(connectionFactory, 30000, Executors.newSingleThreadScheduledExecutor(), exchange, routingKey);
  }

  /**
   * Creates an instance.
   *
   * @param connectionFactory  the connection factory, not null
   * @param timeout  the timeout, positive
   * @param executor  the executor, not null
   * @param exchange  the exchange, not null
   * @param routingKey  the routing key, not null
   */
  public AmqpByteArrayRequestSender(
      ConnectionFactory connectionFactory,
      long timeout,
      ScheduledExecutorService executor,
      String exchange,
      String routingKey) {
    super(new RabbitTemplate(connectionFactory), exchange, routingKey);
    ArgumentChecker.notNull(connectionFactory, "connectionFactory");   
    ArgumentChecker.notNull(executor, "executor");
   
    if (timeout <= 0) {
      throw new IllegalArgumentException("Timeout must be positive");
    }
    _timeout = timeout;
    _executor = executor;
   
    try {
      Connection connection = connectionFactory.createConnection();
      Channel channel = connection.createChannel(false);
     
      Queue.DeclareOk declareResult = channel.queueDeclare();
      _replyToQueue = declareResult.getQueue();
     
      channel.queueBind(_replyToQueue, getExchange(), _replyToQueue);
      connection.close();
     
    } catch (IOException e) {
      throw new RuntimeException("Failed to create reply to queue", e);
    }
   
    _container = new SimpleMessageListenerContainer();
    _container.setConnectionFactory(connectionFactory);
    _container.setQueueNames(_replyToQueue);
    _container.setMessageListener(this);
  }

  //-------------------------------------------------------------------------
  /**
   * Gets the reply-to queue.
   *
   * @return the queue, not null
   */
  public String getReplyToQueue() {
    return _replyToQueue;
  }

  //-------------------------------------------------------------------------
  @Override
  public void sendRequest(final byte[] request, final ByteArrayMessageReceiver responseReceiver) {
    s_logger.debug("Dispatching request of size {} to exchange {}, routing key = {}",
        new Object[] {request.length, getExchange(), getRoutingKey()});
   
    getAmqpTemplate().send(getExchange(), getRoutingKey(), createMessage(request, responseReceiver));
  }

  /**
   * Creates the message.
   *
   * @param request  the request, not null
   * @param responseReceiver  the receiver, not null
   * @return the message, not null
   */
  private Message createMessage(final byte[] request, final ByteArrayMessageReceiver responseReceiver) {
    MessageProperties properties = new MessageProperties();
    Address replyTo = new Address(ExchangeTypes.DIRECT, getExchange(), getReplyToQueue());
    properties.setReplyToAddress(replyTo);
   
    final String correlationId = getReplyToQueue() + "-" + _correlationIdGenerator.getAndIncrement();
    byte[] correlationIdBytes = correlationId.getBytes(Charsets.UTF_8);
    properties.setCorrelationId(correlationIdBytes);
   
    Message message = new Message(request, properties);
   
    _correlationId2MessageReceiver.put(correlationId, responseReceiver);
   
    // Make sure the map stays clean if no response is received before timeout occurs.
    // It would be nice if AmqpTemplate had a receive() method with a timeout parameter.
    _executor.schedule(new Runnable() {
      @Override
      public void run() {
        ByteArrayMessageReceiver receiver = _correlationId2MessageReceiver.remove(correlationId);
        if (receiver != null) {
          s_logger.error("Timeout reached while waiting for a response to send to {}", responseReceiver);
        }
      }
    }, _timeout, TimeUnit.MILLISECONDS);
   
    return message;
  }

  //-------------------------------------------------------------------------
  @Override
  public void start() {
    _container.start();
  }

  @Override
  public void stop() {
    _container.stop();
  }

  @Override
  public boolean isRunning() {
    return _container.isRunning();
  }

  //-------------------------------------------------------------------------
  @Override
  public void onMessage(Message message) {
    byte[] correlationIdBytes = message.getMessageProperties().getCorrelationId();
    if (correlationIdBytes == null) {
      s_logger.error("Got reply with no correlation ID: {} ", message);
      return;
    }
   
    String correlationId = new String(correlationIdBytes, Charsets.UTF_8);
    ByteArrayMessageReceiver receiver = _correlationId2MessageReceiver.remove(correlationId);
    if (receiver != null) {
      receiver.messageReceived(message.getBody());     
    } else {
      s_logger.warn("No receiver for message: {}", message);     
    }
  }

}
TOP

Related Classes of com.opengamma.transport.amqp.AmqpByteArrayRequestSender

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.