/**
* Copyright 2011 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Felipe Oliveira (http://mashup.fm)
*
*/
package play.modules.rabbitmq.consumer;
import play.Logger;
import play.Play;
import play.jobs.Job;
import play.modules.rabbitmq.RabbitMQPlugin;
import play.modules.rabbitmq.util.ExceptionUtil;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.QueueingConsumer;
// TODO: Auto-generated Javadoc
/**
* The Class RabbitMQConsumerJob.
*
* @param <T>
* the generic type
*/
public abstract class RabbitMQConsumer<T> extends Job<T> {
/**
* This is the default value defined by "rabbitmq.retries" on
* application.conf (please override if you need a new value)
*/
public static int retries = RabbitMQPlugin.retries();
/**
* Consume.
*
* @param message
* the message
*/
protected abstract void consume(T message);
/**
* Creates the channel.
*
* @param plugin
* the plugin
* @return the channel
* @throws Exception
* the exception
*/
protected Channel createChannel(RabbitMQPlugin plugin) throws Exception {
// Get Plugin
Channel channel = plugin.createChannel(this.queue(), this.routingKey());
return channel;
}
/**
* Creates the channel.
*
* @param channel
* the channel
* @param plugin
* the plugin
* @return the channel
* @throws Exception
* the exception
*/
protected QueueingConsumer createConsumer(Channel channel, RabbitMQPlugin plugin) throws Exception {
// Get Plugin
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(this.queue(), plugin.isAutoAck(), consumer);
// Log Debug
Logger.info("RabbitMQ Consumer - Channel: %s, Consumer: %s " + channel, consumer);
// Return Channel
return consumer;
}
/**
* Let our baby go!.
*
* @see play.jobs.Job#doJob()
*/
@Override
public void doJob() {
this.goGetHerSon();
}
/**
* Gets the message type.
*
* @return the message type
*/
protected abstract Class getMessageType();
/**
* Go get her son.
*/
private void goGetHerSon() {
// Get Plugin
RabbitMQPlugin plugin = Play.plugin(RabbitMQPlugin.class);
// Define Channel
Channel channel = null;
QueueingConsumer consumer = null;
Long deliveryTag = null;
// Get Channel
while (true) {
// Log Debug
Logger.info("Entering main loop on consumer: " + this);
// Are Consumers Running?
boolean active = RabbitMQPlugin.areConsumersActive();
// Only do work if consumers are running
if (active) {
try {
// Create Channel
if (channel == null || (channel != null && channel.isOpen() == false)) {
consumer = null;
channel = this.createChannel(plugin);
}
// Create Consumer
if (consumer == null) {
consumer = this.createConsumer(channel, plugin);
}
// Get Task
QueueingConsumer.Delivery task = consumer.nextDelivery();
// Date Night
if ((task != null) && (task.getBody() != null)) {
try {
// Fire job that will pass the message to the
// consumer,
// ack the queue and do the retry logic
deliveryTag = task.getEnvelope().getDeliveryTag();
T message = this.toObject(task.getBody());
new RabbitMQMessageConsumerJob(channel, deliveryTag, this.queue(), this, message, this.retries()).doJobWithResult();
} catch (Throwable t) {
// Handle Exception
Logger.error(ExceptionUtil.getStackTrace(t));
}
}
} catch (Throwable t) {
Logger.error("Error creating consumer channel to RabbitMQ, retrying in a few seconds. Exception: %s", ExceptionUtil.getStackTrace(t));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Logger.error(ExceptionUtil.getStackTrace(t));
}
} finally {
if (channel != null) {
// Now tell Daddy everything is cool
try {
if (deliveryTag != null && channel.isOpen()) {
channel.basicAck(deliveryTag, false);
}
} catch (Throwable e) {
Logger.error(ExceptionUtil.getStackTrace("Error doing a basicAck for tag: " + deliveryTag, e));
}
try {
if (channel.getConnection() != null && channel.getConnection().isOpen()) {
channel.getConnection().close();
}
if (channel.isOpen() == true) {
channel.close();
}
} catch (Throwable t) {
Logger.error(ExceptionUtil.getStackTrace(t));
} finally {
channel = null;
}
}
}
} else {
Logger.warn("RabbitMQ consumers are paused and napping for 10 secs...");
try {
Thread.sleep(10000);
} catch (Throwable t) {
}
}
}
}
/**
* Gets the queue name.
*
* @return the queue name
*/
protected abstract String queue();
/**
* Routing key.
*
* @param t
* the t
* @return the string
*/
protected String routingKey() {
return this.queue();
}
/**
* Retries.
*
* @return the int
*/
protected int retries() {
return retries;
}
/**
* To object.
*
* @param bytes
* the bytes
* @return the object
* @throws Exception
* the exception
*/
protected T toObject(byte[] bytes) throws Exception {
return (T) RabbitMQPlugin.mapper().getObject(this.getMessageType(), bytes);
}
}