package bo.gotthardt.queue.rabbitmq; import bo.gotthardt.queue.MessageQueue; import bo.gotthardt.queue.MessageQueueException; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.fasterxml.jackson.databind.ObjectMapper; import com.rabbitmq.client.Channel; import com.rabbitmq.client.Consumer; import com.rabbitmq.client.GetResponse; import com.rabbitmq.client.MessageProperties; import io.dropwizard.jackson.Jackson; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import java.io.Closeable; import java.io.IOException; import java.util.function.Function; /** * RabbitMQ implementation of {@link bo.gotthardt.queue.MessageQueue}. * * To instantiate, see {@link bo.gotthardt.queue.rabbitmq.RabbitMQBundle#getQueue(String, Class)}. * * @author Bo Gotthardt */ @Slf4j class RabbitMQMessageQueue<T> implements MessageQueue<T> { private static final ObjectMapper MAPPER = Jackson.newObjectMapper(); private final Channel channel; @Getter private final String name; private final Class<T> type; private final MetricRegistry metrics; private Meter publish; RabbitMQMessageQueue(Channel channel, String name, Class<T> type, MetricRegistry metrics) { this.channel = channel; this.name = name; this.type = type; this.metrics = metrics; this.publish = metrics.meter(MetricRegistry.name("queue", type.getSimpleName(), name, "publish")); try { channel.queueDeclare(name, true, false, false, null); } catch (IOException e) { throw new MessageQueueException("Unable to declare queue.", e); } } @Override public void publish(T message) { try { channel.basicPublish("", name, MessageProperties.PERSISTENT_TEXT_PLAIN, MAPPER.writeValueAsBytes(message)); publish.mark(); if (log.isTraceEnabled()) { log.trace("Published to '{}' with data '{}'.", name, MAPPER.writeValueAsString(message)); } } catch (IOException e) { throw new MessageQueueException("Unable to publish to queue.", e); } } @Override public Closeable consume(Function<T, Void> processor) { Consumer consumer = new FunctionConsumer<>(channel, processor, type, name, metrics); try { String tag = channel.basicConsume(name, false, consumer); log.info("Set up consumer '{}' for queue '{}'.", tag, name); return () -> channel.basicCancel(tag); } catch (IOException e) { throw new MessageQueueException("Unable to set up consumer.", e); } } @Override public T consumeNext() { try { GetResponse response = channel.basicGet(name, true); return MAPPER.readValue(response.getBody(), type); } catch (IOException e) { throw new MessageQueueException("Unable to consume.", e); } } }