Java tutorial
/** * 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); } } }